Merge inbound to mozilla-central r=merge a=merge
authorAndreea Pavel <apavel@mozilla.com>
Thu, 09 Nov 2017 22:11:53 +0200
changeset 444246 277e2ae05f747e257eaa73e36f1bc31b98a21af9
parent 444245 cbebb4f65eeaea4ac12c8e22383f0a739d2ae429 (current diff)
parent 444214 486620c2345ab558d0507c14a19e49f631b8347f (diff)
child 444247 7d84a639c2715e0278236c42a27486ae1fe79c4c
child 444305 5fa0729021dccefb542e8703d45471e1c58e3d81
child 444335 a4b37418f9024a3b113eb38a5980057ca58506ac
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
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
Merge inbound to mozilla-central r=merge a=merge
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -197,21 +197,17 @@ public:
                                              mImage,
                                              nullptr,
                                              nullptr,
                                              getter_AddRefs(stream),
                                              mEncoder);
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = stream->Available(aImgSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
-    rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
+    rv = NS_ReadInputStreamToBuffer(stream, aImgData, -1, aImgSize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return rv;
   }
 
   NS_IMETHOD Run() override
   {
     uint64_t imgSize;
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1411473.html
@@ -0,0 +1,12 @@
+<html>
+  <head class="reftest-wait">
+    <script>
+      var img = new Image(-256, 1024);
+      img.src = 'data:;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA';
+      img.onload = function () {
+        img.crossOrigin ="Anonymous";
+        document.implementation.createDocument('', '', null).adoptNode(img);
+      };
+    </script>
+  </head>
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -230,8 +230,9 @@ pref(clipboard.autocopy,true) load 13852
 load 1393806.html
 load 1396466.html
 load 1400701.html
 load 1403377.html
 load 1405771.html
 load 1406109-1.html
 pref(dom.webcomponents.enabled,true) load 1324463.html
 pref(dom.webcomponents.customelements.enabled,true) load 1413815.html
+load 1411473.html
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1624,27 +1624,25 @@ nsMessageManagerScriptExecutor::TryCache
     }
 
     nsCOMPtr<nsIInputStream> input;
     rv = channel->Open2(getter_AddRefs(input));
     NS_ENSURE_SUCCESS_VOID(rv);
     nsString dataString;
     char16_t* dataStringBuf = nullptr;
     size_t dataStringLength = 0;
-    uint64_t avail64 = 0;
-    if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) {
-      if (avail64 > UINT32_MAX) {
+    if (input) {
+      nsCString buffer;
+      uint64_t written;
+      if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
         return;
       }
-      nsCString buffer;
-      uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX);
-      if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) {
-        return;
-      }
-      ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail,
+
+      uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
+      ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), size,
                                    EmptyString(), nullptr,
                                    dataStringBuf, dataStringLength);
     }
 
     JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
                                   JS::SourceBufferHolder::GiveOwnership);
 
     if (!dataStringBuf || dataStringLength == 0) {
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_websocket_bigBlob_wsh.py
@@ -0,0 +1,9 @@
+from mod_pywebsocket import msgutil
+
+def web_socket_do_extra_handshake(request):
+  pass
+
+def web_socket_transfer_data(request):
+  while True:
+    line = msgutil.receive_message(request)
+    msgutil.send_message(request, line, True, True)
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -152,16 +152,17 @@ support-files =
   file_mozfiledataurl_img.jpg
   file_record_orientation.html
   file_restrictedEventSource.sjs
   file_settimeout_inner.html
   file_timer_flood.html
   file_viewport_scroll_quirks.html
   file_viewport_scroll_xml.xml
   file_websocket_basic_wsh.py
+  file_websocket_bigBlob_wsh.py
   file_websocket_hello_wsh.py
   file_websocket_http_resource.txt
   file_websocket_permessage_deflate_wsh.py
   file_websocket_permessage_deflate_disabled_wsh.py
   file_websocket_permessage_deflate_rejected_wsh.py
   file_websocket_permessage_deflate_params_wsh.py
   file_websocket_wsh.py
   file_x-frame-options_main.html
@@ -818,16 +819,18 @@ skip-if = toolkit == 'android'
 [test_websocket2.html]
 skip-if = toolkit == 'android'
 [test_websocket3.html]
 skip-if = toolkit == 'android'
 [test_websocket4.html]
 skip-if = toolkit == 'android'
 [test_websocket5.html]
 skip-if = toolkit == 'android'
+[test_websocket_bigBlob.html]
+skip-if = toolkit == 'android'
 [test_window_constructor.html]
 [test_window_content.html]
 [test_window_cross_origin_props.html]
 [test_window_define_nonconfigurable.html]
 [test_window_define_symbol.html]
 [test_window_element_enumeration.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_websocket_bigBlob.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
+  <title>WebSocket test - big blob on content side</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="websocket_helpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket_bigBlob");
+is(ws.readyState, 0, "Initial readyState is 0");
+ws.binaryType = "blob";
+
+ws.onopen = function() {
+  is(ws.readyState, 1, "Open readyState is 1");
+  ws.send(new Blob([new Array(1024*1024).join('123456789ABCDEF')]));
+}
+
+let receivedBlob;
+ws.onmessage = function(e) {
+  ok(e.data instanceof Blob, "We should be receiving a Blob");
+  receivedBlob = e.data;
+  ws.close();
+}
+
+ws.onclose = function(e) {
+  is(ws.readyState, 3, "Close readyState is 3");
+
+  // check blob contents
+  var reader = new FileReader();
+  reader.onload = function(event) {
+    is(reader.result, new Array(1024*1024).join('123456789ABCDEF'), "All data matches");
+  }
+
+  reader.onerror = function(event) {
+    ok(false, "Something bad happen.");
+  }
+
+  reader.onloadend = function(event) {
+    SimpleTest.finish();
+  }
+
+  reader.readAsBinaryString(receivedBlob);
+}
+
+SimpleTest.requestFlakyTimeout("The web socket tests are really fragile, but avoiding timeouts might be hard, since it's testing stuff on the network. " +
+                               "Expect all sorts of flakiness in this test...");
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -392,23 +392,18 @@ DataTransferItem::CreateFileFromInputStr
   nsAutoString fileName;
   nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                    key, fileName);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   uint64_t available;
