Backed out 9 changesets (bug 1203680) for mochitest bustage CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 14 Sep 2015 14:27:57 -0700
changeset 295036 21b8c95e507a158485a12eed3dc23e4ee8f4711d
parent 295035 17276193146d1d03e9c4ce17412d933990ce031b
child 295037 3f02dab20a515af485c055a070d9728ed559e5b4
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1203680, 1093357
milestone43.0a1
backs oute4733b9eb53cdf487057b0905d6d6051bcd9e1ff
fb33eb2a55b082f6426fa3ee47f5f417a7b77242
eb42e21bbb96754c5f1e2a28a944a1824dd2d57e
86642d84e6047784d993201a6cc64384569c996d
1026da4b02fb296593389aa3ab535369800c2f29
311f9810e0b314055715b0d1ec9384fcc09e395e
6fedc85dc0d905a5a8deb3782d138cef68476178
b25230c0a193679868576ac673aacabd6ef79b98
2369d63ef14a58fd2ccaf95b7f24747f75e47642
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 9 changesets (bug 1203680) for mochitest bustage CLOSED TREE Backed out changeset e4733b9eb53c (bug 1203680) Backed out changeset fb33eb2a55b0 Backed out changeset eb42e21bbb96 (bug 1203680) Backed out changeset 86642d84e604 (bug 1203680) Backed out changeset 1026da4b02fb (bug 1203680) Backed out changeset 311f9810e0b3 (bug 1203680) Backed out changeset 6fedc85dc0d9 (bug 1203680) Backed out changeset b25230c0a193 (bug 1093357) Backed out changeset 2369d63ef14a (bug 1203680)
dom/tests/mochitest/fetch/mochitest.ini
dom/workers/ServiceWorkerManager.cpp
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/redirect_post.sjs
dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html
dom/workers/test/serviceworkers/test_file_blob_upload.html
netwerk/base/nsIUploadChannel2.idl
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
xpcom/io/nsStorageStream.cpp
xpcom/io/nsStreamUtils.cpp
xpcom/io/nsStreamUtils.h
xpcom/tests/gtest/TestStorageStream.cpp
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -22,20 +22,20 @@ support-files =
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_headers_mainthread.html]
 [test_fetch_app_protocol.html]
 [test_fetch_basic.html]
 [test_fetch_basic_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_fetch_basic_http.html]
 [test_fetch_basic_http_sw_reroute.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet' # Bug 1137683 for b2g, bug 1174872 for mulet
+skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet' # Bug 1093357 for e10s, bug 1137683 for b2g, bug 1174872 for mulet
 [test_fetch_cors.html]
 [test_fetch_cors_sw_reroute.html]
-skip-if = buildapp == 'b2g' # Bug 1137683 for b2g
+skip-if = e10s || buildapp == 'b2g' # Bug 1093357 for e10s, bug 1137683 for b2g
 [test_formdataparsing.html]
 [test_formdataparsing_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_request.html]
 [test_request_context.html]
 [test_request_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_response.html]
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3862,92 +3862,16 @@ private:
       }
 
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
     }
     return true;
   }
 };
 
-namespace {
-
-class ContinueDispatchFetchEventRunnable : public nsRunnable
-{
-  WorkerPrivate* mWorkerPrivate;
-  nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
-  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
-  nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
-  bool mIsReload;
-public:
-  ContinueDispatchFetchEventRunnable(WorkerPrivate* aWorkerPrivate,
-                                     nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                                     nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
-                                     bool aIsReload)
-    : mWorkerPrivate(aWorkerPrivate)
-    , mChannel(aChannel)
-    , mServiceWorker(aServiceWorker)
-    , mClientInfo(aClientInfo)
-    , mIsReload(aIsReload)
-  {
-  }
-
-  void
-  HandleError()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    NS_WARNING("Unexpected error while dispatching fetch event!");
-    DebugOnly<nsresult> rv = mChannel->ResetInterception();
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to resume intercepted network request");
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsCOMPtr<nsIChannel> channel;
-    nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      HandleError();
-      return NS_OK;
-    }
-
-    // The channel might have encountered an unexpected error while ensuring
-    // the upload stream is cloneable.  Check here and reset the interception
-    // if that happens.
-    nsresult status;
-    rv = channel->GetStatus(&status);
-    if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
-      HandleError();
-      return NS_OK;
-    }
-
-    nsRefPtr<FetchEventRunnable> event =
-      new FetchEventRunnable(mWorkerPrivate, mChannel, mServiceWorker,
-                             mClientInfo, mIsReload);
-    rv = event->Init();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      HandleError();
-      return NS_OK;
-    }
-
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    if (NS_WARN_IF(!event->Dispatch(jsapi.cx()))) {
-      HandleError();
-      return NS_OK;
-    }
-
-    return NS_OK;
-  }
-};
-
-} // anonymous namespace
-
 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
 
 void
 ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
                                          nsIDocument* aDoc,
                                          nsIInterceptedChannel* aChannel,
                                          bool aIsReload,
                                          ErrorResult& aRv)
