From 7f215371c0656f7ea0a06926069c2a2cadabc538 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 17 Jul 2017 09:41:29 +1000 Subject: [PATCH] Backed out changeset cb1548354e0c (bug 1352559, part 3) for causing *many* crashes in Nightly. r=backout --- dom/plugins/base/nsNPAPIPlugin.cpp | 127 +++++++++++++++++- dom/plugins/base/nsNPAPIPlugin.h | 9 ++ dom/plugins/base/nsNPAPIPluginInstance.cpp | 8 ++ .../base/nsNPAPIPluginStreamListener.cpp | 91 +++++++++++++ .../base/nsNPAPIPluginStreamListener.h | 19 +++ dom/plugins/ipc/PPluginInstance.ipdl | 8 ++ dom/plugins/ipc/PPluginStream.ipdl | 37 +++++ dom/plugins/ipc/PluginInstanceChild.cpp | 39 ++++++ dom/plugins/ipc/PluginInstanceChild.h | 8 ++ dom/plugins/ipc/PluginInstanceParent.cpp | 40 +++++- dom/plugins/ipc/PluginInstanceParent.h | 8 ++ dom/plugins/ipc/PluginModuleChild.cpp | 65 ++++++++- dom/plugins/ipc/PluginModuleParent.cpp | 5 + dom/plugins/ipc/PluginStreamChild.cpp | 64 +++++++++ dom/plugins/ipc/PluginStreamChild.h | 55 ++++++++ dom/plugins/ipc/PluginStreamParent.cpp | 72 ++++++++++ dom/plugins/ipc/PluginStreamParent.h | 46 +++++++ dom/plugins/ipc/moz.build | 5 + dom/plugins/test/testplugin/nptest.cpp | 123 +++++++++++++---- ipc/ipdl/sync-messages.ini | 2 + 20 files changed, 795 insertions(+), 36 deletions(-) create mode 100644 dom/plugins/ipc/PPluginStream.ipdl create mode 100644 dom/plugins/ipc/PluginStreamChild.cpp create mode 100644 dom/plugins/ipc/PluginStreamChild.h create mode 100644 dom/plugins/ipc/PluginStreamParent.cpp create mode 100644 dom/plugins/ipc/PluginStreamParent.h diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index fed9c04c7fdb..d0289ca3eb1a 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -119,9 +119,9 @@ static NPNetscapeFuncs sBrowserFuncs = { _geturl, _posturl, _requestread, - nullptr, - nullptr, - nullptr, + _newstream, + _write, + _destroystream, _status, _useragent, _memalloc, @@ -776,6 +776,127 @@ _posturl(NPP npp, const char *relativeURL, const char *target, len, buf); } +NPError +_newstream(NPP npp, NPMIMEType type, const char* target, NPStream* *result) +{ + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_newstream called from the wrong thread\n")); + return NPERR_INVALID_PARAM; + } + NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPN_NewStream: npp=%p, type=%s, target=%s\n", (void*)npp, + (const char *)type, target)); + + NPError err = NPERR_INVALID_INSTANCE_ERROR; + if (npp && npp->ndata) { + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata; + + PluginDestructionGuard guard(inst); + + nsCOMPtr stream; + if (NS_SUCCEEDED(inst->NewStreamFromPlugin((const char*) type, target, + getter_AddRefs(stream)))) { + auto* wrapper = new nsNPAPIStreamWrapper(stream, nullptr); + if (wrapper) { + (*result) = &wrapper->mNPStream; + err = NPERR_NO_ERROR; + } else { + err = NPERR_OUT_OF_MEMORY_ERROR; + } + } else { + err = NPERR_GENERIC_ERROR; + } + } + return err; +} + +int32_t +_write(NPP npp, NPStream *pstream, int32_t len, void *buffer) +{ + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_write called from the wrong thread\n")); + return 0; + } + NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPN_Write: npp=%p, url=%s, len=%d, buffer=%s\n", (void*)npp, + pstream->url, len, (char*)buffer)); + + // negative return indicates failure to the plugin + if (!npp) + return -1; + + PluginDestructionGuard guard(npp); + + nsNPAPIStreamWrapper* wrapper = static_cast(pstream->ndata); + if (!wrapper) { + return -1; + } + + nsIOutputStream* stream = wrapper->GetOutputStream(); + if (!stream) { + return -1; + } + + uint32_t count = 0; + nsresult rv = stream->Write((char *)buffer, len, &count); + + if (NS_FAILED(rv)) { + return -1; + } + + return (int32_t)count; +} + +NPError +_destroystream(NPP npp, NPStream *pstream, NPError reason) +{ + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_destroystream called from the wrong thread\n")); + return NPERR_INVALID_PARAM; + } + NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPN_DestroyStream: npp=%p, url=%s, reason=%d\n", (void*)npp, + pstream->url, (int)reason)); + + if (!npp) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginDestructionGuard guard(npp); + + nsNPAPIStreamWrapper *streamWrapper = static_cast(pstream->ndata); + if (!streamWrapper) { + return NPERR_INVALID_PARAM; + } + + nsNPAPIPluginStreamListener *listener = streamWrapper->GetStreamListener(); + if (listener) { + // This type of stream is going from the browser to the plugin. It's either the + // initial src/data stream or another stream resulting from NPN_GetURL* or + // NPN_PostURL*. + // + // Calling OnStopBinding on the listener may cause it to be deleted due to the + // releasing of its last references. + listener->OnStopBinding(nullptr, NS_BINDING_ABORTED); + } else { + // This type of stream (NPStream) was created via NPN_NewStream. The plugin holds + // the reference until it is to be deleted here. Deleting the wrapper will + // release the wrapped nsIOutputStream. + // + // The NPStream the plugin references should always be a sub-object of its own + // 'ndata', which is our nsNPAPIStramWrapper. See bug 548441. + NS_ASSERTION((char*)streamWrapper <= (char*)pstream && + ((char*)pstream) + sizeof(*pstream) + <= ((char*)streamWrapper) + sizeof(*streamWrapper), + "pstream is not a subobject of wrapper"); + delete streamWrapper; + } + + // 'listener' and/or 'streamWrapper' may be invalid (deleted) at this point. Don't + // touch them again! + + return NPERR_NO_ERROR; +} + void _status(NPP npp, const char *message) { diff --git a/dom/plugins/base/nsNPAPIPlugin.h b/dom/plugins/base/nsNPAPIPlugin.h index f876cf68c4ed..780a7d812742 100644 --- a/dom/plugins/base/nsNPAPIPlugin.h +++ b/dom/plugins/base/nsNPAPIPlugin.h @@ -271,6 +271,15 @@ NPError _posturl(NPP npp, const char* relativeURL, const char *target, uint32_t len, const char *buf, NPBool file); +NPError +_newstream(NPP npp, NPMIMEType type, const char* window, NPStream** pstream); + +int32_t +_write(NPP npp, NPStream *pstream, int32_t len, void *buffer); + +NPError +_destroystream(NPP npp, NPStream *pstream, NPError reason); + void _status(NPP npp, const char *message); diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index ffefd9df5455..7758382bbcda 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -511,6 +511,14 @@ nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window) return NS_OK; } +nsresult +nsNPAPIPluginInstance::NewStreamFromPlugin(const char* type, const char* target, + nsIOutputStream* *result) +{ + nsPluginStreamToFile* stream = new nsPluginStreamToFile(target, mOwner); + return stream->QueryInterface(kIOutputStreamIID, (void**)result); +} + nsresult nsNPAPIPluginInstance::NewStreamListener(const char* aURL, void* notifyData, nsNPAPIPluginStreamListener** listener) diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp index 214d194da8b3..ca2f98aa9fdb 100644 --- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp @@ -34,7 +34,98 @@ nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper() } } +NS_IMPL_ISUPPORTS(nsPluginStreamToFile, nsIOutputStream) + +nsPluginStreamToFile::nsPluginStreamToFile(const char* target, + nsIPluginInstanceOwner* owner) +: mTarget(PL_strdup(target)), +mOwner(owner) +{ + nsresult rv; + nsCOMPtr pluginTmp; + rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pluginTmp)); + if (NS_FAILED(rv)) return; + + mTempFile = do_QueryInterface(pluginTmp, &rv); + if (NS_FAILED(rv)) return; + + // need to create a file with a unique name - use target as the basis + rv = mTempFile->AppendNative(nsDependentCString(target)); + if (NS_FAILED(rv)) return; + + // Yes, make it unique. + rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700); + if (NS_FAILED(rv)) return; + + // create the file + rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600); + if (NS_FAILED(rv)) + return; + + // construct the URL we'll use later in calls to GetURL() + NS_GetURLSpecFromFile(mTempFile, mFileURL); + +#ifdef DEBUG + printf("File URL = %s\n", mFileURL.get()); +#endif +} + +nsPluginStreamToFile::~nsPluginStreamToFile() +{ + // should we be deleting mTempFile here? + if (nullptr != mTarget) + PL_strfree(mTarget); +} + +NS_IMETHODIMP +nsPluginStreamToFile::Flush() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsPluginStreamToFile::Write(const char* aBuf, uint32_t aCount, + uint32_t *aWriteCount) +{ + mOutputStream->Write(aBuf, aCount, aWriteCount); + mOutputStream->Flush(); + mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0, false); + return NS_OK; +} + +NS_IMETHODIMP +nsPluginStreamToFile::WriteFrom(nsIInputStream *inStr, uint32_t count, + uint32_t *_retval) +{ + NS_NOTREACHED("WriteFrom"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsPluginStreamToFile::WriteSegments(nsReadSegmentFun reader, void * closure, + uint32_t count, uint32_t *_retval) +{ + NS_NOTREACHED("WriteSegments"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsPluginStreamToFile::IsNonBlocking(bool *aNonBlocking) +{ + *aNonBlocking = false; + return NS_OK; +} + +NS_IMETHODIMP +nsPluginStreamToFile::Close(void) +{ + mOutputStream->Close(); + mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0, false); + return NS_OK; +} + // nsNPAPIPluginStreamListener Methods + NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener, nsITimerCallback, nsIHTTPHeaderListener) diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.h b/dom/plugins/base/nsNPAPIPluginStreamListener.h index c64a5d85025d..bcf3d505f836 100644 --- a/dom/plugins/base/nsNPAPIPluginStreamListener.h +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h @@ -40,6 +40,25 @@ protected: nsNPAPIPluginStreamListener* mStreamListener; // only valid if browser initiated }; +// Used to handle NPN_NewStream() - writes the stream as received by the plugin +// to a file and at completion (NPN_DestroyStream), tells the browser to load it into +// a plugin-specified target +class nsPluginStreamToFile : public nsIOutputStream +{ +public: + nsPluginStreamToFile(const char* target, nsIPluginInstanceOwner* owner); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAM +protected: + virtual ~nsPluginStreamToFile(); + char* mTarget; + nsCString mFileURL; + nsCOMPtr mTempFile; + nsCOMPtr mOutputStream; + nsIPluginInstanceOwner* mOwner; +}; + class nsNPAPIPluginStreamListener : public nsITimerCallback, public nsIHTTPHeaderListener { diff --git a/dom/plugins/ipc/PPluginInstance.ipdl b/dom/plugins/ipc/PPluginInstance.ipdl index a8b5e5672d5a..3d6c2ff98257 100644 --- a/dom/plugins/ipc/PPluginInstance.ipdl +++ b/dom/plugins/ipc/PPluginInstance.ipdl @@ -7,6 +7,7 @@ include protocol PPluginBackgroundDestroyer; include protocol PPluginModule; include protocol PPluginScriptableObject; include protocol PBrowserStream; +include protocol PPluginStream; include protocol PStreamNotify; include protocol PPluginSurface; @@ -67,6 +68,7 @@ intr protocol PPluginInstance manages PPluginBackgroundDestroyer; manages PPluginScriptableObject; manages PBrowserStream; + manages PPluginStream; manages PStreamNotify; manages PPluginSurface; @@ -288,6 +290,12 @@ child: returns (NPError rv, uint16_t stype); +parent: + /* NPN_NewStream */ + intr PPluginStream(nsCString mimeType, + nsCString target) + returns (NPError result); + parent: intr PluginFocusChange(bool gotFocus); diff --git a/dom/plugins/ipc/PPluginStream.ipdl b/dom/plugins/ipc/PPluginStream.ipdl new file mode 100644 index 000000000000..49ba00e792bc --- /dev/null +++ b/dom/plugins/ipc/PPluginStream.ipdl @@ -0,0 +1,37 @@ +/* -*- 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 protocol PPluginInstance; + + +using mozilla::plugins::Buffer from "mozilla/plugins/PluginMessageUtils.h"; +using NPError from "npapi.h"; +using NPReason from "npapi.h"; + +namespace mozilla { +namespace plugins { + +/** + * PPluginStream represents an NPStream sent from the plugin to the browser. + */ + +intr protocol PPluginStream +{ + manager PPluginInstance; + +parent: + intr NPN_Write(Buffer data) returns (int32_t written); + +both: + /** + * ~PPluginStream 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 NPN_Write). + */ + intr __delete__(NPReason reason, bool artificial); +}; + +} // namespace plugins +} // namespace mozilla diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index da9406aa7974..74fc31b1afbf 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -8,6 +8,7 @@ #include "PluginInstanceChild.h" #include "PluginModuleChild.h" #include "BrowserStreamChild.h" +#include "PluginStreamChild.h" #include "StreamNotifyChild.h" #include "PluginProcessChild.h" #include "gfxASurface.h" @@ -2528,6 +2529,22 @@ PluginInstanceChild::DeallocPBrowserStreamChild(PBrowserStreamChild* stream) return true; } +PPluginStreamChild* +PluginInstanceChild::AllocPPluginStreamChild(const nsCString& mimeType, + const nsCString& target, + NPError* result) +{ + MOZ_CRASH("not callable"); + return nullptr; +} + +bool +PluginInstanceChild::DeallocPPluginStreamChild(PPluginStreamChild* stream) +{ + AssertPluginThread(); + delete stream; + return true; +} PStreamNotifyChild* PluginInstanceChild::AllocPStreamNotifyChild(const nsCString& url, @@ -2643,6 +2660,28 @@ PluginInstanceChild::GetActorForNPObject(NPObject* aObject) return actor; } +NPError +PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow, + NPStream** aStream) +{ + AssertPluginThread(); + AutoStackHelper guard(this); + + auto* ps = new PluginStreamChild(); + + NPError result; + CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType), + NullableString(aWindow), &result); + if (NPERR_NO_ERROR != result) { + *aStream = nullptr; + PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true); + return result; + } + + *aStream = &ps->mStream; + return NPERR_NO_ERROR; +} + void PluginInstanceChild::NPN_URLRedirectResponse(void* notifyData, NPBool allow) { diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h index abd056fef388..757a79889354 100644 --- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -175,6 +175,14 @@ protected: virtual bool DeallocPBrowserStreamChild(PBrowserStreamChild* stream) override; + virtual PPluginStreamChild* + AllocPPluginStreamChild(const nsCString& mimeType, + const nsCString& target, + NPError* result) override; + + virtual bool + DeallocPPluginStreamChild(PPluginStreamChild* stream) override; + virtual PStreamNotifyChild* AllocPStreamNotifyChild(const nsCString& url, const nsCString& target, const bool& post, const nsCString& buffer, diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index e15524550584..ac1024b160d0 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -14,6 +14,7 @@ #include "BrowserStreamParent.h" #include "PluginBackgroundDestroyer.h" #include "PluginModuleParent.h" +#include "PluginStreamParent.h" #include "StreamNotifyParent.h" #include "npfunctions.h" #include "nsAutoPtr.h" @@ -240,6 +241,20 @@ PluginInstanceParent::DeallocPBrowserStreamParent(PBrowserStreamParent* stream) return true; } +PPluginStreamParent* +PluginInstanceParent::AllocPPluginStreamParent(const nsCString& mimeType, + const nsCString& target, + NPError* result) +{ + return new PluginStreamParent(this, mimeType, target, result); +} + +bool +PluginInstanceParent::DeallocPPluginStreamParent(PPluginStreamParent* stream) +{ + delete stream; + return true; +} mozilla::ipc::IPCResult PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value, @@ -1775,13 +1790,24 @@ PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason) // returns an error code. return NPERR_NO_ERROR; } - MOZ_ASSERT(s->IsBrowserStream()); - BrowserStreamParent* sp = - static_cast(s); - if (sp->mNPP != this) - MOZ_CRASH("Mismatched plugin data"); - sp->NPP_DestroyStream(reason); - return NPERR_NO_ERROR; + if (s->IsBrowserStream()) { + BrowserStreamParent* sp = + static_cast(s); + if (sp->mNPP != this) + MOZ_CRASH("Mismatched plugin data"); + + sp->NPP_DestroyStream(reason); + return NPERR_NO_ERROR; + } + else { + PluginStreamParent* sp = + static_cast(s); + if (sp->mInstance != this) + MOZ_CRASH("Mismatched plugin data"); + + return PPluginStreamParent::Call__delete__(sp, reason, false) ? + NPERR_NO_ERROR : NPERR_GENERIC_ERROR; + } } void diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h index ea71bc48b888..7a56a4522f95 100644 --- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -45,6 +45,7 @@ class PluginInstanceParent : public PPluginInstanceParent { friend class PluginModuleParent; friend class BrowserStreamParent; + friend class PluginStreamParent; friend class StreamNotifyParent; #if defined(XP_WIN) @@ -89,6 +90,13 @@ public: virtual bool DeallocPBrowserStreamParent(PBrowserStreamParent* stream) override; + virtual PPluginStreamParent* + AllocPPluginStreamParent(const nsCString& mimeType, + const nsCString& target, + NPError* result) override; + virtual bool + DeallocPPluginStreamParent(PPluginStreamParent* stream) override; + virtual mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value, NPError* result) override; diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index f19e416a8a3d..c9213047c472 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -31,6 +31,7 @@ #include "mozilla/plugins/PluginInstanceChild.h" #include "mozilla/plugins/StreamNotifyChild.h" #include "mozilla/plugins/BrowserStreamChild.h" +#include "mozilla/plugins/PluginStreamChild.h" #include "mozilla/Sprintf.h" #include "mozilla/Unused.h" @@ -840,6 +841,15 @@ static NPError _posturl(NPP aNPP, const char* relativeURL, const char *target, uint32_t len, const char *buf, NPBool file); +static NPError +_newstream(NPP aNPP, NPMIMEType type, const char* window, NPStream** pstream); + +static int32_t +_write(NPP aNPP, NPStream *pstream, int32_t len, void *buffer); + +static NPError +_destroystream(NPP aNPP, NPStream *pstream, NPError reason); + static void _status(NPP aNPP, const char *message); @@ -974,9 +984,9 @@ const NPNetscapeFuncs PluginModuleChild::sBrowserFuncs = { mozilla::plugins::child::_geturl, mozilla::plugins::child::_posturl, mozilla::plugins::child::_requestread, - nullptr, - nullptr, - nullptr, + mozilla::plugins::child::_newstream, + mozilla::plugins::child::_write, + mozilla::plugins::child::_destroystream, mozilla::plugins::child::_status, mozilla::plugins::child::_useragent, mozilla::plugins::child::_memalloc, @@ -1227,6 +1237,55 @@ _posturl(NPP aNPP, return err; } +NPError +_newstream(NPP aNPP, + NPMIMEType aMIMEType, + const char* aWindow, + NPStream** aStream) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + return InstCast(aNPP)->NPN_NewStream(aMIMEType, aWindow, aStream); +} + +int32_t +_write(NPP aNPP, + NPStream* aStream, + int32_t aLength, + void* aBuffer) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(0); + + PluginStreamChild* ps = + static_cast(static_cast(aStream->ndata)); + ps->EnsureCorrectInstance(InstCast(aNPP)); + ps->EnsureCorrectStream(aStream); + return ps->NPN_Write(aLength, aBuffer); +} + +NPError +_destroystream(NPP aNPP, + NPStream* aStream, + NPError aReason) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + PluginInstanceChild* p = InstCast(aNPP); + AStream* s = static_cast(aStream->ndata); + if (s->IsBrowserStream()) { + BrowserStreamChild* bs = static_cast(s); + bs->EnsureCorrectInstance(p); + bs->NPN_DestroyStream(aReason); + } + else { + PluginStreamChild* ps = static_cast(s); + ps->EnsureCorrectInstance(p); + PPluginStreamChild::Call__delete__(ps, aReason, false); + } + return NPERR_NO_ERROR; +} void _status(NPP aNPP, diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index f6da3e1330d8..307092eac7a3 100644 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -949,6 +949,11 @@ PluginModuleChromeParent::GetManagingInstance(mozilla::ipc::IProtocol* aProtocol static_cast(aProtocol); return static_cast(actor->Manager()); } + case PPluginStreamMsgStart: { + PPluginStreamParent* actor = + static_cast(aProtocol); + return static_cast(actor->Manager()); + } case PStreamNotifyMsgStart: { PStreamNotifyParent* actor = static_cast(aProtocol); diff --git a/dom/plugins/ipc/PluginStreamChild.cpp b/dom/plugins/ipc/PluginStreamChild.cpp new file mode 100644 index 000000000000..056545c6d2bd --- /dev/null +++ b/dom/plugins/ipc/PluginStreamChild.cpp @@ -0,0 +1,64 @@ +/* -*- 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 "PluginStreamChild.h" +#include "mozilla/plugins/PluginInstanceChild.h" + +namespace mozilla { +namespace plugins { + +PluginStreamChild::PluginStreamChild() + : mClosed(false) +{ + memset(&mStream, 0, sizeof(mStream)); + mStream.ndata = static_cast(this); +} + +mozilla::ipc::IPCResult +PluginStreamChild::Answer__delete__(const NPReason& reason, + const bool& artificial) +{ + AssertPluginThread(); + if (!artificial) + NPP_DestroyStream(reason); + return IPC_OK(); +} + +int32_t +PluginStreamChild::NPN_Write(int32_t length, void* buffer) +{ + AssertPluginThread(); + + int32_t written = 0; + CallNPN_Write(nsCString(static_cast(buffer), length), + &written); + if (written < 0) + PPluginStreamChild::Call__delete__(this, NPERR_GENERIC_ERROR, true); + // careful after here! |this| just got deleted + + return written; +} + +void +PluginStreamChild::NPP_DestroyStream(NPError reason) +{ + AssertPluginThread(); + + if (mClosed) + return; + + mClosed = true; + Instance()->mPluginIface->destroystream( + &Instance()->mData, &mStream, reason); +} + +PluginInstanceChild* +PluginStreamChild::Instance() +{ + return static_cast(Manager()); +} + +} // namespace plugins +} // namespace mozilla diff --git a/dom/plugins/ipc/PluginStreamChild.h b/dom/plugins/ipc/PluginStreamChild.h new file mode 100644 index 000000000000..2d8965b449d1 --- /dev/null +++ b/dom/plugins/ipc/PluginStreamChild.h @@ -0,0 +1,55 @@ +/* -*- 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/. */ + +#ifndef mozilla_plugins_PluginStreamChild_h +#define mozilla_plugins_PluginStreamChild_h + +#include "mozilla/plugins/PPluginStreamChild.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceChild; + +class PluginStreamChild : public PPluginStreamChild, public AStream +{ + friend class PluginInstanceChild; + +public: + PluginStreamChild(); + virtual ~PluginStreamChild() { } + + virtual bool IsBrowserStream() override { return false; } + + virtual mozilla::ipc::IPCResult Answer__delete__(const NPReason& reason, + const bool& artificial) override; + + int32_t NPN_Write(int32_t length, void* buffer); + void NPP_DestroyStream(NPError reason); + + void EnsureCorrectInstance(PluginInstanceChild* i) + { + if (i != Instance()) + MOZ_CRASH("Incorrect stream instance"); + } + void EnsureCorrectStream(NPStream* s) + { + if (s != &mStream) + MOZ_CRASH("Incorrect stream data"); + } + +private: + PluginInstanceChild* Instance(); + + NPStream mStream; + bool mClosed; +}; + + +} // namespace plugins +} // namespace mozilla + +#endif diff --git a/dom/plugins/ipc/PluginStreamParent.cpp b/dom/plugins/ipc/PluginStreamParent.cpp new file mode 100644 index 000000000000..f0dcfdec1d73 --- /dev/null +++ b/dom/plugins/ipc/PluginStreamParent.cpp @@ -0,0 +1,72 @@ +/* -*- 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 "PluginStreamParent.h" +#include "PluginInstanceParent.h" + +namespace mozilla { +namespace plugins { + +PluginStreamParent::PluginStreamParent(PluginInstanceParent* npp, + const nsCString& mimeType, + const nsCString& target, + NPError* result) + : mInstance(npp) + , mClosed(false) +{ + *result = mInstance->mNPNIface->newstream(mInstance->mNPP, + const_cast(mimeType.get()), + NullableStringGet(target), + &mStream); + if (*result == NPERR_NO_ERROR) + mStream->pdata = static_cast(this); + else + mStream = nullptr; +} + +void +PluginStreamParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Implement me! Bug 1005166 +} + +mozilla::ipc::IPCResult +PluginStreamParent::AnswerNPN_Write(const Buffer& data, int32_t* written) +{ + if (mClosed) { + *written = -1; + return IPC_OK(); + } + + *written = mInstance->mNPNIface->write(mInstance->mNPP, mStream, + data.Length(), + const_cast(data.get())); + if (*written < 0) + mClosed = true; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +PluginStreamParent::Answer__delete__(const NPError& reason, + const bool& artificial) +{ + if (!artificial) + this->NPN_DestroyStream(reason); + return IPC_OK(); +} + +void +PluginStreamParent::NPN_DestroyStream(NPReason reason) +{ + if (mClosed) + return; + + mInstance->mNPNIface->destroystream(mInstance->mNPP, mStream, reason); + mClosed = true; +} + +} // namespace plugins +} // namespace mozilla diff --git a/dom/plugins/ipc/PluginStreamParent.h b/dom/plugins/ipc/PluginStreamParent.h new file mode 100644 index 000000000000..78b6f61c027e --- /dev/null +++ b/dom/plugins/ipc/PluginStreamParent.h @@ -0,0 +1,46 @@ +/* -*- 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/. */ + +#ifndef mozilla_plugins_PluginStreamParent_h +#define mozilla_plugins_PluginStreamParent_h + +#include "mozilla/plugins/PPluginStreamParent.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceParent; + +class PluginStreamParent : public PPluginStreamParent, public AStream +{ + friend class PluginModuleParent; + friend class PluginInstanceParent; + +public: + PluginStreamParent(PluginInstanceParent* npp, const nsCString& mimeType, + const nsCString& target, NPError* result); + virtual ~PluginStreamParent() { } + + virtual bool IsBrowserStream() override { return false; } + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual mozilla::ipc::IPCResult AnswerNPN_Write(const Buffer& data, int32_t* written) override; + + virtual mozilla::ipc::IPCResult Answer__delete__(const NPError& reason, const bool& artificial) override; + +private: + void NPN_DestroyStream(NPReason reason); + + PluginInstanceParent* mInstance; + NPStream* mStream; + bool mClosed; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff --git a/dom/plugins/ipc/moz.build b/dom/plugins/ipc/moz.build index 3658f29f70af..7f328fcc8aed 100644 --- a/dom/plugins/ipc/moz.build +++ b/dom/plugins/ipc/moz.build @@ -34,6 +34,8 @@ EXPORTS.mozilla.plugins += [ 'PluginScriptableObjectParent.h', 'PluginScriptableObjectUtils-inl.h', 'PluginScriptableObjectUtils.h', + 'PluginStreamChild.h', + 'PluginStreamParent.h', 'PluginUtilsOSX.h', 'StreamNotifyChild.h', 'StreamNotifyParent.h', @@ -75,6 +77,8 @@ UNIFIED_SOURCES += [ 'PluginQuirks.cpp', 'PluginScriptableObjectChild.cpp', 'PluginScriptableObjectParent.cpp', + 'PluginStreamChild.cpp', + 'PluginStreamParent.cpp', ] SOURCES += [ @@ -109,6 +113,7 @@ IPDL_SOURCES += [ 'PPluginInstance.ipdl', 'PPluginModule.ipdl', 'PPluginScriptableObject.ipdl', + 'PPluginStream.ipdl', 'PPluginSurface.ipdl', 'PStreamNotify.ipdl', ] diff --git a/dom/plugins/test/testplugin/nptest.cpp b/dom/plugins/test/testplugin/nptest.cpp index 4ad6c57b96fa..16acddf63368 100644 --- a/dom/plugins/test/testplugin/nptest.cpp +++ b/dom/plugins/test/testplugin/nptest.cpp @@ -478,33 +478,68 @@ static void sendBufferToFrame(NPP instance) outbuf.append("Error: no data in buffer"); } - // Convert CRLF to LF, and escape most other non-alphanumeric chars. - for (size_t i = 0; i < outbuf.length(); i++) { - if (outbuf[i] == '\n') { - outbuf.replace(i, 1, "%0a"); - i += 2; + if (instanceData->npnNewStream && + instanceData->err.str().length() == 0) { + char typeHTML[] = "text/html"; + NPStream* stream; + printf("calling NPN_NewStream..."); + NPError err = NPN_NewStream(instance, typeHTML, + instanceData->frame.c_str(), &stream); + printf("return value %d\n", err); + if (err != NPERR_NO_ERROR) { + instanceData->err << "NPN_NewStream returned " << err; + return; } - else if (outbuf[i] == '\r') { - outbuf.replace(i, 1, ""); - i -= 1; - } - else { - int ascii = outbuf[i]; - if (!((ascii >= ',' && ascii <= ';') || - (ascii >= 'A' && ascii <= 'Z') || - (ascii >= 'a' && ascii <= 'z'))) { - char hex[10]; - sprintf(hex, "%%%x", ascii); - outbuf.replace(i, 1, hex); - i += 2; + + int32_t bytesToWrite = outbuf.length(); + int32_t bytesWritten = 0; + while ((bytesToWrite - bytesWritten) > 0) { + int32_t numBytes = (bytesToWrite - bytesWritten) < + instanceData->streamChunkSize ? + bytesToWrite - bytesWritten : instanceData->streamChunkSize; + int32_t written = NPN_Write(instance, stream, + numBytes, (void*)(outbuf.c_str() + bytesWritten)); + if (written <= 0) { + instanceData->err << "NPN_Write returned " << written; + break; } + bytesWritten += numBytes; + printf("%d bytes written, total %d\n", written, bytesWritten); + } + err = NPN_DestroyStream(instance, stream, NPRES_DONE); + if (err != NPERR_NO_ERROR) { + instanceData->err << "NPN_DestroyStream returned " << err; } } + else { + // Convert CRLF to LF, and escape most other non-alphanumeric chars. + for (size_t i = 0; i < outbuf.length(); i++) { + if (outbuf[i] == '\n') { + outbuf.replace(i, 1, "%0a"); + i += 2; + } + else if (outbuf[i] == '\r') { + outbuf.replace(i, 1, ""); + i -= 1; + } + else { + int ascii = outbuf[i]; + if (!((ascii >= ',' && ascii <= ';') || + (ascii >= 'A' && ascii <= 'Z') || + (ascii >= 'a' && ascii <= 'z'))) { + char hex[10]; + sprintf(hex, "%%%x", ascii); + outbuf.replace(i, 1, hex); + i += 2; + } + } + } - NPError err = NPN_GetURL(instance, outbuf.c_str(), - instanceData->frame.c_str()); - if (err != NPERR_NO_ERROR) { - instanceData->err << "NPN_GetURL returned " << err; + NPError err = NPN_GetURL(instance, outbuf.c_str(), + instanceData->frame.c_str()); + if (err != NPERR_NO_ERROR) { + instanceData->err << "NPN_GetURL returned " << err; + } } } @@ -817,6 +852,7 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* instanceData->focusState = ACTIVATION_STATE_UNKNOWN; instanceData->focusEventCount = 0; instanceData->eventModel = 0; + instanceData->closeStream = false; instanceData->wantsAllStreams = false; instanceData->mouseUpEventCount = 0; instanceData->bugMode = -1; @@ -948,6 +984,9 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* strcmp(argv[i], "false") == 0) { instanceData->cleanupWidget = false; } + if (!strcmp(argn[i], "closestream")) { + instanceData->closeStream = true; + } if (strcmp(argn[i], "bugmode") == 0) { instanceData->bugMode = atoi(argv[i]); } @@ -1379,7 +1418,15 @@ NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buf nd->size = newsize; return len; } - if (instanceData->streamMode == NP_SEEK && + + if (instanceData->closeStream) { + instanceData->closeStream = false; + if (instanceData->testrange != nullptr) { + NPN_RequestRead(stream, instanceData->testrange); + } + NPN_DestroyStream(instance, stream, NPRES_USER_BREAK); + } + else if (instanceData->streamMode == NP_SEEK && stream->end != 0 && stream->end == ((uint32_t)instanceData->streamBufSize + len)) { // If the complete stream has been written, and we're doing a seek test, @@ -1406,13 +1453,21 @@ NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buf printf("data matches!\n"); } TestRange* range = instanceData->testrange; + bool stillwaiting = false; while(range != nullptr) { if (offset == range->offset && (uint32_t)len == range->length) { range->waiting = false; } + if (range->waiting) stillwaiting = true; range = reinterpret_cast(range->next); } + if (!stillwaiting) { + NPError err = NPN_DestroyStream(instance, stream, NPRES_DONE); + if (err != NPERR_NO_ERROR) { + instanceData->err << "Error: NPN_DestroyStream returned " << err; + } + } } else { if (instanceData->streamBufSize == 0) { @@ -1854,7 +1909,29 @@ NPN_PostURL(NPP instance, const char *url, return sBrowserFuncs->posturl(instance, url, target, len, buf, file); } +NPError +NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason) +{ + return sBrowserFuncs->destroystream(instance, stream, reason); +} +NPError +NPN_NewStream(NPP instance, + NPMIMEType type, + const char* target, + NPStream** stream) +{ + return sBrowserFuncs->newstream(instance, type, target, stream); +} + +int32_t +NPN_Write(NPP instance, + NPStream* stream, + int32_t len, + void* buf) +{ + return sBrowserFuncs->write(instance, stream, len, buf); +} bool NPN_Enumerate(NPP instance, diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index d40169262b97..ec1bba138d77 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -747,6 +747,8 @@ description = description = [PPluginInstance::NPP_NewStream] description = +[PPluginInstance::PPluginStream] +description = [PPluginInstance::PluginFocusChange] description = [PPluginInstance::SetPluginFocus]