-  rv = aStream->Available(&available);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
   void* data = nullptr;
-  rv = NS_ReadInputStreamToBuffer(aStream, &data, available);
+  rv = NS_ReadInputStreamToBuffer(aStream, &data, -1, &available);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return File::CreateMemoryFile(mDataTransfer, data, available, fileName,
                                 mType, PR_Now());
 }
 
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -956,30 +956,25 @@ HTMLCanvasElement::MozGetAsFileImpl(cons
 {
   nsCOMPtr<nsIInputStream> stream;
   nsAutoString type(aType);
   nsresult rv = ExtractData(nsContentUtils::GetCurrentJSContext(),
                             type, EmptyString(), getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t imgSize;
-  rv = stream->Available(&imgSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
   void* imgData = nullptr;
-  rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
+  rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(OwnerDoc()->GetScopeObject());
 
   // The File takes ownership of the buffer
   RefPtr<File> file =
-    File::CreateMemoryFile(win, imgData, (uint32_t)imgSize, aName, type,
-                           PR_Now());
+    File::CreateMemoryFile(win, imgData, imgSize, aName, type, PR_Now());
 
   file.forget(aResult);
   return NS_OK;
 }
 
 nsresult
 HTMLCanvasElement::GetContext(const nsAString& aContextId,
                               nsISupports** aContext)
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -695,17 +695,17 @@ HTMLImageElement::IntrinsicState() const
 
 void
 HTMLImageElement::NodeInfoChanged(nsIDocument* aOldDoc)
 {
   nsGenericHTMLElement::NodeInfoChanged(aOldDoc);
   // Force reload image if adoption steps are run.
   // If loading is temporarily disabled, don't even launch script runner.
   // Otherwise script runner may run later when someone has reenabled loading.
-  if (LoadingEnabled()) {
+  if (LoadingEnabled() && OwnerDoc()->ShouldLoadImages()) {
     // Use script runner for the case the adopt is from appendChild.
     // Bug 1076583 - We still behave synchronously in the non-responsive case
     nsContentUtils::AddScriptRunner(
       (InResponsiveMode())
         ? NewRunnableMethod<bool>("dom::HTMLImageElement::QueueImageLoadTask",
                                   this,
                                   &HTMLImageElement::QueueImageLoadTask,
                                   true)
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -35,17 +35,17 @@ function assertStackContainsSeq(got, exp
             continue;
         var parts = got[i].split(',');
         for (var j = 0; j < parts.length; j++) {
             var frame = parts[j];
             frame = frame.replace(/ \([^\)]*\)/g, "");
             frame = frame.replace(/fast FFI trampoline to native/g, "N");
             frame = frame.replace(/^call to( asm.js)? native .*\(in wasm\)$/g, "N");
             frame = frame.replace(/(fast|slow) FFI trampoline/g, "<");
-            frame = frame.replace(/entry trampoline/g, ">");
+            frame = frame.replace(/slow entry trampoline/g, ">");
             frame = frame.replace(/(\/[^\/,<]+)*\/testProfiling.js/g, "");
             frame = frame.replace(/testBuiltinD2D/g, "");
             frame = frame.replace(/testBuiltinF2F/g, "");
             frame = frame.replace(/testBuiltinDD2D/g, "");
             frame = frame.replace(/assertThrowsInstanceOf/g, "");
             frame = frame.replace(/^ffi[12]?/g, "");
             normalized.push(frame);
         }
--- a/js/src/jit-test/tests/wasm/profiling.js
+++ b/js/src/jit-test/tests/wasm/profiling.js
@@ -8,17 +8,17 @@ try {
 
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 const Table = WebAssembly.Table;
 
 function normalize(stack)
 {
     var wasmFrameTypes = [
-        {re:/^entry trampoline \(in wasm\)$/,                        sub:">"},
+        {re:/^slow entry trampoline \(in wasm\)$/,                   sub:">"},
         {re:/^wasm-function\[(\d+)\] \(.*\)$/,                       sub:"$1"},
         {re:/^(fast|slow) FFI trampoline (to native )?\(in wasm\)$/, sub:"<"},
         {re:/^call to[ asm.js]? native (.*) \(in wasm\)$/,           sub:"$1"},
         {re:/ \(in wasm\)$/,                                         sub:""}
     ];
 
     var framesIn = stack.split(',');
     var framesOut = [];
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -162,50 +162,50 @@ class CodeSegment
 // function definition index.
 
 class FuncExport
 {
     Sig sig_;
     MOZ_INIT_OUTSIDE_CTOR struct CacheablePod {
         uint32_t funcIndex_;
         uint32_t codeRangeIndex_;
-        uint32_t entryOffset_;      // Machine code offset
+        uint32_t interpEntryOffset_; // Machine code offset
     } pod;
 
   public:
     FuncExport() = default;
     explicit FuncExport(Sig&& sig, uint32_t funcIndex)
       : sig_(Move(sig))
     {
         pod.funcIndex_ = funcIndex;
         pod.codeRangeIndex_ = UINT32_MAX;
-        pod.entryOffset_ = UINT32_MAX;
+        pod.interpEntryOffset_ = UINT32_MAX;
     }
-    void initEntryOffset(uint32_t entryOffset) {
-        MOZ_ASSERT(pod.entryOffset_ == UINT32_MAX);
-        pod.entryOffset_ = entryOffset;
+    void initInterpEntryOffset(uint32_t entryOffset) {
+        MOZ_ASSERT(pod.interpEntryOffset_ == UINT32_MAX);
+        pod.interpEntryOffset_ = entryOffset;
     }
     void initCodeRangeIndex(uint32_t codeRangeIndex) {
         MOZ_ASSERT(pod.codeRangeIndex_ == UINT32_MAX);
         pod.codeRangeIndex_ = codeRangeIndex;
     }
 
     const Sig& sig() const {
         return sig_;
     }
     uint32_t funcIndex() const {
         return pod.funcIndex_;
     }
     uint32_t codeRangeIndex() const {
         MOZ_ASSERT(pod.codeRangeIndex_ != UINT32_MAX);
         return pod.codeRangeIndex_;
     }
-    uint32_t entryOffset() const {
-        MOZ_ASSERT(pod.entryOffset_ != UINT32_MAX);
-        return pod.entryOffset_;
+    uint32_t interpEntryOffset() const {
+        MOZ_ASSERT(pod.interpEntryOffset_ != UINT32_MAX);
+        return pod.interpEntryOffset_;
     }
 
     WASM_DECLARE_SERIALIZABLE(FuncExport)
 };
 
 typedef Vector<FuncExport, 0, SystemAllocPolicy> FuncExportVector;
 
 // An FuncImport contains the runtime metadata needed to implement a call to an
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -587,17 +587,17 @@ AssertMatchesCallSite(const JitActivatio
 {
 #ifdef DEBUG
     const Code* code = LookupCode(callerPC);
     MOZ_ASSERT(code);
 
     const CodeRange* callerCodeRange = code->lookupRange(callerPC);
     MOZ_ASSERT(callerCodeRange);
 
-    if (callerCodeRange->kind() == CodeRange::Entry) {
+    if (callerCodeRange->kind() == CodeRange::InterpEntry) {
         MOZ_ASSERT(callerFP == nullptr);
         return;
     }
 
     const CallSite* callsite = code->lookupCallSite(callerPC);
     MOZ_ASSERT(callsite);
 #endif
 }
@@ -619,17 +619,17 @@ ProfilingFrameIterator::initFromExitFP(c
     // Since we don't have the pc for fp, start unwinding at the caller of fp.
     // This means that the innermost frame is skipped. This is fine because:
     //  - for import exit calls, the innermost frame is a thunk, so the first
     //    frame that shows up is the function calling the import;
     //  - for Math and other builtin calls as well as interrupts, we note the absence
     //    of an exit reason and inject a fake "builtin" frame; and
     //  - for async interrupts, we just accept that we'll lose the innermost frame.
     switch (codeRange_->kind()) {
-      case CodeRange::Entry:
+      case CodeRange::InterpEntry:
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         break;
       case CodeRange::Function:
         fp = fp->callerFP;
         callerPC_ = fp->returnAddress;
         callerFP_ = fp->callerFP;
         AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
@@ -811,17 +811,17 @@ js::wasm::StartUnwinding(const JitActiva
       case CodeRange::UnalignedExit:
         // These code stubs execute after the prologue/epilogue have completed
         // so pc/fp contains the right values here.
         fixedPC = pc;
         fixedFP = fp;
         *unwoundCaller = false;
         AssertMatchesCallSite(activation, fp->returnAddress, fp->callerFP);
         break;
-      case CodeRange::Entry:
+      case CodeRange::InterpEntry:
         // The entry trampoline is the final frame in an wasm JitActivation. The
         // entry trampoline also doesn't GeneratePrologue/Epilogue so we can't
         // use the general unwinding logic above.
         break;
       case CodeRange::Throw:
         // The throw stub executes a small number of instructions before popping
         // the entire activation. To simplify testing, we simply pretend throw
         // stubs have already popped the entire stack.
@@ -894,17 +894,17 @@ ProfilingFrameIterator::operator++()
         MOZ_ASSERT(!callerFP_);
         codeRange_ = nullptr;
         MOZ_ASSERT(done());
         return;
     }
 
     if (!callerFP_) {
         codeRange_ = code_->lookupRange(callerPC_);
-        MOZ_ASSERT(codeRange_->kind() == CodeRange::Entry);
+        MOZ_ASSERT(codeRange_->kind() == CodeRange::InterpEntry);
         callerPC_ = nullptr;
         MOZ_ASSERT(!done());
         return;
     }
 
     code_ = &callerFP_->tls->instance->code();
     MOZ_ASSERT(code_ == LookupCode(callerPC_));
 
@@ -921,17 +921,17 @@ ProfilingFrameIterator::operator++()
       case CodeRange::OutOfBoundsExit:
       case CodeRange::UnalignedExit:
       case CodeRange::FarJumpIsland:
         stackAddress_ = callerFP_;
         callerPC_ = callerFP_->returnAddress;
         AssertMatchesCallSite(*activation_, callerPC_, callerFP_->callerFP);
         callerFP_ = callerFP_->callerFP;
         break;
-      case CodeRange::Entry:
+      case CodeRange::InterpEntry:
         MOZ_CRASH("should have had null caller fp");
       case CodeRange::Interrupt:
       case CodeRange::Throw:
         MOZ_CRASH("code range doesn't have frame");
     }
 
     MOZ_ASSERT(!done());
 }
@@ -1047,19 +1047,16 @@ ThunkedNativeToDescription(SymbolicAddre
 
 const char*
 ProfilingFrameIterator::label() const
 {
     MOZ_ASSERT(!done());
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
-    //
-    // NB: these labels are parsed for location by
-    //     devtools/client/performance/modules/logic/frame-utils.js
     static const char* importJitDescription = "fast FFI trampoline (in wasm)";
     static const char* importInterpDescription = "slow FFI trampoline (in wasm)";
     static const char* builtinNativeDescription = "fast FFI trampoline to native (in wasm)";
     static const char* trapDescription = "trap handling (in wasm)";
     static const char* debugTrapDescription = "debug trap handling (in wasm)";
 
     if (!exitReason_.isFixed())
         return ThunkedNativeToDescription(exitReason_.symbolic());
@@ -1076,17 +1073,17 @@ ProfilingFrameIterator::label() const
       case ExitReason::Fixed::Trap:
         return trapDescription;
       case ExitReason::Fixed::DebugTrap:
         return debugTrapDescription;
     }
 
     switch (codeRange_->kind()) {
       case CodeRange::Function:          return code_->profilingLabel(codeRange_->funcIndex());
-      case CodeRange::Entry:             return "entry trampoline (in wasm)";
+      case CodeRange::InterpEntry:       return "slow entry trampoline (in wasm)";
       case CodeRange::ImportJitExit:     return importJitDescription;
       case CodeRange::BuiltinThunk:      return builtinNativeDescription;
       case CodeRange::ImportInterpExit:  return importInterpDescription;
       case CodeRange::TrapExit:          return trapDescription;
       case CodeRange::DebugTrap:         return debugTrapDescription;
       case CodeRange::OutOfBoundsExit:   return "out-of-bounds stub (in wasm)";
       case CodeRange::UnalignedExit:     return "unaligned trap stub (in wasm)";
       case CodeRange::FarJumpIsland:     return "interstitial (in wasm)";
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -489,18 +489,18 @@ ModuleGenerator::linkCallSites()
 void
 ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange)
 {
     switch (codeRange.kind()) {
       case CodeRange::Function:
         MOZ_ASSERT(funcToCodeRange_[codeRange.funcIndex()] == BAD_CODE_RANGE);
         funcToCodeRange_[codeRange.funcIndex()] = codeRangeIndex;
         break;
-      case CodeRange::Entry:
-        metadataTier_->lookupFuncExport(codeRange.funcIndex()).initEntryOffset(codeRange.begin());
+      case CodeRange::InterpEntry:
+        metadataTier_->lookupFuncExport(codeRange.funcIndex()).initInterpEntryOffset(codeRange.begin());
         break;
       case CodeRange::ImportJitExit:
         metadataTier_->funcImports[codeRange.funcIndex()].initJitExitOffset(codeRange.begin());
         break;
       case CodeRange::ImportInterpExit:
         metadataTier_->funcImports[codeRange.funcIndex()].initInterpExitOffset(codeRange.begin());
         break;
       case CodeRange::TrapExit:
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -689,17 +689,17 @@ Instance::callExport(JSContext* cx, uint
           }
         }
     }
 
     {
         JitActivation activation(cx);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
-        auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase(tier) + func.entryOffset());
+        auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase(tier) + func.interpEntryOffset());
         if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), tlsData()))
             return false;
     }
 
     if (isAsmJS() && args.isConstructing()) {
         // By spec, when a JS function is called as a constructor and this
         // function returns a primary type, which is the case for all asm.js
         // exported functions, the returned value is discarded and an empty
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -251,17 +251,17 @@ static const unsigned NonVolatileRegsPus
 #endif
 static const unsigned FramePushedBeforeAlign = NonVolatileRegsPushSize + sizeof(void*);
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI. The
 // signature of the entry point is Module::ExportFuncPtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of ExportFuncPtr to the export's signature's ABI.
 static bool
-GenerateEntry(MacroAssembler& masm, const FuncExport& fe, Offsets* offsets)
+GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe, Offsets* offsets)
 {
     masm.haltingAlign(CodeAlignment);
 
     offsets->begin = masm.currentOffset();
 
     // Save the return address if it wasn't already saved by the call insn.
 #if defined(JS_CODEGEN_ARM)
     masm.push(lr);
@@ -1365,19 +1365,19 @@ wasm::GenerateStubs(const ModuleEnvironm
         if (!GenerateImportJitExit(masm, fi, &throwLabel, &jitOffsets))
             return false;
         if (!code->codeRanges.emplaceBack(funcIndex, jitOffsets))
             return false;
     }
 
     for (const FuncExport& fe : exports) {
         Offsets offsets;
-        if (!GenerateEntry(masm, fe, &offsets))
+        if (!GenerateInterpEntry(masm, fe, &offsets))
             return false;
-        if (!code->codeRanges.emplaceBack(CodeRange::Entry, fe.funcIndex(), offsets))
+        if (!code->codeRanges.emplaceBack(CodeRange::InterpEntry, fe.funcIndex(), offsets))
             return false;
     }
 
     for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
         CallableOffsets offsets;
         if (!GenerateTrapExit(masm, trap, &throwLabel, &offsets))
             return false;
         if (!code->codeRanges.emplaceBack(trap, offsets))
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -702,17 +702,17 @@ CodeRange::CodeRange(Kind kind, uint32_t
     ret_(0),
     end_(offsets.end),
     kind_(kind)
 {
     u.funcIndex_ = funcIndex;
     u.func.lineOrBytecode_ = 0;
     u.func.beginToNormalEntry_ = 0;
     u.func.beginToTierEntry_ = 0;
-    MOZ_ASSERT(kind == Entry);
+    MOZ_ASSERT(kind == InterpEntry);
     MOZ_ASSERT(begin_ <= end_);
 }
 
 CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
   : begin_(offsets.begin),
     ret_(offsets.ret),
     end_(offsets.end),
     kind_(kind)
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -988,17 +988,17 @@ typedef Vector<FuncOffsets, 0, SystemAll
 // module's code segment. A CodeRange describes what the code does and, for
 // function bodies, the name and source coordinates of the function.
 
 class CodeRange
 {
   public:
     enum Kind {
         Function,          // function definition
-        Entry,             // calls into wasm from C++
+        InterpEntry,       // calls into wasm from C++
         ImportJitExit,     // fast-path calling from wasm into JIT code
         ImportInterpExit,  // slow-path calling from wasm into C++ interp
         BuiltinThunk,      // fast-path calling from wasm into a C++ native
         TrapExit,          // calls C++ to report and jumps to throw stub
         DebugTrap,         // calls C++ to handle debug event
         FarJumpIsland,     // inserted to connect otherwise out-of-range insns
         OutOfBoundsExit,   // stub jumped to by non-standard asm.js SIMD/Atomics
         UnalignedExit,     // stub jumped to by non-standard ARM unaligned trap
@@ -1092,17 +1092,17 @@ class CodeRange
         MOZ_ASSERT(hasReturn());
         return ret_;
     }
 
     // Functions, export stubs and import stubs all have an associated function
     // index.
 
     bool hasFuncIndex() const {
-        return isFunction() || isImportExit() || kind() == Entry;
+        return isFunction() || isImportExit() || kind() == InterpEntry;
     }
     uint32_t funcIndex() const {
         MOZ_ASSERT(hasFuncIndex());
         return u.funcIndex_;
     }
 
     // TrapExit CodeRanges have a Trap field.
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -8,16 +8,18 @@
 #include "HttpLog.h"
 
 #include "nsNetUtil.h"
 
 #include "mozilla/Encoding.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
 #include "nsCategoryCache.h"
 #include "nsContentUtils.h"
 #include "nsHashKeys.h"
 #include "nsHttp.h"
 #include "nsIAsyncStreamCopier.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
@@ -1406,55 +1408,369 @@ NS_NewPostDataStream(nsIInputStream  **r
     rv = stream->SetData(data.BeginReading(), data.Length());
     if (NS_FAILED(rv))
         return rv;
 
     stream.forget(result);
     return NS_OK;
 }
 
+namespace {
+
+#define BUFFER_SIZE 4096
+
+class BufferWriter final : public Runnable
+                         , public nsIInputStreamCallback
+{
+public:
+    NS_DECL_ISUPPORTS_INHERITED
+
+    BufferWriter(nsIInputStream* aInputStream,
+                 void* aBuffer, int64_t aCount)
+        : Runnable("BufferWriterRunnable")
+        , mMonitor("BufferWriter.mMonitor")
+        , mInputStream(aInputStream)
+        , mBuffer(aBuffer)
+        , mCount(aCount)
+        , mWrittenData(0)
+        , mBufferType(mBuffer ? eExternal : eInternal)
+        , mAsyncResult(NS_OK)
+        , mBufferSize(0)
+    {
+        MOZ_ASSERT(aInputStream);
+        MOZ_ASSERT(aCount == -1 || aCount > 0);
+        MOZ_ASSERT_IF(mBuffer, aCount > 0);
+    }
+
+    nsresult
+    Write()
+    {
+        // Let's make the inputStream buffered if it's not.
+        if (!NS_InputStreamIsBuffered(mInputStream)) {
+            nsCOMPtr<nsIInputStream> bufferedStream;
+            nsresult rv =
+                NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
+                                          mInputStream.forget(), BUFFER_SIZE);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            mInputStream = bufferedStream;
+        }
+
+        mAsyncInputStream = do_QueryInterface(mInputStream);
+
+        if (!mAsyncInputStream) {
+            return WriteSync();
+        }
+
+        // Let's use mAsyncInputStream only.
+        mInputStream = nullptr;
+
+        return WriteAsync();
+    }
+
+    uint64_t
+    WrittenData() const
+    {
+        return mWrittenData;
+    }
+
+    void*
+    StealBuffer()
+    {
+        MOZ_ASSERT(mBufferType == eInternal);
+
+        void* buffer = mBuffer;
+        mBuffer = nullptr;
+
+        return buffer;
+    }
+
+private:
+    ~BufferWriter()
+    {
+        if (mBuffer && mBufferType == eInternal) {
+            free(mBuffer);
+        }
+
+        if (mTaskQueue) {
+            mTaskQueue->BeginShutdown();
+        }
+    }
+
+    nsresult
+    WriteSync()
+    {
+        uint64_t length = (uint64_t)mCount;
+
+        if (mCount == -1) {
+            nsresult rv = mInputStream->Available(&length);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            if (length == 0) {
+                // nothing to read.
+                return NS_OK;
+            }
+        }
+
+        if (mBufferType == eInternal) {
+            mBuffer = malloc(length);
+            if (NS_WARN_IF(!mBuffer)) {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+        }
+
+        uint32_t writtenData;
+        nsresult rv = mInputStream->ReadSegments(NS_CopySegmentToBuffer,
+                                                 mBuffer, length,
+                                                 &writtenData);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mWrittenData = writtenData;
+        return NS_OK;
+    }
+
+    nsresult
+    WriteAsync()
+    {
+        if (mCount > 0 && mBufferType == eInternal) {
+            mBuffer = malloc(mCount);
+            if (NS_WARN_IF(!mBuffer)) {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+        }
+
+        nsCOMPtr<nsIEventTarget> target =
+            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+        if (!target) {
+            return NS_ERROR_FAILURE;
+        }
+
+        mTaskQueue = new TaskQueue(target.forget());
+
+        MonitorAutoLock lock(mMonitor);
+
+        nsCOMPtr<nsIRunnable> runnable = this;
+        nsresult rv = mTaskQueue->Dispatch(runnable.forget(),
+                                           AbstractThread::DontAssertDispatchSuccess);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        lock.Wait();
+
+        return mAsyncResult;
+    }
+
+    // This method runs on the I/O Thread when the owning thread is blocked by
+    // the mMonitor. It is called multiple times until mCount is greater than 0
+    // or until there is something to read in the stream.
+    NS_IMETHOD
+    Run() override
+    {
+        MOZ_ASSERT(mAsyncInputStream);
+        MOZ_ASSERT(!mInputStream);
+
+        if (mCount == 0) {
+            OperationCompleted(NS_OK);
+            return NS_OK;
+        }
+
+        if (mCount == -1 && !MaybeExpandBufferSize()) {
+            OperationCompleted(NS_ERROR_OUT_OF_MEMORY);
+            return NS_OK;
+        }
+
+        uint64_t offset = mWrittenData;
+        uint64_t length = mCount == -1 ? BUFFER_SIZE : mCount;
+
+        // Let's try to read it directly.
+        uint32_t writtenData;
+        nsresult rv = mAsyncInputStream->ReadSegments(NS_CopySegmentToBuffer,
+                                                     static_cast<char*>(mBuffer) + offset,
+                                                     length, &writtenData);
+
+        // Operation completed.
+        if (NS_SUCCEEDED(rv) && writtenData == 0) {
+            OperationCompleted(NS_OK);
+            return NS_OK;
+        }
+
+        // If we succeeded, let's try to read again.
+        if (NS_SUCCEEDED(rv)) {
+            mWrittenData += writtenData;
+            if (mCount != -1) {
+                MOZ_ASSERT(mCount >= writtenData);
+                mCount -= writtenData;
+            }
+
+            nsCOMPtr<nsIRunnable> runnable = this;
+            rv = mTaskQueue->Dispatch(runnable.forget(),
+                                      AbstractThread::DontAssertDispatchSuccess);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+                OperationCompleted(rv);
+            }
+        
+            return NS_OK;
+        }
+
+        // Async wait...
+        if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+            rv = mAsyncInputStream->AsyncWait(this, 0, length, mTaskQueue);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+                OperationCompleted(rv);
+            }
+            return NS_OK;
+        }
+
+        // Error.
+        OperationCompleted(rv);
+        return NS_OK;
+    }
+
+    NS_IMETHOD
+    OnInputStreamReady(nsIAsyncInputStream* aStream) override
+    {
+        MOZ_ASSERT(aStream == mAsyncInputStream);
+        // The stream is ready, let's read it again.
+        return Run();
+    }
+
+    // This function is called from the I/O thread and it will unblock the
+    // owning thread.
+    void
+    OperationCompleted(nsresult aRv)
+    {
+        MonitorAutoLock lock(mMonitor);
+
+        mAsyncResult = aRv;
+
+        // This will unlock the owning thread.
+        lock.Notify();
+    }
+
+    bool
+    MaybeExpandBufferSize()
+    {
+        MOZ_ASSERT(mCount == -1);
+
+        if (mBufferSize >= mWrittenData + BUFFER_SIZE) {
+            // The buffer is big enough.
+            return true;
+        }
+
+        CheckedUint32 bufferSize =
+            std::max<uint32_t>(static_cast<uint32_t>(mWrittenData),
+                               BUFFER_SIZE);
+        while (bufferSize.isValid() &&
+               bufferSize.value() < mWrittenData + BUFFER_SIZE) {
+            bufferSize *= 2;
+        }
+
+        if (!bufferSize.isValid()) {
+            return false;
+        }
+
+        void* buffer = realloc(mBuffer, bufferSize.value());
+        if (!buffer) {
+            return false;
+        }
+
+        mBuffer = buffer;
+        mBufferSize = bufferSize.value();
+        return true;
+    }
+
+    Monitor mMonitor;
+
+    nsCOMPtr<nsIInputStream> mInputStream;
+    nsCOMPtr<nsIAsyncInputStream> mAsyncInputStream;
+
+    RefPtr<TaskQueue> mTaskQueue;
+
+    void* mBuffer;
+    int64_t mCount;
+    uint64_t mWrittenData;
+
+    enum {
+        // The buffer is allocated internally and this object must release it
+        // in the DTOR if not stolen. The buffer can be reallocated.
+        eInternal,
+
+        // The buffer is not owned by this object and it cannot be reallocated.
+        eExternal,
+    } mBufferType;
+
+    // The following set if needed for the async read.
+    nsresult mAsyncResult;
+    uint64_t mBufferSize;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(BufferWriter, Runnable, nsIInputStreamCallback)
+
+} // anonymous namespace
+
 nsresult
-NS_ReadInputStreamToBuffer(nsIInputStream *aInputStream,
-                           void **aDest,
-                           uint32_t aCount)
+NS_ReadInputStreamToBuffer(nsIInputStream* aInputStream,
+                           void** aDest,
+                           int64_t aCount,
+                           uint64_t* aWritten)
 {
-    nsresult rv;
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(aCount >= -1);
+
+    uint64_t dummyWritten;
+    if (!aWritten) {
+        aWritten = &dummyWritten;
+    }
+
+    if (aCount == 0) {
+        *aWritten = 0;
+        return NS_OK;
+    }
+
+    // This will take care of allocating and reallocating aDest.
+    RefPtr<BufferWriter> writer =
+       new BufferWriter(aInputStream, *aDest, aCount);
+
+    nsresult rv = writer->Write();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aWritten = writer->WrittenData();
 
     if (!*aDest) {
-        *aDest = malloc(aCount);
-        if (!*aDest)
-            return NS_ERROR_OUT_OF_MEMORY;
+        *aDest = writer->StealBuffer();
     }
 
-    char * p = reinterpret_cast<char*>(*aDest);
-    uint32_t bytesRead;
-    uint32_t totalRead = 0;
-    while (1) {
-        rv = aInputStream->Read(p + totalRead, aCount - totalRead, &bytesRead);
-        if (!NS_SUCCEEDED(rv))
-            return rv;
-        totalRead += bytesRead;
-        if (totalRead == aCount)
-            break;
-        // if Read reads 0 bytes, we've hit EOF
-        if (bytesRead == 0)
-            return NS_ERROR_UNEXPECTED;
-    }
-    return rv;
+    return NS_OK;
 }
 
 nsresult
-NS_ReadInputStreamToString(nsIInputStream *aInputStream,
-                           nsACString &aDest,
-                           uint32_t aCount)
+NS_ReadInputStreamToString(nsIInputStream* aInputStream,
+                           nsACString& aDest,
+                           int64_t aCount,
+                           uint64_t* aWritten)
 {
-    if (!aDest.SetLength(aCount, mozilla::fallible))
-        return NS_ERROR_OUT_OF_MEMORY;
-    void* dest = aDest.BeginWriting();
-    return NS_ReadInputStreamToBuffer(aInputStream, &dest, aCount);
+    uint64_t dummyWritten;
+    if (!aWritten) {
+        aWritten = &dummyWritten;
+    }
+
+    void* dest = nullptr;
+    nsresult rv = NS_ReadInputStreamToBuffer(aInputStream, &dest, aCount,
+                                             aWritten);
+    MOZ_ASSERT_IF(NS_FAILED(rv), dest == nullptr);
+
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!dest) {
+      MOZ_ASSERT(*aWritten == 0);
+      aDest.Truncate();
+      return NS_OK;
+    }
+
+    aDest.Adopt(reinterpret_cast<char*>(dest), *aWritten);
+    return NS_OK;
 }
 
 nsresult
 NS_NewURI(nsIURI **result,
           const nsACString &spec,
           const char *charset /* = nullptr */,
           nsIURI *baseURI /* = nullptr */,
           nsIIOService *ioService /* = nullptr */)     // pass in nsIIOService to optimize callers
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -521,23 +521,43 @@ nsresult NS_NewBufferedOutputStream(nsIO
                                     already_AddRefed<nsIOutputStream> aOutputStream,
                                     uint32_t aBufferSize);
 
 // returns an input stream compatible with nsIUploadChannel::SetUploadStream()
 nsresult NS_NewPostDataStream(nsIInputStream  **result,
                               bool              isFile,
                               const nsACString &data);
 
+/**
+ * This function reads an inputStream and stores its content into a buffer. In
+ * general, you should avoid using this function because, it blocks the current
+ * thread until the operation is done.
+ * If the inputStream is async, the reading happens on an I/O thread.
+ *
+ * @param aInputStream the inputStream.
+ * @param aDest the destination buffer. if *aDest is null, it will be allocated
+ *              with the size of the written data. if aDest is not null, aCount
+ *              must greater than 0.
+ * @param aCount the amount of data to read. Use -1 if you want that all the
+ *               stream is read.
+ * @param aWritten this pointer will be used to store the number of data
+ *                 written in the buffer. If you don't need, pass nullptr.
+ */
 nsresult NS_ReadInputStreamToBuffer(nsIInputStream *aInputStream,
                                     void **aDest,
-                                    uint32_t aCount);
+                                    int64_t aCount,
+                                    uint64_t* aWritten = nullptr);
 
+/**
+ * See the comment for NS_ReadInputStreamToBuffer
+ */
 nsresult NS_ReadInputStreamToString(nsIInputStream *aInputStream,
                                     nsACString &aDest,
-                                    uint32_t aCount);
+                                    int64_t aCount,
+                                    uint64_t* aWritten = nullptr);
 
 nsresult
 NS_LoadPersistentPropertiesFromURISpec(nsIPersistentProperties **outResult,
                                        const nsACString         &aSpec);
 
 /**
  * NS_QueryNotificationCallbacks implements the canonical algorithm for
  * querying interfaces from a channel's notification callbacks.  It first
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1042,23 +1042,16 @@ public:
       return BeginReading();
     return (uint8_t *)(mMsg.pString.mOrigValue ? mMsg.pString.mOrigValue->BeginReading() : nullptr);
   }
 
   nsresult ConvertStreamToString()
   {
     MOZ_ASSERT(mMsgType == kMsgTypeStream, "Not a stream!");
 
-#ifdef DEBUG
-    // Make sure we got correct length from Blob
-    uint64_t bytes;
-    mMsg.pStream->Available(&bytes);
-    NS_ASSERTION(bytes == mLength, "Stream length != blob length!");
-#endif
-
     nsAutoPtr<nsCString> temp(new nsCString());
     nsresult rv = NS_ReadInputStreamToString(mMsg.pStream, *temp, mLength);
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     mMsg.pStream->Close();
     mMsg.pStream->Release();
     mMsg.pString.mValue = temp.forget();
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -2899,29 +2899,24 @@ DataChannelConnection::ReadBlob(already_
   // ~DataChannelConnection() to run on MainThread
 
   // XXX to do this safely, we must enqueue these atomically onto the
   // output socket.  We need a sender thread(s?) to enqueue data into the
   // socket and to avoid main-thread IO that might block.  Even on a
   // background thread, we may not want to block on one stream's data.
   // I.e. run non-blocking and service multiple channels.
 
-  // For now as a hack, send as a single blast of queued packets which may
-  // be deferred until buffer space is available.
-  uint64_t len;
-
   // Must not let Dispatching it cause the DataChannelConnection to get
   // released on the wrong thread.  Using WrapRunnable(RefPtr<DataChannelConnection>(aThis),...
   // will occasionally cause aThis to get released on this thread.  Also, an explicit Runnable
   // lets us avoid copying the blob data an extra time.
   RefPtr<DataChannelBlobSendRunnable> runnable = new DataChannelBlobSendRunnable(aThis,
-                                                                                   aStream);
+                                                                                 aStream);
   // avoid copying the blob data by passing the mData from the runnable
-  if (NS_FAILED(aBlob->Available(&len)) ||
-      NS_FAILED(NS_ReadInputStreamToString(aBlob, runnable->mData, len))) {
+  if (NS_FAILED(NS_ReadInputStreamToString(aBlob, runnable->mData, -1))) {
     // Bug 966602:  Doesn't return an error to the caller via onerror.
     // We must release DataChannelConnection on MainThread to avoid issues (bug 876167)
     // aThis is now owned by the runnable; release it there
     NS_ReleaseOnMainThreadSystemGroup(
       "DataChannelBlobSendRunnable", runnable.forget());
     return;
   }
   aBlob->Close();
new file mode 100644
--- /dev/null
+++ b/netwerk/test/gtest/TestReadStreamToString.cpp
@@ -0,0 +1,156 @@
+#include "gtest/gtest.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetUtil.h"
+
+// Here we test the reading the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamFullSize) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream;
+  ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+  uint64_t written;
+  nsAutoCString result;
+
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, buffer.Length(),
+                                              &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading less than the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamLessThan) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream;
+  ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+  uint64_t written;
+  nsAutoCString result;
+
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, 5, &written));
+  ASSERT_EQ((uint64_t)5, written);
+  ASSERT_TRUE(nsCString(buffer.get(), 5).Equals(result));
+}
+
+// Here we test the reading more than the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamMoreThan) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream;
+  ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+  uint64_t written;
+  nsAutoCString result;
+
+  // Reading more than the buffer size.
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result,
+                                              buffer.Length() + 5, &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading a sync stream without passing the size
+TEST(TestReadStreamToString, SyncStreamUnknownSize) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream;
+  ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+  uint64_t written;
+  nsAutoCString result;
+
+  // Reading all without passing the size
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamFullSize) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+  uint64_t written;
+  nsAutoCString result;
+
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, buffer.Length(),
+                                              &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading less than the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamLessThan) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+  uint64_t written;
+  nsAutoCString result;
+
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, 5, &written));
+  ASSERT_EQ((uint64_t)5, written);
+  ASSERT_TRUE(nsCString(buffer.get(), 5).Equals(result));
+}
+
+// Here we test the reading more than the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamMoreThan) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+  uint64_t written;
+  nsAutoCString result;
+
+  // Reading more than the buffer size.
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result,
+                                              buffer.Length() + 5, &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading an async stream without passing the size
+TEST(TestReadStreamToString, AsyncStreamUnknownSize) {
+  nsCString buffer;
+  buffer.AssignLiteral("Hello world!");
+
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+  uint64_t written;
+  nsAutoCString result;
+
+  // Reading all without passing the size
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading an async big stream without passing the size
+TEST(TestReadStreamToString, AsyncStreamUnknownBigSize) {
+  nsCString buffer;
+
+  buffer.SetLength(4096 * 2);
+  for (uint32_t i = 0; i < 4096 * 2; ++i) {
+    buffer.BeginWriting()[i] = i % 10;
+  }
+
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+  uint64_t written;
+  nsAutoCString result;
+
+  // Reading all without passing the size
+  ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+  ASSERT_EQ(buffer.Length(), written);
+  ASSERT_TRUE(buffer.Equals(result));
+}
--- a/netwerk/test/gtest/moz.build
+++ b/netwerk/test/gtest/moz.build
@@ -6,16 +6,17 @@
 
 UNIFIED_SOURCES += [
     'TestBufferedInputStream.cpp',
     'TestHeaders.cpp',
     'TestHttpAuthUtils.cpp',
     'TestMozURL.cpp',
     'TestPartiallySeekableInputStream.cpp',
     'TestProtocolProxyService.cpp',
+    'TestReadStreamToString.cpp',
     'TestStandardURL.cpp',
 ]
 
 TEST_DIRS += [
     'parse-ftp',
 ]
 
 LOCAL_INCLUDES += [