зеркало из https://github.com/mozilla/gecko-dev.git
220 строки
7.0 KiB
C++
220 строки
7.0 KiB
C++
/* -*- 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 {
|
|
namespace 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 plugins */
|
|
} /* namespace mozilla */
|