diff options
Diffstat (limited to 'dom/plugins/ipc/BrowserStreamChild.cpp')
-rw-r--r-- | dom/plugins/ipc/BrowserStreamChild.cpp | 217 |
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 |