@@ -4011,38 +3935,31 @@ ServiceWorkerManager::DispatchFetchEvent
 
   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
     new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
 
   nsRefPtr<ServiceWorker> sw = static_cast<ServiceWorker*>(serviceWorker.get());
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(sw));
 
-  nsCOMPtr<nsIRunnable> continueRunnable =
-    new ContinueDispatchFetchEventRunnable(sw->GetWorkerPrivate(), handle,
-                                           serviceWorkerHandle, clientInfo,
-                                           aIsReload);
-
-  nsCOMPtr<nsIChannel> innerChannel;
-  aRv = aChannel->GetChannel(getter_AddRefs(innerChannel));
+  // clientInfo is null if we don't have a controlled document
+  nsRefPtr<FetchEventRunnable> event =
+    new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle,
+                           clientInfo, aIsReload);
+  aRv = event->Init();
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
-
-  // If there is no upload stream, then continue immediately
-  if (!uploadChannel) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(continueRunnable->Run()));
+  AutoJSAPI api;
+  api.Init();
+  if (NS_WARN_IF(!event->Dispatch(api.cx()))) {
+    aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
-
-  // Otherwise, ensure the upload stream can be cloned directly.  This may
-  // require some async copying, so provide a callback.
-  aRv = uploadChannel->EnsureUploadStreamIsCloneable(continueRunnable);
 }
 
 bool
 ServiceWorkerManager::IsAvailable(const OriginAttributes& aOriginAttributes,
                                   nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
 
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -155,18 +155,16 @@ support-files =
   opaque_intercept_worker.js
   notify_loaded.js
   test_request_context.js
   fetch_event_client.js
   sw_clients/dummy.html
   fetch/plugin/worker.js
   fetch/plugin/plugins.html
   eventsource/*
-  sw_clients/file_blob_upload_frame.html
-  redirect_post.sjs
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
@@ -248,9 +246,8 @@ skip-if = toolkit == "android" || toolki
 [test_workerUnregister.html]
 [test_workerUpdate.html]
 [test_workerupdatefoundevent.html]
 [test_opaque_intercept.html]
 [test_fetch_event_client_postmessage.html]
 [test_escapedSlashes.html]
 [test_eventsource_intercept.html]
 [test_not_intercept_plugin.html]
-[test_file_blob_upload.html]
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/redirect_post.sjs
+++ /dev/null
@@ -1,35 +0,0 @@
-const CC = Components.Constructor;
-const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                             "nsIBinaryInputStream",
-                             "setInputStream");
-
-function handleRequest(request, response)
-{
-  var query = {};
-  request.queryString.split('&').forEach(function (val) {
-    var [name, value] = val.split('=');
-    query[name] = unescape(value);
-  });
-
-  var bodyStream = new BinaryInputStream(request.bodyInputStream);
-  var bodyBytes = [];
-  while ((bodyAvail = bodyStream.available()) > 0)
-    Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
-
-  var body = decodeURIComponent(
-    escape(String.fromCharCode.apply(null, bodyBytes)));
-
-  var currentHop = query.hop ? parseInt(query.hop) : 0;
-
-  var obj = JSON.parse(body);
-  if (currentHop < obj.hops) {
-    var newURL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs?hop=' +
-                 (1 + currentHop);
-    response.setStatusLine(null, 307, 'redirect');
-    response.setHeader('Location', newURL);
-    return;
-  }
-
-  response.setHeader('Content-Type', 'application/json');
-  response.write(body);
-}
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html
+++ /dev/null
@@ -1,77 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>test file blob upload with SW interception</title>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-if (!parent) {
-  dump("sw_clients/file_blob_upload_frame.html shouldn't be launched directly!");
-}
-
-function makeFileBlob(obj) {
-  return new Promise(function(resolve, reject) {
-
-    var request = indexedDB.open(window.location.pathname, 1);
-    request.onerror = reject;
-    request.onupgradeneeded = function(evt) {
-      var db = evt.target.result;
-      db.onerror = reject;
-
-      var objectStore = db.createObjectStore('test', { autoIncrement: true });
-      var index = objectStore.createIndex('test', 'index');
-    };
-
-    request.onsuccess = function(evt) {
-      var db = evt.target.result;
-      db.onerror = reject;
-
-      var blob = new Blob([JSON.stringify(obj)],
-                          { type: 'application/json' });
-      var data = { blob: blob, index: 5 };
-
-      objectStore = db.transaction('test', 'readwrite').objectStore('test');
-      objectStore.add(data).onsuccess = function(evt) {
-        var key = evt.target.result;
-        objectStore = db.transaction('test').objectStore('test');
-        objectStore.get(key).onsuccess = function(evt) {
-          resolve(evt.target.result.blob);
-        };
-      };
-    };
-  });
-}
-
-navigator.serviceWorker.ready.then(function() {
-  parent.postMessage({ status: 'READY' }, '*');
-});
-
-var URL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs';
-
-addEventListener('message', function(evt) {
-  if (evt.data.type = 'TEST') {
-    makeFileBlob(evt.data.body).then(function(blob) {
-      return fetch(URL, { method: 'POST', body: blob });
-    }).then(function(response) {
-      return response.json();
-    }).then(function(result) {
-      parent.postMessage({ status: 'OK', result: result }, '*');
-    }).catch(function(e) {
-      parent.postMessage({ status: 'ERROR', result: e.toString() }, '*');
-    });
-  }
-});
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_file_blob_upload.html
+++ /dev/null
@@ -1,146 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1203680 - Test interception of file blob uploads</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-  var registration;
-  var iframe;
-  function start() {
-    return navigator.serviceWorker.register("empty.js",
-                                            { scope: "./sw_clients/" })
-      .then((swr) => registration = swr);
-  }
-
-  function unregister() {
-    if (iframe) {
-      iframe.remove();
-      iframe = null;
-    }
-
-    return registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      ok(false, "Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function withFrame() {
-    var content = document.getElementById("content");
-    ok(content, "Parent exists.");
-
-    iframe = document.createElement("iframe");
-    iframe.setAttribute('src', "sw_clients/file_blob_upload_frame.html");
-    content.appendChild(iframe);
-
-    return new Promise(function(resolve, reject) {
-      window.addEventListener('message', function readyCallback(evt) {
-        window.removeEventListener('message', readyCallback);
-        if (evt.data.status === 'READY') {
-          resolve();
-        } else {
-          reject(evt.data.result);
-        }
-      });
-    });
-  }
-
-  function postBlob(body) {
-    return new Promise(function(resolve, reject) {
-      window.addEventListener('message', function postBlobCallback(evt) {
-        window.removeEventListener('message', postBlobCallback);
-        if (evt.data.status === 'OK') {
-          is(JSON.stringify(body), JSON.stringify(evt.data.result),
-             'body echoed back correctly');
-          resolve();
-        } else {
-          reject(evt.data.result);
-        }
-      });
-
-      iframe.contentWindow.postMessage({ type: 'TEST', body: body }, '*');
-    });
-  }
-
-  function generateMessage(length) {
-
-    var lorem =
-      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas '
-      'vehicula tortor eget ultrices. Sed et luctus est. Nunc eu orci ligula. '
-      'In vel ornare eros, eget lacinia diam. Praesent vel metus mattis, '
-      'cursus nulla sit amet, rhoncus diam. Aliquam nulla tortor, aliquet et '
-      'viverra non, dignissim vel tellus. Praesent sed ex in dolor aliquet '
-      'aliquet. In at facilisis sem, et aliquet eros. Maecenas feugiat nisl '
-      'quis elit blandit posuere. Duis viverra odio sed eros consectetur, '
-      'viverra mattis ligula volutpat.';
-
-    var result = '';
-
-    while (result.length < length) {
-      var remaining = length - result.length;
-      if (remaining < lorem.length) {
-        result += lorem.slice(0, remaining);
-      } else {
-        result += lorem;
-      }
-    }
-
-    return result;
-  }
-
-  var smallBody = generateMessage(64);
-  var mediumBody = generateMessage(1024);
-
-  // TODO: Test large bodies over the default pipe size.  Currently stalls
-  //       due to bug 1134372.
-  //var largeBody = generateMessage(100 * 1024);
-
-  function runTest() {
-    start()
-      .then(withFrame)
-      .then(function() {
-        return postBlob({ hops: 0, message: smallBody });
-      })
-      .then(function() {
-        return postBlob({ hops: 1, message: smallBody });
-      })
-      .then(function() {
-        return postBlob({ hops: 10, message: smallBody });
-      })
-      .then(function() {
-        return postBlob({ hops: 0, message: mediumBody });
-      })
-      .then(function() {
-        return postBlob({ hops: 1, message: mediumBody });
-      })
-      .then(function() {
-        return postBlob({ hops: 10, message: mediumBody });
-      })
-      .then(unregister)
-      .catch(function(e) {
-        ok(false, "Some test failed with error " + e);
-      }).then(SimpleTest.finish);
-  }
-
-  SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
-  ]}, runTest);
-</script>
-</pre>
-</body>
-</html>
-
--- a/netwerk/base/nsIUploadChannel2.idl
+++ b/netwerk/base/nsIUploadChannel2.idl
@@ -1,19 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 
 interface nsIInputStream;
-interface nsIRunnable;
 
-[scriptable, uuid(2f712b52-19c5-4e0c-9e8f-b5c7c3b67049)]
+[scriptable, uuid(62e6529a-5cf6-491a-82ef-b3a8273cdd19)]
 interface nsIUploadChannel2 : nsISupports
 {
     /**
      * Sets a stream to be uploaded by this channel with the specified
      * Content-Type and Content-Length header values.
      *
      * Most implementations of this interface require that the stream:
      *   (1) implement threadsafe addRef and release
@@ -43,25 +42,13 @@ interface nsIUploadChannel2 : nsISupport
     /**
      * Value of aStreamHasHeaders from the last successful call to
      * explicitSetUploadStream.  TRUE indicates the attached upload stream
      * contians request headers.
      */
     readonly attribute boolean uploadStreamHasHeaders;
 
     /**
-     * Ensure the upload stream, if any, is cloneable.  This may involve
-     * async copying, so a callback runnable must be provided.  It will
-     * invoked on the current thread when the upload stream is ready
-     * for cloning.  If the stream is already cloneable, then the callback
-     * will be invoked synchronously.
-     */
-    [noscript]
-    void ensureUploadStreamIsCloneable(in nsIRunnable aCallback);
-
-    /**
-     * Clones the upload stream.  May return failure if the upload stream
-     * is not cloneable.  If this is not acceptable, use the
-     * ensureUploadStreamIsCloneable() method first.
+     * Clones the upload stream and returns an equivalent stream.
      */
     [noscript]
     nsIInputStream cloneUploadStream();
 };
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -14,17 +14,16 @@
 #include "nsMimeTypes.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 
 #include "nsICachingChannel.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsISeekableStream.h"
