mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 551049 (was bug 532208) part 1 - Make PBrowserStream destructor uni-directional by delivering all data asynchronously and doing two-phase (acknowledged) destruction, r=bent
This commit is contained in:
parent
1fc1bc4a5a
commit
39df629db6
@ -54,6 +54,7 @@ BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
|
||||
uint16_t* stype)
|
||||
: mInstance(instance)
|
||||
, mClosed(false)
|
||||
, mState(CONSTRUCTING)
|
||||
, mURL(url)
|
||||
, mHeaders(headers)
|
||||
{
|
||||
@ -91,49 +92,43 @@ BrowserStreamChild::StreamConstructed(
|
||||
rv = mInstance->mPluginIface->newstream(
|
||||
&mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
|
||||
&mStream, seekable, stype);
|
||||
if (rv != NPERR_NO_ERROR)
|
||||
if (rv != NPERR_NO_ERROR) {
|
||||
mClosed = true;
|
||||
mState = DELETING;
|
||||
}
|
||||
else {
|
||||
mState = ALIVE;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserStreamChild::AnswerNPP_WriteReady(const int32_t& newlength,
|
||||
int32_t *size)
|
||||
BrowserStreamChild::RecvWrite(const int32_t& offset,
|
||||
const Buffer& data,
|
||||
const uint32_t& newlength)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
AssertPluginThread();
|
||||
|
||||
if (mClosed) {
|
||||
*size = 0;
|
||||
if (ALIVE != mState)
|
||||
NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
|
||||
|
||||
if (mClosed)
|
||||
return true;
|
||||
}
|
||||
|
||||
mStream.end = newlength;
|
||||
|
||||
*size = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
|
||||
return true;
|
||||
}
|
||||
NS_ASSERTION(data.Length() > 0, "Empty data");
|
||||
|
||||
bool
|
||||
BrowserStreamChild::AnswerNPP_Write(const int32_t& offset,
|
||||
const Buffer& data,
|
||||
int32_t* consumed)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s (offset=%i, data.length=%i)", FULLFUNCTION,
|
||||
offset, data.Length()));
|
||||
PendingData* newdata = mPendingData.AppendElement();
|
||||
newdata->offset = offset;
|
||||
newdata->data = data;
|
||||
newdata->curpos = 0;
|
||||
|
||||
AssertPluginThread();
|
||||
DeliverData();
|
||||
|
||||
if (mClosed) {
|
||||
*consumed = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
*consumed = mInstance->mPluginIface->write(&mInstance->mData, &mStream,
|
||||
offset, data.Length(),
|
||||
const_cast<char*>(data.get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -144,6 +139,9 @@ BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname)
|
||||
|
||||
AssertPluginThread();
|
||||
|
||||
if (ALIVE != mState)
|
||||
NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
|
||||
|
||||
if (mClosed)
|
||||
return true;
|
||||
|
||||
@ -153,12 +151,32 @@ BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname)
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserStreamChild::Answer__delete__(const NPError& reason,
|
||||
const bool& artificial)
|
||||
BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_METHOD;
|
||||
|
||||
if (ALIVE != mState)
|
||||
NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
|
||||
|
||||
mState = DYING;
|
||||
|
||||
mClosed = true;
|
||||
mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream, reason);
|
||||
|
||||
SendStreamDestroyed();
|
||||
mState = DELETING;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserStreamChild::Recv__delete__()
|
||||
{
|
||||
AssertPluginThread();
|
||||
if (!artificial)
|
||||
NPP_DestroyStream(reason);
|
||||
|
||||
if (DELETING != mState)
|
||||
NS_RUNTIMEABORT("Bad state, not DELETING");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -169,6 +187,9 @@ BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
|
||||
|
||||
AssertPluginThread();
|
||||
|
||||
if (ALIVE != mState || mClosed)
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
IPCByteRanges ranges;
|
||||
for (; aRangeList; aRangeList = aRangeList->next) {
|
||||
IPCByteRange br = {aRangeList->offset, aRangeList->length};
|
||||
@ -181,17 +202,60 @@ BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
|
||||
}
|
||||
|
||||
void
|
||||
BrowserStreamChild::NPP_DestroyStream(NPError reason)
|
||||
BrowserStreamChild::DeliverData()
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s (reason=%i)", FULLFUNCTION, reason));
|
||||
|
||||
AssertPluginThread();
|
||||
|
||||
if (mClosed)
|
||||
if (ALIVE != mState || mClosed) {
|
||||
ClearSuspendedTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream, reason);
|
||||
mClosed = true;
|
||||
while (mPendingData.Length()) {
|
||||
PendingData& cur = mPendingData[0];
|
||||
while (cur.curpos < cur.data.Length()) {
|
||||
int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
|
||||
if (r == 0) {
|
||||
SetSuspendedTimer();
|
||||
return;
|
||||
}
|
||||
r = mInstance->mPluginIface->write(
|
||||
&mInstance->mData, &mStream,
|
||||
cur.offset + cur.curpos, // offset
|
||||
cur.data.Length() - cur.curpos, // length
|
||||
const_cast<char*>(cur.data.BeginReading() + cur.curpos));
|
||||
if (r == 0) {
|
||||
SetSuspendedTimer();
|
||||
return;
|
||||
}
|
||||
if (r < 0) { // error condition
|
||||
if (ALIVE == mState && !mClosed) { // re-check
|
||||
mClosed = true;
|
||||
SendNPN_DestroyStream(NPRES_NETWORK_ERR);
|
||||
}
|
||||
ClearSuspendedTimer();
|
||||
return;
|
||||
}
|
||||
cur.curpos += r;
|
||||
}
|
||||
mPendingData.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
ClearSuspendedTimer();
|
||||
}
|
||||
|
||||
void
|
||||
BrowserStreamChild::SetSuspendedTimer()
|
||||
{
|
||||
if (mSuspendedTimer.IsRunning())
|
||||
return;
|
||||
mSuspendedTimer.Start(
|
||||
base::TimeDelta::FromMilliseconds(100),
|
||||
this, &BrowserStreamChild::DeliverData);
|
||||
}
|
||||
|
||||
void
|
||||
BrowserStreamChild::ClearSuspendedTimer()
|
||||
{
|
||||
mSuspendedTimer.Stop();
|
||||
}
|
||||
|
||||
} /* namespace plugins */
|
||||
|
@ -74,16 +74,12 @@ public:
|
||||
const bool& seekable,
|
||||
uint16_t* stype);
|
||||
|
||||
virtual bool AnswerNPP_WriteReady(const int32_t& newlength,
|
||||
int32_t *size);
|
||||
virtual bool AnswerNPP_Write(const int32_t& offset,
|
||||
const Buffer& data,
|
||||
int32_t* consumed);
|
||||
|
||||
virtual bool RecvWrite(const int32_t& offset,
|
||||
const Buffer& data,
|
||||
const uint32_t& newsize);
|
||||
virtual bool AnswerNPP_StreamAsFile(const nsCString& fname);
|
||||
virtual bool Answer__delete__(const NPError& reason,
|
||||
const bool& artificial);
|
||||
|
||||
virtual bool RecvNPP_DestroyStream(const NPReason& reason);
|
||||
virtual bool Recv__delete__();
|
||||
|
||||
void EnsureCorrectInstance(PluginInstanceChild* i)
|
||||
{
|
||||
@ -97,14 +93,37 @@ public:
|
||||
}
|
||||
|
||||
NPError NPN_RequestRead(NPByteRange* aRangeList);
|
||||
void NPP_DestroyStream(NPError reason);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Deliver the data currently in mPending, scheduling
|
||||
* or cancelling the suspended timer as needed.
|
||||
*/
|
||||
void DeliverData();
|
||||
void SetSuspendedTimer();
|
||||
void ClearSuspendedTimer();
|
||||
|
||||
PluginInstanceChild* mInstance;
|
||||
NPStream mStream;
|
||||
bool mClosed;
|
||||
enum {
|
||||
CONSTRUCTING,
|
||||
ALIVE,
|
||||
DYING,
|
||||
DELETING
|
||||
} mState;
|
||||
nsCString mURL;
|
||||
nsCString mHeaders;
|
||||
|
||||
struct PendingData
|
||||
{
|
||||
int32_t offset;
|
||||
Buffer data;
|
||||
int32_t curpos;
|
||||
};
|
||||
nsTArray<PendingData> mPendingData;
|
||||
|
||||
base::RepeatingTimer<BrowserStreamChild> mSuspendedTimer;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
@ -3,6 +3,10 @@
|
||||
#include "BrowserStreamParent.h"
|
||||
#include "PluginInstanceParent.h"
|
||||
|
||||
// How much data are we willing to send across the wire
|
||||
// in one chunk?
|
||||
static const int32_t kSendDataChunk = 0x1000;
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
@ -10,6 +14,7 @@ BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
|
||||
NPStream* stream)
|
||||
: mNPP(npp)
|
||||
, mStream(stream)
|
||||
, mState(ALIVE)
|
||||
{
|
||||
mStream->pdata = static_cast<AStream*>(this);
|
||||
}
|
||||
@ -24,6 +29,19 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
switch (mState) {
|
||||
case ALIVE:
|
||||
break;
|
||||
|
||||
case DYING:
|
||||
*result = NPERR_GENERIC_ERROR;
|
||||
return true;
|
||||
|
||||
default:
|
||||
NS_ERROR("Unexpected state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mStream)
|
||||
return false;
|
||||
|
||||
@ -43,24 +61,49 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserStreamParent::Answer__delete__(const NPError& reason,
|
||||
const bool& artificial)
|
||||
BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
|
||||
{
|
||||
if (!artificial)
|
||||
NPN_DestroyStream(reason);
|
||||
switch (mState) {
|
||||
case ALIVE:
|
||||
break;
|
||||
|
||||
case DYING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
NS_ERROR("Unexpected state");
|
||||
return false;
|
||||
};
|
||||
|
||||
mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BrowserStreamParent::NPP_DestroyStream(NPReason reason)
|
||||
{
|
||||
NS_ASSERTION(ALIVE == mState, "NPP_DestroyStream called twice?");
|
||||
mState = DYING;
|
||||
SendNPP_DestroyStream(reason);
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserStreamParent::RecvStreamDestroyed()
|
||||
{
|
||||
if (DYING != mState) {
|
||||
NS_ERROR("Unexpected state");
|
||||
return false;
|
||||
}
|
||||
|
||||
mState = DELETING;
|
||||
Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t
|
||||
BrowserStreamParent::WriteReady()
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
int32_t result;
|
||||
if (!CallNPP_WriteReady(mStream->end, &result))
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
return kSendDataChunk;
|
||||
}
|
||||
|
||||
int32_t
|
||||
@ -70,13 +113,17 @@ BrowserStreamParent::Write(int32_t offset,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
int32_t result;
|
||||
if (!CallNPP_Write(offset,
|
||||
nsCString(static_cast<char*>(buffer), len),
|
||||
&result))
|
||||
return -1;
|
||||
NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?");
|
||||
NS_ASSERTION(len > 0, "Non-positive length to NPP_Write");
|
||||
|
||||
return result;
|
||||
if (len > kSendDataChunk)
|
||||
len = kSendDataChunk;
|
||||
|
||||
SendWrite(offset,
|
||||
nsCString(static_cast<char*>(buffer), len),
|
||||
mStream->end);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
@ -84,15 +131,11 @@ BrowserStreamParent::StreamAsFile(const char* fname)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
NS_ASSERTION(ALIVE == mState,
|
||||
"Calling streamasfile after NPP_DestroyStream?");
|
||||
|
||||
CallNPP_StreamAsFile(nsCString(fname));
|
||||
}
|
||||
|
||||
NPError
|
||||
BrowserStreamParent::NPN_DestroyStream(NPReason reason)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
|
||||
return mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
|
@ -61,18 +61,27 @@ public:
|
||||
virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
|
||||
NPError* result);
|
||||
|
||||
virtual bool
|
||||
Answer__delete__(const NPError& reason, const bool& artificial);
|
||||
virtual bool RecvNPN_DestroyStream(const NPReason& reason);
|
||||
|
||||
virtual bool RecvStreamDestroyed();
|
||||
|
||||
int32_t WriteReady();
|
||||
int32_t Write(int32_t offset, int32_t len, void* buffer);
|
||||
void StreamAsFile(const char* fname);
|
||||
|
||||
void NPP_DestroyStream(NPReason reason);
|
||||
|
||||
private:
|
||||
NPError NPN_DestroyStream(NPError reason);
|
||||
using PBrowserStreamParent::SendNPP_DestroyStream;
|
||||
|
||||
PluginInstanceParent* mNPP;
|
||||
NPStream* mStream;
|
||||
|
||||
enum {
|
||||
ALIVE,
|
||||
DYING,
|
||||
DELETING
|
||||
} mState;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
@ -57,26 +57,44 @@ rpc protocol PBrowserStream
|
||||
manager PPluginInstance;
|
||||
|
||||
child:
|
||||
rpc NPP_WriteReady(int32_t newlength)
|
||||
returns (int32_t size);
|
||||
|
||||
rpc NPP_Write(int32_t offset,
|
||||
Buffer data)
|
||||
returns (int32_t consumed);
|
||||
|
||||
async Write(int32_t offset, Buffer data,
|
||||
uint32_t newlength);
|
||||
rpc NPP_StreamAsFile(nsCString fname);
|
||||
|
||||
/**
|
||||
* NPP_DestroyStream may race with other messages: the child acknowledges
|
||||
* the message with StreamDestroyed before this actor is deleted.
|
||||
*/
|
||||
async NPP_DestroyStream(NPReason reason);
|
||||
async __delete__();
|
||||
|
||||
parent:
|
||||
rpc NPN_RequestRead(IPCByteRanges ranges)
|
||||
returns (NPError result);
|
||||
async NPN_DestroyStream(NPReason reason);
|
||||
async StreamDestroyed();
|
||||
|
||||
both:
|
||||
/**
|
||||
* ~PBrowserStream is for both NPN_DestroyStream and NPP_DestroyStream.
|
||||
* @param artificial True when the stream is closed as a by-product of
|
||||
* some other call (such as a failure in NPP_Write).
|
||||
*/
|
||||
rpc __delete__(NPReason reason, bool artificial);
|
||||
/*
|
||||
TODO: turn on state machine.
|
||||
|
||||
// need configurable start state: if the constructor
|
||||
// returns an error in result, start state should
|
||||
// be DELETING.
|
||||
start state ALIVE:
|
||||
send Write goto ALIVE;
|
||||
call NPP_StreamAsFile goto ALIVE;
|
||||
send NPP_DestroyStream goto ALIVE;
|
||||
answer NPN_RequestRead goto ALIVE;
|
||||
recv NPN_DestroyStream goto DYING;
|
||||
|
||||
state DYING:
|
||||
answer NPN_RequestRead goto DYING;
|
||||
recv NPN_DestroyStream goto DYING;
|
||||
recv StreamDestroyed goto DELETING;
|
||||
|
||||
state DELETING:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
@ -612,7 +612,7 @@ PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
if (NPERR_NO_ERROR != err)
|
||||
PBrowserStreamParent::Call__delete__(bs, NPERR_GENERIC_ERROR, true);
|
||||
PBrowserStreamParent::Send__delete__(bs);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -630,7 +630,7 @@ PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
|
||||
if (sp->mNPP != this)
|
||||
NS_RUNTIMEABORT("Mismatched plugin data");
|
||||
|
||||
PBrowserStreamParent::Call__delete__(sp, reason, false);
|
||||
sp->NPP_DestroyStream(reason);
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
else {
|
||||
|
@ -898,7 +898,7 @@ _destroystream(NPP aNPP,
|
||||
if (s->IsBrowserStream()) {
|
||||
BrowserStreamChild* bs = static_cast<BrowserStreamChild*>(s);
|
||||
bs->EnsureCorrectInstance(p);
|
||||
PBrowserStreamChild::Call__delete__(bs, aReason, false);
|
||||
bs->SendNPN_DestroyStream(aReason);
|
||||
}
|
||||
else {
|
||||
PluginStreamChild* ps = static_cast<PluginStreamChild*>(s);
|
||||
|
Loading…
Reference in New Issue
Block a user