Backed out changeset cb1548354e0c (bug 1352559, part 3) for causing *many* crashes in Nightly. r=backout
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 17 Jul 2017 09:41:29 +1000
changeset 609596 aff336ac161daa3ea350e59a288963edbd58ed39
parent 609461 5f44d10bacca2d693413b529e0caadc73e634e1e
child 609597 b82187110ea8c133ad110686940b456c277f80e4
child 609600 2290d3866a0aee8a8bae2920f013926d2b4153f2
child 609601 fa39293a894b47a3fd766f46c5c62626b9f12de2
child 609602 579b16f27c64da2693f66a30a88e0969baaf5e5f
child 609603 fdfe3920d6d6fb6efe3da56fba7c3eb8f7ac604e
child 609610 d4f4c6616a59d48ccbe03c48e165705deb89aaef
child 609621 acbf593c3e5d3e2356c12cfa6d997c27f835e35e
child 609624 87eeec1ce38706797aaaed4787b585a4810ee47d
child 609625 d4a1a68d626dd20933e7f4f76be650387961ab20
child 609627 33c04f53e1e03e641e762e75e50aa02b94c8e2bb
child 609629 4ba903f9aebcb60262fadcaa608108e46dd5e843
child 609645 d261d2b77272df5ed9a6858a663aeb8e0d006707
child 609646 8fa65a148c20c563fcf409542aa4b3976f09677e
child 609647 051494822a469a0882c1034105a2f435d5f9dacf
child 609648 e169a7373c3b6bbcd08d38fa5e57c81b5cdfd047
child 609650 ba05814576996ce582c61d1f0f1277e09132ec3b
child 609655 f7642f3036778580fdf097c68528bfd7dbfd1589
child 609656 ae63a81ced76e2b7e74271e8a855fd34d112d01c
child 609659 097105d5c69329e95f5ecd240de8210e19a461f0
child 609668 e5aebe32e8c6c06d16adac9e211f0f9637955384
child 609669 33c747cd9dcba84f893c8c41a104c998793b032c
child 609677 9600e2e0075274acbdd33df471390873b093e528
child 609686 2b05993d207c54630ee5d9af272ac446449a0351
child 609693 0c24d0da2f4b116e06bd6e72450d9f395c282100
child 609698 4e5537f0367c6584ad6578f93c378dfc98d78bd3
child 609699 9c8fe9ea12606f369b5cf9b72320220869cca672
child 609700 14b169dfe2156107dfed0c2ec93ad8365056c8c4
child 609702 a284119e47acf5da91697cea37461264183ad4fa
child 609703 b3e179be7528dd54a5c7b3250150fe311d47b88f
child 609704 fd8306fb0a05712598594550f06c690efa68ad57
child 609705 427bb76f51a4d3de0b0ceb16cd0ea6ada8e95247
child 609706 abb97262ad6835e539f9a27e91ff01cb73038602
child 609707 701bf1e78dcdd53d2249f54bb7208bfd21e518b0
child 609713 a82109bec9d75a6ba20571afb83ab56f3fd6b479
child 609714 315f80d8787aa93bffd5b5f355c7f3cb742e5426
child 609715 82267e4ab9bcc625f619dd3549124d0da756140d
child 609722 2184a3b17dee6e0270a7d16fb484ee6fb94e2d42
child 609724 394e0273c25d4a249774bb5439c2610adf040ff1
child 609725 1f436c834ccf02f00e747f7d324179deae91cdd9
child 609726 ad20b2746f54da3841f7c95e6ae754824ffe4f93
child 609727 367758f692156f9f4d877a7d2fdf730e706ff520
child 609728 1772b3c23ed3c41ef5742dd0159e25784e308570
child 609730 bfa143d099fde404ba0f3c11d5c14bee6f294665
child 609732 f2039d741295405100df389fa3b6be73647514f5
child 609761 b8783d6fffdbebed01455743185cea68784d5367
child 609787 6e221219028dd3f79895384c3bf445ce680d2901
child 609808 612acc524d2c72761da7579b7296735eaf5f97b3
child 609809 2145534f2fa632ecb7626e75c5e35a0e2de9136c
child 609819 2b4456af001964d01f08034614810bad3a2716f3
child 609825 3feaa81929a4768d2e26a8125b4d1b9d2c062c8a
child 609830 a43860753b03f1175ee5003b67492a6c8a32df71
child 609852 b0be0fcc9ee1fc7a745b6859410fe6160fb71a92
child 609855 be8f4e7e12c297d993e6e11dad84548209c510a2
child 609861 c3a8002e8fe62d2fd34af8417feaf3aab8dec52f
child 609869 513752dabe86a8fa68c08c3dc31110af8f0823b1
child 609884 75aa03aa49af97c9699bfb6fa2de1d09b591f711
child 610268 78a2ddca14657abc54a64dbecc29221f293cdb99
child 610278 34d34a35b4145d485ab6bc774bd2989e52d12421
child 610279 76d4d6371d519dde4e29b870a54e46bf7b5239f3
child 610283 fc7ea1bb55830d68dcace1e9d211a6a7cb0b35f5
child 610293 91fbb94cdf796cbce41dad4f937d8de0ea7d6e54
child 610294 80f007720153acae294a0af862322b309e0cba82
child 610321 2fe4b373d00d8e841cc2d124d68e99729117b18c
child 610326 9c6bfbdd548b7b8d02072bdc0b4aea2516f42db2
child 610344 a879141feb01efcdbe8e3ee81cf8a4a01f90286b
child 610357 af2d8f151742084a26d4953e68431d4d8eb21b7c
child 610372 9ed4ed85ca2fc451a396ba959d913f65603a763c
child 610561 7bb2650282f8f783e15ec05c3b8f37d01b429ba7
child 610622 9dd1efc3835cf163bdb1551dc434a296fe5862f7
child 610655 bb2d1a2cc4b37748da04d7cb91ca5ecb57fa63dd
child 610712 4ccbb432e1533c349af4b9987183435c9beec7ab
child 610869 2026fe579006ef6e804fd70d14f22c1b565d1386
child 610963 037f7e952f6dd3f7e74d123647502ac29263b878
child 610972 c22cde1b6f1c74f33057f84e2ca67f2425bdca3a
child 611095 5f34f67749b0123490ac43246322dfef35a20a58
child 611107 14a1c70ea0bf1b75396b38871272fc34b8fc4e51
child 611125 b1eaafba67f64e0157cb751fe31b9daa0125a999
child 611253 809ad8266549cd301a5d49acf04dd5a07336516b
child 611260 ff645328b423f7699d46b3b04df537af016b3734
child 611292 ef9d77f541a2091f955a7809f28206559eb22c38
child 611301 54ace9324fec6020717088f856d6e712252e03ae
child 611882 9b8cac5eed26261dba9742f9fb1780640d1bbbab
child 611965 e17f1f61d0c3e11c0532aa5232e44cff43e4903c
child 612134 17df6ef258ec790b3f605e4d97cb4c3fe7cbf8dc
child 613012 6934245255adf067ffa19e5c1a5538b71e803ec6
push id68593
push userbmo:evan@tseng.io
push dateMon, 17 Jul 2017 02:16:37 +0000
reviewersbackout
bugs1352559
milestone56.0a1
backs outcb1548354e0cf582dca6b0ab0d27712d01a4c819
Backed out changeset cb1548354e0c (bug 1352559, part 3) for causing *many* crashes in Nightly. r=backout
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPlugin.h
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginStreamListener.cpp
dom/plugins/base/nsNPAPIPluginStreamListener.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PPluginStream.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginStreamChild.cpp
dom/plugins/ipc/PluginStreamChild.h
dom/plugins/ipc/PluginStreamParent.cpp
dom/plugins/ipc/PluginStreamParent.h
dom/plugins/ipc/moz.build
dom/plugins/test/testplugin/nptest.cpp
ipc/ipdl/sync-messages.ini
--- 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 =