-#include "nsIStorageStream.h"
 #include "nsITimedChannel.h"
 #include "nsIEncodedChannel.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIMutableArray.h"
 #include "nsEscape.h"
 #include "nsStreamListenerWrapper.h"
 #include "nsISecurityConsoleMessage.h"
 #include "nsURLHelper.h"
@@ -570,124 +569,55 @@ HttpBaseChannel::SetUploadStream(nsIInpu
   // if stream is null, ExplicitSetUploadStream returns error.
   // So we need special case for GET method.
   mUploadStreamHasHeaders = false;
   mRequestHead.SetMethod(NS_LITERAL_CSTRING("GET")); // revert to GET request
   mUploadStream = stream;
   return NS_OK;
 }
 
-namespace {
-
-void
-CopyComplete(void* aClosure, nsresult aStatus) {
-  // Called on the STS thread by NS_AsyncCopy
-  auto channel = static_cast<HttpBaseChannel*>(aClosure);
-  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArg<nsresult>(
-    channel, &HttpBaseChannel::EnsureUploadStreamIsCloneableComplete, aStatus);
-  NS_DispatchToMainThread(runnable.forget());
-}
-
-} // anonymous namespace
-
-NS_IMETHODIMP
-HttpBaseChannel::EnsureUploadStreamIsCloneable(nsIRunnable* aCallback)
+static void
+EnsureStreamBuffered(nsCOMPtr<nsIInputStream>& aStream)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-  NS_ENSURE_ARG_POINTER(aCallback);
-
-  // We could in theory allow multiple callers to use this method,
-  // but the complexity does not seem worth it yet.  Just fail if
-  // this is called more than once simultaneously.
-  NS_ENSURE_FALSE(mUploadCloneableCallback, NS_ERROR_UNEXPECTED);
-
-  // If the CloneUploadStream() will succeed, then synchronously invoke
-  // the callback to indicate we're already cloneable.
-  if (!mUploadStream || NS_InputStreamIsCloneable(mUploadStream)) {
-    aCallback->Run();
-    return NS_OK;
+  if (!NS_InputStreamIsBuffered(aStream)) {
+    nsCOMPtr<nsIInputStream> bufferedStream;
+    nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
+                                            aStream,
+                                            4096);
+    NS_ENSURE_SUCCESS_VOID(rv);
+    aStream.swap(bufferedStream);
   }
