--- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -114,19 +114,19 @@ using namespace mozilla::plugins::parent // We should make this const... static NPNetscapeFuncs sBrowserFuncs = { sizeof(sBrowserFuncs), (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR, _geturl, _posturl, _requestread, - nullptr, - nullptr, - nullptr, + _newstream, + _write, + _destroystream, _status, _useragent, _memalloc, _memfree, _memflush, _reloadplugins, _getJavaEnv, _getJavaPeer, @@ -771,16 +771,137 @@ NPError PluginDestructionGuard guard(npp); return MakeNewNPAPIStreamInternal(npp, relativeURL, target, eNPPStreamTypeInternal_Post, false, nullptr, 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<nsIOutputStream> 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<nsNPAPIStreamWrapper*>(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<nsNPAPIStreamWrapper*>(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) { // NPN_Status is no longer supported. } void _memfree (void *ptr)
--- a/dom/plugins/base/nsNPAPIPlugin.h +++ b/dom/plugins/base/nsNPAPIPlugin.h @@ -266,16 +266,25 @@ NPError NPError _posturlnotify(NPP npp, const char* relativeURL, const char *target, uint32_t len, const char *buf, NPBool file, void* notifyData); 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); void _memfree (void *ptr); uint32_t _memflush(uint32_t size);
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -507,16 +507,24 @@ nsresult nsNPAPIPluginInstance::SetWindo ("NPP SetWindow called: this=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d], return=%d\n", this, window->x, window->y, window->width, window->height, window->clipRect.top, window->clipRect.bottom, window->clipRect.left, window->clipRect.right, error)); } 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) { RefPtr<nsNPAPIPluginStreamListener> sl = new nsNPAPIPluginStreamListener(this, notifyData, aURL); mStreamListeners.AppendElement(sl); sl.forget(listener);
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp @@ -29,17 +29,108 @@ nsNPAPIStreamWrapper::nsNPAPIStreamWrapp nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper() { if (mOutputStream) { mOutputStream->Close(); } } +NS_IMPL_ISUPPORTS(nsPluginStreamToFile, nsIOutputStream) + +nsPluginStreamToFile::nsPluginStreamToFile(const char* target, + nsIPluginInstanceOwner* owner) +: mTarget(PL_strdup(target)), +mOwner(owner) +{ + nsresult rv; + nsCOMPtr<nsIFile> 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) nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, void* notifyData, const char* aURL) : mStreamBuffer(nullptr) , mNotifyURL(aURL ? PL_strdup(aURL) : nullptr)
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.h +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h @@ -35,16 +35,35 @@ public: nsNPAPIPluginStreamListener* GetStreamListener() { return mStreamListener; } NPStream mNPStream; protected: nsCOMPtr<nsIOutputStream> mOutputStream; // only valid if not browser initiated 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<nsIFile> mTempFile; + nsCOMPtr<nsIOutputStream> mOutputStream; + nsIPluginInstanceOwner* mOwner; +}; + class nsNPAPIPluginStreamListener : public nsITimerCallback, public nsIHTTPHeaderListener { private: typedef mozilla::PluginLibrary PluginLibrary; public: NS_DECL_ISUPPORTS
--- a/dom/plugins/ipc/PPluginInstance.ipdl +++ b/dom/plugins/ipc/PPluginInstance.ipdl @@ -2,16 +2,17 @@ /* 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 PPluginBackgroundDestroyer; include protocol PPluginModule; include protocol PPluginScriptableObject; include protocol PBrowserStream; +include protocol PPluginStream; include protocol PStreamNotify; include protocol PPluginSurface; include "mozilla/GfxMessageUtils.h"; include "mozilla/layers/LayersMessageUtils.h"; using NPError from "npapi.h"; using struct mozilla::plugins::NPRemoteWindow from "mozilla/plugins/PluginMessageUtils.h"; @@ -62,16 +63,17 @@ union OptionalShmem { intr protocol PPluginInstance { manager PPluginModule; manages PPluginBackgroundDestroyer; manages PPluginScriptableObject; manages PBrowserStream; + manages PPluginStream; manages PStreamNotify; manages PPluginSurface; child: intr __delete__(); // This is only used on Windows and, for windowed plugins, must be called // before the first call to NPP_SetWindow. @@ -284,16 +286,22 @@ child: // Implements the legacy (synchronous) version of NPP_NewStream for when // async plugin init is preffed off. intr NPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable) returns (NPError rv, uint16_t stype); parent: + /* NPN_NewStream */ + intr PPluginStream(nsCString mimeType, + nsCString target) + returns (NPError result); + +parent: intr PluginFocusChange(bool gotFocus); child: intr SetPluginFocus(); intr UpdateWindow(); async PPluginBackgroundDestroyer(); };
new file mode 100644 --- /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
--- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -3,16 +3,17 @@ * 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 "PluginBackgroundDestroyer.h" #include "PluginInstanceChild.h" #include "PluginModuleChild.h" #include "BrowserStreamChild.h" +#include "PluginStreamChild.h" #include "StreamNotifyChild.h" #include "PluginProcessChild.h" #include "gfxASurface.h" #include "gfxPlatform.h" #include "gfx2DGlue.h" #include "nsNPAPIPluginInstance.h" #include "mozilla/gfx/2D.h" #ifdef MOZ_X11 @@ -2523,16 +2524,32 @@ PluginInstanceChild::AllocPBrowserStream bool PluginInstanceChild::DeallocPBrowserStreamChild(PBrowserStreamChild* stream) { AssertPluginThread(); delete 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, const nsCString& target, const bool& post, const nsCString& buffer, const bool& file, NPError* result) @@ -2638,16 +2655,38 @@ PluginInstanceChild::GetActorForNPObject NS_ERROR("Failed to send constructor message!"); return nullptr; } actor->InitializeLocal(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) { if (!notifyData) { return; } InfallibleTArray<PStreamNotifyChild*> notifyStreams;
--- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -170,16 +170,24 @@ protected: const uint32_t& length, const uint32_t& lastmodified, PStreamNotifyChild* notifyData, const nsCString& headers) override; 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, const bool& file, NPError* result) override; virtual bool DeallocPStreamNotifyChild(PStreamNotifyChild* notifyData) override;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -9,16 +9,17 @@ #include "mozilla/BasicEvents.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "PluginInstanceParent.h" #include "BrowserStreamParent.h" #include "PluginBackgroundDestroyer.h" #include "PluginModuleParent.h" +#include "PluginStreamParent.h" #include "StreamNotifyParent.h" #include "npfunctions.h" #include "nsAutoPtr.h" #include "gfxASurface.h" #include "gfxContext.h" #include "gfxPlatform.h" #include "gfxSharedImageSurface.h" #include "nsNetUtil.h" @@ -235,16 +236,30 @@ PluginInstanceParent::AllocPBrowserStrea bool PluginInstanceParent::DeallocPBrowserStreamParent(PBrowserStreamParent* stream) { delete 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, NPError* result) { #ifdef XP_WIN HWND id; #elif defined(MOZ_X11) @@ -1770,23 +1785,34 @@ PluginInstanceParent::NPP_DestroyStream( AStream* s = static_cast<AStream*>(stream->pdata); if (!s) { // The stream has already been deleted by other means. // With async plugin init this could happen if async NPP_NewStream // returns an error code. return NPERR_NO_ERROR; } - MOZ_ASSERT(s->IsBrowserStream()); - BrowserStreamParent* sp = - static_cast<BrowserStreamParent*>(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<BrowserStreamParent*>(s); + if (sp->mNPP != this) + MOZ_CRASH("Mismatched plugin data"); + + sp->NPP_DestroyStream(reason); + return NPERR_NO_ERROR; + } + else { + PluginStreamParent* sp = + static_cast<PluginStreamParent*>(s); + if (sp->mInstance != this) + MOZ_CRASH("Mismatched plugin data"); + + return PPluginStreamParent::Call__delete__(sp, reason, false) ? + NPERR_NO_ERROR : NPERR_GENERIC_ERROR; + } } void PluginInstanceParent::NPP_Print(NPPrint* platformPrint) { // TODO: implement me NS_ERROR("Not implemented"); }
--- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -40,16 +40,17 @@ namespace plugins { class PBrowserStreamParent; class PluginModuleParent; class D3D11SurfaceHolder; class PluginInstanceParent : public PPluginInstanceParent { friend class PluginModuleParent; friend class BrowserStreamParent; + friend class PluginStreamParent; friend class StreamNotifyParent; #if defined(XP_WIN) public: /** * Helper method for looking up instances based on a supplied id. */ static PluginInstanceParent* @@ -84,16 +85,23 @@ public: AllocPBrowserStreamParent(const nsCString& url, const uint32_t& length, const uint32_t& lastmodified, PStreamNotifyParent* notifyData, const nsCString& headers) override; 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; virtual mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVWindowNPObject( PPluginScriptableObjectParent** value, NPError* result) override; virtual mozilla::ipc::IPCResult
--- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -26,16 +26,17 @@ #ifdef MOZ_X11 # include "nsX11ErrorHandler.h" # include "mozilla/X11Util.h" #endif #include "mozilla/ipc/ProcessChild.h" #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" #include "nsNPAPIPlugin.h" #ifdef XP_WIN #include "nsWindowsDllInterceptor.h" #include "mozilla/widget/AudioSession.h" @@ -835,16 +836,25 @@ static NPError static NPError _posturlnotify(NPP aNPP, const char* relativeURL, const char *target, uint32_t len, const char *buf, NPBool file, void* notifyData); 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); static void _memfree (void *ptr); static uint32_t _memflush(uint32_t size); @@ -969,19 +979,19 @@ static void } /* namespace mozilla */ const NPNetscapeFuncs PluginModuleChild::sBrowserFuncs = { sizeof(sBrowserFuncs), (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR, 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, mozilla::plugins::child::_memfree, mozilla::plugins::child::_memflush, mozilla::plugins::child::_reloadplugins, mozilla::plugins::child::_getjavaenv, mozilla::plugins::child::_getjavapeer, @@ -1222,16 +1232,65 @@ NPError // FIXME what should happen when |aBuffer| is null? InstCast(aNPP)->CallNPN_PostURL(NullableString(aRelativeURL), NullableString(aTarget), nsDependentCString(aBuffer, aLength), aIsFile, &err); 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<PluginStreamChild*>(static_cast<AStream*>(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*>(aStream->ndata); + if (s->IsBrowserStream()) { + BrowserStreamChild* bs = static_cast<BrowserStreamChild*>(s); + bs->EnsureCorrectInstance(p); + bs->NPN_DestroyStream(aReason); + } + else { + PluginStreamChild* ps = static_cast<PluginStreamChild*>(s); + ps->EnsureCorrectInstance(p); + PPluginStreamChild::Call__delete__(ps, aReason, false); + } + return NPERR_NO_ERROR; +} void _status(NPP aNPP, const char* aMessage) { // NPN_Status is no longer supported. }
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -944,16 +944,21 @@ PluginModuleChromeParent::GetManagingIns static_cast<PPluginScriptableObjectParent*>(aProtocol); return static_cast<PluginInstanceParent*>(actor->Manager()); } case PBrowserStreamMsgStart: { PBrowserStreamParent* actor = static_cast<PBrowserStreamParent*>(aProtocol); return static_cast<PluginInstanceParent*>(actor->Manager()); } + case PPluginStreamMsgStart: { + PPluginStreamParent* actor = + static_cast<PPluginStreamParent*>(aProtocol); + return static_cast<PluginInstanceParent*>(actor->Manager()); + } case PStreamNotifyMsgStart: { PStreamNotifyParent* actor = static_cast<PStreamNotifyParent*>(aProtocol); return static_cast<PluginInstanceParent*>(actor->Manager()); } #ifdef XP_WIN case PPluginSurfaceMsgStart: { PPluginSurfaceParent* actor =
new file mode 100644 --- /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<AStream*>(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<char*>(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<PluginInstanceChild*>(Manager()); +} + +} // namespace plugins +} // namespace mozilla
new file mode 100644 --- /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
new file mode 100644 --- /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<char*>(mimeType.get()), + NullableStringGet(target), + &mStream); + if (*result == NPERR_NO_ERROR) + mStream->pdata = static_cast<AStream*>(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<char*>(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
new file mode 100644 --- /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
--- a/dom/plugins/ipc/moz.build +++ b/dom/plugins/ipc/moz.build @@ -29,16 +29,18 @@ EXPORTS.mozilla.plugins += [ 'PluginModuleParent.h', 'PluginProcessChild.h', 'PluginProcessParent.h', 'PluginQuirks.h', 'PluginScriptableObjectChild.h', 'PluginScriptableObjectParent.h', 'PluginScriptableObjectUtils-inl.h', 'PluginScriptableObjectUtils.h', + 'PluginStreamChild.h', + 'PluginStreamParent.h', 'PluginUtilsOSX.h', 'StreamNotifyChild.h', 'StreamNotifyParent.h', ] if CONFIG['OS_ARCH'] == 'WINNT': EXPORTS.mozilla.plugins += [ 'PluginSurfaceParent.h', @@ -70,16 +72,18 @@ UNIFIED_SOURCES += [ 'PluginInstanceParent.cpp', 'PluginMessageUtils.cpp', 'PluginModuleParent.cpp', 'PluginProcessChild.cpp', 'PluginProcessParent.cpp', 'PluginQuirks.cpp', 'PluginScriptableObjectChild.cpp', 'PluginScriptableObjectParent.cpp', + 'PluginStreamChild.cpp', + 'PluginStreamParent.cpp', ] SOURCES += [ 'PluginInstanceChild.cpp', # 'PluginThreadCallback' : ambiguous symbol 'PluginModuleChild.cpp', # Redefinition of mozilla::WindowsDllInterceptor sUser32Intercept ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': @@ -104,16 +108,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind IPDL_SOURCES += [ 'PBrowserStream.ipdl', 'PluginTypes.ipdlh', 'PPluginBackgroundDestroyer.ipdl', 'PPluginInstance.ipdl', 'PPluginModule.ipdl', 'PPluginScriptableObject.ipdl', + 'PPluginStream.ipdl', 'PPluginSurface.ipdl', 'PStreamNotify.ipdl', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [
--- a/dom/plugins/test/testplugin/nptest.cpp +++ b/dom/plugins/test/testplugin/nptest.cpp @@ -473,43 +473,78 @@ static void sendBufferToFrame(NPP instan } else if (bufsize > 0) { outbuf.append(buf); } else { 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; + + 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); } - 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); + 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; + } } } static void XPSleep(unsigned int seconds) { #ifdef XP_WIN Sleep(1000 * seconds); #else @@ -812,16 +847,17 @@ NPP_New(NPMIMEType pluginType, NPP insta memset(&instanceData->window, 0, sizeof(instanceData->window)); instanceData->crashOnDestroy = false; instanceData->cleanupWidget = true; // only used by nptest_gtk instanceData->topLevelWindowActivationState = ACTIVATION_STATE_UNKNOWN; instanceData->topLevelWindowActivationEventCount = 0; instanceData->focusState = ACTIVATION_STATE_UNKNOWN; instanceData->focusEventCount = 0; instanceData->eventModel = 0; + instanceData->closeStream = false; instanceData->wantsAllStreams = false; instanceData->mouseUpEventCount = 0; instanceData->bugMode = -1; instanceData->asyncDrawing = AD_NONE; instanceData->frontBuffer = nullptr; instanceData->backBuffer = nullptr; instanceData->placeholderWnd = nullptr; instanceData->cssZoomFactor = 1.0; @@ -943,16 +979,19 @@ NPP_New(NPMIMEType pluginType, NPP insta // "cleanupwidget" is only used with nptest_gtk, defaulting to true. It // indicates whether the plugin should destroy its window in response to // NPP_Destroy (or let the platform destroy the widget when the parent // window gets destroyed). if (strcmp(argn[i], "cleanupwidget") == 0 && 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]); } // Try to emulate java's codebase handling: Use the last seen codebase // value, regardless of whether it is in attributes or params. if (strcasecmp(argn[i], "codebase") == 0) { instanceData->javaCodebase = argv[i]; } @@ -1374,17 +1413,25 @@ NPP_Write(NPP instance, NPStream* stream if (nd && nd != &kNotifyData) { uint32_t newsize = nd->size + len; nd->data = (char*) realloc(nd->data, newsize); memcpy(nd->data + nd->size, buffer, len); 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, // then call NPN_RequestRead. // prevent recursion instanceData->streamMode = NP_NORMAL; if (instanceData->testrange != nullptr) { @@ -1401,23 +1448,31 @@ NPP_Write(NPP instance, NPStream* stream if (memcmp(buffer, streamBuf + offset, len)) { instanceData->err << "Error: data written from NPN_RequestRead doesn't match"; } else { 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<TestRange*>(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) { instanceData->streamBuf = malloc(len + 1); streamBuf = reinterpret_cast<char *>(instanceData->streamBuf); } else { instanceData->streamBuf = @@ -1849,17 +1904,39 @@ NPN_PostURLNotify(NPP instance, const ch NPError NPN_PostURL(NPP instance, const char *url, const char *target, uint32_t len, const char *buf, NPBool file) { 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, NPObject *npobj, NPIdentifier **identifiers, uint32_t *identifierCount) { return sBrowserFuncs->enumerate(instance, npobj, identifiers,
--- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -742,16 +742,18 @@ description = [PPluginInstance::NPN_SetValueForURL] description = [PPluginInstance::NPN_ConvertPoint] description = [PPluginInstance::GetCompositionString] description = [PPluginInstance::NPP_NewStream] description = +[PPluginInstance::PPluginStream] +description = [PPluginInstance::PluginFocusChange] description = [PPluginInstance::SetPluginFocus] description = [PPluginInstance::UpdateWindow] description = [PPluginModule::ModuleSupportsAsyncRender] description =