Bug 1352559 - Remove support for plugin-provided streams; NPN_NewStream, PPluginStream and other supporting machinery, r?bsmedberg draft
authordan1bh <dan1bh@yahoo.co.uk>
Fri, 23 Jun 2017 16:46:59 +0000
changeset 606801 793f60ab6037c3c4fbca64081593b352b43e22c4
parent 585981 b6b634c2541250b8ff83feca2296ae927c3063cb
child 636863 36e5acfc2c26527fe19daa2dc7a797a2c08dc7fc
push id67805
push userbmo:dan1bh@yahoo.co.uk
push dateTue, 11 Jul 2017 12:56:59 +0000
reviewersbsmedberg
bugs1352559
milestone55.0a1
Bug 1352559 - Remove support for plugin-provided streams; NPN_NewStream, PPluginStream and other supporting machinery, r?bsmedberg MozReview-Commit-ID: 68GlVQdDJgM
dom/plugins/base/nsNPAPIPluginStreamListener.cpp
dom/plugins/base/nsNPAPIPluginStreamListener.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PPluginStream.ipdl
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginModuleChild.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
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -30,108 +30,17 @@ 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,35 +35,16 @@ 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,17 +2,16 @@
 /* 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";
 
 using NPError from "npapi.h";
 using struct mozilla::plugins::NPRemoteWindow from "mozilla/plugins/PluginMessageUtils.h";
 using struct mozilla::plugins::NPRemoteEvent from "mozilla/plugins/PluginMessageUtils.h";
@@ -62,17 +61,16 @@ 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.
@@ -291,22 +289,16 @@ child:
   intr NPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable)
     returns (NPError rv,
              uint16_t stype);
 
   // Implements the async plugin init version of NPP_NewStream.
   async AsyncNPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable);
 
 parent:
-  /* NPN_NewStream */
-  intr PPluginStream(nsCString mimeType,
-                    nsCString target)
-    returns (NPError result);
-
-parent:
   intr PluginFocusChange(bool gotFocus);
 
 child:
   intr SetPluginFocus();
   intr UpdateWindow();
 
   async PPluginBackgroundDestroyer();
 };
deleted file mode 100644
--- a/dom/plugins/ipc/PPluginStream.ipdl
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- 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/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1790,27 +1790,24 @@ PluginInstanceParent::NPP_DestroyStream(
                       FULLFUNCTION, (void*) stream, (int) reason));
 
     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;
-    }
-    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;
-    }
-   
+    }
+	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;
 }
 
 void
 PluginInstanceParent::NPP_Print(NPPrint* platformPrint)
 {
     // TODO: implement me
     NS_ERROR("Not implemented");
 }
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -949,18 +949,18 @@ static void
 
 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,
+    nullptr,
+    nullptr,
     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,
deleted file mode 100644
--- a/dom/plugins/ipc/PluginStreamChild.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- 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
deleted file mode 100644
--- a/dom/plugins/ipc/PluginStreamChild.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- 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
deleted file mode 100644
--- a/dom/plugins/ipc/PluginStreamParent.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- 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
deleted file mode 100644
--- a/dom/plugins/ipc/PluginStreamParent.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- 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
@@ -31,18 +31,16 @@ 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',
@@ -75,18 +73,16 @@ 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':
@@ -111,17 +107,16 @@ 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,39 +473,37 @@ 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;
-      }
-      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[8];
-          sprintf(hex, "%%%x", ascii);
-          outbuf.replace(i, 1, hex);
-          i += 2;
-        }
-     // }
-    }
+	// 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[8];
+			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;
     }
   }
 }
@@ -814,17 +812,16 @@ 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;
@@ -946,19 +943,16 @@ 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];
     }
@@ -1379,25 +1373,18 @@ 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->closeStream) {
-    instanceData->closeStream = false;
-    if (instanceData->testrange != nullptr) {
-      NPN_RequestRead(stream, instanceData->testrange);
-    }
-  }
-  else if (instanceData->streamMode == NP_SEEK &&
+  }  
+  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) {