-
-  nsCOMPtr<nsIStorageStream> storageStream;
-  nsresult rv = NS_NewStorageStream(4096, UINT32_MAX,
-                                    getter_AddRefs(storageStream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIInputStream> newUploadStream;
-  rv = storageStream->NewInputStream(0, getter_AddRefs(newUploadStream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIOutputStream> sink;
-  rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIInputStream> source;
-  if (NS_InputStreamIsBuffered(mUploadStream)) {
-    source = mUploadStream;
-  } else {
-    rv = NS_NewBufferedInputStream(getter_AddRefs(source), mUploadStream, 4096);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  nsCOMPtr<nsIEventTarget> target =
-    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-
-  mUploadCloneableCallback = aCallback;
-
-  rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
-                    4096, // copy segment size
-                    CopyComplete, this);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    mUploadCloneableCallback = nullptr;
-    return rv;
-  }
-
-  // Since we're consuming the old stream, replace it with the new
-  // stream immediately.
-  mUploadStream = newUploadStream;
-
-  // Explicity hold the stream alive until copying is complete.  This will
-  // be released in EnsureUploadStreamIsCloneableComplete().
-  AddRef();
-
-  return NS_OK;
-}
-
-void
-HttpBaseChannel::EnsureUploadStreamIsCloneableComplete(nsresult aStatus)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-  MOZ_ASSERT(mUploadCloneableCallback);
-
-  if (NS_SUCCEEDED(mStatus)) {
-    mStatus = aStatus;
-  }
-
-  mUploadCloneableCallback->Run();
-  mUploadCloneableCallback = nullptr;
-
-  // Release the reference we grabbed in EnsureUploadStreamIsCloneable() now
-  // that the copying is complete.
-  Release();
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::CloneUploadStream(nsIInputStream** aClonedStream)
 {
   NS_ENSURE_ARG_POINTER(aClonedStream);
   *aClonedStream = nullptr;
 
   if (!mUploadStream) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIInputStream> clonedStream;
-  nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
+  nsCOMPtr<nsIInputStream> replacementStream;
+  nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream),
+                                    getter_AddRefs(replacementStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (replacementStream) {
+    mUploadStream.swap(replacementStream);
+
+    // Ensure that the replacement stream is buffered.
+    EnsureStreamBuffered(mUploadStream);
+  }
+
+  // Ensure that the cloned stream is buffered.
+  EnsureStreamBuffered(clonedStream);
+
   clonedStream.forget(aClonedStream);
 
   return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIUploadChannel2
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -264,20 +264,16 @@ public: /* Necko internal use only... */
     static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
                                            nsHttpRequestHead::ParsedMethodType method);
 
     // Like nsIEncodedChannel::DoApplyConversions except context is set to
     // mListenerContext.
     nsresult DoApplyContentConversions(nsIStreamListener *aNextListener,
                                        nsIStreamListener **aNewNextListener);
 
-    // Callback on main thread when NS_AsyncCopy() is finished populating
-    // the new mUploadStream.
-    void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
-
 protected:
   nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
 
   // Handle notifying listener, removing from loadgroup if request failed.
   void     DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
@@ -328,17 +324,16 @@ protected:
   nsCOMPtr<nsILoadInfo>             mLoadInfo;
   nsCOMPtr<nsIInterfaceRequestor>   mCallbacks;
   nsCOMPtr<nsIProgressEventSink>    mProgressSink;
   nsCOMPtr<nsIURI>                  mReferrer;
   nsCOMPtr<nsIApplicationCache>     mApplicationCache;
 
   nsHttpRequestHead                 mRequestHead;
   nsCOMPtr<nsIInputStream>          mUploadStream;
-  nsCOMPtr<nsIRunnable>             mUploadCloneableCallback;
   nsAutoPtr<nsHttpResponseHead>     mResponseHead;
   nsRefPtr<nsHttpConnectionInfo>    mConnectionInfo;
   nsCOMPtr<nsIProxyInfo>            mProxyInfo;
   nsCOMPtr<nsISupports>             mSecurityInfo;
 
   nsCString                         mSpec; // ASCII encoded URL spec
   nsCString                         mContentTypeHint;
   nsCString                         mContentCharsetHint;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2265,17 +2265,18 @@ HttpChannelChild::OverrideWithSynthesize
   // however, we want to respect the encoding of the final result instead.
   if (!WillRedirect(aResponseHead)) {
     SetApplyConversion(false);
   }
 
   mResponseHead = aResponseHead;
   mSynthesizedResponse = true;
 
-  if (WillRedirect(mResponseHead)) {
+  uint16_t status = mResponseHead->Status();
+  if (status != 200 && status != 404) {
     // Continue with the original cross-process request
     nsresult rv = ContinueAsyncOpen();
     NS_ENSURE_SUCCESS_VOID(rv);
     return;
   }
 
   // In our current implementation, the FetchEvent handler will copy the
   // response stream completely into the pipe backing the input stream so we
--- a/xpcom/io/nsStorageStream.cpp
+++ b/xpcom/io/nsStorageStream.cpp
@@ -455,25 +455,17 @@ nsStorageInputStream::ReadSegments(nsWri
   while (remainingCapacity) {
     availableInSegment = mSegmentEnd - mReadCursor;
     if (!availableInSegment) {
       uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor;
       if (!available) {
         goto out;
       }
 
-      // We have data in the stream, but if mSegmentEnd is zero, then we
-      // were likely constructed prior to any data being written into
-      // the stream.  Therefore, if mSegmentEnd is non-zero, we should
-      // move into the next segment; otherwise, we should stay in this
-      // segment so our input state can be updated and we can properly
-      // perform the initial read.
-      if (mSegmentEnd > 0) {
-        mSegmentNum++;
-      }
+      mSegmentNum++;
       mReadCursor = 0;
       mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
       availableInSegment = mSegmentEnd;
     }
     const char* cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
 
     count = XPCOM_MIN(availableInSegment, remainingCapacity);
     rv = aWriter(this, aClosure, cur + mReadCursor, aCount - remainingCapacity,
--- a/xpcom/io/nsStreamUtils.cpp
+++ b/xpcom/io/nsStreamUtils.cpp
@@ -852,27 +852,16 @@ NS_FillArray(FallibleTArray<char>& aDest
   // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
   // in nsTArray.h:
   aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
 
   MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
   return rv;
 }
 
-bool
-NS_InputStreamIsCloneable(nsIInputStream* aSource)
-{
-  if (!aSource) {
-    return false;
-  }
-
-  nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
-  return cloneable && cloneable->GetCloneable();
-}
-
 nsresult
 NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
                     nsIInputStream** aReplacementOut)
 {
   if (NS_WARN_IF(!aSource)) {
     return NS_ERROR_FAILURE;
   }
 
--- a/xpcom/io/nsStreamUtils.h
+++ b/xpcom/io/nsStreamUtils.h
@@ -260,22 +260,16 @@ struct MOZ_STACK_CLASS nsWriteSegmentThu
  *        failed
  * @return the result from aInput->Read(...)
  */
 extern NS_METHOD
 NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
              uint32_t aKeep, uint32_t* aNewBytes);
 
 /**
- * Return true if the given stream can be directly cloned.
- */
-extern bool
-NS_InputStreamIsCloneable(nsIInputStream* aSource);
-
-/**
  * Clone the provided source stream in the most efficient way possible.  This
  * first attempts to QI to nsICloneableInputStream to use Clone().  If that is
  * not supported or its cloneable attribute is false, then a fallback clone is
  * provided by copying the source to a pipe.  In this case the caller must
  * replace the source stream with the resulting replacement stream.  The clone
  * and the replacement stream are then cloneable using nsICloneableInputStream
  * without duplicating memory.  This fallback clone using the pipe is only
  * performed if a replacement stream parameter is also passed in.
--- a/xpcom/tests/gtest/TestStorageStream.cpp
+++ b/xpcom/tests/gtest/TestStorageStream.cpp
@@ -9,29 +9,27 @@
 #include "Helpers.h"
 #include "nsCOMPtr.h"
 #include "nsICloneableInputStream.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIStorageStream.h"
 
 namespace {
-
 void
 WriteData(nsIOutputStream* aOut, nsTArray<char>& aData, uint32_t aNumBytes,
           nsACString& aDataWritten)
 {
   uint32_t n;
   nsresult rv = aOut->Write(aData.Elements(), aNumBytes, &n);
   EXPECT_TRUE(NS_SUCCEEDED(rv));
   aDataWritten.Append(aData.Elements(), aNumBytes);
 }
 
 } // namespace
-
 TEST(StorageStreams, Main)
 {
   // generate some test data we will write in 4k chunks to the stream
   nsTArray<char> kData;
   testing::CreateData(4096, kData);
 
   // track how much data was written so we can compare at the end
   nsAutoCString dataWritten;
@@ -86,45 +84,8 @@ TEST(StorageStreams, Main)
 
   // now, read all
   rv = stor->NewInputStream(0, getter_AddRefs(in));
   EXPECT_TRUE(NS_SUCCEEDED(rv));
 
   testing::ConsumeAndValidateStream(in, dataWritten);
   in = nullptr;
 }
-
-TEST(StorageStreams, EarlyInputStream)
-{
-  // generate some test data we will write in 4k chunks to the stream
-  nsTArray<char> kData;
-  testing::CreateData(4096, kData);
-
-  // track how much data was written so we can compare at the end
-  nsAutoCString dataWritten;
-
-  nsresult rv;
-  nsCOMPtr<nsIStorageStream> stor;
-
-  rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor));
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
-  // Get input stream before writing data into the output stream
-  nsCOMPtr<nsIInputStream> in;
-  rv = stor->NewInputStream(0, getter_AddRefs(in));
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
-  // Write data to output stream
-  nsCOMPtr<nsIOutputStream> out;
-  rv = stor->GetOutputStream(0, getter_AddRefs(out));
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
-  WriteData(out, kData, kData.Length(), dataWritten);
-  WriteData(out, kData, kData.Length(), dataWritten);
-
-  rv = out->Close();
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-  out = nullptr;
-
-  // Should be able to consume input stream
-  testing::ConsumeAndValidateStream(in, dataWritten);
-  in = nullptr;
-}