summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/BrowserStreamChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/ipc/BrowserStreamChild.cpp')
-rw-r--r--dom/plugins/ipc/BrowserStreamChild.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/dom/plugins/ipc/BrowserStreamChild.cpp b/dom/plugins/ipc/BrowserStreamChild.cpp
new file mode 100644
index 0000000000..21e6706ba3
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/plugins/BrowserStreamChild.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+
+namespace mozilla::plugins {
+
+BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ StreamNotifyChild* notifyData,
+ const nsCString& headers)
+ : mInstance(instance),
+ mStreamStatus(kStreamOpen),
+ mDestroyPending(NOT_DESTROYED),
+ mNotifyPending(false),
+ mInstanceDying(false),
+ mState(CONSTRUCTING),
+ mURL(url),
+ mHeaders(headers),
+ mStreamNotify(notifyData),
+ mDeliveryTracker(this) {
+ PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s)", FULLFUNCTION, url.get(), length,
+ lastmodified, (void*)notifyData, headers.get()));
+
+ AssertPluginThread();
+
+ memset(&mStream, 0, sizeof(mStream));
+ mStream.ndata = static_cast<AStream*>(this);
+ mStream.url = NullableStringGet(mURL);
+ mStream.end = length;
+ mStream.lastmodified = lastmodified;
+ mStream.headers = NullableStringGet(mHeaders);
+ if (notifyData) {
+ mStream.notifyData = notifyData->mClosure;
+ notifyData->SetAssociatedStream(this);
+ }
+}
+
+NPError BrowserStreamChild::StreamConstructed(const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype) {
+ NPError rv = NPERR_NO_ERROR;
+
+ *stype = NP_NORMAL;
+ rv = mInstance->mPluginIface->newstream(
+ &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
+ &mStream, seekable, stype);
+
+ // NP_NORMAL is the only permissible stream type
+ if (*stype != NP_NORMAL) {
+ rv = NPERR_INVALID_PARAM;
+ // The plugin thinks the stream is alive, so we kill it explicitly
+ (void)mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream,
+ NPRES_NETWORK_ERR);
+ }
+
+ if (rv != NPERR_NO_ERROR) {
+ mState = DELETING;
+ if (mStreamNotify) {
+ mStreamNotify->SetAssociatedStream(nullptr);
+ mStreamNotify = nullptr;
+ }
+ } else {
+ mState = ALIVE;
+ }
+
+ return rv;
+}
+
+BrowserStreamChild::~BrowserStreamChild() {
+ NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::RecvWrite(const int32_t& offset,
+ const uint32_t& newlength,
+ const Buffer& data) {
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ MOZ_CRASH("Unexpected state: received data after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus) return IPC_OK();
+
+ mStream.end = newlength;
+
+ NS_ASSERTION(data.Length() > 0, "Empty data");
+
+ PendingData* newdata = mPendingData.AppendElement();
+ newdata->offset = offset;
+ newdata->data = data;
+ newdata->curpos = 0;
+
+ EnsureDeliveryPending();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::RecvNPP_DestroyStream(
+ const NPReason& reason) {
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (ALIVE != mState)
+ MOZ_CRASH("Unexpected state: recevied NPP_DestroyStream twice?");
+
+ mState = DYING;
+ mDestroyPending = DESTROY_PENDING;
+ if (NPRES_DONE != reason) mStreamStatus = reason;
+
+ EnsureDeliveryPending();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::Recv__delete__() {
+ AssertPluginThread();
+
+ if (DELETING != mState) MOZ_CRASH("Bad state, not DELETING");
+
+ return IPC_OK();
+}
+
+void BrowserStreamChild::EnsureDeliveryPending() {
+ MessageLoop::current()->PostTask(
+ mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
+}
+
+void BrowserStreamChild::Deliver() {
+ while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
+ if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
+ SetSuspendedTimer();
+ return;
+ }
+ }
+ ClearSuspendedTimer();
+
+ NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(),
+ "Exit out of the data-delivery loop with pending data");
+ mPendingData.Clear();
+
+ if (DESTROY_PENDING == mDestroyPending) {
+ mDestroyPending = DESTROYED;
+ if (mState != DYING) MOZ_CRASH("mDestroyPending but state not DYING");
+
+ NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!");
+ if (kStreamOpen == mStreamStatus) mStreamStatus = NPRES_DONE;
+
+ (void)mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream,
+ mStreamStatus);
+ }
+ if (DESTROYED == mDestroyPending && mNotifyPending) {
+ NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
+
+ mNotifyPending = false;
+ mStreamNotify->NPP_URLNotify(mStreamStatus);
+ delete mStreamNotify;
+ mStreamNotify = nullptr;
+ }
+ if (DYING == mState && DESTROYED == mDestroyPending && !mStreamNotify &&
+ !mInstanceDying) {
+ SendStreamDestroyed();
+ mState = DELETING;
+ }
+}
+
+bool BrowserStreamChild::DeliverPendingData() {
+ if (mState != ALIVE && mState != DYING) MOZ_CRASH("Unexpected state");
+
+ NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending");
+
+ while (mPendingData[0].curpos <
+ static_cast<int32_t>(mPendingData[0].data.Length())) {
+ int32_t r =
+ mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
+ if (kStreamOpen != mStreamStatus) return false;
+ if (0 == r) // plugin wants to suspend delivery
+ return true;
+
+ r = mInstance->mPluginIface->write(
+ &mInstance->mData, &mStream,
+ mPendingData[0].offset + mPendingData[0].curpos, // offset
+ mPendingData[0].data.Length() - mPendingData[0].curpos, // length
+ const_cast<char*>(mPendingData[0].data.BeginReading() +
+ mPendingData[0].curpos));
+ if (kStreamOpen != mStreamStatus) return false;
+ if (0 == r) return true;
+ if (r < 0) { // error condition
+ mStreamStatus = NPRES_NETWORK_ERR;
+
+ // Set up stream destruction
+ EnsureDeliveryPending();
+ return false;
+ }
+ mPendingData[0].curpos += r;
+ }
+ mPendingData.RemoveElementAt(0);
+ return false;
+}
+
+void BrowserStreamChild::SetSuspendedTimer() {
+ if (mSuspendedTimer.IsRunning()) return;
+ mSuspendedTimer.Start(base::TimeDelta::FromMilliseconds(
+ 100), // 100ms copied from Mozilla plugin host
+ this, &BrowserStreamChild::Deliver);
+}
+
+void BrowserStreamChild::ClearSuspendedTimer() { mSuspendedTimer.Stop(); }
+
+} // namespace mozilla::plugins