Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 28 Jan 2015 14:36:44 -0500
changeset 239635 4fab18f1ff555ca5826fffc86ff1761dcb5d058d
parent 239606 9b6b80222e663e5fe1a9327f535266f914ff684c (current diff)
parent 239634 13dd7bfcb9b1864327c8b5c2b69c739ef92befa0 (diff)
child 239662 1daa622bbe42f8a85e0b4880c5c25df8ea60e95f
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewersmerge
milestone38.0a1
Merge inbound to m-c. a=merge
js/src/jit-test/tests/ion/simd-bug1121299.js
js/src/jit-test/tests/ion/simd-bug1123631.js
js/src/jit-test/tests/ion/simd-unbox.js
--- a/browser/components/feeds/FeedConverter.js
+++ b/browser/components/feeds/FeedConverter.js
@@ -230,33 +230,38 @@ FeedConverter.prototype = {
         }
       }
           
       var ios = 
           Cc["@mozilla.org/network/io-service;1"].
           getService(Ci.nsIIOService);
       var chromeChannel;
 
+      // handling a redirect, hence forwarding the loadInfo from the old channel
+      // to the newchannel.
+      var oldChannel = this._request.QueryInterface(Ci.nsIChannel);
+      var loadInfo = oldChannel.loadInfo;
+
       // If there was no automatic handler, or this was a podcast,
       // photostream or some other kind of application, show the preview page
       // if the parser returned a document.
       if (result.doc) {
 
         // Store the result in the result service so that the display
         // page can access it.
         feedService.addFeedResult(result);
 
         // Now load the actual XUL document.
         var aboutFeedsURI = ios.newURI("about:feeds", null, null);
-        chromeChannel = ios.newChannelFromURI(aboutFeedsURI, null);
+        chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
         chromeChannel.originalURI = result.uri;
         chromeChannel.owner =
           Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI);
       } else {
-        chromeChannel = ios.newChannelFromURI(result.uri, null);
+        chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
       }
 
       chromeChannel.loadGroup = this._request.loadGroup;
       chromeChannel.asyncOpen(this._listener, null);
     }
     finally {
       this._releaseHandles();
     }
@@ -541,17 +546,22 @@ GenericProtocolHandler.prototype = {
     var uri = netutil.newSimpleNestedURI(inner);
     uri.spec = inner.spec.replace(prefix, scheme);
     return uri;
   },
   
   newChannel: function GPH_newChannel(aUri) {
     var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI;
     var channel = Cc["@mozilla.org/network/io-service;1"].
-                  getService(Ci.nsIIOService).newChannelFromURI(inner, null);
+                  getService(Ci.nsIIOService).newChannelFromURI2(inner,
+                                                                 null,      // aLoadingNode
+                                                                 Services.scriptSecurityManager.getSystemPrincipal(),
+                                                                 null,      // aTriggeringPrincipal
+                                                                 Ci.nsILoadInfo.SEC_NORMAL,
+                                                                 Ci.nsIContentPolicy.TYPE_OTHER);
     if (channel instanceof Components.interfaces.nsIHttpChannel)
       // Set this so we know this is supposed to be a feed
       channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
     channel.originalURI = aUri;
     return channel;
   },
   
   QueryInterface: function GPH_QueryInterface(iid) {
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -1123,19 +1123,29 @@ FeedWriter.prototype = {
    * @param aWindow 
    *        The window of the document invoking the BrowserFeedWriter
    */
   _getOriginalURI: function FW__getOriginalURI(aWindow) {
     var chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
                getInterface(Ci.nsIWebNavigation).
                QueryInterface(Ci.nsIDocShell).currentDocumentChannel;
 
+    var nullPrincipal = Cc["@mozilla.org/nullprincipal;1"].
+                        createInstance(Ci.nsIPrincipal);
+
     var resolvedURI = Cc["@mozilla.org/network/io-service;1"].
                       getService(Ci.nsIIOService).
-                      newChannel("about:feeds", null, null).URI;
+                      newChannel2("about:feeds",
+                                  null,
+                                  null,
+                                  null, // aLoadingNode
+                                  nullPrincipal,
+                                  null, // aTriggeringPrincipal
+                                  Ci.nsILoadInfo.SEC_NORMAL,
+                                  Ci.nsIContentPolicy.TYPE_OTHER).URI;
 
     if (resolvedURI.equals(chan.URI))
       return chan.originalURI;
 
     return null;
   },
 
   _window: null,
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -891,23 +891,26 @@ Messages.Simple.prototype = Heritage.ext
    * @private
    * @return Element
    */
   _renderBody: function()
   {
     let body = this.document.createElementNS(XHTML_NS, "span");
     body.className = "message-body-wrapper message-body devtools-monospace";
 
-    let anchor, container = body;
+    let bodyInner = this.document.createElementNS(XHTML_NS, "span");
+    body.appendChild(bodyInner);
+
+    let anchor, container = bodyInner;
     if (this._link || this._linkCallback) {
       container = anchor = this.document.createElementNS(XHTML_NS, "a");
       anchor.href = this._link || "#";
       anchor.draggable = false;
       this._addLinkCallback(anchor, this._linkCallback);
-      body.appendChild(anchor);
+      bodyInner.appendChild(anchor);
     }
 
     if (typeof this._message == "function") {
       container.appendChild(this._message(this));
     } else if (this._message instanceof Ci.nsIDOMNode) {
       container.appendChild(this._message);
     } else {
       container.textContent = this._message;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
@@ -52,17 +52,19 @@ function consoleOpened(aHud) {
     HUD.ui.output.selectMessage(msg);
 
     outputNode.focus();
 
     goUpdateCommand("cmd_copy");
     controller = top.document.commandDispatcher.getControllerForCommand("cmd_copy");
     is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-    let selection = HUD.iframeWindow.getSelection() + "";
+    // Remove new lines since getSelection() includes one between message and line
+    // number, but the clipboard doesn't (see bug 1119503)
+    let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
     isnot(selection.indexOf("bug587617"), -1,
           "selection text includes 'bug587617'");
 
     waitForClipboard((str) => { return selection.trim() == str.trim(); },
       () => { goDoCommand("cmd_copy") },
       deferred.resolve, deferred.resolve);
   });
   return deferred.promise;
@@ -75,17 +77,19 @@ function testContextMenuCopy() {
 
   let contextMenuId = outputNode.parentNode.getAttribute("context");
   let contextMenu = HUD.ui.document.getElementById(contextMenuId);
   ok(contextMenu, "the output node has a context menu");
 
   let copyItem = contextMenu.querySelector("*[command='cmd_copy']");
   ok(copyItem, "the context menu on the output node has a \"Copy\" item");
 
-  let selection = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
 
   copyItem.doCommand();
 
   waitForClipboard((str) => { return selection.trim() == str.trim(); },
     () => { goDoCommand("cmd_copy") },
     deferred.resolve, deferred.resolve);
   HUD = outputNode = null;
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
@@ -64,15 +64,17 @@ function performTest(HUD, [result]) {
   HUD.outputNode.focus();
 
   goUpdateCommand("cmd_copy");
 
   controller = top.document.commandDispatcher.
                getControllerForCommand("cmd_copy");
   is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-  let selectionText = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selectionText = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
   isnot(selectionText.indexOf("foobarBazBug613280"), -1,
         "selection text includes 'foobarBazBug613280'");
 
   waitForClipboard((str) => { return str.trim() == selectionText.trim(); },
                    clipboard_setup, clipboard_copy_done, clipboard_copy_done);
 }
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -112,17 +112,17 @@ a {
 .message-location > .line-number {
   flex: none;
 }
 
 .message-flex-body {
   display: flex;
 }
 
-.message-body {
+.message-body > * {
   white-space: pre-wrap;
   word-wrap: break-word;
 }
 
 .message-flex-body > .message-body {
   display: block;
   flex: 1 1 auto;
   vertical-align: middle;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -748,17 +748,17 @@ toolbarbutton[sdk-button="true"][cui-are
   -moz-padding-end: 17px;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   -moz-margin-start: -15px;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
-  -moz-border-end: none;
+  -moz-border-end: none transparent;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   padding: 8px 5px 7px;
 }
 
 #nav-bar .toolbaritem-combined-buttons {
   margin-left: 2px;
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -92,17 +92,18 @@ SelectionCopyHelper(nsISelection *aSel, 
   // is. if it is a selection into input/textarea element or in a html content
   // with pre-wrap style : text/plain. Otherwise text/html.
   // see nsHTMLCopyEncoder::SetSelection
   nsAutoString mimeType;
   mimeType.AssignLiteral(kUnicodeMime);
 
   // Do the first and potentially trial encoding as preformatted and raw.
   uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
-                          | nsIDocumentEncoder::OutputRaw;
+                          | nsIDocumentEncoder::OutputRaw
+                          | nsIDocumentEncoder::OutputForPlainTextClipboardCopy;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = docEncoder->SetSelection(aSel);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1215,37 +1215,30 @@ nsDOMWindowUtils::SendKeyEvent(const nsA
     event.charCode = aCharCode;
   } else {
     event.keyCode = aKeyCode;
     event.charCode = 0;
   }
 
   uint32_t locationFlag = (aAdditionalFlags &
     (KEY_FLAG_LOCATION_STANDARD | KEY_FLAG_LOCATION_LEFT |
-     KEY_FLAG_LOCATION_RIGHT | KEY_FLAG_LOCATION_NUMPAD |
-     KEY_FLAG_LOCATION_MOBILE | KEY_FLAG_LOCATION_JOYSTICK));
+     KEY_FLAG_LOCATION_RIGHT | KEY_FLAG_LOCATION_NUMPAD));
   switch (locationFlag) {
     case KEY_FLAG_LOCATION_STANDARD:
       event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
       break;
     case KEY_FLAG_LOCATION_LEFT:
       event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
       break;
     case KEY_FLAG_LOCATION_RIGHT:
       event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
       break;
     case KEY_FLAG_LOCATION_NUMPAD:
       event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
       break;
-    case KEY_FLAG_LOCATION_MOBILE:
-      event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE;
-      break;
-    case KEY_FLAG_LOCATION_JOYSTICK:
-      event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_JOYSTICK;
-      break;
     default:
       if (locationFlag != 0) {
         return NS_ERROR_INVALID_ARG;
       }
       // If location flag isn't set, choose the location from keycode.
       switch (aKeyCode) {
         case nsIDOMKeyEvent::DOM_VK_NUMPAD0:
         case nsIDOMKeyEvent::DOM_VK_NUMPAD1:
--- a/dom/base/nsIDocumentEncoder.idl
+++ b/dom/base/nsIDocumentEncoder.idl
@@ -223,16 +223,23 @@ interface nsIDocumentEncoder : nsISuppor
   const unsigned long OutputNonTextContentAsPlaceholder = (1 << 23);
 
   /**
    * Don't Strip ending spaces from a line (only for serializing to plaintext).
    */
   const unsigned long OutputDontRemoveLineEndingSpaces = (1 << 24);
 
   /**
+   * Serialize in a way that is suitable for copying a plaintext version of the
+   * document to the clipboard.  This can for example cause line endings to be
+   * injected at preformatted block element boundaries.
+   */
+  const unsigned long OutputForPlainTextClipboardCopy = (1 << 25);
+
+  /**
    * Initialize with a pointer to the document and the mime type.
    * @param aDocument Document to encode.
    * @param aMimeType MimeType to use. May also be set by SetMimeType.
    * @param aFlags Flags to use while encoding. May also be set by SetFlags.
    */
   void init(in nsIDOMDocument aDocument,
             in AString aMimeType,
             in unsigned long aFlags);
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -86,16 +86,18 @@ nsPlainTextSerializer::nsPlainTextSerial
   mCurrentLineWidth = 0;
 
   // Flow
   mEmptyLines = 1; // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormatted = false;
   mStartedOutput = false;
 
+  mPreformattedBlockBoundary = false;
+
   // initialize the tag stack to zero:
   // The stack only ever contains pointers to static atoms, so they don't
   // need refcounting.
   mTagStack = new nsIAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
@@ -162,16 +164,18 @@ nsPlainTextSerializer::Init(uint32_t aFl
   else {
     // Platform/default
     mLineBreak.AssignLiteral(NS_LINEBREAK);
   }
 
   mLineBreakDue = false;
   mFloatingLines = -1;
 
+  mPreformattedBlockBoundary = false;
+
   if (mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
     mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs);
 
     mHeaderStrategy =
       Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy);
 
     // DontWrapAnyQuotes is set according to whether plaintext mail
@@ -432,16 +436,26 @@ nsPlainTextSerializer::DoOpenContainer(n
       // Serialize current node as placeholder character
       Write(NS_LITERAL_STRING("\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (mPreformattedBlockBoundary && DoOutput()) {
+      // Should always end a line, but get no more whitespace
+      if (mFloatingLines < 0)
+        mFloatingLines = 0;
+      mLineBreakDue = true;
+    }
+    mPreformattedBlockBoundary = false;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -665,17 +679,17 @@ nsPlainTextSerializer::DoOpenContainer(n
     }
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
 
   // Else make sure we'll separate block level tags,
   // even if we're about to leave, before doing any other formatting.
-  else if (nsContentUtils::IsHTMLBlock(aTag)) {
+  else if (IsElementBlock(mElement)) {
     EnsureVerticalSpace(0);
   }
 
   //////////////////////////////////////////////////////////////
   if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
@@ -762,16 +776,24 @@ nsPlainTextSerializer::DoOpenContainer(n
 nsresult
 nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
   if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (DoOutput() && IsInPre() && IsElementBlock(mElement)) {
+      // If we're closing a preformatted block element, output a line break
+      // when we find a new container.
+      mPreformattedBlockBoundary = true;
+    }
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -882,18 +904,17 @@ nsPlainTextSerializer::DoCloseContainer(
       mIndent -= kTabSize;
       mFloatingLines = 1;
     }
     mLineBreakDue = true;
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
-  else if (nsContentUtils::IsHTMLBlock(aTag)
-           && aTag != nsGkAtoms::script) {
+  else if (IsElementBlock(mElement) && aTag != nsGkAtoms::script) {
     // All other blocks get 1 vertical space after them
     // in formatted mode, otherwise 0.
     // This is hard. Sometimes 0 is a better number, but
     // how to know?
     if (mFlags & nsIDocumentEncoder::OutputFormatted)
       EnsureVerticalSpace(1);
     else {
       if (mFloatingLines < 0)
@@ -1032,16 +1053,18 @@ nsPlainTextSerializer::DoAddText(bool aI
     mURL.Truncate();
   }
   Write(aText);
 }
 
 nsresult
 nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag)
 {
+  mPreformattedBlockBoundary = false;
+
   // If we don't want any output, just return
   if (!DoOutput()) {
     return NS_OK;
   }
 
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
@@ -1773,16 +1796,30 @@ nsPlainTextSerializer::IsElementPreforma
   if (styleContext) {
     const nsStyleText* textStyle = styleContext->StyleText();
     return textStyle->WhiteSpaceOrNewlineIsSignificant();
   }
   // Fall back to looking at the tag, in case there is no style information.
   return GetIdForContent(aElement) == nsGkAtoms::pre;
 }
 
+bool
+nsPlainTextSerializer::IsElementBlock(Element* aElement)
+{
+  nsRefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr,
+                                                         nullptr);
+  if (styleContext) {
+    const nsStyleDisplay* displayStyle = styleContext->StyleDisplay();
+    return displayStyle->IsBlockOutsideStyle();
+  }
+  // Fall back to looking at the tag, in case there is no style information.
+  return nsContentUtils::IsHTMLBlock(GetIdForContent(aElement));
+}
+
 /**
  * This method is required only to identify LI's inside OL.
  * Returns TRUE if we are inside an OL tag and FALSE otherwise.
  */
 bool
 nsPlainTextSerializer::IsInOL()
 {
   int32_t i = mTagStackIndex;
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -110,16 +110,17 @@ private:
   bool GetLastBool(const nsTArray<bool>& aStack);
   void SetLastBool(nsTArray<bool>& aStack, bool aValue);
   void PushBool(nsTArray<bool>& aStack, bool aValue);
   bool PopBool(nsTArray<bool>& aStack);
 
   bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag);
 
   bool IsElementPreformatted(mozilla::dom::Element* aElement);
+  bool IsElementBlock(mozilla::dom::Element* aElement);
 
 private:
   nsString         mCurrentLine;
   uint32_t         mHeadLevel;
   bool             mAtFirstColumn;
 
   // Handling of quoted text (for mail):
   // Quotes need to be wrapped differently from non-quoted text,
@@ -164,17 +165,19 @@ private:
 
   bool             mInWhitespace;
   bool             mPreFormatted;
   bool             mStartedOutput; // we've produced at least a character
 
   // While handling a new tag, this variable should remind if any line break
   // is due because of a closing tag. Setting it to "TRUE" while closing the tags.
   // Hence opening tags are guaranteed to start with appropriate line breaks.
-  bool             mLineBreakDue; 
+  bool             mLineBreakDue;
+
+  bool             mPreformattedBlockBoundary;
 
   nsString         mURL;
   int32_t          mHeaderStrategy;    /* Header strategy (pref)
                                           0 = no indention
                                           1 = indention, increased with
                                               header level (default)
                                           2 = numbering and slight indention */
   int32_t          mHeaderCounter[7];  /* For header-numbering:
--- a/dom/base/test/TestPlainTextSerializer.cpp
+++ b/dom/base/test/TestPlainTextSerializer.cpp
@@ -133,16 +133,41 @@ TestPreElement()
     return NS_ERROR_FAILURE;
   }
 
   passed("prettyprinted HTML to text serialization test");
   return NS_OK;
 }
 
 nsresult
+TestBlockElement()
+{
+  nsString test;
+  test.AppendLiteral(
+    "<html>" NS_LINEBREAK
+    "<body>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  first" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  second" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "</body>" NS_LINEBREAK "</html>");
+
+  ConvertBufToPlainText(test, 0);
+  if (!test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) {
+    fail("Wrong prettyprinted html to text serialization");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("prettyprinted HTML to text serialization test");
+  return NS_OK;
+}
+
+nsresult
 TestPlainTextSerializer()
 {
   nsString test;
   test.AppendLiteral("<html><base>base</base><head><span>span</span></head>"
                      "<body>body</body></html>");
   ConvertBufToPlainText(test, 0);
   if (!test.EqualsLiteral("basespanbody")) {
     fail("Wrong html to text serialization");
@@ -158,16 +183,19 @@ TestPlainTextSerializer()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestPrettyPrintedHtml();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestPreElement();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = TestBlockElement();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Add new tests here...
   return NS_OK;
 }
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("PlainTextSerializer");
   if (xpcom.failed())
--- a/dom/base/test/test_bug116083.html
+++ b/dom/base/test/test_bug116083.html
@@ -15,16 +15,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div style="white-space: pre">foo  bar</div>
 <div style="white-space: pre-wrap">foo  bar</div>
 <div style="white-space: pre-line">foo  bar</div>
 <div style="white-space: -moz-pre-space">foo  bar</div>
 <div data-result="bar  baz"><span style="white-space: pre">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-wrap">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-line">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: -moz-pre-space">bar  </span>baz</div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
 <div data-result="&#10;foo bar&#10;">foo  bar</div>
 </div>
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function nextTest() {
   var div = document.querySelector("#content>div");
   if (!div) {
--- a/dom/events/test/test_dom_keyboard_event.html
+++ b/dom/events/test/test_dom_keyboard_event.html
@@ -195,36 +195,24 @@ function testSynthesizedKeyLocation()
     { key: "VK_DOWN", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     },
     { key: "VK_DOWN", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
     },
-    { key: "VK_DOWN", isModifier: false,
-      event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
-               location: KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK },
-    },
     { key: "5", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     },
     { key: "VK_NUMPAD5", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
     },
-    { key: "5", isModifier: false,
-      event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
-               location: KeyboardEvent.DOM_KEY_LOCATION_MOBILE },
-    },
-    { key: "VK_NUMPAD5", isModifier: false,
-      event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
-               location: KeyboardEvent.DOM_KEY_LOCATION_MOBILE },
-    },
     { key: "+", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     },
     { key: "VK_ADD", isModifier: false,
       event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false,
                location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
     },
@@ -254,22 +242,18 @@ function testSynthesizedKeyLocation()
   {
     switch (aLocation) {
       case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
         return "DOM_KEY_LOCATION_STANDARD";
       case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
         return "DOM_KEY_LOCATION_LEFT";
       case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
         return "DOM_KEY_LOCATION_RIGHT";
-      case KeyboardEvent.DOM_KEY_LOCATION_MOBILE:
-        return "DOM_KEY_LOCATION_MOBILE";
       case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
         return "DOM_KEY_LOCATION_NUMPAD";
-      case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK:
-        return "DOM_KEY_LOCATION_JOYSTICK";
       default:
         return "Invalid value (" + aLocation + ")";
     }
   }
 
   var currentTest, description;
   var events = { keydown: false, keypress: false, keyup: false };
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -45,17 +45,17 @@ interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsITranslationNodeList;
 interface nsIJSRAIIHelper;
 interface nsIContentPermissionRequest;
 
-[scriptable, uuid(04db2684-f9ed-4d70-827d-3d5b87825238)]
+[scriptable, uuid(5ed850de-2b57-4555-ac48-93292e852eab)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -551,18 +551,16 @@ interface nsIDOMWindowUtils : nsISupport
   const unsigned long KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS = 0x0002;
 
   // if one of these flags is set, the KeyboardEvent.location will be the value.
   // Otherwise, it will be computed from aKeyCode.
   const unsigned long KEY_FLAG_LOCATION_STANDARD = 0x0010;
   const unsigned long KEY_FLAG_LOCATION_LEFT     = 0x0020;
   const unsigned long KEY_FLAG_LOCATION_RIGHT    = 0x0040;
   const unsigned long KEY_FLAG_LOCATION_NUMPAD   = 0x0080;
-  const unsigned long KEY_FLAG_LOCATION_MOBILE   = 0x0100;
-  const unsigned long KEY_FLAG_LOCATION_JOYSTICK = 0x0200;
 
   boolean sendKeyEvent(in AString aType,
                        in long aKeyCode,
                        in long aCharCode,
                        in long aModifiers,
                        [optional] in unsigned long aAdditionalFlags);
 
   /**
--- a/dom/interfaces/events/nsIDOMKeyEvent.idl
+++ b/dom/interfaces/events/nsIDOMKeyEvent.idl
@@ -245,16 +245,14 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent
                                          in unsigned long charCodeArg);
 
   bool getModifierState(in DOMString keyArg);
 
   const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
   const unsigned long DOM_KEY_LOCATION_LEFT     = 0x01;
   const unsigned long DOM_KEY_LOCATION_RIGHT    = 0x02;
   const unsigned long DOM_KEY_LOCATION_NUMPAD   = 0x03;
-  const unsigned long DOM_KEY_LOCATION_MOBILE   = 0x04;
-  const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05;
 
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
 
   readonly attribute DOMString key;
 };
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -31,16 +31,19 @@
 
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
+// Default timeout msecs until try to enter dormant state by heuristic.
+static const int DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS = 60000;
+
 namespace mozilla {
 
 // Number of estimated seconds worth of data we need to have buffered
 // ahead of the current playback position before we allow the media decoder
 // to report that it can play through the entire media without the decode
 // catching up with the download. Having this margin make the
 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
@@ -118,37 +121,66 @@ NS_IMPL_ISUPPORTS(MediaMemoryTracker, ns
 
 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
 
 void MediaDecoder::NotifyOwnerActivityChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
-  if (!mDecoderStateMachine ||
-      !mDecoderStateMachine->IsDormantNeeded() ||
-      mPlayState == PLAY_STATE_SHUTDOWN) {
-    return;
-  }
-
   if (!mOwner) {
     NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
     return;
   }
 
+  UpdateDormantState(false /* aDormantTimeout */, false /* aActivity */);
+  // Start dormant timer if necessary
+  StartDormantTimer();
+}
+
+void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  if (!mDecoderStateMachine ||
+      mPlayState == PLAY_STATE_SHUTDOWN ||
+      !mOwner->GetVideoFrameContainer() ||
+      !mDecoderStateMachine->IsDormantNeeded())
+  {
+    return;
+  }
+
   bool prevDormant = mIsDormant;
   mIsDormant = false;
-  if (!mOwner->IsActive() && mOwner->GetVideoFrameContainer()) {
+  if (!mOwner->IsActive()) {
     mIsDormant = true;
   }
 #ifdef MOZ_WIDGET_GONK
-  if (mOwner->IsHidden() && mOwner->GetVideoFrameContainer()) {
+  if (mOwner->IsHidden()) {
     mIsDormant = true;
   }
 #endif
+  // Try to enable dormant by idle heuristic, when the owner is hidden.
+  bool prevHeuristicDormant = mIsHeuristicDormant;
+  mIsHeuristicDormant = false;
+  if (mIsHeuristicDormantSupported && mOwner->IsHidden()) {
+    if (aDormantTimeout && !aActivity &&
+        (mPlayState == PLAY_STATE_PAUSED || mPlayState == PLAY_STATE_ENDED)) {
+      // Enable heuristic dormant
+      mIsHeuristicDormant = true;
+    } else if(prevHeuristicDormant && !aActivity) {
+      // Continue heuristic dormant
+      mIsHeuristicDormant = true;
+    }
+
+    if (mIsHeuristicDormant) {
+      mIsDormant = true;
+    }
+  }
 
   if (prevDormant == mIsDormant) {
     // No update to dormant state
     return;
   }
 
   if (mIsDormant) {
     // enter dormant state
@@ -162,16 +194,57 @@ void MediaDecoder::NotifyOwnerActivityCh
     ChangeState(PLAY_STATE_LOADING);
   } else {
     // exit dormant state
     // trigger to state machine.
     mDecoderStateMachine->SetDormant(false);
   }
 }
 
+void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+  MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
+  ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
+  decoder->UpdateDormantState(true /* aDormantTimeout */,
+                              false /* aActivity */);
+}
+
+void MediaDecoder::StartDormantTimer()
+{
+  if (!mIsHeuristicDormantSupported) {
+    return;
+  }
+
+  if (mIsHeuristicDormant ||
+      mShuttingDown ||
+      !mOwner ||
+      !mOwner->IsHidden() ||
+      (mPlayState != PLAY_STATE_PAUSED &&
+       mPlayState != PLAY_STATE_ENDED))
+  {
+    return;
+  }
+
+  if (!mDormantTimer) {
+    mDormantTimer = do_CreateInstance("@mozilla.org/timer;1");
+  }
+  mDormantTimer->InitWithFuncCallback(&MediaDecoder::DormantTimerExpired,
+                                      this,
+                                      mHeuristicDormantTimeout,
+                                      nsITimer::TYPE_ONE_SHOT);
+}
+
+void MediaDecoder::CancelDormantTimer()
+{
+  if (mDormantTimer) {
+    mDormantTimer->Cancel();
+  }
+}
+
 void MediaDecoder::Pause()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (mPlayState == PLAY_STATE_LOADING ||
       mPlayState == PLAY_STATE_SEEKING ||
       mPlayState == PLAY_STATE_ENDED) {
     mNextState = PLAY_STATE_PAUSED;
@@ -467,17 +540,23 @@ MediaDecoder::MediaDecoder() :
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
   mMinimizePreroll(false),
   mMediaTracksConstructed(false),
-  mIsDormant(false)
+  mIsDormant(false),
+  mIsHeuristicDormantSupported(
+    Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)),
+  mHeuristicDormantTimeout(
+    Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
+                        DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)),
+  mIsHeuristicDormant(false)
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
@@ -516,16 +595,18 @@ void MediaDecoder::Shutdown()
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
     mResource->Close();
   }
 
+  CancelDormantTimer();
+
   ChangeState(PLAY_STATE_SHUTDOWN);
 
   mOwner = nullptr;
 
   MediaShutdownManager::Instance().Unregister(this);
 }
 
 MediaDecoder::~MediaDecoder()
@@ -617,16 +698,18 @@ nsresult MediaDecoder::ScheduleStateMach
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mDecoderStateMachine->ScheduleStateMachine();
 }
 
 nsresult MediaDecoder::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
+
   NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
   if (mPausedForPlaybackRateNull) {
     return NS_OK;
   }
   nsresult res = ScheduleStateMachineThread();
   NS_ENSURE_SUCCESS(res,res);
   if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
     mNextState = PLAY_STATE_PLAYING;
@@ -639,16 +722,17 @@ nsresult MediaDecoder::Play()
   ChangeState(PLAY_STATE_PLAYING);
   return NS_OK;
 }
 
 nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
 
   NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
 
   int64_t timeUsecs = 0;
   nsresult rv = SecondsToUsecs(aTime, timeUsecs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
@@ -1187,16 +1271,20 @@ void MediaDecoder::ChangeState(PlayState
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (mPlayState == PLAY_STATE_ENDED) {
     RemoveMediaTracks();
   }
 
   ApplyStateToStateMachine(mPlayState);
 
+  CancelDormantTimer();
+  // Start dormant timer if necessary
+  StartDormantTimer();
+
   GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -183,16 +183,17 @@ destroying the MediaDecoder object.
 */
 #if !defined(MediaDecoder_h_)
 #define MediaDecoder_h_
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsAutoPtr.h"
+#include "nsITimer.h"
 #include "MediaResource.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MediaStreamGraph.h"
 #include "AbstractMediaDecoder.h"
 #include "necko-config.h"
 #ifdef MOZ_EME
@@ -362,16 +363,18 @@ public:
 
   // Notify activity of the decoder owner is changed.
   // Based on the activity, dormant state is updated.
   // Dormant state is a state to free all scarce media resources
   //  (like hw video codec), did not decoding and stay dormant.
   // It is used to share scarece media resources in system.
   virtual void NotifyOwnerActivityChanged();
 
+  void UpdateDormantState(bool aDormantTimeout, bool aActivity);
+
   // Pause video playback.
   virtual void Pause();
   // Adjust the speed of the playback, optionally with pitch correction,
   virtual void SetVolume(double aVolume);
   // Sets whether audio is being captured. If it is, we won't play any
   // of our audio.
   virtual void SetAudioCaptured(bool aCaptured);
 
@@ -1017,16 +1020,24 @@ public:
   {
     GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded);
   }
 
 protected:
   virtual ~MediaDecoder();
   void SetStateMachineParameters();
 
+  static void DormantTimerExpired(nsITimer *aTimer, void *aClosure);
+
+  // Start a timer for heuristic dormant.
+  void StartDormantTimer();
+
+  // Cancel a timer for heuristic dormant.
+  void CancelDormantTimer();
+
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
   // Current decoding position in the stream. This is where the decoder
   // is up to consuming the stream. This is not adjusted during decoder
   // seek operations, but it's updated at the end when we start playing
   // back again.
@@ -1214,13 +1225,25 @@ protected:
   bool mMediaTracksConstructed;
 
   // Stores media info, including info of audio tracks and video tracks, should
   // only be accessed from main thread.
   nsAutoPtr<MediaInfo> mInfo;
 
   // True if MediaDecoder is in dormant state.
   bool mIsDormant;
+
+  // True if heuristic dormant is supported.
+  const bool mIsHeuristicDormantSupported;
+
+  // Timeout ms of heuristic dormant timer.
+  const int mHeuristicDormantTimeout;
+
+  // True if MediaDecoder is in dormant by heuristic.
+  bool mIsHeuristicDormant;
+
+  // Timer to schedule updating dormant state.
+  nsCOMPtr<nsITimer> mDormantTimer;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -16,18 +16,16 @@ interface KeyboardEvent : UIEvent
   readonly attribute boolean          metaKey;
 
   boolean getModifierState(DOMString key);
 
   const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
   const unsigned long DOM_KEY_LOCATION_LEFT     = 0x01;
   const unsigned long DOM_KEY_LOCATION_RIGHT    = 0x02;
   const unsigned long DOM_KEY_LOCATION_NUMPAD   = 0x03;
-  const unsigned long DOM_KEY_LOCATION_MOBILE   = 0x04;
-  const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05;
 
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
   readonly attribute boolean       isComposing;
 
   readonly attribute DOMString key;
   [Pref="dom.keyboardevent.code.enabled"]
   readonly attribute DOMString code;
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -48,16 +48,17 @@ gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfx
       mSubtableOffset(0),
       mUVSTableOffset(0),
       mNumLongHMetrics(0),
       mNumLongVMetrics(0),
       mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
       mUseFontGlyphWidths(false),
       mInitialized(false),
       mVerticalInitialized(false),
+      mLoadedLocaGlyf(false),
       mLocaLongOffsets(false)
 {
 }
 
 gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
 {
     if (mCmapTable) {
         hb_blob_destroy(mCmapTable);
@@ -341,77 +342,37 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
         } else {
             *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
                                 int16_t(vorg->defaultVertOriginY));
         }
         return;
     }
 
     if (mVmtxTable) {
-        if (mLocaTable && mGlyfTable) {
-            // TrueType outlines: use glyph bbox + top sidebearing
-            uint32_t offset; // offset of glyph record in the 'glyf' table
-            uint32_t len;
-            const char* data = hb_blob_get_data(mLocaTable, &len);
-            if (mLocaLongOffsets) {
-                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
-                    *aY = 0;
-                    return;
-                }
-                const AutoSwap_PRUint32* offsets =
-                    reinterpret_cast<const AutoSwap_PRUint32*>(data);
-                offset = offsets[aGlyph];
-                if (offset == offsets[aGlyph + 1]) {
-                    // empty glyph
-                    *aY = 0;
-                    return;
-                }
-            } else {
-                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
-                    *aY = 0;
-                    return;
-                }
-                const AutoSwap_PRUint16* offsets =
-                    reinterpret_cast<const AutoSwap_PRUint16*>(data);
-                offset = uint16_t(offsets[aGlyph]);
-                if (offset == uint16_t(offsets[aGlyph + 1])) {
-                    // empty glyph
-                    *aY = 0;
-                    return;
-                }
-                offset *= 2;
-            }
-
-            struct Glyf { // we only need the bounding-box at the beginning
-                          // of the glyph record, not the actual outline data
-                AutoSwap_PRInt16 numberOfContours;
-                AutoSwap_PRInt16 xMin;
-                AutoSwap_PRInt16 yMin;
-                AutoSwap_PRInt16 xMax;
-                AutoSwap_PRInt16 yMax;
-            };
-            data = hb_blob_get_data(mGlyfTable, &len);
-            if (offset + sizeof(Glyf) > len) {
+        bool emptyGlyf;
+        const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+        if (glyf) {
+            if (emptyGlyf) {
                 *aY = 0;
                 return;
             }
-            const Glyf* glyf = reinterpret_cast<const Glyf*>(data + offset);
 
             if (aGlyph >= uint32_t(mNumLongVMetrics)) {
                 aGlyph = mNumLongVMetrics - 1;
             }
             const GlyphMetrics* metrics =
                 reinterpret_cast<const GlyphMetrics*>
                     (hb_blob_get_data(mVmtxTable, nullptr));
             *aY = -FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
                                 (int16_t(metrics->metrics[aGlyph].lsb) +
                                  int16_t(glyf->yMax)));
             return;
         } else {
-            // XXX TODO: CFF outlines - need to get glyph extents.
+            // XXX TODO: not a truetype font; need to get glyph extents
+            // via some other API?
             // For now, fall through to default code below.
         }
     }
 
     // XXX should we consider using OS/2 sTypo* metrics if available?
 
     gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
                                       TRUETYPE_TAG('h','h','e','a'));
@@ -427,16 +388,122 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
         }
     }
 
     NS_NOTREACHED("we shouldn't be here!");
     *aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
 }
 
 static hb_bool_t
+HBGetGlyphExtents(hb_font_t *font, void *font_data,
+                  hb_codepoint_t glyph,
+                  hb_glyph_extents_t *extents,
+                  void *user_data)
+{
+    const gfxHarfBuzzShaper::FontCallbackData *fcd =
+        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+    return fcd->mShaper->GetGlyphExtents(glyph, extents);
+}
+
+// Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
+// Returns null if not found, otherwise pointer to the beginning of the
+// glyph's data. Sets aEmptyGlyf true if there is no actual data;
+// otherwise, it's guaranteed that we can read at least the bounding box.
+const gfxHarfBuzzShaper::Glyf*
+gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const
+{
+    if (!mLoadedLocaGlyf) {
+        mLoadedLocaGlyf = true; // only try this once; if it fails, this
+                                // isn't a truetype font
+        gfxFontEntry *entry = mFont->GetFontEntry();
+        uint32_t len;
+        gfxFontEntry::AutoTable headTable(entry,
+                                          TRUETYPE_TAG('h','e','a','d'));
+        const HeadTable* head =
+            reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
+                                                                &len));
+        if (len < sizeof(HeadTable)) {
+            return nullptr;
+        }
+        mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
+        mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
+        mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
+    }
+
+    if (!mLocaTable || !mGlyfTable) {
+        // it's not a truetype font
+        return nullptr;
+    }
+
+    uint32_t offset; // offset of glyph record in the 'glyf' table
+    uint32_t len;
+    const char* data = hb_blob_get_data(mLocaTable, &len);
+    if (mLocaLongOffsets) {
+        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
+            return nullptr;
+        }
+        const AutoSwap_PRUint32* offsets =
+            reinterpret_cast<const AutoSwap_PRUint32*>(data);
+        offset = offsets[aGlyph];
+        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+    } else {
+        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
+            return nullptr;
+        }
+        const AutoSwap_PRUint16* offsets =
+            reinterpret_cast<const AutoSwap_PRUint16*>(data);
+        offset = uint16_t(offsets[aGlyph]);
+        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+        offset *= 2;
+    }
+
+    data = hb_blob_get_data(mGlyfTable, &len);
+    if (offset + sizeof(Glyf) > len) {
+        return nullptr;
+    }
+
+    return reinterpret_cast<const Glyf*>(data + offset);
+}
+
+hb_bool_t
+gfxHarfBuzzShaper::GetGlyphExtents(hb_codepoint_t aGlyph,
+                                   hb_glyph_extents_t *aExtents) const
+{
+    bool emptyGlyf;
+    const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+    if (!glyf) {
+        // TODO: for non-truetype fonts, get extents some other way?
+        return false;
+    }
+
+    if (emptyGlyf) {
+        aExtents->x_bearing = 0;
+        aExtents->y_bearing = 0;
+        aExtents->width = 0;
+        aExtents->height = 0;
+        return true;
+    }
+
+    double f = mFont->FUnitsToDevUnitsFactor();
+    aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
+    aExtents->width =
+        FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
+
+    // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
+    // positive-upwards; hence the apparently-reversed subtractions here.
+    aExtents->y_bearing =
+        FloatToFixed(int16_t(glyf->yMax) * f -
+                     mFont->GetHorizontalMetrics().emAscent);
+    aExtents->height =
+        FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
+
+    return true;
+}
+
+static hb_bool_t
 HBGetContourPoint(hb_font_t *font, void *font_data,
                   unsigned int point_index, hb_codepoint_t glyph,
                   hb_position_t *x, hb_position_t *y,
                   void *user_data)
 {
     /* not yet implemented - no support for used of hinted contour points
        to fine-tune anchor positions in GPOS AnchorFormat2 */
     return false;
@@ -1061,16 +1128,19 @@ gfxHarfBuzzShaper::Initialize()
                                                HBGetGlyphVAdvance,
                                                nullptr, nullptr);
         hb_font_funcs_set_glyph_h_origin_func(sHBFontFuncs,
                                               HBGetGlyphHOrigin,
                                               nullptr, nullptr);
         hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
                                               HBGetGlyphVOrigin,
                                               nullptr, nullptr);
+        hb_font_funcs_set_glyph_extents_func(sHBFontFuncs,
+                                             HBGetGlyphExtents,
+                                             nullptr, nullptr);
         hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
                                                    HBGetContourPoint,
                                                    nullptr, nullptr);
         hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
                                                HBGetHKerning,
                                                nullptr, nullptr);
 
         sHBUnicodeFuncs =
@@ -1220,30 +1290,16 @@ gfxHarfBuzzShaper::InitializeVertical()
                               sizeof(VORGrec)) {
                 // VORG table is an unknown version, or not large enough
                 // to be valid -- discard it.
                 NS_WARNING("discarding invalid VORG table");
                 hb_blob_destroy(mVORGTable);
                 mVORGTable = nullptr;
             }
         }
-    } else if (mVmtxTable) {
-        // Otherwise, try to load loca and glyf tables so that we can read
-        // bounding boxes (needed to support vertical glyph origin).
-        uint32_t len;
-        gfxFontEntry::AutoTable headTable(entry,
-                                          TRUETYPE_TAG('h','e','a','d'));
-        const HeadTable* head =
-            reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
-                                                                &len));
-        if (len >= sizeof(HeadTable)) {
-            mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
-            mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
-            mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
-        }
     }
 
     return true;
 }
 
 bool
 gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -68,16 +68,19 @@ public:
     HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
                       hb_codepoint_t glyph,
                       hb_position_t *x, hb_position_t *y,
                       void *user_data);
 
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
+    hb_bool_t GetGlyphExtents(hb_codepoint_t aGlyph,
+                              hb_glyph_extents_t *aExtents) const;
+
     static hb_script_t
     GetHBScriptUsedForShaping(int32_t aScript) {
         // Decide what harfbuzz script code will be used for shaping
         hb_script_t hbScript;
         if (aScript <= MOZ_SCRIPT_INHERITED) {
             // For unresolved "common" or "inherited" runs,
             // default to Latin for now.
             hbScript = HB_SCRIPT_LATIN;
@@ -102,16 +105,27 @@ protected:
     nscoord GetGlyphPositions(gfxContext *aContext,
                               hb_buffer_t *aBuffer,
                               nsTArray<nsPoint>& aPositions,
                               uint32_t aAppUnitsPerDevUnit);
 
     bool InitializeVertical();
     bool LoadHmtxTable();
 
+    struct Glyf { // we only need the bounding-box at the beginning
+                  // of the glyph record, not the actual outline data
+        AutoSwap_PRInt16 numberOfContours;
+        AutoSwap_PRInt16 xMin;
+        AutoSwap_PRInt16 yMin;
+        AutoSwap_PRInt16 xMax;
+        AutoSwap_PRInt16 yMax;
+    };
+
+    const Glyf *FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const;
+
     // harfbuzz face object: we acquire a reference from the font entry
     // on shaper creation, and release it in our destructor
     hb_face_t         *mHBFace;
 
     // size-specific font object, owned by the gfxHarfBuzzShaper
     hb_font_t         *mHBFont;
 
     FontCallbackData   mCallbackData;
@@ -156,12 +170,15 @@ protected:
     // directly
     bool mUseFontGetGlyph;
     // Whether the font implements GetGlyphWidth, or we should read tables
     // directly to get ideal widths
     bool mUseFontGlyphWidths;
 
     bool mInitialized;
     bool mVerticalInitialized;
-    bool mLocaLongOffsets;
+
+    // these are set from the FindGlyf callback on first use of the glyf data
+    mutable bool mLoadedLocaGlyf;
+    mutable bool mLocaLongOffsets;
 };
 
 #endif /* GFX_HARFBUZZSHAPER_H */
--- a/hal/linux/UPowerClient.cpp
+++ b/hal/linux/UPowerClient.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Hal.h"
 #include "HalLog.h"
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #include <mozilla/dom/battery/Constants.h>
 #include "nsAutoRef.h"
+#include <cmath>
 
 /*
  * Helper that manages the destruction of glib objects as soon as they leave
  * the current scope.
  *
  * We are specializing nsAutoRef class.
  */
 
@@ -423,24 +424,24 @@ UPowerClient::UpdateSavedInfo(GHashTable
     case eState_Discharging:
     case eState_Empty:
     case eState_PendingDischarge:
       mCharging = false;
       break;
   }
 
   /*
-   * The battery level might be very close to 100% (like 99.xxxx%) without
+   * The battery level might be very close to 100% (like 99%) without
    * increasing. It seems that upower sets the battery state as 'full' in that
    * case so we should trust it and not even try to get the value.
    */
   if (isFull) {
     mLevel = 1.0;
   } else {
-    mLevel = g_value_get_double(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "Percentage")))*0.01;
+    mLevel = round(g_value_get_double(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "Percentage"))))*0.01;
   }
 
   if (isFull) {
     mRemainingTime = 0;
   } else {
     mRemainingTime = mCharging ? g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToFull")))
                                : g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToEmpty")));
 
rename from js/src/jit-test/tests/ion/simd-bug1121299.js
rename to js/src/jit-test/tests/SIMD/bug1121299.js
--- a/js/src/jit-test/tests/ion/simd-bug1121299.js
+++ b/js/src/jit-test/tests/SIMD/bug1121299.js
@@ -1,8 +1,11 @@
+if (!this.hasOwnProperty("SIMD"))
+  quit();
+
 setJitCompilerOption("baseline.warmup.trigger", 10);
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 function test_1(i) {
   if (i >= 40)
     return;
   var a = SIMD.float32x4(1.1, 2.2, 3.3, 4.6);
   SIMD.int32x4.fromFloat32x4(a);
rename from js/src/jit-test/tests/ion/simd-bug1123631.js
rename to js/src/jit-test/tests/SIMD/bug1123631.js
rename from js/src/jit-test/tests/ion/simd-unbox.js
rename to js/src/jit-test/tests/SIMD/unbox.js
--- a/js/src/jit-test/tests/asm.js/bug1122338.js
+++ b/js/src/jit-test/tests/asm.js/bug1122338.js
@@ -1,14 +1,17 @@
 // The bug was that the asm.js linker did not catch that an unshared
 // view was created on a shared buffer.  (Nor did it catch the vice
 // versa case.)  As a consequence the code was not rejected (and run
 // as plain JS) as it should be.  That gave rise to a difference in
 // output.
 
+if (!this.SharedArrayBuffer)
+    quit(0);
+
 // Original test
 
 g = (function(stdlib, n, heap) {
     "use asm";
     var Float32ArrayView = new stdlib.Float32Array(heap);
     function f() {
         return +Float32ArrayView[0]
     }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -3730,16 +3730,20 @@ nsRegion nsDisplayOpacity::GetOpaqueRegi
   return nsRegion();
 }
 
 // nsDisplayOpacity uses layers for rendering
 already_AddRefed<Layer>
 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerLayerParameters& aContainerParameters) {
+  if (mOpacity == 0 && mFrame->GetContent() &&
+      !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) {
+    return nullptr;
+  }
   nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            aContainerParameters, nullptr);
   if (!container)
     return nullptr;
 
   container->SetOpacity(mOpacity);
   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -2916,20 +2916,32 @@ nsBlockFrame::AttributeChanged(int32_t  
       }
     }
   }
 
   return rv;
 }
 
 static inline bool
-IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
-{
-  if (aCoord.GetUnit() == eStyleUnit_Auto)
+IsNonAutoNonZeroBSize(const nsStyleCoord& aCoord)
+{
+  nsStyleUnit unit = aCoord.GetUnit();
+  if (unit == eStyleUnit_Auto ||
+      // The enumerated values were originally aimed at inline-size
+      // (or width, as it was before logicalization). For now, let them
+      // return false here, so we treat them like 'auto' pending a
+      // real implementation. (See bug 1126420.)
+      //
+      // FIXME (bug 567039, bug 527285)
+      // This isn't correct for the 'fill' value, which should more
+      // likely (but not necessarily, depending on the available space)
+      // be returning true.
+      unit == eStyleUnit_Enumerated) {
     return false;
+  }
   if (aCoord.IsCoordPercentCalcUnit()) {
     // If we evaluate the length/percent/calc at a percentage basis of
     // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
     // length, percent, or combination thereof.  Test > 0 so we clamp
     // negative calc() results to 0.
     return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
            nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
   }
@@ -2946,23 +2958,23 @@ nsBlockFrame::IsSelfEmpty()
   if (GetStateBits() & NS_BLOCK_MARGIN_ROOT) {
     return false;
   }
 
   const nsStylePosition* position = StylePosition();
   bool vertical = GetWritingMode().IsVertical();
 
   if (vertical) {
-    if (IsNonAutoNonZeroHeight(position->mMinWidth) ||
-        IsNonAutoNonZeroHeight(position->mWidth)) {
+    if (IsNonAutoNonZeroBSize(position->mMinWidth) ||
+        IsNonAutoNonZeroBSize(position->mWidth)) {
       return false;
     }
   } else {
-    if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
-        IsNonAutoNonZeroHeight(position->mHeight)) {
+    if (IsNonAutoNonZeroBSize(position->mMinHeight) ||
+        IsNonAutoNonZeroBSize(position->mHeight)) {
       return false;
     }
   }
 
   const nsStyleBorder* border = StyleBorder();
   const nsStylePadding* padding = StylePadding();
 
   if (vertical) {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1943,16 +1943,27 @@ nsIFrame::BuildDisplayListForStackingCon
     return;
 
   // Replaced elements have their visibility handled here, because
   // they're visually atomic
   if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
     return;
 
   const nsStyleDisplay* disp = StyleDisplay();
+  // We can stop right away if this is a zero-opacity stacking context and
+  // we're painting, and we're not animating opacity. Don't do this
+  // if we're going to compute plugin geometry, since opacity-0 plugins
+  // need to have display items built for them.
+  if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() &&
+      !aBuilder->WillComputePluginGeometry() &&
+      !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
+      !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) {
+    return;
+  }
+
   if (disp->mWillChangeBitField != 0) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
   nsRect dirtyRect = aDirtyRect;
 
   bool inTransform = aBuilder->IsInTransform();
   bool isTransformed = IsTransformed();
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/fallback-mark-stacking-1-notref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+@font-face {
+  font-family: test;
+  /* Note that this font has no OpenType layout tables: */
+  src: url(../fonts/sil/GenR102.ttf);
+}
+body {
+  font-family: test;
+}
+.test {
+  font-size: 100px;
+  line-height: 2;
+}
+</style>
+</head>
+<body>
+These examples should NOT look the same:
+<div class=test>x&#x303;&#x302; x&#x302;&#x303;</div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/fallback-mark-stacking-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+@font-face {
+  font-family: test;
+  /* Note that this font has no OpenType layout tables: */
+  src: url(../fonts/sil/GenR102.ttf);
+}
+body {
+  font-family: test;
+}
+.test {
+  font-size: 100px;
+  line-height: 2;
+}
+</style>
+</head>
+<body>
+These examples should NOT look the same:
+<div class=test>x&#x302;&#x303; x&#x303;&#x302;</div>
+</body>
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -158,16 +158,18 @@ random-if(!winWidget) == arial-bold-lam-
 # Fallback (presentation-forms) shaping with a font that lacks GSUB/GPOS
 # These tests are not valid with Mac or FT2 font backends because our masking of complex-script ranges
 # in the 'cmap' will prevent the test font (without GSUB) being used.
 skip-if(B2G) fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-1.html arabic-fallback-1-ref.html
 fails-if(cocoaWidget||Android||B2G) HTTP(..) == arabic-fallback-2.html arabic-fallback-2-ref.html
 fails-if(cocoaWidget||Android||B2G) HTTP(..) == arabic-fallback-3.html arabic-fallback-3-ref.html
 fails-if(!cocoaWidget&&!Android&&!B2G) HTTP(..) != arabic-fallback-4.html arabic-fallback-4-notref.html
 == arabic-marks-1.html arabic-marks-1-ref.html
+# harfbuzz fallback mark stacking in the absence of GPOS:
+HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html
 
 == 726392-1.html 726392-1-ref.html
 == 726392-2.html 726392-2-ref.html
 skip-if(B2G) == 726392-3.html 726392-3-ref.html
 == 745555-1.html 745555-1-ref.html
 == 745555-2.html 745555-2-ref.html
 == 820255.html 820255-ref.html
 
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -110,36 +110,16 @@ public class GeckoEvent {
 
         public final int value;
 
         private NativeGeckoEvent(int value) {
             this.value = value;
         }
     }
 
-    /**
-     * The DomKeyLocation enum encapsulates the DOM KeyboardEvent's constants.
-     * @see https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Key_location_constants
-     */
-    @JNITarget
-    public enum DomKeyLocation {
-        DOM_KEY_LOCATION_STANDARD(0),
-        DOM_KEY_LOCATION_LEFT(1),
-        DOM_KEY_LOCATION_RIGHT(2),
-        DOM_KEY_LOCATION_NUMPAD(3),
-        DOM_KEY_LOCATION_MOBILE(4),
-        DOM_KEY_LOCATION_JOYSTICK(5);
-
-        public final int value;
-
-        private DomKeyLocation(int value) {
-            this.value = value;
-        }
-    }
-
     // Encapsulation of common IME actions.
     @JNITarget
     public enum ImeAction {
         IME_SYNCHRONIZE(0),
         IME_REPLACE_TEXT(1),
         IME_SET_SELECTION(2),
         IME_ADD_COMPOSITION_RANGE(3),
         IME_UPDATE_COMPOSITION(4),
@@ -218,17 +198,16 @@ public class GeckoEvent {
     private int mRangeStyles;
     private int mRangeLineStyle;
     private boolean mRangeBoldLine;
     private int mRangeForeColor;
     private int mRangeBackColor;
     private int mRangeLineColor;
     private Location mLocation;
     private Address mAddress;
-    private DomKeyLocation mDomKeyLocation;
 
     private int     mConnectionType;
     private boolean mIsWifi;
     private int     mDHCPGateway;
 
     private int mNativeWindow;
 
     private short mScreenOrientation;
@@ -308,40 +287,16 @@ public class GeckoEvent {
             int unmodifiedMetaState =
                 mMetaState & ~(KeyEvent.META_ALT_MASK |
                                KeyEvent.META_CTRL_MASK |
                                KeyEvent.META_META_MASK);
             if (unmodifiedMetaState != mMetaState) {
                 mDOMPrintableKeyValue = k.getUnicodeChar(unmodifiedMetaState);
             }
         }
-        mDomKeyLocation = isJoystickButton(mKeyCode) ? DomKeyLocation.DOM_KEY_LOCATION_JOYSTICK
-                                                     : DomKeyLocation.DOM_KEY_LOCATION_MOBILE;
-    }
-
-    /**
-     * This method tests if a key is one of the described in:
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0
-     * @param keyCode int with the key code (Android key constant from KeyEvent)
-     * @return true if the key is one of the listed above, false otherwise.
-     */
-    private static boolean isJoystickButton(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_UP:
-                return true;
-            default:
-                if (Versions.feature12Plus) {
-                    return KeyEvent.isGamepadButton(keyCode);
-                }
-                return GeckoEvent.isGamepadButton(keyCode);
-        }
     }
 
     /**
      * This method is a replacement for the the KeyEvent.isGamepadButton method to be
      * compatible with Build.VERSION.SDK_INT < 12. This is an implementation of the
      * same method isGamepadButton available after SDK 12.
      * @param keyCode int with the key code (Android key constant from KeyEvent).
      * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1314,16 +1314,17 @@ pref("network.http.spdy.chunk-size", 160
 pref("network.http.spdy.timeout", 180);
 pref("network.http.spdy.coalesce-hostnames", true);
 pref("network.http.spdy.persistent-settings", false);
 pref("network.http.spdy.ping-threshold", 58);
 pref("network.http.spdy.ping-timeout", 8);
 pref("network.http.spdy.send-buffer-size", 131072);
 pref("network.http.spdy.allow-push", true);
 pref("network.http.spdy.push-allowance", 131072);
+pref("network.http.spdy.default-concurrent", 100);
 
 // alt-svc allows separation of transport routing from
 // the origin host without using a proxy.
 pref("network.http.altsvc.enabled", true);
 pref("network.http.altsvc.oe", true);
 
 pref("network.http.diagnostics", false);
 
--- a/netwerk/protocol/http/ASpdySession.h
+++ b/netwerk/protocol/http/ASpdySession.h
@@ -45,16 +45,18 @@ public:
   const static uint32_t kTCPSendBufferSize = 131072;
 
   // until we have an API that can push back on receiving data (right now
   // WriteSegments is obligated to accept data and buffer) there is no
   // reason to throttle with the rwin other than in server push
   // scenarios.
   const static uint32_t kInitialRwin = 256 * 1024 * 1024;
 
+  const static uint32_t kDefaultMaxConcurrent = 100;
+
   // soft errors are errors that terminate a stream without terminating the
   // connection. In general non-network errors are stream errors as well
   // as network specific items like cancels.
   bool SoftStreamError(nsresult code)
   {
     if (NS_SUCCEEDED(code) || code == NS_BASE_STREAM_WOULD_BLOCK) {
       return false;
     }
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -84,17 +84,16 @@ Http2Session::Http2Session(nsISocketTran
   , mContinuedPromiseStream(0)
   , mShouldGoAway(false)
   , mClosed(false)
   , mCleanShutdown(false)
   , mTLSProfileConfirmed(false)
   , mGoAwayReason(NO_HTTP_ERROR)
   , mGoAwayID(0)
   , mOutgoingGoAwayID(0)
-  , mMaxConcurrent(kDefaultMaxConcurrent)
   , mConcurrent(0)
   , mServerPushedResources(0)
   , mServerInitialStreamWindow(kDefaultRwin)
   , mLocalSessionWindow(kDefaultRwin)
   , mServerSessionWindow(kDefaultRwin)
   , mOutputQueueSize(kDefaultQueueSize)
   , mOutputQueueUsed(0)
   , mOutputQueueSent(0)
@@ -114,17 +113,17 @@ Http2Session::Http2Session(nsISocketTran
   LOG3(("Http2Session::Http2Session %p serial=0x%X\n", this, mSerial));
 
   mInputFrameBuffer = new char[mInputFrameBufferSize];
   mOutputQueueBuffer = new char[mOutputQueueSize];
   mDecompressBuffer.SetCapacity(kDefaultBufferSize);
   mDecompressor.SetCompressor(&mCompressor);
 
   mPushAllowance = gHttpHandler->SpdyPushAllowance();
-
+  mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
   SendHello();
 
   mLastDataReadEpoch = mLastReadEpoch;
 
   mPingThreshold = gHttpHandler->SpdyPingThreshold();
 
   mNegotiatedToken.AssignLiteral(HTTP2_DRAFT_LATEST_TOKEN);
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -130,17 +130,16 @@ public:
   const static uint32_t kDefaultBufferSize = 2048;
 
   // kDefaultQueueSize must be >= other queue size constants
   const static uint32_t kDefaultQueueSize =  32768;
   const static uint32_t kQueueMinimumCleanup = 24576;
   const static uint32_t kQueueTailRoom    =  4096;
   const static uint32_t kQueueReserved    =  1024;
 
-  const static uint32_t kDefaultMaxConcurrent = 100;
   const static uint32_t kMaxStreamID = 0x7800000;
 
   // This is a sentinel for a deleted stream. It is not a valid
   // 31 bit stream ID.
   const static uint32_t kDeadStreamID = 0xffffdead;
 
   // below the emergency threshold of local window we ack every received
   // byte. Above that we coalesce bytes into the MinimumToAck size.
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -179,21 +179,20 @@ Http2Stream::ReadSegments(nsAHttpSegment
       uint32_t wasted = 0;
       mSegmentReader = reader;
       OnReadSegment("", 0, &wasted);
       mSegmentReader = nullptr;
     }
 
     // If the sending flow control window is open (!mBlockedOnRwin) then
     // continue sending the request
-    if (!mBlockedOnRwin && mUpstreamState != GENERATING_HEADERS &&
+    if (!mBlockedOnRwin && mOpenGenerated &&
         !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
       MOZ_ASSERT(!mQueued);
       MOZ_ASSERT(mRequestHeadersDone);
-      MOZ_ASSERT(mOpenGenerated);
       LOG3(("Http2Stream::ReadSegments %p 0x%X: Sending request data complete, "
             "mUpstreamState=%x\n",this, mStreamID, mUpstreamState));
       if (mSentFin) {
         ChangeState(UPSTREAM_COMPLETE);
       } else {
         GenerateDataFrameHeader(0, true);
         ChangeState(SENDING_FIN_STREAM);
         mSession->TransactionHasDataToWrite(this);
@@ -1229,17 +1228,17 @@ Http2Stream::OnReadSegment(const char *b
       if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) {
         return rv;
       }
     }
 
     if (mRequestHeadersDone && !mOpenGenerated) {
       if (!mSession->TryToActivate(this)) {
         LOG3(("Http2Stream::OnReadSegment %p cannot activate now. queued.\n", this));
-        return NS_OK;
+        return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
       }
       if (NS_FAILED(rv = GenerateOpen())) {
         return rv;
       }
    }
 
     LOG3(("ParseHttpRequestHeaders %p used %d of %d. "
           "requestheadersdone = %d mOpenGenerated = %d\n",
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -58,17 +58,16 @@ SpdySession31::SpdySession31(nsISocketTr
   , mInputFrameDataLast(false)
   , mInputFrameDataStream(nullptr)
   , mNeedsCleanup(nullptr)
   , mShouldGoAway(false)
   , mClosed(false)
   , mCleanShutdown(false)
   , mDataPending(false)
   , mGoAwayID(0)
-  , mMaxConcurrent(kDefaultMaxConcurrent)
   , mConcurrent(0)
   , mServerPushedResources(0)
   , mServerInitialStreamWindow(kDefaultRwin)
   , mLocalSessionWindow(kDefaultRwin)
   , mRemoteSessionWindow(kDefaultRwin)
   , mOutputQueueSize(kDefaultQueueSize)
   , mOutputQueueUsed(0)
   , mOutputQueueSent(0)
@@ -84,17 +83,17 @@ SpdySession31::SpdySession31(nsISocketTr
 
   LOG3(("SpdySession31::SpdySession31 %p serial=0x%X\n", this, mSerial));
 
   mInputFrameBuffer = new char[mInputFrameBufferSize];
   mOutputQueueBuffer = new char[mOutputQueueSize];
   zlibInit();
 
   mPushAllowance = gHttpHandler->SpdyPushAllowance();
-
+  mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
   GenerateSettings();
 
   mLastDataReadEpoch = mLastReadEpoch;
 
   mPingThreshold = gHttpHandler->SpdyPingThreshold();
 }
 
--- a/netwerk/protocol/http/SpdySession31.h
+++ b/netwerk/protocol/http/SpdySession31.h
@@ -129,17 +129,16 @@ public:
   const static uint32_t kDefaultBufferSize = 2048;
 
   // kDefaultQueueSize must be >= other queue size constants
   const static uint32_t kDefaultQueueSize =  32768;
   const static uint32_t kQueueMinimumCleanup = 24576;
   const static uint32_t kQueueTailRoom    =  4096;
   const static uint32_t kQueueReserved    =  1024;
 
-  const static uint32_t kDefaultMaxConcurrent = 100;
   const static uint32_t kMaxStreamID = 0x7800000;
 
   // This is a sentinel for a deleted stream. It is not a valid
   // 31 bit stream ID.
   const static uint32_t kDeadStreamID = 0xffffdead;
 
   // below the emergency threshold of local window we ack every received
   // byte. Above that we coalesce bytes into the MinimumToAck size.
--- a/netwerk/protocol/http/SpdyStream31.cpp
+++ b/netwerk/protocol/http/SpdyStream31.cpp
@@ -155,17 +155,17 @@ SpdyStream31::ReadSegments(nsAHttpSegmen
       uint32_t wasted = 0;
       mSegmentReader = reader;
       OnReadSegment("", 0, &wasted);
       mSegmentReader = nullptr;
     }
 
     // If the sending flow control window is open (!mBlockedOnRwin) then
     // continue sending the request
-    if (!mBlockedOnRwin && mUpstreamState != GENERATING_SYN_STREAM &&
+    if (!mBlockedOnRwin && mSynFrameGenerated &&
         !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
       LOG3(("SpdyStream31::ReadSegments %p 0x%X: Sending request data complete, "
             "mUpstreamState=%x finondata=%d",this, mStreamID,
             mUpstreamState, mSentFinOnData));
       if (mSentFinOnData) {
         ChangeState(UPSTREAM_COMPLETE);
       } else {
         GenerateDataFrameHeader(0, true);
@@ -1486,17 +1486,17 @@ SpdyStream31::OnReadSegment(const char *
       if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) {
         return rv;
       }
     }
 
     if (mRequestHeadersDone && !mSynFrameGenerated) {
       if (!mSession->TryToActivate(this)) {
         LOG3(("SpdyStream31::OnReadSegment %p cannot activate now. queued.\n", this));
-        return NS_OK;
+        return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
       }
       if (NS_FAILED(rv = GenerateSynFrame())) {
         return rv;
       }
     }
 
     LOG3(("ParseHttpRequestHeaders %p used %d of %d. "
           "requestheadersdone = %d mSynFrameGenerated = %d\n",
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -189,16 +189,17 @@ nsHttpHandler::nsHttpHandler()
     , mCoalesceSpdy(true)
     , mSpdyPersistentSettings(false)
     , mAllowPush(true)
     , mEnableAltSvc(true)
     , mEnableAltSvcOE(true)
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPushAllowance(32768)
+    , mDefaultSpdyConcurrent(ASpdySession::kDefaultMaxConcurrent)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
     , mParallelSpeculativeConnectLimit(6)
     , mRequestTokenBucketEnabled(true)
     , mRequestTokenBucketMinParallelism(6)
     , mRequestTokenBucketHz(100)
     , mRequestTokenBucketBurst(32)
@@ -1282,16 +1283,24 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
         rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
         if (NS_SUCCEEDED(rv)) {
             mSpdyPushAllowance =
                 static_cast<uint32_t>
                 (clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
         }
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.default-concurrent"))) {
+        rv = prefs->GetIntPref(HTTP_PREF("spdy.default-concurrent"), &val);
+        if (NS_SUCCEEDED(rv)) {
+            mDefaultSpdyConcurrent =
+                static_cast<uint32_t>(std::max<int32_t>(std::min<int32_t>(val, 9999), 1));
+        }
+    }
+
     // The amount of seconds to wait for a spdy ping response before
     // closing the session.
     if (PREF_CHANGED(HTTP_PREF("spdy.send-buffer-size"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.send-buffer-size"), &val);
         if (NS_SUCCEEDED(rv))
             mSpdySendBufferSize = (uint32_t) clamped(val, 1500, 0x7fffffff);
     }
 
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -100,16 +100,17 @@ public:
     bool           IsHttp2DraftEnabled() { return mHttp2DraftEnabled; }
     bool           IsHttp2Enabled() { return mHttp2DraftEnabled && mHttp2Enabled; }
     bool           EnforceHttp2TlsProfile() { return mEnforceHttp2TlsProfile; }
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseSpdyPersistentSettings() { return mSpdyPersistentSettings; }
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     uint32_t       SpdyPushAllowance()       { return mSpdyPushAllowance; }
+    uint32_t       DefaultSpdyConcurrent()   { return mDefaultSpdyConcurrent; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     bool           AllowPush()   { return mAllowPush; }
     bool           AllowAltSvc() { return mEnableAltSvc; }
     bool           AllowAltSvcOE() { return mEnableAltSvcOE; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
     bool           CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
@@ -482,16 +483,17 @@ private:
     uint32_t           mEnableAltSvcOE : 1;
 
     // Try to use SPDY features instead of HTTP/1.1 over SSL
     SpdyInformation    mSpdyInfo;
 
     uint32_t       mSpdySendingChunkSize;
     uint32_t       mSpdySendBufferSize;
     uint32_t       mSpdyPushAllowance;
+    uint32_t       mDefaultSpdyConcurrent;
     PRIntervalTime mSpdyPingThreshold;
     PRIntervalTime mSpdyPingTimeout;
 
     // The maximum amount of time to wait for socket transport to be
     // established. In milliseconds.
     uint32_t       mConnectTimeout;
 
     // The maximum number of current global half open sockets allowable
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -10,16 +10,17 @@ function generateContent(size) {
     content += "0";
   }
   return content;
 }
 
 var posts = [];
 posts.push(generateContent(10));
 posts.push(generateContent(250000));
+posts.push(generateContent(128000));
 
 // pre-calculated md5sums (in hex) of the above posts
 var md5s = ['f1b708bba17f1ce948dc979f4d7092bc',
             '2ef8d3b6c8f329318eb1a119b12622b6'];
 
 var bigListenerData = generateContent(128 * 1024);
 var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836';
 
@@ -272,36 +273,68 @@ function test_http2_xhr() {
 
 var concurrent_channels = [];
 
 var Http2ConcurrentListener = function() {};
 
 Http2ConcurrentListener.prototype = new Http2CheckListener();
 Http2ConcurrentListener.prototype.count = 0;
 Http2ConcurrentListener.prototype.target = 0;
+Http2ConcurrentListener.prototype.reset = 0;
+Http2ConcurrentListener.prototype.recvdHdr = 0;
 
 Http2ConcurrentListener.prototype.onStopRequest = function(request, ctx, status) {
   this.count++;
   do_check_true(this.isHttp2Connection);
+  if (this.recvdHdr > 0) {
+    do_check_eq(request.getResponseHeader("X-Recvd"), this.recvdHdr);
+  }
+
   if (this.count == this.target) {
+    if (this.reset > 0) {
+      prefs.setIntPref("network.http.spdy.default-concurrent", this.reset);
+    }
     run_next_test();
     do_test_finished();
   }
 };
 
 function test_http2_concurrent() {
   var concurrent_listener = new Http2ConcurrentListener();
   concurrent_listener.target = 201;
+  concurrent_listener.reset = prefs.getIntPref("network.http.spdy.default-concurrent");
+  prefs.setIntPref("network.http.spdy.default-concurrent", 100);
+
   for (var i = 0; i < concurrent_listener.target; i++) {
     concurrent_channels[i] = makeChan("https://localhost:" + serverPort + "/750ms");
     concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
     concurrent_channels[i].asyncOpen(concurrent_listener, null);
   }
 }
 
+function test_http2_concurrent_post() {
+  var concurrent_listener = new Http2ConcurrentListener();
+  concurrent_listener.target = 8;
+  concurrent_listener.recvdHdr = posts[2].length;
+  concurrent_listener.reset = prefs.getIntPref("network.http.spdy.default-concurrent");
+  prefs.setIntPref("network.http.spdy.default-concurrent", 3);
+
+  for (var i = 0; i < concurrent_listener.target; i++) {
+    concurrent_channels[i] = makeChan("https://localhost:" + serverPort + "/750msPost");
+    concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
+    var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+               .createInstance(Ci.nsIStringInputStream);
+    stream.data = posts[2];
+    var uchan = concurrent_channels[i].QueryInterface(Ci.nsIUploadChannel);
+    uchan.setUploadStream(stream, "text/plain", stream.available());
+    concurrent_channels[i].requestMethod = "POST";
+    concurrent_channels[i].asyncOpen(concurrent_listener, null);
+  }
+}
+
 // Test to make sure we get multiplexing right
 function test_http2_multiplex() {
   var chan1 = makeChan("https://localhost:" + serverPort + "/multiplex1");
   var chan2 = makeChan("https://localhost:" + serverPort + "/multiplex2");
   var listener1 = new Http2MultiplexListener();
   var listener2 = new Http2MultiplexListener();
   chan1.asyncOpen(listener1, null);
   chan2.asyncOpen(listener2, null);
@@ -620,16 +653,17 @@ function test_complete() {
 // hack - the header test resets the multiplex object on the server,
 // so make sure header is always run before the multiplex test.
 //
 // make sure post_big runs first to test race condition in restarting
 // a stalled stream when a SETTINGS frame arrives
 var tests = [ test_http2_post_big
             , test_http2_basic
             , test_http2_concurrent
+            , test_http2_concurrent_post
             , test_http2_basic_unblocked_dep
             , test_http2_nospdy
             , test_http2_push1
             , test_http2_push2
             , test_http2_push3
             , test_http2_push4
 	    , test_http2_altsvc
             , test_http2_doubleheader
--- a/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h
+++ b/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h
@@ -98,17 +98,19 @@ Log(const char* aMessageType,
 
   std::string msg = msgStream.str();
 #if defined(DEBUG)
   // Use NS_DebugBreak directly as we want child process prefix, but not source
   // file or line number.
   NS_DebugBreak(NS_DEBUG_WARNING, nullptr, msg.c_str(), nullptr, -1);
 #endif
 
-  nsContentUtils::LogMessageToConsole(msg.c_str());
+  if (nsContentUtils::IsInitialized()) {
+    nsContentUtils::LogMessageToConsole(msg.c_str());
+  }
 }
 
 // Initialize sandbox logging if required.
 static void
 InitLoggingIfRequired()
 {
   if (!sProvideLogFunctionCb) {
     return;
--- a/services/common/rest.js
+++ b/services/common/rest.js
@@ -300,17 +300,22 @@ RESTRequest.prototype = {
     if (onComplete) {
       this.onComplete = onComplete;
     }
     if (onProgress) {
       this.onProgress = onProgress;
     }
 
     // Create and initialize HTTP channel.
-    let channel = Services.io.newChannelFromURI(this.uri, null, null)
+    let channel = Services.io.newChannelFromURI2(this.uri,
+                                                 null,      // aLoadingNode
+                                                 Services.scriptSecurityManager.getSystemPrincipal(),
+                                                 null,      // aTriggeringPrincipal
+                                                 Ci.nsILoadInfo.SEC_NORMAL,
+                                                 Ci.nsIContentPolicy.TYPE_OTHER)
                           .QueryInterface(Ci.nsIRequest)
                           .QueryInterface(Ci.nsIHttpChannel);
     this.channel = channel;
     channel.loadFlags |= this.loadFlags;
     channel.notificationCallbacks = this;
 
     // Set request headers.
     let headers = this._headers;
--- a/services/sync/Weave.js
+++ b/services/sync/Weave.js
@@ -175,17 +175,22 @@ AboutWeaveLog.prototype = {
 
   getURIFlags: function(aURI) {
     return 0;
   },
 
   newChannel: function(aURI) {
     let dir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
     let uri = Services.io.newFileURI(dir);
-    let channel = Services.io.newChannelFromURI(uri);
+    let channel = Services.io.newChannelFromURI2(uri,
+                                                 null,      // aLoadingNode
+                                                 Services.scriptSecurityManager.getSystemPrincipal(),
+                                                 null,      // aTriggeringPrincipal
+                                                 Ci.nsILoadInfo.SEC_NORMAL,
+                                                 Ci.nsIContentPolicy.TYPE_OTHER);
     channel.originalURI = aURI;
 
     // Ensure that the about page has the same privileges as a regular directory
     // view. That way links to files can be opened.
     let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
                 .getService(Ci.nsIScriptSecurityManager);
     let principal = ssm.getNoAppCodebasePrincipal(uri);
     channel.owner = principal;
--- a/services/sync/modules/resource.js
+++ b/services/sync/modules/resource.js
@@ -141,17 +141,24 @@ AsyncResource.prototype = {
 
   // ** {{{ AsyncResource._createRequest }}} **
   //
   // This method returns a new IO Channel for requests to be made
   // through. It is never called directly, only {{{_doRequest}}} uses it
   // to obtain a request channel.
   //
   _createRequest: function Res__createRequest(method) {
-    let channel = Services.io.newChannel(this.spec, null, null)
+    let channel = Services.io.newChannel2(this.spec,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_OTHER)
                           .QueryInterface(Ci.nsIRequest)
                           .QueryInterface(Ci.nsIHttpChannel);
 
     channel.loadFlags |= DEFAULT_LOAD_FLAGS;
 
     // Setup a callback to handle channel notifications.
     let listener = new ChannelNotificationListener(this.headerNames);
     channel.notificationCallbacks = listener;
--- a/services/sync/tests/unit/test_errorhandler_filelog.js
+++ b/services/sync/tests/unit/test_errorhandler_filelog.js
@@ -2,16 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 const logsdir            = FileUtils.getDir("ProfD", ["weave", "logs"], true);
 const LOG_PREFIX_SUCCESS = "success-";
 const LOG_PREFIX_ERROR   = "error-";
 
 // Delay to wait before cleanup, to allow files to age.
 // This is so large because the file timestamp granularity is per-second, and
 // so otherwise we can end up with all of our files -- the ones we want to
@@ -76,21 +77,26 @@ add_test(function test_logOnSuccess_fals
     run_next_test();
   });
 
   // Fake a successful sync.
   Svc.Obs.notify("weave:service:sync:finish");
 });
 
 function readFile(file, callback) {
-  NetUtil.asyncFetch(file, function (inputStream, statusCode, request) {
+  NetUtil.asyncFetch2(file, function (inputStream, statusCode, request) {
     let data = NetUtil.readInputStreamToString(inputStream,
                                                inputStream.available());
     callback(statusCode, data);
-  });
+  },
+  null,      // aLoadingNode
+  Services.scriptSecurityManager.getSystemPrincipal(),
+  null,      // aTriggeringPrincipal
+  Ci.nsILoadInfo.SEC_NORMAL,
+  Ci.nsIContentPolicy.TYPE_OTHER);
 }
 
 add_test(function test_logOnSuccess_true() {
   Svc.Prefs.set("log.appender.file.logOnSuccess", true);
 
   let log = Log.repository.getLogger("Sync.Test.FileLog");
   const MESSAGE = "this WILL show up";
   log.info(MESSAGE);
--- a/services/sync/tps/extensions/mozmill/resource/stdlib/EventUtils.js
+++ b/services/sync/tps/extensions/mozmill/resource/stdlib/EventUtils.js
@@ -547,22 +547,16 @@ function synthesizeKey(aKey, aEvent, aWi
           flags |= utils.KEY_FLAG_LOCATION_LEFT;
           break;
         case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
           flags |= utils.KEY_FLAG_LOCATION_RIGHT;
           break;
         case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
           flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
           break;
-        case KeyboardEvent.DOM_KEY_LOCATION_MOBILE:
-          flags |= utils.KEY_FLAG_LOCATION_MOBILE;
-          break;
-        case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK:
-          flags |= utils.KEY_FLAG_LOCATION_JOYSTICK;
-          break;
       }
     }
 
     if (!("type" in aEvent) || !aEvent.type) {
       // Send keydown + (optional) keypress + keyup events.
       var keyDownDefaultHappened =
         utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
       if (isKeypressFiredKey(keyCode)) {
--- a/services/sync/tps/extensions/tps/resource/modules/addons.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/addons.jsm
@@ -15,17 +15,24 @@ Cu.import("resource://services-sync/addo
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://tps/logger.jsm");
 
 const ADDONSGETURL = "http://127.0.0.1:4567/";
 const STATE_ENABLED = 1;
 const STATE_DISABLED = 2;
 
 function GetFileAsText(file) {
-  let channel = Services.io.newChannel(file, null, null);
+  let channel = Services.io.newChannel2(file,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let inputStream = channel.open();
   if (channel instanceof Ci.nsIHttpChannel &&
       channel.responseStatus != 200) {
     return "";
   }
 
   let streamBuf = "";
   let sis = Cc["@mozilla.org/scriptableinputstream;1"]
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -581,22 +581,16 @@ function synthesizeKey(aKey, aEvent, aWi
           flags |= utils.KEY_FLAG_LOCATION_LEFT;
           break;
         case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
           flags |= utils.KEY_FLAG_LOCATION_RIGHT;
           break;
         case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
           flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
           break;
-        case KeyboardEvent.DOM_KEY_LOCATION_MOBILE:
-          flags |= utils.KEY_FLAG_LOCATION_MOBILE;
-          break;
-        case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK:
-          flags |= utils.KEY_FLAG_LOCATION_JOYSTICK;
-          break;
       }
     }
 
     if (!("type" in aEvent) || !aEvent.type) {
       // Send keydown + (optional) keypress + keyup events.
       var keyDownDefaultHappened =
         utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
       if (isKeypressFiredKey(keyCode) && keyDownDefaultHappened) {
--- a/testing/xpcshell/moz-http2/moz-http2.js
+++ b/testing/xpcshell/moz-http2/moz-http2.js
@@ -278,16 +278,39 @@ function handleRequest(req, res) {
       res.setHeader('X-Calculated-MD5', md5);
       res.writeHead(200);
       res.end(content);
     });
 
     return;
   }
 
+  else if (u.pathname === "/750msPost") {
+    if (req.method != "POST") {
+      res.writeHead(405);
+      res.end('Unexpected method: ' + req.method);
+      return;
+    }
+
+    var accum = 0;
+    req.on('data', function receivePostData(chunk) {
+      accum += chunk.length;
+    });
+    req.on('end', function finishPost() {
+      res.setHeader('X-Recvd', accum);
+      var rl = new runlater();
+      rl.req = req;
+      rl.resp = res;
+      setTimeout(executeRunLater, 750, rl);
+      return;
+    });
+
+    return;
+  }
+
   else if (u.pathname === "/h11required_stream") {
     if (req.httpVersionMajor === 2) {
       h11required_conn = req.stream.connection;
       res.stream.reset('HTTP_1_1_REQUIRED');
       return;
     }
   }
 
--- a/testing/xpcshell/node-http2/HISTORY.md
+++ b/testing/xpcshell/node-http2/HISTORY.md
@@ -1,12 +1,25 @@
 Version history
 ===============
 
-### 3.0.0 (2014-08-XX) ###
+### 3.1.0 (2014-12-11) ###
+
+* Upgrade to the latest draft: [draft-ietf-httpbis-http2-16]
+ * This involves some state transition changes that are technically incompatible with draft-14. If you need to be assured to interop on -14, continue using 3.0.1
+
+[draft-ietf-httpbis-http2-16]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-16
+
+### 3.0.1 (2014-11-20) ###
+
+* Bugfix release.
+* Fixed #81 and #87
+* Fixed a bug in flow control (without GitHub issue)
+
+### 3.0.0 (2014-08-25) ###
 
 * Re-join node-http2 and node-http2-protocol into one repository
 * API Changes
  * The default versions of createServer, request, and get now enforce TLS-only
  * The raw versions of createServer, request, and get are now under http2.raw instead of http2
  * What was previously in the http2-protocol repository/module is now available under http2.protocol from this repo/module
  * http2-protocol.ImplementedVersion is now http2.protocol.VERSION (the ALPN token)
 
--- a/testing/xpcshell/node-http2/README.md
+++ b/testing/xpcshell/node-http2/README.md
@@ -1,12 +1,12 @@
 node-http2
 ==========
 
-An HTTP/2 ([draft-ietf-httpbis-http2-14](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14))
+An HTTP/2 ([draft-ietf-httpbis-http2-16](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16))
 client and server implementation for node.js.
 
 ![Travis CI status](https://travis-ci.org/molnarg/node-http2.svg?branch=master)
 
 Installation
 ------------
 
 ```
@@ -111,22 +111,22 @@ The developer documentation is generated
 ### Running the tests ###
 
 It's easy, just run `npm test`. The tests are written in BDD style, so they are a good starting
 point to understand the code.
 
 ### Test coverage ###
 
 To generate a code coverage report, run `npm test --coverage` (which runs very slowly, be patient).
-Code coverage summary as of version 3.0.0:
+Code coverage summary as of version 3.0.1:
 ```
-Statements   : 91.85% ( 1747/1902 )
-Branches     : 81.61% ( 688/843 )
-Functions    : 90.95% ( 211/232 )
-Lines        : 91.92% ( 1741/1894 )
+Statements   : 92.09% ( 1759/1910 )
+Branches     : 82.56% ( 696/843 )
+Functions    : 91.38% ( 212/232 )
+Lines        : 92.17% ( 1753/1902 )
 ```
 
 There's a hosted version of the detailed (line-by-line) coverage report
 [here](http://molnarg.github.io/node-http2/coverage/lcov-report/lib/).
 
 ### Logging ###
 
 Logging is turned off by default. You can turn it on by passing a bunyan logger as `log` option when
@@ -146,22 +146,27 @@ Running the example server and client wi
 
 ```bash
 $ HTTP2_LOG=info node ./example/client.js 'http://localhost:8080/server.js' >/dev/null
 ```
 
 Contributors
 ------------
 
+The co-maintainer of the project is [Nick Hurley](https://github.com/todesschaf).
+
 Code contributions are always welcome! People who contributed to node-http2 so far:
 
-* Nick Hurley
-* Mike Belshe
-* Yoshihiro Iwanaga
-* vsemogutor
+* [Nick Hurley](https://github.com/todesschaf)
+* [Mike Belshe](https://github.com/mbelshe)
+* [Yoshihiro Iwanaga](https://github.com/iwanaga)
+* [Igor Novikov](https://github.com/vsemogutor)
+* [James Willcox](https://github.com/snorp)
+* [David Björklund](https://github.com/kesla)
+* [Patrick McManus](https://github.com/mcmanus)
 
 Special thanks to Google for financing the development of this module as part of their [Summer of
 Code program](https://developers.google.com/open-source/soc/) (project: [HTTP/2 prototype server
 implementation](https://google-melange.appspot.com/gsoc/project/google/gsoc2013/molnarg/5001)), and
 Nick Hurley of Mozilla, my GSoC mentor, who helped with regular code review and technical advices.
 
 License
 -------
--- a/testing/xpcshell/node-http2/example/client.js
+++ b/testing/xpcshell/node-http2/example/client.js
@@ -1,24 +1,23 @@
 var fs = require('fs');
 var path = require('path');
 var http2 = require('..');
 
+// Setting the global logger (optional)
 http2.globalAgent = new http2.Agent({
   log: require('../test/util').createLogger('client')
 });
 
+// We use self signed certs in the example code so we ignore cert errors
 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
 
 // Sending the request
-// It would be `var request = http2.get(process.argv.pop());` if we wouldn't care about plain mode
-var options = require('url').parse(process.argv.pop());
-options.plain = Boolean(process.env.HTTP2_PLAIN);
-var request = http2.request(options);
-request.end();
+var url = process.argv.pop();
+var request = process.env.HTTP2_PLAIN ? http2.raw.get(url) : http2.get(url);
 
 // Receiving the response
 request.on('response', function(response) {
   response.pipe(process.stdout);
   response.on('end', finish);
 });
 
 // Receiving push streams
--- a/testing/xpcshell/node-http2/example/server.js
+++ b/testing/xpcshell/node-http2/example/server.js
@@ -1,28 +1,18 @@
 var fs = require('fs');
 var path = require('path');
 var http2 = require('..');
 
-var options = process.env.HTTP2_PLAIN ? {
-  plain: true
-} : {
-  key: fs.readFileSync(path.join(__dirname, '/localhost.key')),
-  cert: fs.readFileSync(path.join(__dirname, '/localhost.crt'))
-};
-
-// Passing bunyan logger (optional)
-options.log = require('../test/util').createLogger('server');
-
 // We cache one file to be able to do simple performance tests without waiting for the disk
 var cachedFile = fs.readFileSync(path.join(__dirname, './server.js'));
 var cachedUrl = '/server.js';
 
-// Creating the server
-var server = http2.createServer(options, function(request, response) {
+// The callback to handle requests
+function onRequest(request, response) {
   var filename = path.join(__dirname, request.url);
 
   // Serving server.js from cache. Useful for microbenchmarks.
   if (request.url === cachedUrl) {
     response.end(cachedFile);
   }
 
   // Reading file from disk if it exists and is safe.
@@ -39,11 +29,27 @@ var server = http2.createServer(options,
     fs.createReadStream(filename).pipe(response);
   }
 
   // Otherwise responding with 404.
   else {
     response.writeHead('404');
     response.end();
   }
-});
+}
+
+// Creating a bunyan logger (optional)
+var log = require('../test/util').createLogger('server');
 
+// Creating the server in plain or TLS mode (TLS mode is the default)
+var server;
+if (process.env.HTTP2_PLAIN) {
+  server = http2.raw.createServer({
+    log: log
+  }, onRequest);
+} else {
+  server = http2.createServer({
+    log: log,
+    key: fs.readFileSync(path.join(__dirname, '/localhost.key')),
+    cert: fs.readFileSync(path.join(__dirname, '/localhost.crt'))
+  }, onRequest);
+}
 server.listen(process.env.HTTP2_PORT || 8080);
--- a/testing/xpcshell/node-http2/lib/http.js
+++ b/testing/xpcshell/node-http2/lib/http.js
@@ -116,17 +116,17 @@
 //   - **request.setSocketKeepAlive([enable], [initialDelay])**
 //
 // - **Class: http2.IncomingMessage**
 //   - **Event: 'close'**
 //   - **message.setTimeout(timeout, [callback])**
 //
 // [1]: http://nodejs.org/api/https.html
 // [2]: http://nodejs.org/api/http.html
-// [3]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.3.2
+// [3]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.4
 // [expect-continue]: https://github.com/http2/http2-spec/issues/18
 // [connect]: https://github.com/http2/http2-spec/issues/230
 
 // Common server and client side code
 // ==================================
 
 var net = require('net');
 var url = require('url');
@@ -241,17 +241,17 @@ function IncomingMessage(stream) {
   this._lastHeadersSeen = undefined;
 
   // * Other metadata is filled in when the headers arrive.
   stream.once('headers', this._onHeaders.bind(this));
   stream.once('end', this._onEnd.bind(this));
 }
 IncomingMessage.prototype = Object.create(PassThrough.prototype, { constructor: { value: IncomingMessage } });
 
-// [Request Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.3.1)
+// [Request Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3)
 // * `headers` argument: HTTP/2.0 request and response header fields carry information as a series
 //   of key-value pairs. This includes the target URI for the request, the status code for the
 //   response, as well as HTTP header fields.
 IncomingMessage.prototype._onHeaders = function _onHeaders(headers) {
   // * Detects malformed headers
   this._validateHeaders(headers);
 
   // * Store the _regular_ headers in `this.headers`
@@ -597,17 +597,17 @@ exports.http.createServer = exports.http
 // IncomingRequest class
 // ---------------------
 
 function IncomingRequest(stream) {
   IncomingMessage.call(this, stream);
 }
 IncomingRequest.prototype = Object.create(IncomingMessage.prototype, { constructor: { value: IncomingRequest } });
 
-// [Request Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.3.1)
+// [Request Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3)
 // * `headers` argument: HTTP/2.0 request and response header fields carry information as a series
 //   of key-value pairs. This includes the target URI for the request, the status code for the
 //   response, as well as HTTP header fields.
 IncomingRequest.prototype._onHeaders = function _onHeaders(headers) {
   // * The ":method" header field includes the HTTP method
   // * The ":scheme" header field includes the scheme portion of the target URI
   // * The ":authority" header field includes the authority portion of the target URI
   // * The ":path" header field includes the path and query parts of the target URI.
@@ -748,47 +748,51 @@ exports.OutgoingRequest = OutgoingReques
 exports.IncomingResponse = IncomingResponse;
 exports.Agent = Agent;
 exports.globalAgent = undefined;
 
 function requestRaw(options, callback) {
   if (typeof options === "string") {
     options = url.parse(options);
   }
-  if ((options.protocol && options.protocol !== "http:") || !options.plain) {
+  options.plain = true;
+  if (options.protocol && options.protocol !== "http:") {
     throw new Error('This interface only supports http-schemed URLs');
   }
   return (options.agent || exports.globalAgent).request(options, callback);
 }
 
 function requestTLS(options, callback) {
   if (typeof options === "string") {
     options = url.parse(options);
   }
-  if ((options.protocol && options.protocol !== "https:") || options.plain) {
+  options.plain = false;
+  if (options.protocol && options.protocol !== "https:") {
     throw new Error('This interface only supports https-schemed URLs');
   }
   return (options.agent || exports.globalAgent).request(options, callback);
 }
 
 function getRaw(options, callback) {
   if (typeof options === "string") {
     options = url.parse(options);
   }
-  if ((options.protocol && options.protocol !== "http:") || !options.plain) {
+  options.plain = true;
+  if (options.protocol && options.protocol !== "http:") {
     throw new Error('This interface only supports http-schemed URLs');
   }
   return (options.agent || exports.globalAgent).get(options, callback);
 }
 
 function getTLS(options, callback) {
   if (typeof options === "string") {
     options = url.parse(options);
   }
-  if ((options.protocol && options.protocol !== "https:") || options.plain) {
+  options.plain = false;
+  if (options.protocol && options.protocol !== "https:") {
     throw new Error('This interface only supports https-schemed URLs');
   }
   return (options.agent || exports.globalAgent).get(options, callback);
 }
 
 // Agent class
 // -----------
 
@@ -858,30 +862,30 @@ Agent.prototype.request = function reque
       host: options.host,
       port: options.port,
       localAddress: options.localAddress
     });
     endpoint.pipe(endpoint.socket).pipe(endpoint);
     request._start(endpoint.createStream(), options);
   }
 
-  // * HTTP/2 over TLS negotiated using NPN or ALPN
+  // * HTTP/2 over TLS negotiated using NPN or ALPN, or fallback to HTTPS1
   else {
     var started = false;
     options.ALPNProtocols = supportedProtocols;
     options.NPNProtocols = supportedProtocols;
     options.servername = options.host; // Server Name Indication
     options.agent = this._httpsAgent;
     options.ciphers = options.ciphers || cipherSuites;
     var httpsRequest = https.request(options);
 
     httpsRequest.on('socket', function(socket) {
       var negotiatedProtocol = socket.alpnProtocol || socket.npnProtocol;
       if (negotiatedProtocol != null) { // null in >=0.11.0, undefined in <0.11.0
-        negotiated()
+        negotiated();
       } else {
         socket.on('secureConnect', negotiated);
       }
     });
 
     var self = this;
     function negotiated() {
       var endpoint;
@@ -889,21 +893,22 @@ Agent.prototype.request = function reque
       if (negotiatedProtocol === protocol.VERSION) {
         httpsRequest.socket.emit('agentRemove');
         unbundleSocket(httpsRequest.socket);
         endpoint = new Endpoint(self._log, 'CLIENT', self._settings);
         endpoint.socket = httpsRequest.socket;
         endpoint.pipe(endpoint.socket).pipe(endpoint);
       }
       if (started) {
+        // ** In the meantime, an other connection was made to the same host...
         if (endpoint) {
+          // *** and it turned out to be HTTP2 and the request was multiplexed on that one, so we should close this one
           endpoint.close();
-        } else {
-          httpsRequest.abort();
         }
+        // *** otherwise, the fallback to HTTPS1 is already done.
       } else {
         if (endpoint) {
           self._log.info({ e: endpoint, server: options.host + ':' + options.port },
                          'New outgoing HTTP/2 connection');
           self.endpoints[key] = endpoint;
           self.emit(key, endpoint);
         } else {
           self.emit(key, undefined);
@@ -1074,17 +1079,17 @@ OutgoingRequest.prototype._onPromise = f
 // IncomingResponse class
 // ----------------------
 
 function IncomingResponse(stream) {
   IncomingMessage.call(this, stream);
 }
 IncomingResponse.prototype = Object.create(IncomingMessage.prototype, { constructor: { value: IncomingResponse } });
 
-// [Response Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.3.2)
+// [Response Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.4)
 // * `headers` argument: HTTP/2.0 request and response header fields carry information as a series
 //   of key-value pairs. This includes the target URI for the request, the status code for the
 //   response, as well as HTTP header fields.
 IncomingResponse.prototype._onHeaders = function _onHeaders(headers) {
   // * A single ":status" header field is defined that carries the HTTP status code field. This
   //   header field MUST be included in all responses.
   // * A client MUST treat the absence of the ":status" header field, the presence of multiple
   //   values, or an invalid value as a stream error of type PROTOCOL_ERROR.
--- a/testing/xpcshell/node-http2/lib/index.js
+++ b/testing/xpcshell/node-http2/lib/index.js
@@ -1,21 +1,21 @@
-// [node-http2][homepage] is an [HTTP/2 (draft 14)][http2] implementation for [node.js][node].
+// [node-http2][homepage] is an [HTTP/2 (draft 16)][http2] implementation for [node.js][node].
 //
 // The core of the protocol is implemented in the protocol sub-directory. This directory provides
 // two important features on top of the protocol:
 //
 // * Implementation of different negotiation schemes that can be used to start a HTTP2 connection.
 //   These include TLS ALPN, Upgrade and Plain TCP.
 //
 // * Providing an API very similar to the standard node.js [HTTPS module API][node-https]
 //   (which is in turn very similar to the [HTTP module API][node-http]).
 //
 // [homepage]:            https://github.com/molnarg/node-http2
-// [http2]:               http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
+// [http2]:               http://tools.ietf.org/html/draft-ietf-httpbis-http2-16
 // [node]:                http://nodejs.org/
 // [node-https]:          http://nodejs.org/api/https.html
 // [node-http]:           http://nodejs.org/api/http.html
 
 module.exports   = require('./http');
 
 /*
                   HTTP API
--- a/testing/xpcshell/node-http2/lib/protocol/compressor.js
+++ b/testing/xpcshell/node-http2/lib/protocol/compressor.js
@@ -1118,20 +1118,20 @@ Compressor.prototype.compress = function
   function compressHeader(name) {
     var value = headers[name];
     name = String(name).toLowerCase();
 
     // * To allow for better compression efficiency, the Cookie header field MAY be split into
     //   separate header fields, each with one or more cookie-pairs.
     if (name == 'cookie') {
       if (!(value instanceof Array)) {
-        value = [value]
+        value = [value];
       }
       value = Array.prototype.concat.apply([], value.map(function(cookie) {
-        return String(cookie).split(';').map(trim)
+        return String(cookie).split(';').map(trim);
       }));
     }
 
     if (value instanceof Array) {
       for (var i = 0; i < value.length; i++) {
         compressor.write([name, String(value[i])]);
       }
     } else {
@@ -1251,17 +1251,17 @@ Decompressor.prototype.decompress = func
       headers[name] = value;
     }
   }
 
   // * If there are multiple Cookie header fields after decompression, these MUST be concatenated
   //   into a single octet string using the two octet delimiter of 0x3B, 0x20 (the ASCII
   //   string "; ").
   if (('cookie' in headers) && (headers['cookie'] instanceof Array)) {
-    headers['cookie'] = headers['cookie'].join('; ')
+    headers['cookie'] = headers['cookie'].join('; ');
   }
 
   return headers;
 };
 
 // When a `frame` arrives
 Decompressor.prototype._transform = function _transform(frame, encoding, done) {
   // * and the collection process is already `_inProgress`, the frame is simply stored, except if
@@ -1335,10 +1335,10 @@ function cut(buffer, size) {
     var chunkSize = Math.min(size, buffer.length - cursor);
     chunks.push(buffer.slice(cursor, cursor + chunkSize));
     cursor += chunkSize;
   } while(cursor < buffer.length);
   return chunks;
 }
 
 function trim(string) {
-  return string.trim()
+  return string.trim();
 }
--- a/testing/xpcshell/node-http2/lib/protocol/flow.js
+++ b/testing/xpcshell/node-http2/lib/protocol/flow.js
@@ -14,17 +14,17 @@ exports.Flow = Flow;
 // Public API
 // ----------
 
 // * **Event: 'error' (type)**: signals an error
 //
 // * **setInitialWindow(size)**: the initial flow control window size can be changed *any time*
 //   ([as described in the standard][1]) using this method
 //
-// [1]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.9.2
+// [1]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.9.2
 
 // API for child classes
 // ---------------------
 
 // * **new Flow([flowControlId])**: creating a new flow that will listen for WINDOW_UPDATES frames
 //   with the given `flowControlId` (or every update frame if not given)
 //
 // * **_send()**: called when more frames should be pushed. The child class is expected to override
@@ -76,17 +76,19 @@ Flow.prototype._receive = function _rece
   throw new Error('The _receive(frame, callback) method has to be overridden by the child class!');
 };
 
 // `_receive` is called by `_write` which in turn is [called by Duplex][1] when someone `write()`s
 // to the flow. It emits the 'receiving' event and notifies the window size tracking code if the
 // incoming frame is a WINDOW_UPDATE.
 // [1]: http://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback_1
 Flow.prototype._write = function _write(frame, encoding, callback) {
-  if (frame.flags.END_STREAM || (frame.type === 'RST_STREAM')) {
+  var sentToUs = (this._flowControlId === undefined) || (frame.stream === this._flowControlId);
+
+  if (sentToUs && (frame.flags.END_STREAM || (frame.type === 'RST_STREAM'))) {
     this._ended = true;
   }
 
   if ((frame.type === 'DATA') && (frame.data.length > 0)) {
     this._receive(frame, function() {
       this._received += frame.data.length;
       if (!this._restoreWindowTimer) {
         this._restoreWindowTimer = setImmediate(this._restoreWindow.bind(this));
@@ -94,18 +96,17 @@ Flow.prototype._write = function _write(
       callback();
     }.bind(this));
   }
 
   else {
     this._receive(frame, callback);
   }
 
-  if ((frame.type === 'WINDOW_UPDATE') &&
-      ((this._flowControlId === undefined) || (frame.stream === this._flowControlId))) {
+  if (sentToUs && (frame.type === 'WINDOW_UPDATE')) {
     this._updateWindow(frame);
   }
 };
 
 // `_restoreWindow` basically acknowledges the DATA frames received since it's last call. It sends
 // a WINDOW_UPDATE that restores the flow control window of the remote end.
 // TODO: push this directly into the output queue. No need to wait for DATA frames in the queue.
 Flow.prototype._restoreWindow = function _restoreWindow() {
--- a/testing/xpcshell/node-http2/lib/protocol/framer.js
+++ b/testing/xpcshell/node-http2/lib/protocol/framer.js
@@ -141,17 +141,17 @@ Deserializer.prototype._transform = func
       }
       this._next(COMMON_HEADER_SIZE);
     }
   }
 
   done();
 };
 
-// [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-4.1)
+// [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1)
 // --------------------------------------------------------------
 //
 // HTTP/2.0 frames share a common base format consisting of a 9-byte header followed by 0 to 2^24 - 1
 // bytes of data.
 //
 // Additional size limits can be set by specific application uses. HTTP limits the frame size to
 // 16,384 octets by default, though this can be increased by a receiver.
 //
@@ -264,17 +264,17 @@ Deserializer.commonHeader = function rea
 
 // Every frame type is registered in the following places:
 //
 // * `frameTypes`: a register of frame type codes (used by `commonHeader()`)
 // * `frameFlags`: a register of valid flags for frame types (used by `commonHeader()`)
 // * `typeSpecificAttributes`: a register of frame specific frame object attributes (used by
 //   logging code and also serves as documentation for frame objects)
 
-// [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.1)
+// [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.1)
 // ------------------------------------------------------------
 //
 // DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a
 // stream.
 //
 // The DATA frame defines the following flags:
 //
 // * END_STREAM (0x1):
@@ -303,17 +303,17 @@ Deserializer.DATA = function readData(bu
 
   if (paddingLength) {
     frame.data = buffer.slice(dataOffset, -1 * paddingLength);
   } else {
     frame.data = buffer.slice(dataOffset);
   }
 };
 
-// [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.2)
+// [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.2)
 // --------------------------------------------------------------
 //
 // The HEADERS frame (type=0x1) allows the sender to create a stream.
 //
 // The HEADERS frame defines the following flags:
 //
 // * END_STREAM (0x1):
 //   Bit 1 being set indicates that this frame is the last that the endpoint will send for the
@@ -385,17 +385,17 @@ Deserializer.HEADERS = function readHead
 
   if (paddingLength) {
     frame.data = buffer.slice(dataOffset, -1 * paddingLength);
   } else {
     frame.data = buffer.slice(dataOffset);
   }
 };
 
-// [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.3)
+// [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.3)
 // -------------------------------------------------------
 //
 // The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream.
 //
 // The PRIORITY frame does not define any flags.
 
 frameTypes[0x2] = 'PRIORITY';
 
@@ -430,17 +430,17 @@ Deserializer.PRIORITY = function readPri
   var dependencyData = new Buffer(4);
   buffer.copy(dependencyData, 0, 0, 4);
   frame.exclusiveDependency = !!(dependencyData[0] & 0x80);
   dependencyData[0] &= 0x7f;
   frame.priorityDependency = dependencyData.readUInt32BE(0);
   frame.priorityWeight = buffer.readUInt8(4);
 };
 
-// [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.4)
+// [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.4)
 // -----------------------------------------------------------
 //
 // The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream.
 //
 // No type-flags are defined.
 
 frameTypes[0x3] = 'RST_STREAM';
 
@@ -468,17 +468,17 @@ Serializer.RST_STREAM = function writeRs
 Deserializer.RST_STREAM = function readRstStream(buffer, frame) {
   frame.error = errorCodes[buffer.readUInt32BE(0)];
   if (!frame.error) {
     // Unknown error codes are considered equivalent to INTERNAL_ERROR
     frame.error = 'INTERNAL_ERROR';
   }
 };
 
-// [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.5)
+// [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.5)
 // -------------------------------------------------------
 //
 // The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints
 // communicate.
 //
 // The SETTINGS frame defines the following flag:
 
 // * ACK (0x1):
@@ -575,17 +575,17 @@ definedSettings[3] = { name: 'SETTINGS_M
 // * SETTINGS_INITIAL_WINDOW_SIZE (4):
 //   indicates the sender's initial stream window size (in bytes) for new streams.
 definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false };
 
 // * SETTINGS_MAX_FRAME_SIZE (5):
 //   indicates the maximum size of a frame the receiver will allow.
 definedSettings[5] = { name: 'SETTINGS_MAX_FRAME_SIZE', flag: false };
 
-// [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.6)
+// [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.6)
 // ---------------------------------------------------------------
 //
 // The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the
 // sender intends to initiate.
 //
 // The PUSH_PROMISE frame defines the following flags:
 //
 // * END_PUSH_PROMISE (0x4):
@@ -636,17 +636,17 @@ Deserializer.PUSH_PROMISE = function rea
   dataOffset += 4;
   if (paddingLength) {
     frame.data = buffer.slice(dataOffset, -1 * paddingLength);
   } else {
     frame.data = buffer.slice(dataOffset);
   }
 };
 
-// [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.7)
+// [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.7)
 // -----------------------------------------------
 //
 // The PING frame (type=0x6) is a mechanism for measuring a minimal round-trip time from the
 // sender, as well as determining whether an idle connection is still functional.
 //
 // The PING frame defines one type-specific flag:
 //
 // * ACK (0x1):
@@ -666,17 +666,17 @@ Serializer.PING = function writePing(fra
 
 Deserializer.PING = function readPing(buffer, frame) {
   if (buffer.length !== 8) {
     return 'FRAME_SIZE_ERROR';
   }
   frame.data = buffer;
 };
 
-// [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.8)
+// [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.8)
 // ---------------------------------------------------
 //
 // The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this connection.
 //
 // The GOAWAY frame does not define any flags.
 
 frameTypes[0x7] = 'GOAWAY';
 
@@ -717,17 +717,17 @@ Deserializer.GOAWAY = function readGoawa
   frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff;
   frame.error = errorCodes[buffer.readUInt32BE(4)];
   if (!frame.error) {
     // Unknown error types are to be considered equivalent to INTERNAL ERROR
     frame.error = 'INTERNAL_ERROR';
   }
 };
 
-// [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.9)
+// [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.9)
 // -----------------------------------------------------------------
 //
 // The WINDOW_UPDATE frame (type=0x8) is used to implement flow control.
 //
 // The WINDOW_UPDATE frame does not define any flags.
 
 frameTypes[0x8] = 'WINDOW_UPDATE';
 
@@ -755,17 +755,17 @@ Deserializer.WINDOW_UPDATE = function re
     return 'FRAME_SIZE_ERROR';
   }
   frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff;
   if (frame.window_size === 0) {
     return 'PROTOCOL_ERROR';
   }
 };
 
-// [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.10)
+// [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.10)
 // ------------------------------------------------------------
 //
 // The CONTINUATION frame (type=0x9) is used to continue a sequence of header block fragments.
 //
 // The CONTINUATION frame defines the following flag:
 //
 // * END_HEADERS (0x4):
 //   The END_HEADERS bit indicates that this frame ends the sequence of header block fragments
@@ -780,17 +780,17 @@ typeSpecificAttributes.CONTINUATION = ['
 Serializer.CONTINUATION = function writeContinuation(frame, buffers) {
   buffers.push(frame.data);
 };
 
 Deserializer.CONTINUATION = function readContinuation(buffer, frame) {
   frame.data = buffer;
 };
 
-// [ALTSVC](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.11)
+// [ALTSVC](http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04#section-4)
 // ------------------------------------------------------------
 //
 // The ALTSVC frame (type=0xA) advertises the availability of an alternative service to the client.
 //
 // The ALTSVC frame does not define any flags.
 
 frameTypes[0xA] = 'ALTSVC';
 
@@ -869,17 +869,17 @@ Deserializer.ALTSVC = function readAltSv
   frame.port = buffer.readUInt16BE(4);
   var pidLength = buffer.readUInt8(7);
   frame.protocolID = buffer.toString('ascii', 8, 8 + pidLength);
   var hostLength = buffer.readUInt8(8 + pidLength);
   frame.host = buffer.toString('ascii', 9 + pidLength, 9 + pidLength + hostLength);
   frame.origin = buffer.toString('ascii', 9 + pidLength + hostLength);
 };
 
-// [BLOCKED](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-6.12)
+// BLOCKED
 // ------------------------------------------------------------
 //
 // The BLOCKED frame (type=0xB) indicates that the sender is unable to send data
 // due to a closed flow control window.
 //
 // The BLOCKED frame does not define any flags and contains no payload.
 
 frameTypes[0xB] = 'BLOCKED';
@@ -889,17 +889,17 @@ frameFlags.BLOCKED = [];
 typeSpecificAttributes.BLOCKED = [];
 
 Serializer.BLOCKED = function writeBlocked(frame, buffers) {
 };
 
 Deserializer.BLOCKED = function readBlocked(buffer, frame) {
 };
 
-// [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-7)
+// [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-7)
 // ------------------------------------------------------------
 
 var errorCodes = [
   'NO_ERROR',
   'PROTOCOL_ERROR',
   'INTERNAL_ERROR',
   'FLOW_CONTROL_ERROR',
   'SETTINGS_TIMEOUT',
--- a/testing/xpcshell/node-http2/lib/protocol/index.js
+++ b/testing/xpcshell/node-http2/lib/protocol/index.js
@@ -1,9 +1,9 @@
-// [node-http2-protocol][homepage] is an implementation of the [HTTP/2 (draft 14)][http2]
+// [node-http2-protocol][homepage] is an implementation of the [HTTP/2 (draft 16)][http2]
 // framing layer for [node.js][node].
 //
 // The main building blocks are [node.js streams][node-stream] that are connected through pipes.
 //
 // The main components are:
 //
 // * [Endpoint](endpoint.html): represents an HTTP/2 endpoint (client or server). It's
 //   responsible for the the first part of the handshake process (sending/receiving the
@@ -23,20 +23,20 @@
 //
 // * [Compressor and Decompressor](compressor.html): compression and decompression of HEADER and
 //   PUSH_PROMISE frames
 //
 // * [Serializer and Deserializer](framer.html): the lowest layer in the stack that transforms
 //   between the binary and the JavaScript object representation of HTTP/2 frames
 //
 // [homepage]:            https://github.com/molnarg/node-http2
-// [http2]:               http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
-// [http2-connheader]:    http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-3.5
-// [http2-stream]:        http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5
-// [http2-streamstate]:   http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.1
+// [http2]:               http://tools.ietf.org/html/draft-ietf-httpbis-http2-16
+// [http2-connheader]:    http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-3.5
+// [http2-stream]:        http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5
+// [http2-streamstate]:   http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
 // [node]:                http://nodejs.org/
 // [node-stream]:         http://nodejs.org/api/stream.html
 // [node-https]:          http://nodejs.org/api/https.html
 // [node-http]:           http://nodejs.org/api/http.html
 
 exports.VERSION = 'h2-16';
 
 exports.Endpoint = require('./endpoint').Endpoint;
--- a/testing/xpcshell/node-http2/lib/protocol/stream.js
+++ b/testing/xpcshell/node-http2/lib/protocol/stream.js
@@ -347,17 +347,17 @@ Stream.prototype._finishing = function _
     this._log.debug({ frame: lastFrame }, 'Marking last frame with END_STREAM flag.');
     lastFrame.flags.END_STREAM = true;
     this._transition(true, endFrame);
   } else {
     this._pushUpstream(endFrame);
   }
 };
 
-// [Stream States](http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.1)
+// [Stream States](http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1)
 // ----------------
 //
 //                           +--------+
 //                     PP    |        |    PP
 //                  ,--------|  idle  |--------.
 //                 /         |        |         \
 //                v          +--------+          v
 //         +----------+          |           +----------+
@@ -459,17 +459,17 @@ Stream.prototype._transition = function 
     //   releases the stream reservation.
     // * An endpoint may receive PRIORITY frame in this state.
     // * An endpoint MUST NOT send any other type of frame in this state.
     case 'RESERVED_LOCAL':
       if (sending && HEADERS) {
         this._setState('HALF_CLOSED_REMOTE');
       } else if (RST_STREAM) {
         this._setState('CLOSED');
-      } else if (receiving && PRIORITY) {
+      } else if (PRIORITY) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // A stream in the **reserved (remote)** state has been reserved by a remote peer.
     //
@@ -478,17 +478,17 @@ Stream.prototype._transition = function 
     // * Receiving a HEADERS frame causes the stream to transition to "half closed (local)".
     // * An endpoint MAY send PRIORITY frames in this state to reprioritize the stream.
     // * Receiving any other type of frame MUST be treated as a stream error of type PROTOCOL_ERROR.
     case 'RESERVED_REMOTE':
       if (RST_STREAM) {
         this._setState('CLOSED');
       } else if (receiving && HEADERS) {
         this._setState('HALF_CLOSED_LOCAL');
-      } else if (BLOCKED || (sending && PRIORITY)) {
+      } else if (BLOCKED || PRIORITY) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // The **open** state is where both peers can send frames. In this state, sending peers observe
     // advertised stream level flow control limits.
@@ -513,17 +513,17 @@ Stream.prototype._transition = function 
     //
     // * A stream transitions from this state to "closed" when a frame that contains a END_STREAM
     //   flag is received, or when either peer sends a RST_STREAM frame.
     // * An endpoint MAY send or receive PRIORITY frames in this state to reprioritize the stream.
     // * WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the END_STREAM flag.
     case 'HALF_CLOSED_LOCAL':
       if (RST_STREAM || (receiving && frame.flags.END_STREAM)) {
         this._setState('CLOSED');
-      } else if (BLOCKED || ALTSVC ||receiving || (sending && (PRIORITY || WINDOW_UPDATE))) {
+      } else if (BLOCKED || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // A stream that is **half closed (remote)** is no longer being used by the peer to send frames.
     // In this state, an endpoint is no longer obligated to maintain a receiver flow control window
@@ -533,17 +533,17 @@ Stream.prototype._transition = function 
     //   respond with a stream error of type STREAM_CLOSED.
     // * A stream can transition from this state to "closed" by sending a frame that contains a
     //   END_STREAM flag, or when either peer sends a RST_STREAM frame.
     // * An endpoint MAY send or receive PRIORITY frames in this state to reprioritize the stream.
     // * A receiver MAY receive a WINDOW_UPDATE frame on a "half closed (remote)" stream.
     case 'HALF_CLOSED_REMOTE':
       if (RST_STREAM || (sending && frame.flags.END_STREAM)) {
         this._setState('CLOSED');
-      } else if (BLOCKED || ALTSVC ||sending || (receiving && (WINDOW_UPDATE || PRIORITY))) {
+      } else if (BLOCKED || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // The **closed** state is the terminal state.
     //
@@ -561,19 +561,19 @@ Stream.prototype._transition = function 
     //   that cannot be withdrawn. An endpoint that sends a RST_STREAM frame MUST ignore frames that
     //   it receives on closed streams after it has sent a RST_STREAM frame. An endpoint MAY choose
     //   to limit the period over which it ignores frames and treat frames that arrive after this
     //   time as being in error.
     // * An endpoint might receive a PUSH_PROMISE frame after it sends RST_STREAM. PUSH_PROMISE
     //   causes a stream to become "reserved". If promised streams are not desired, a RST_STREAM
     //   can be used to close any of those streams.
     case 'CLOSED':
-      if ((sending && RST_STREAM) ||
+      if (PRIORITY || (sending && RST_STREAM) ||
           (receiving && this._closedByUs &&
-           (this._closedWithRst || WINDOW_UPDATE || PRIORITY || RST_STREAM || ALTSVC))) {
+           (this._closedWithRst || WINDOW_UPDATE || RST_STREAM || ALTSVC))) {
         /* No state change */
       } else {
         streamError = 'STREAM_CLOSED';
       }
       break;
   }
 
   // Noting that the connection was closed by the other endpoint. It may be important in edge cases.
@@ -632,17 +632,17 @@ Stream.prototype._transition = function 
     // * When receiving something invalid, sending an RST_STREAM using the `reset` method.
     //   This will automatically cause a transition to the CLOSED state.
     else {
       this._log.error(info, 'Received illegal frame.');
       if (connectionError) {
         this.emit('connectionError', connectionError);
       } else {
         this.reset(streamError);
-        this.emit('error', streamError)
+        this.emit('error', streamError);
       }
     }
   }
 };
 
 // Bunyan serializers
 // ------------------
 
--- a/testing/xpcshell/node-http2/package.json
+++ b/testing/xpcshell/node-http2/package.json
@@ -1,25 +1,25 @@
 {
   "name": "http2",
-  "version": "3.0.0",
+  "version": "3.1.0",
   "description": "An HTTP/2 client and server implementation",
   "main": "lib/index.js",
   "engines" : {
     "node" : ">=0.10.19"
   },
   "devDependencies": {
     "istanbul": "*",
     "chai": "*",
     "mocha": "*",
     "docco": "*",
     "bunyan": "*"
   },
   "scripts": {
-    "test": "istanbul test _mocha -- --reporter spec --slow 200",
+    "test": "istanbul test _mocha -- --reporter spec --slow 500 --timeout 15000",
     "doc": "docco lib/* --output doc --layout parallel --css doc/docco.css"
   },
   "repository": {
     "type": "git",
     "url": "https://github.com/molnarg/node-http2.git"
   },
   "homepage": "https://github.com/molnarg/node-http2",
   "bugs": {
@@ -31,13 +31,16 @@
     "client",
     "server"
   ],
   "author": "Gábor Molnár <gabor@molnar.es> (http://gabor.molnar.es)",
   "contributors": [
     "Nick Hurley",
     "Mike Belshe",
     "Yoshihiro Iwanaga",
-    "vsemogutor"
+    "Igor Novikov",
+    "James Willcox",
+    "David Björklund",
+    "Patrick McManus"
   ],
   "license": "MIT",
   "readmeFilename": "README.md"
 }
--- a/testing/xpcshell/node-http2/test/compressor.js
+++ b/testing/xpcshell/node-http2/test/compressor.js
@@ -401,31 +401,31 @@ describe('compressor.js', function() {
           expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
         }
         table = HuffmanTable.huffmanTable;
         for (decoded in test_huffman_response) {
           encoded = test_huffman_response[decoded];
           expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
         }
       });
-    })
+    });
     describe('method decode(buffer)', function() {
       it('should return the Huffman decoded version of the input buffer', function() {
         var table = HuffmanTable.huffmanTable;
         for (var decoded in test_huffman_request) {
           var encoded = test_huffman_request[decoded];
-          expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded)
+          expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded);
         }
         table = HuffmanTable.huffmanTable;
         for (decoded in test_huffman_response) {
           encoded = test_huffman_response[decoded];
-          expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded)
+          expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded);
         }
       });
-    })
+    });
   });
 
   describe('HeaderSetCompressor', function() {
     describe('static method .integer(I, N)', function() {
       it('should return an array of buffers that represent the N-prefix coded form of the integer I', function() {
         for (var i = 0; i < test_integers.length; i++) {
           var test = test_integers[i];
           test.buffer.cursor = 0;
@@ -564,12 +564,12 @@ describe('compressor.js', function() {
           while (Math.random() > 0.1) {
             buffer.push(Math.floor(Math.random() * 256))
           }
           buffer = new Buffer(buffer);
           var table = HuffmanTable.huffmanTable;
           var result = table.decode(table.encode(buffer));
           expect(result).to.deep.equal(buffer);
         }
-      })
-    })
+      });
+    });
   });
 });
--- a/testing/xpcshell/node-http2/test/connection.js
+++ b/testing/xpcshell/node-http2/test/connection.js
@@ -126,17 +126,17 @@ describe('connection.js', function() {
         done = util.callNTimes(2, done);
         client_stream.on('headers', function(headers) {
           expect(headers).to.deep.equal(response_headers);
           done();
         });
         client_stream.on('data', function(data) {
           expect(data).to.deep.equal(response_data);
           done();
-        })
+        });
       });
     });
     describe('server push', function() {
       it('should work as expected', function(done) {
         var request_headers = { ':method': 'get', ':path': '/' };
         var response_headers = { ':status': '200' };
         var push_request_headers = { ':method': 'get', ':path': '/x' };
         var push_response_headers = { ':status': '200' };
--- a/testing/xpcshell/node-http2/test/flow.js
+++ b/testing/xpcshell/node-http2/test/flow.js
@@ -249,12 +249,12 @@ describe('flow.js', function() {
 
           expect(input).to.deep.equal(output);
 
           done();
         });
 
         // Start piping
         flow1.pipe(flow2).pipe(flow1);
-      })
+      });
     });
   });
 });
--- a/testing/xpcshell/node-http2/test/http.js
+++ b/testing/xpcshell/node-http2/test/http.js
@@ -119,17 +119,17 @@ describe('http.js', function() {
           throw new Error('Should not send headers twice');
         } else {
           called = true;
         }
       }, once: util.noop };
       var response = new http2.OutgoingResponse(stream);
 
       response.writeHead(200);
-      response.writeHead(404)
+      response.writeHead(404);
     });
   });
   describe('test scenario', function() {
     describe('simple request', function() {
       it('should work as expected', function(done) {
         var path = '/x';
         var message = 'Hello world';
 
@@ -144,16 +144,76 @@ describe('http.js', function() {
               expect(data.toString()).to.equal(message);
               server.close();
               done();
             });
           });
         });
       });
     });
+    describe('2 simple request in parallel', function() {
+      it('should work as expected', function(originalDone) {
+        var path = '/x';
+        var message = 'Hello world';
+        done = util.callNTimes(2, function() {
+          server.close();
+          originalDone();
+        });
+
+        var server = http2.createServer(options, function(request, response) {
+          expect(request.url).to.equal(path);
+          response.end(message);
+        });
+
+        server.listen(1234, function() {
+          http2.get('https://localhost:1234' + path, function(response) {
+            response.on('data', function(data) {
+              expect(data.toString()).to.equal(message);
+              done();
+            });
+          });
+          http2.get('https://localhost:1234' + path, function(response) {
+            response.on('data', function(data) {
+              expect(data.toString()).to.equal(message);
+              done();
+            });
+          });
+        });
+      });
+    });
+    describe('100 simple request in a series', function() {
+      it('should work as expected', function(done) {
+        var path = '/x';
+        var message = 'Hello world';
+
+        var server = http2.createServer(options, function(request, response) {
+          expect(request.url).to.equal(path);
+          response.end(message);
+        });
+
+        var n = 100;
+        server.listen(1242, function() {
+          doRequest();
+          function doRequest() {
+            http2.get('https://localhost:1242' + path, function(response) {
+              response.on('data', function(data) {
+                expect(data.toString()).to.equal(message);
+                if (n) {
+                  n -= 1;
+                  doRequest();
+                } else {
+                  server.close();
+                  done();
+                }
+              });
+            });
+          }
+        });
+      });
+    });
     describe('request with payload', function() {
       it('should work as expected', function(done) {
         var path = '/x';
         var message = 'Hello world';
 
         var server = http2.createServer(options, function(request, response) {
           expect(request.url).to.equal(path);
           request.once('data', function(data) {
@@ -235,17 +295,16 @@ describe('http.js', function() {
       });
     });
     describe('request over plain TCP', function() {
       it('should work as expected', function(done) {
         var path = '/x';
         var message = 'Hello world';
 
         var server = http2.raw.createServer({
-          plain: true,
           log: util.serverLog
         }, function(request, response) {
           expect(request.url).to.equal(path);
           response.end(message);
         });
 
         server.listen(1237, function() {
           var request = http2.raw.request({
@@ -259,16 +318,40 @@ describe('http.js', function() {
               server.close();
               done();
             });
           });
           request.end();
         });
       });
     });
+    describe('get over plain TCP', function() {
+      it('should work as expected', function(done) {
+        var path = '/x';
+        var message = 'Hello world';
+
+        var server = http2.raw.createServer({
+          log: util.serverLog
+        }, function(request, response) {
+          expect(request.url).to.equal(path);
+          response.end(message);
+        });
+
+        server.listen(1237, function() {
+          var request = http2.raw.get('http://localhost:1237/x', function(response) {
+            response.on('data', function(data) {
+              expect(data.toString()).to.equal(message);
+              server.close();
+              done();
+            });
+          });
+          request.end();
+        });
+      });
+    });
     describe('request to an HTTPS/1 server', function() {
       it('should fall back to HTTPS/1 successfully', function(done) {
         var path = '/x';
         var message = 'Hello world';
 
         var server = https.createServer(options, function(request, response) {
           expect(request.url).to.equal(path);
           response.end(message);
@@ -279,16 +362,46 @@ describe('http.js', function() {
             response.on('data', function(data) {
               expect(data.toString()).to.equal(message);
               done();
             });
           });
         });
       });
     });
+    describe('2 parallel request to an HTTPS/1 server', function() {
+      it('should fall back to HTTPS/1 successfully', function(originalDone) {
+        var path = '/x';
+        var message = 'Hello world';
+        done = util.callNTimes(2, function() {
+          server.close();
+          originalDone();
+        });
+
+        var server = https.createServer(options, function(request, response) {
+          expect(request.url).to.equal(path);
+          response.end(message);
+        });
+
+        server.listen(6789, function() {
+          http2.get('https://localhost:6789' + path, function(response) {
+            response.on('data', function(data) {
+              expect(data.toString()).to.equal(message);
+              done();
+            });
+          });
+          http2.get('https://localhost:6789' + path, function(response) {
+            response.on('data', function(data) {
+              expect(data.toString()).to.equal(message);
+              done();
+            });
+          });
+        });
+      });
+    });
     describe('HTTPS/1 request to a HTTP/2 server', function() {
       it('should fall back to HTTPS/1 successfully', function(done) {
         var path = '/x';
         var message = 'Hello world';
 
         var server = http2.createServer(options, function(request, response) {
           expect(request.url).to.equal(path);
           response.end(message);
--- a/testing/xpcshell/node-http2/test/stream.js
+++ b/testing/xpcshell/node-http2/test/stream.js
@@ -107,30 +107,28 @@ var example_frames = [
   { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
   { type: 'DATA', flags: {}, data: new Buffer(5) },
   { type: 'PUSH_PROMISE', flags: {}, headers: {}, promised_stream: new Stream(util.log, null) }
 ];
 
 var invalid_incoming_frames = {
   IDLE: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
-    { type: 'PRIORITY', flags: {}, priority: 1 },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} },
     { type: 'RST_STREAM', flags: {}, error: 'CANCEL' }
   ],
   RESERVED_LOCAL: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
     { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} }
   ],
   RESERVED_REMOTE: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
-    { type: 'PRIORITY', flags: {}, priority: 1 },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} }
   ],
   OPEN: [
   ],
   HALF_CLOSED_LOCAL: [
   ],
   HALF_CLOSED_REMOTE: [
@@ -138,23 +136,21 @@ var invalid_incoming_frames = {
     { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} }
   ]
 };
 
 var invalid_outgoing_frames = {
   IDLE: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
-    { type: 'PRIORITY', flags: {}, priority: 1 },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} }
   ],
   RESERVED_LOCAL: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
-    { type: 'PRIORITY', flags: {}, priority: 1 },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} }
   ],
   RESERVED_REMOTE: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
     { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} }
@@ -164,17 +160,16 @@ var invalid_outgoing_frames = {
   HALF_CLOSED_LOCAL: [
     { type: 'DATA', flags: {}, data: new Buffer(5) },
     { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
     { type: 'PUSH_PROMISE', flags: {}, headers: {} }
   ],
   HALF_CLOSED_REMOTE: [
   ],
   CLOSED: [
-    { type: 'PRIORITY', flags: {}, priority: 1 },
     { type: 'WINDOW_UPDATE', flags: {}, settings: {} },
     { type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
     { type: 'DATA', flags: {}, data: new Buffer(5) },
     { type: 'PUSH_PROMISE', flags: {}, headers: {}, promised_stream: new Stream(util.log, null) }
   ]
 };
 
 describe('stream.js', function() {
@@ -183,26 +178,26 @@ describe('stream.js', function() {
       it('should emit error, and answer RST_STREAM for invalid incoming frames', function() {
         Object.keys(invalid_incoming_frames).forEach(function(state) {
           invalid_incoming_frames[state].forEach(function(invalid_frame) {
             var stream = createStream();
             var connectionErrorHappened = false;
             stream.state = state;
             stream.once('connectionError', function() { connectionErrorHappened = true; });
             stream._transition(false, invalid_frame);
-            expect(connectionErrorHappened)
+            expect(connectionErrorHappened);
           });
         });
 
         // CLOSED state as a result of incoming END_STREAM (or RST_STREAM)
         var stream = createStream();
         stream.headers({});
         stream.end();
         stream.upstream.write({ type: 'HEADERS', headers:{}, flags: { END_STREAM: true }, count_change: util.noop });
-        example_frames.forEach(function(invalid_frame) {
+        example_frames.slice(1).forEach(function(invalid_frame) {
           invalid_frame.count_change = util.noop;
           expect(stream._transition.bind(stream, false, invalid_frame)).to.throw('Uncaught, unspecified "error" event.');
         });
 
         // CLOSED state as a result of outgoing END_STREAM
         stream = createStream();
         stream.upstream.write({ type: 'HEADERS', headers:{}, flags: { END_STREAM: true }, count_change: util.noop });
         stream.headers({});
--- a/testing/xpcshell/node-http2/test/util.js
+++ b/testing/xpcshell/node-http2/test/util.js
@@ -81,9 +81,9 @@ exports.shuffleBuffers = function shuffl
 
   while (written < concatenated.length) {
     var chunk_size = Math.min(concatenated.length - written, Math.ceil(Math.random()*20));
     output.push(concatenated.slice(written, written + chunk_size));
     written += chunk_size;
   }
 
   return output;
-}
+};
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -562,17 +562,17 @@ End of 5th\n\
       let measureButton = doc.getElementById("measureButton");
       let verbose = doc.getElementById("verbose");
       verbose.checked = aVerbose;
       measureButton.click();
 
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          return aActual === aExpected;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match for " + aFrameId);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -101,17 +101,17 @@
     }
 
     SimpleTest.executeSoon(function() {
       let mostRecentActual;
       document.getElementById("amFrame").focus();
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          return aActual === aExpected;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match");
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
@@ -101,17 +101,17 @@
       function copyPasteAndCheck() {
         // Copy and paste frame contents, and filter out non-deterministic
         // differences.
         synthesizeKey("A", {accelKey: true});
         synthesizeKey("C", {accelKey: true});
         let actual = SpecialPowers.getClipboardData("text/unicode");
         actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
 
-        if (actual === aExpected) {
+        if (actual.trim() === aExpected.trim()) {
           SimpleTest.ok(true, "Clipboard has the expected contents");
           aNext();
         } else {
           numFailures++;
           if (numFailures === maxFailures) {
             ok(false, "pasted text doesn't match");
             dump("******EXPECTED******\n");
             dump(aExpected);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
@@ -49,17 +49,17 @@
     function copyPasteAndCheck() {
       // Copy and paste frame contents, and filter out non-deterministic
       // differences.
       synthesizeKey("A", {accelKey: true});
       synthesizeKey("C", {accelKey: true});
       let actual = SpecialPowers.getClipboardData("text/unicode");
       actual = actual.replace(/\(pid \d+\)/, "(pid NNN)");
 
-      if (actual === aExpected) {
+      if (actual.trim() === aExpected.trim()) {
         SimpleTest.ok(true, "Clipboard has the expected contents");
         aNext();
       } else {
         numFailures++;
         if (numFailures === maxFailures) {
           ok(false, "pasted text doesn't match");
           dump("******EXPECTED******\n");
           dump(aExpected);
--- a/toolkit/devtools/DevToolsUtils.js
+++ b/toolkit/devtools/DevToolsUtils.js
@@ -458,30 +458,37 @@ exports.fetch = function fetch(aURL, aOp
     scheme = Services.io.extractScheme(url);
   }
 
   switch (scheme) {
     case "file":
     case "chrome":
     case "resource":
       try {
-        NetUtil.asyncFetch(url, function onFetch(aStream, aStatus, aRequest) {
-          if (!components.isSuccessCode(aStatus)) {
-            deferred.reject(new Error("Request failed with status code = "
-                                      + aStatus
-                                      + " after NetUtil.asyncFetch for url = "
-                                      + url));
-            return;
-          }
+        NetUtil.asyncFetch2(
+          url,
+          function onFetch(aStream, aStatus, aRequest) {
+            if (!components.isSuccessCode(aStatus)) {
+              deferred.reject(new Error("Request failed with status code = "
+                                        + aStatus
+                                        + " after NetUtil.asyncFetch2 for url = "
+                                        + url));
+              return;
+            }
 
-          let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-          contentType = aRequest.contentType;
-          deferred.resolve(source);
-          aStream.close();
-        });
+            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+            contentType = aRequest.contentType;
+            deferred.resolve(source);
+            aStream.close();
+          },
+          null,      // aLoadingNode
+          Services.scriptSecurityManager.getSystemPrincipal(),
+          null,      // aTriggeringPrincipal
+          Ci.nsILoadInfo.SEC_NORMAL,
+          Ci.nsIContentPolicy.TYPE_OTHER);
       } catch (ex) {
         deferred.reject(ex);
       }
       break;
 
     default:
     let channel;
       try {
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -194,24 +194,32 @@ SrcdirProvider.prototype = {
   unload: function(reason) {
     loader.unload(this.loader, reason);
     delete this.loader;
   },
 
   _readFile: function(filename) {
     let deferred = promise.defer();
     let file = new FileUtils.File(filename);
-    NetUtil.asyncFetch(file, (inputStream, status) => {
-      if (!Components.isSuccessCode(status)) {
-        deferred.reject(new Error("Couldn't load manifest: " + filename + "\n"));
-        return;
-      }
-      var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
-      deferred.resolve(data);
-    });
+    NetUtil.asyncFetch2(
+      file,
+      (inputStream, status) => {
+        if (!Components.isSuccessCode(status)) {
+          deferred.reject(new Error("Couldn't load manifest: " + filename + "\n"));
+          return;
+        }
+        var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
+        deferred.resolve(data);
+      },
+      null,      // aLoadingNode
+      Services.scriptSecurityManager.getSystemPrincipal(),
+      null,      // aTriggeringPrincipal
+      Ci.nsILoadInfo.SEC_NORMAL,
+      Ci.nsIContentPolicy.TYPE_OTHER);
+
     return deferred.promise;
   },
 
   _writeFile: function(filename, data) {
     let promise = OS.File.writeAtomic(filename, data, {encoding: "utf-8"});
     return promise.then(null, (ex) => new Error("Couldn't write manifest: " + ex + "\n"));
   },
 
--- a/toolkit/devtools/apps/app-actor-front.js
+++ b/toolkit/devtools/apps/app-actor-front.js
@@ -182,27 +182,34 @@ function uploadPackageBulk(client, webap
 
     let request = client.startBulkRequest({
       actor: actor,
       type: "stream",
       length: fileSize
     });
 
     request.on("bulk-send-ready", ({copyFrom}) => {
-      NetUtil.asyncFetch(packageFile, function(inputStream) {
-        let copying = copyFrom(inputStream);
-        copying.on("progress", (e, progress) => {
-          progressCallback(progress);
-        });
-        copying.then(() => {
-          console.log("Bulk upload done");
-          inputStream.close();
-          deferred.resolve(actor);
-        });
-      });
+      NetUtil.asyncFetch2(
+        packageFile,
+        function(inputStream) {
+          let copying = copyFrom(inputStream);
+          copying.on("progress", (e, progress) => {
+            progressCallback(progress);
+          });
+          copying.then(() => {
+            console.log("Bulk upload done");
+            inputStream.close();
+            deferred.resolve(actor);
+          });
+        },
+        null,      // aLoadingNode
+        Services.scriptSecurityManager.getSystemPrincipal(),
+        null,      // aTriggeringPrincipal
+        Ci.nsILoadInfo.SEC_NORMAL,
+        Ci.nsIContentPolicy.TYPE_OTHER);
     });
   }
 
   return deferred.promise;
 }
 
 function removeServerTemporaryFile(client, fileActor) {
   let request = {
--- a/toolkit/devtools/apps/tests/debugger-protocol-helper.js
+++ b/toolkit/devtools/apps/tests/debugger-protocol-helper.js
@@ -81,17 +81,24 @@ function webappActorRequest(request, onR
   }
 
   request.to = gActor;
   gClient.request(request, onResponse);
 }
 
 
 function downloadURL(url, file) {
-  let channel = Services.io.newChannel(url, null, null);
+  let channel = Services.io.newChannel2(url,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let istream = channel.open();
   let bstream = Cc["@mozilla.org/binaryinputstream;1"]
                   .createInstance(Ci.nsIBinaryInputStream);
   bstream.setInputStream(istream);
   let data = bstream.readBytes(bstream.available());
 
   let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
                   .createInstance(Ci.nsIFileOutputStream);
--- a/toolkit/devtools/gcli/commands/screenshot.js
+++ b/toolkit/devtools/gcli/commands/screenshot.js
@@ -151,17 +151,24 @@ exports.items = [
                                   .QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIWebNavigation)
                                   .QueryInterface(Ci.nsILoadContext);
 
         if (clipboard) {
           try {
             let io = Cc["@mozilla.org/network/io-service;1"]
                       .getService(Ci.nsIIOService);
-            let channel = io.newChannel(data, null, null);
+            let channel = io.newChannel2(data,
+                                         null,
+                                         null,
+                                         null,      // aLoadingNode
+                                         Services.scriptSecurityManager.getSystemPrincipal(),
+                                         null,      // aTriggeringPrincipal
+                                         Ci.nsILoadInfo.SEC_NORMAL,
+                                         Ci.nsIContentPolicy.TYPE_IMAGE);
             let input = channel.open();
             let imgTools = Cc["@mozilla.org/image/tools;1"]
                             .getService(Ci.imgITools);
 
             let container = {};
             imgTools.decodeImageData(input, channel.contentType, container);
 
             let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
--- a/toolkit/devtools/server/actors/styleeditor.js
+++ b/toolkit/devtools/server/actors/styleeditor.js
@@ -662,44 +662,65 @@ function fetch(aURL, aOptions={ loadFrom
     scheme = Services.io.extractScheme(url);
   }
 
   switch (scheme) {
     case "file":
     case "chrome":
     case "resource":
       try {
-        NetUtil.asyncFetch(url, function onFetch(aStream, aStatus, aRequest) {
-          if (!components.isSuccessCode(aStatus)) {
-            deferred.reject(new Error("Request failed with status code = "
-                                      + aStatus
-                                      + " after NetUtil.asyncFetch for url = "
-                                      + url));
-            return;
-          }
+        NetUtil.asyncFetch2(
+          url,
+          function onFetch(aStream, aStatus, aRequest) {
+            if (!components.isSuccessCode(aStatus)) {
+              deferred.reject(new Error("Request failed with status code = "
+                                        + aStatus
+                                        + " after NetUtil.asyncFetch2 for url = "
+                                        + url));
+              return;
+            }
 
-          let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-          contentType = aRequest.contentType;
-          deferred.resolve(source);
-          aStream.close();
-        });
+            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+            contentType = aRequest.contentType;
+            deferred.resolve(source);
+            aStream.close();
+          },
+          null,      // aLoadingNode
+          Services.scriptSecurityManager.getSystemPrincipal(),
+          null,      // aTriggeringPrincipal
+          Ci.nsILoadInfo.SEC_NORMAL,
+          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       } catch (ex) {
         deferred.reject(ex);
       }
       break;
 
     default:
       let channel;
       try {
-        channel = Services.io.newChannel(url, null, null);
+        channel = Services.io.newChannel2(url,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
         // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but
         // newChannel won't be able to handle it.
         url = "file:///" + url;
-        channel = Services.io.newChannel(url, null, null);
+        channel = Services.io.newChannel2(url,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       }
       let chunks = [];
       let streamListener = {
         onStartRequest: function(aRequest, aContext, aStatusCode) {
           if (!components.isSuccessCode(aStatusCode)) {
             deferred.reject(new Error("Request failed with status code = "
                                       + aStatusCode
                                       + " in onStartRequest handler for url = "
--- a/toolkit/devtools/server/actors/stylesheets.js
+++ b/toolkit/devtools/server/actors/stylesheets.js
@@ -1081,44 +1081,65 @@ function fetch(aURL, aOptions={ loadFrom
     scheme = Services.io.extractScheme(url);
   }
 
   switch (scheme) {
     case "file":
     case "chrome":
     case "resource":
       try {
-        NetUtil.asyncFetch(url, function onFetch(aStream, aStatus, aRequest) {
-          if (!components.isSuccessCode(aStatus)) {
-            deferred.reject(new Error("Request failed with status code = "
-                                      + aStatus
-                                      + " after NetUtil.asyncFetch for url = "
-                                      + url));
-            return;
-          }
+        NetUtil.asyncFetch2(
+          url,
+          function onFetch(aStream, aStatus, aRequest) {
+            if (!components.isSuccessCode(aStatus)) {
+              deferred.reject(new Error("Request failed with status code = "
+                                        + aStatus
+                                        + " after NetUtil.asyncFetch2 for url = "
+                                        + url));
+              return;
+            }
 
-          let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-          contentType = aRequest.contentType;
-          deferred.resolve(source);
-          aStream.close();
-        });
+            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+            contentType = aRequest.contentType;
+            deferred.resolve(source);
+            aStream.close();
+          },
+          null,      // aLoadingNode
+          Services.scriptSecurityManager.getSystemPrincipal(),
+          null,      // aTriggeringPrincipal
+          Ci.nsILoadInfo.SEC_NORMAL,
+          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       } catch (ex) {
         deferred.reject(ex);
       }
       break;
 
     default:
       let channel;
       try {
-        channel = Services.io.newChannel(url, null, null);
+        channel = Services.io.newChannel2(url,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
         // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but
         // newChannel won't be able to handle it.
         url = "file:///" + url;
-        channel = Services.io.newChannel(url, null, null);
+        channel = Services.io.newChannel2(url,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_STYLESHEET);
       }
       let chunks = [];
       let streamListener = {
         onStartRequest: function(aRequest, aContext, aStatusCode) {
           if (!components.isSuccessCode(aStatusCode)) {
             deferred.reject(new Error("Request failed with status code = "
                                       + aStatusCode
                                       + " in onStartRequest handler for url = "
--- a/toolkit/devtools/transport/tests/unit/test_client_server_bulk.js
+++ b/toolkit/devtools/transport/tests/unit/test_client_server_bulk.js
@@ -2,16 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let { DebuggerServer } =
   Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let Pipe = CC("@mozilla.org/pipe;1", "nsIPipe", "init");
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 function run_test() {
   initTestDebuggerServer();
   add_test_bulk_actor();
 
   add_task(function() {
@@ -56,21 +57,28 @@ TestBulkActor.prototype = {
   },
 
   bulkReply: function({to, type}) {
     this.conn.startBulkSend({
       actor: to,
       type: type,
       length: really_long().length
     }).then(({copyFrom}) => {
-      NetUtil.asyncFetch(getTestTempFile("bulk-input"), input => {
-        copyFrom(input).then(() => {
-          input.close();
-        });
-      });
+      NetUtil.asyncFetch2(
+        getTestTempFile("bulk-input"),
+        input => {
+          copyFrom(input).then(() => {
+            input.close();
+          });
+        },
+        null,      // aLoadingNode
+        Services.scriptSecurityManager.getSystemPrincipal(),
+        null,      // aTriggeringPrincipal
+        Ci.nsILoadInfo.SEC_NORMAL,
+        Ci.nsIContentPolicy.TYPE_OTHER);
     });
   },
 
   jsonReply: function({length, copyTo}) {
     do_check_eq(length, really_long().length);
 
     let outputFile = getTestTempFile("bulk-output", true);
     outputFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
@@ -155,22 +163,29 @@ let test_bulk_request_cs = Task.async(fu
     let request = client.startBulkRequest({
       actor: response.testBulk,
       type: actorType,
       length: really_long().length
     });
 
     // Send bulk data to server
     request.on("bulk-send-ready", ({copyFrom}) => {
-      NetUtil.asyncFetch(getTestTempFile("bulk-input"), input => {
-        copyFrom(input).then(() => {
-          input.close();
-          bulkCopyDeferred.resolve();
-        });
-      });
+      NetUtil.asyncFetch2(
+        getTestTempFile("bulk-input"),
+        input => {
+          copyFrom(input).then(() => {
+            input.close();
+            bulkCopyDeferred.resolve();
+          });
+        },
+        null,      // aLoadingNode
+        Services.scriptSecurityManager.getSystemPrincipal(),
+        null,      // aTriggeringPrincipal
+        Ci.nsILoadInfo.SEC_NORMAL,
+        Ci.nsIContentPolicy.TYPE_OTHER);
     });
 
     // Set up reply handling for this type
     replyHandlers[replyType](request).then(() => {
       client.close();
       transport.close();
     });
   }).then(null, do_throw);
@@ -237,23 +252,30 @@ function verify_files() {
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
   let compareDeferred = promise.defer();
-  NetUtil.asyncFetch(getTestTempFile("bulk-output"), input => {
-    let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
-    // Avoid do_check_eq here so we don't log the contents
-    do_check_true(outputData === reallyLong);
-    input.close();
-    compareDeferred.resolve();
-  });
+  NetUtil.asyncFetch2(
+    getTestTempFile("bulk-output"),
+    input => {
+      let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
+      // Avoid do_check_eq here so we don't log the contents
+      do_check_true(outputData === reallyLong);
+      input.close();
+      compareDeferred.resolve();
+    },
+    null,      // aLoadingNode
+    Services.scriptSecurityManager.getSystemPrincipal(),
+    null,      // aTriggeringPrincipal
+    Ci.nsILoadInfo.SEC_NORMAL,
+    Ci.nsIContentPolicy.TYPE_OTHER);
 
   return compareDeferred.promise.then(cleanup_files);
 }
 
 function cleanup_files() {
   let inputFile = getTestTempFile("bulk-input", true);
   if (inputFile.exists()) {
     inputFile.remove(false);
--- a/toolkit/devtools/transport/tests/unit/test_queue.js
+++ b/toolkit/devtools/transport/tests/unit/test_queue.js
@@ -35,21 +35,28 @@ let test_transport = Task.async(function
   writeTestTempFile("bulk-input", reallyLong);
 
   do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
 
   let transport = yield transportFactory();
 
   // Sending from client to server
   function write_data({copyFrom}) {
-    NetUtil.asyncFetch(getTestTempFile("bulk-input"), function(input, status) {
-      copyFrom(input).then(() => {
-        input.close();
-      });
-    });
+    NetUtil.asyncFetch2(
+      getTestTempFile("bulk-input"),
+      function(input, status) {
+        copyFrom(input).then(() => {
+          input.close();
+        });
+      },
+      null,      // aLoadingNode
+      Services.scriptSecurityManager.getSystemPrincipal(),
+      null,      // aTriggeringPrincipal
+      Ci.nsILoadInfo.SEC_NORMAL,
+      Ci.nsIContentPolicy.TYPE_OTHER);
   }
 
   // Receiving on server from client
   function on_bulk_packet({actor, type, length, copyTo}) {
     do_check_eq(actor, "root");
     do_check_eq(type, "file-stream");
     do_check_eq(length, reallyLong.length);
 
@@ -143,23 +150,30 @@ function verify() {
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
   let compareDeferred = promise.defer();
-  NetUtil.asyncFetch(getTestTempFile("bulk-output"), input => {
-    let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
-    // Avoid do_check_eq here so we don't log the contents
-    do_check_true(outputData === reallyLong);
-    input.close();
-    compareDeferred.resolve();
-  });
+  NetUtil.asyncFetch2(
+    getTestTempFile("bulk-output"),
+    input => {
+      let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
+      // Avoid do_check_eq here so we don't log the contents
+      do_check_true(outputData === reallyLong);
+      input.close();
+      compareDeferred.resolve();
+    },
+    null,      // aLoadingNode
+    Services.scriptSecurityManager.getSystemPrincipal(),
+    null,      // aTriggeringPrincipal
+    Ci.nsILoadInfo.SEC_NORMAL,
+    Ci.nsIContentPolicy.TYPE_OTHER);
 
   return compareDeferred.promise.then(cleanup_files);
 }
 
 function cleanup_files() {
   let inputFile = getTestTempFile("bulk-input", true);
   if (inputFile.exists()) {
     inputFile.remove(false);
--- a/toolkit/devtools/transport/tests/unit/test_transport_bulk.js
+++ b/toolkit/devtools/transport/tests/unit/test_transport_bulk.js
@@ -1,15 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let { DebuggerServer } =
   Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 function run_test() {
   initTestDebuggerServer();
 
   add_task(function() {
     yield test_bulk_transfer_transport(socket_transport);
     yield test_bulk_transfer_transport(local_transport);
     DebuggerServer.destroy();
@@ -35,21 +36,28 @@ let test_bulk_transfer_transport = Task.
   writeTestTempFile("bulk-input", reallyLong);
 
   do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
 
   let transport = yield transportFactory();
 
   // Sending from client to server
   function write_data({copyFrom}) {
-    NetUtil.asyncFetch(getTestTempFile("bulk-input"), function(input, status) {
-      copyFrom(input).then(() => {
-        input.close();
-      });
-    });
+    NetUtil.asyncFetch2(
+      getTestTempFile("bulk-input"),
+      function(input, status) {
+        copyFrom(input).then(() => {
+          input.close();
+        });
+      },
+      null,      // aLoadingNode
+      Services.scriptSecurityManager.getSystemPrincipal(),
+      null,      // aTriggeringPrincipal
+      Ci.nsILoadInfo.SEC_NORMAL,
+      Ci.nsIContentPolicy.TYPE_OTHER);
   }
 
   // Receiving on server from client
   function on_bulk_packet({actor, type, length, copyTo}) {
     do_check_eq(actor, "root");
     do_check_eq(type, "file-stream");
     do_check_eq(length, reallyLong.length);
 
@@ -114,23 +122,30 @@ function verify() {
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
   let compareDeferred = promise.defer();
-  NetUtil.asyncFetch(getTestTempFile("bulk-output"), input => {
-    let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
-    // Avoid do_check_eq here so we don't log the contents
-    do_check_true(outputData === reallyLong);
-    input.close();
-    compareDeferred.resolve();
-  });
+  NetUtil.asyncFetch2(
+    getTestTempFile("bulk-output"),
+    input => {
+      let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
+      // Avoid do_check_eq here so we don't log the contents
+      do_check_true(outputData === reallyLong);
+      input.close();
+      compareDeferred.resolve();
+    },
+    null,      // aLoadingNode
+    Services.scriptSecurityManager.getSystemPrincipal(),
+    null,      // aTriggeringPrincipal
+    Ci.nsILoadInfo.SEC_NORMAL,
+    Ci.nsIContentPolicy.TYPE_OTHER);
 
   return compareDeferred.promise.then(cleanup_files);
 }
 
 function cleanup_files() {
   let inputFile = getTestTempFile("bulk-input", true);
   if (inputFile.exists()) {
     inputFile.remove(false);
--- a/toolkit/devtools/webconsole/network-helper.js
+++ b/toolkit/devtools/webconsole/network-helper.js
@@ -270,37 +270,46 @@ let NetworkHelper = {
    *        Assumed charset of the cached content. Used if there is no charset
    *        on the channel directly.
    * @param function aCallback
    *        Callback that is called with the loaded cached content if available
    *        or null if something failed while getting the cached content.
    */
   loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback)
   {
-    let channel = NetUtil.newChannel(aUrl);
+    let channel = NetUtil.newChannel2(aUrl,
+                                      null,
+                                      null,
+                                      null,      // aLoadingNode
+                                      Services.scriptSecurityManager.getSystemPrincipal(),
+                                      null,      // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
 
     // Ensure that we only read from the cache and not the server.
     channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE |
       Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
       Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
 
-    NetUtil.asyncFetch(channel, (aInputStream, aStatusCode, aRequest) => {
-      if (!components.isSuccessCode(aStatusCode)) {
-        aCallback(null);
-        return;
-      }
+    NetUtil.asyncFetch2(
+      channel,
+      (aInputStream, aStatusCode, aRequest) => {
+        if (!components.isSuccessCode(aStatusCode)) {
+          aCallback(null);
+          return;
+        }
 
-      // Try to get the encoding from the channel. If there is none, then use
-      // the passed assumed aCharset.
-      let aChannel = aRequest.QueryInterface(Ci.nsIChannel);
-      let contentCharset = aChannel.contentCharset || aCharset;
+        // Try to get the encoding from the channel. If there is none, then use
+        // the passed assumed aCharset.
+        let aChannel = aRequest.QueryInterface(Ci.nsIChannel);
+        let contentCharset = aChannel.contentCharset || aCharset;
 
-      // Read the content of the stream using contentCharset as encoding.
-      aCallback(this.readAndConvertFromStream(aInputStream, contentCharset));
-    });
+        // Read the content of the stream using contentCharset as encoding.
+        aCallback(this.readAndConvertFromStream(aInputStream, contentCharset));
+      });
   },
 
   /**
    * Parse a raw Cookie header value.
    *
    * @param string aHeader
    *        The raw Cookie header value.
    * @return array
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -175,16 +175,18 @@ public:
   {
     if (mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
       aCodeName = mCodeValue;
       return;
     }
     GetDOMCodeName(mCodeNameIndex, aCodeName);
   }
 
+  static uint32_t ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex);
+
   static void GetDOMKeyName(KeyNameIndex aKeyNameIndex,
                             nsAString& aKeyName);
   static void GetDOMCodeName(CodeNameIndex aCodeNameIndex,
                              nsAString& aCodeName);
 
   static const char* GetCommandStr(Command aCommand);
 
   void AssignKeyEventData(const WidgetKeyboardEvent& aEvent, bool aCopyTargets)
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -388,9 +388,60 @@ WidgetKeyboardEvent::GetCommandStr(Comma
   };
 #undef NS_DEFINE_COMMAND
 
   MOZ_RELEASE_ASSERT(static_cast<size_t>(aCommand) < ArrayLength(kCommands),
                      "Illegal command enumeration value");
   return kCommands[aCommand];
 }
 
+/* static */ uint32_t
+WidgetKeyboardEvent::ComputeLocationFromCodeValue(CodeNameIndex aCodeNameIndex)
+{
+  // Following commented out cases are not defined in PhysicalKeyCodeNameList.h
+  // but are defined by D3E spec.  So, they should be uncommented when the
+  // code values are defined in the header.
+  switch (aCodeNameIndex) {
+    case CODE_NAME_INDEX_AltLeft:
+    case CODE_NAME_INDEX_ControlLeft:
+    case CODE_NAME_INDEX_OSLeft:
+    case CODE_NAME_INDEX_ShiftLeft:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
+    case CODE_NAME_INDEX_AltRight:
+    case CODE_NAME_INDEX_ControlRight:
+    case CODE_NAME_INDEX_OSRight:
+    case CODE_NAME_INDEX_ShiftRight:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
+    case CODE_NAME_INDEX_Numpad0:
+    case CODE_NAME_INDEX_Numpad1:
+    case CODE_NAME_INDEX_Numpad2:
+    case CODE_NAME_INDEX_Numpad3:
+    case CODE_NAME_INDEX_Numpad4:
+    case CODE_NAME_INDEX_Numpad5:
+    case CODE_NAME_INDEX_Numpad6:
+    case CODE_NAME_INDEX_Numpad7:
+    case CODE_NAME_INDEX_Numpad8:
+    case CODE_NAME_INDEX_Numpad9:
+    case CODE_NAME_INDEX_NumpadAdd:
+    case CODE_NAME_INDEX_NumpadBackspace:
+    // case CODE_NAME_INDEX_NumpadClear:
+    // case CODE_NAME_INDEX_NumpadClearEntry:
+    case CODE_NAME_INDEX_NumpadComma:
+    case CODE_NAME_INDEX_NumpadDecimal:
+    case CODE_NAME_INDEX_NumpadDivide:
+    case CODE_NAME_INDEX_NumpadEnter:
+    case CODE_NAME_INDEX_NumpadEqual:
+    // case CODE_NAME_INDEX_NumpadMemoryAdd:
+    // case CODE_NAME_INDEX_NumpadMemoryClear:
+    // case CODE_NAME_INDEX_NumpadMemoryRecall:
+    // case CODE_NAME_INDEX_NumpadMemoryStore:
+    case CODE_NAME_INDEX_NumpadMemorySubtract:
+    case CODE_NAME_INDEX_NumpadMultiply:
+    // case CODE_NAME_INDEX_NumpadParenLeft:
+    // case CODE_NAME_INDEX_NumpadParenRight:
+    case CODE_NAME_INDEX_NumpadSubtract:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+    default:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
+  }
+}
+
 } // namespace mozilla
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -35,17 +35,16 @@ jfieldID AndroidGeckoEvent::jNativeWindo
 
 jfieldID AndroidGeckoEvent::jCharactersField = 0;
 jfieldID AndroidGeckoEvent::jCharactersExtraField = 0;
 jfieldID AndroidGeckoEvent::jDataField = 0;
 jfieldID AndroidGeckoEvent::jDOMPrintableKeyValueField = 0;
 jfieldID AndroidGeckoEvent::jKeyCodeField = 0;
 jfieldID AndroidGeckoEvent::jScanCodeField = 0;
 jfieldID AndroidGeckoEvent::jMetaStateField = 0;
-jfieldID AndroidGeckoEvent::jDomKeyLocationField = 0;
 jfieldID AndroidGeckoEvent::jFlagsField = 0;
 jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
 jfieldID AndroidGeckoEvent::jBaseUnicodeCharField = 0;
 jfieldID AndroidGeckoEvent::jRepeatCountField = 0;
 jfieldID AndroidGeckoEvent::jCountField = 0;
 jfieldID AndroidGeckoEvent::jStartField = 0;
 jfieldID AndroidGeckoEvent::jEndField = 0;
 jfieldID AndroidGeckoEvent::jPointerIndexField = 0;
@@ -67,19 +66,16 @@ jfieldID AndroidGeckoEvent::jHeightField
 jfieldID AndroidGeckoEvent::jIDField = 0;
 jfieldID AndroidGeckoEvent::jGamepadButtonField = 0;
 jfieldID AndroidGeckoEvent::jGamepadButtonPressedField = 0;
 jfieldID AndroidGeckoEvent::jGamepadButtonValueField = 0;
 jfieldID AndroidGeckoEvent::jGamepadValuesField = 0;
 jfieldID AndroidGeckoEvent::jPrefNamesField = 0;
 jfieldID AndroidGeckoEvent::jObjectField = 0;
 
-jclass AndroidGeckoEvent::jDomKeyLocationClass = 0;
-jfieldID AndroidGeckoEvent::jDomKeyLocationValueField = 0;
-
 jclass AndroidPoint::jPointClass = 0;
 jfieldID AndroidPoint::jXField = 0;
 jfieldID AndroidPoint::jYField = 0;
 
 jclass AndroidRect::jRectClass = 0;
 jfieldID AndroidRect::jBottomField = 0;
 jfieldID AndroidRect::jLeftField = 0;
 jfieldID AndroidRect::jRightField = 0;
@@ -145,17 +141,16 @@ AndroidGeckoEvent::InitGeckoEventClass(J
     jRectField = geckoEvent.getField("mRect", "Landroid/graphics/Rect;");
 
     jCharactersField = geckoEvent.getField("mCharacters", "Ljava/lang/String;");
     jCharactersExtraField = geckoEvent.getField("mCharactersExtra", "Ljava/lang/String;");
     jDataField = geckoEvent.getField("mData", "Ljava/lang/String;");
     jKeyCodeField = geckoEvent.getField("mKeyCode", "I");
     jScanCodeField = geckoEvent.getField("mScanCode", "I");
     jMetaStateField = geckoEvent.getField("mMetaState", "I");
-    jDomKeyLocationField = geckoEvent.getField("mDomKeyLocation", "Lorg/mozilla/gecko/GeckoEvent$DomKeyLocation;");
     jFlagsField = geckoEvent.getField("mFlags", "I");
     jUnicodeCharField = geckoEvent.getField("mUnicodeChar", "I");
     jBaseUnicodeCharField = geckoEvent.getField("mBaseUnicodeChar", "I");
     jDOMPrintableKeyValueField = geckoEvent.getField("mDOMPrintableKeyValue", "I");
     jRepeatCountField = geckoEvent.getField("mRepeatCount", "I");
     jCountField = geckoEvent.getField("mCount", "I");
     jStartField = geckoEvent.getField("mStart", "I");
     jEndField = geckoEvent.getField("mEnd", "I");
@@ -177,21 +172,16 @@ AndroidGeckoEvent::InitGeckoEventClass(J
     jHeightField = geckoEvent.getField("mHeight", "I");
     jIDField = geckoEvent.getField("mID", "I");
     jGamepadButtonField = geckoEvent.getField("mGamepadButton", "I");
     jGamepadButtonPressedField = geckoEvent.getField("mGamepadButtonPressed", "Z");
     jGamepadButtonValueField = geckoEvent.getField("mGamepadButtonValue", "F");
     jGamepadValuesField = geckoEvent.getField("mGamepadValues", "[F");
     jPrefNamesField = geckoEvent.getField("mPrefNames", "[Ljava/lang/String;");
     jObjectField = geckoEvent.getField("mObject", "Ljava/lang/Object;");
-
-    // Init GeckoEvent.DomKeyLocation enum
-    AutoJNIClass domKeyLocation(jEnv, "org/mozilla/gecko/GeckoEvent$DomKeyLocation");
-    jDomKeyLocationClass = domKeyLocation.getGlobalRef();
-    jDomKeyLocationValueField = domKeyLocation.getField("value", "I");
 }
 
 void
 AndroidLocation::InitLocationClass(JNIEnv *jEnv)
 {
     AutoJNIClass location(jEnv, "android/location/Location");
     jLocationClass = location.getGlobalRef();
     jGetLatitudeMethod = location.getMethod("getLatitude", "()D");
@@ -384,28 +374,16 @@ AndroidGeckoEvent::ReadDataField(JNIEnv 
 }
 
 void
 AndroidGeckoEvent::UnionRect(nsIntRect const& aRect)
 {
     mRect = aRect.Union(mRect);
 }
 
-uint32_t
-AndroidGeckoEvent::ReadDomKeyLocation(JNIEnv* jenv, jobject jGeckoEventObj)
-{
-    jobject enumObject = jenv->GetObjectField(jGeckoEventObj,
-                                             jDomKeyLocationField);
-    MOZ_ASSERT(enumObject);
-    int enumValue = jenv->GetIntField(enumObject, jDomKeyLocationValueField);
-    MOZ_ASSERT(enumValue >= nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD &&
-               enumValue <= nsIDOMKeyEvent::DOM_KEY_LOCATION_JOYSTICK);
-    return static_cast<uint32_t>(enumValue);
-}
-
 void
 AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
 {
     NS_ASSERTION(!wrapped_obj, "Init called on non-null wrapped_obj!");
 
     wrapped_obj = jobj;
 
     if (!jobj)
@@ -419,17 +397,16 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
         case SIZE_CHANGED:
             ReadPointArray(mPoints, jenv, jPoints, 2);
             break;
 
         case KEY_EVENT:
         case IME_KEY_EVENT:
             mTime = jenv->GetLongField(jobj, jTimeField);
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
-            mDomKeyLocation = ReadDomKeyLocation(jenv, jobj);
             mFlags = jenv->GetIntField(jobj, jFlagsField);
             mKeyCode = jenv->GetIntField(jobj, jKeyCodeField);
             mScanCode = jenv->GetIntField(jobj, jScanCodeField);
             mUnicodeChar = jenv->GetIntField(jobj, jUnicodeCharField);
             mBaseUnicodeChar = jenv->GetIntField(jobj, jBaseUnicodeCharField);
             mDOMPrintableKeyValue =
                 jenv->GetIntField(jobj, jDOMPrintableKeyValueField);
             mRepeatCount = jenv->GetIntField(jobj, jRepeatCountField);
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -521,17 +521,16 @@ public:
     double Z() { return mZ; }
     const nsIntRect& Rect() { return mRect; }
     nsAString& Characters() { return mCharacters; }
     nsAString& CharactersExtra() { return mCharactersExtra; }
     nsAString& Data() { return mData; }
     int KeyCode() { return mKeyCode; }
     int ScanCode() { return mScanCode; }
     int MetaState() { return mMetaState; }
-    uint32_t DomKeyLocation() { return mDomKeyLocation; }
     Modifiers DOMModifiers() const;
     bool IsAltPressed() const { return (mMetaState & AMETA_ALT_MASK) != 0; }
     bool IsShiftPressed() const { return (mMetaState & AMETA_SHIFT_MASK) != 0; }
     bool IsCtrlPressed() const { return (mMetaState & AMETA_CTRL_MASK) != 0; }
     bool IsMetaPressed() const { return (mMetaState & AMETA_META_MASK) != 0; }
     int Flags() { return mFlags; }
     int UnicodeChar() { return mUnicodeChar; }
     int BaseUnicodeChar() { return mBaseUnicodeChar; }
@@ -580,17 +579,16 @@ protected:
     nsTArray<nsIntPoint> mPoints;
     nsTArray<nsIntPoint> mPointRadii;
     nsTArray<int> mPointIndicies;
     nsTArray<float> mOrientations;
     nsTArray<float> mPressures;
     nsTArray<int> mToolTypes;
     nsIntRect mRect;
     int mFlags, mMetaState;
-    uint32_t mDomKeyLocation;
     int mKeyCode, mScanCode;
     int mUnicodeChar, mBaseUnicodeChar, mDOMPrintableKeyValue;
     int mRepeatCount;
     int mCount;
     int mStart, mEnd;
     int mRangeType, mRangeStyles, mRangeLineStyle;
     bool mRangeBoldLine;
     int mRangeForeColor, mRangeBackColor, mRangeLineColor;
@@ -632,18 +630,16 @@ protected:
                          JNIEnv *jenv,
                          jfieldID field);
     void ReadRectField(JNIEnv *jenv);
     void ReadCharactersField(JNIEnv *jenv);
     void ReadCharactersExtraField(JNIEnv *jenv);
     void ReadDataField(JNIEnv *jenv);
     void ReadStringFromJString(nsString &aString, JNIEnv *jenv, jstring s);
 
-    uint32_t ReadDomKeyLocation(JNIEnv* jenv, jobject jGeckoEventObj);
-
     static jclass jGeckoEventClass;
     static jfieldID jActionField;
     static jfieldID jTypeField;
     static jfieldID jAckNeededField;
     static jfieldID jTimeField;
     static jfieldID jPoints;
     static jfieldID jPointIndicies;
     static jfieldID jOrientations;
@@ -659,17 +655,16 @@ protected:
 
     static jfieldID jCharactersField;
     static jfieldID jCharactersExtraField;
     static jfieldID jDataField;
     static jfieldID jDOMPrintableKeyValueField;
     static jfieldID jKeyCodeField;
     static jfieldID jScanCodeField;
     static jfieldID jMetaStateField;
-    static jfieldID jDomKeyLocationField;
     static jfieldID jFlagsField;
     static jfieldID jCountField;
     static jfieldID jStartField;
     static jfieldID jEndField;
     static jfieldID jPointerIndexField;
     static jfieldID jUnicodeCharField;
     static jfieldID jBaseUnicodeCharField;
     static jfieldID jRepeatCountField;
@@ -696,19 +691,16 @@ protected:
     static jfieldID jIDField;
     static jfieldID jGamepadButtonField;
     static jfieldID jGamepadButtonPressedField;
     static jfieldID jGamepadButtonValueField;
     static jfieldID jGamepadValuesField;
 
     static jfieldID jObjectField;
 
-    static jclass jDomKeyLocationClass;
-    static jfieldID jDomKeyLocationValueField;
-
 public:
     enum {
         NATIVE_POKE = 0,
         KEY_EVENT = 1,
         MOTION_EVENT = 2,
         SENSOR_EVENT = 3,
         PROCESS_OBJECT = 4,
         LOCATION_EVENT = 5,
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1535,17 +1535,18 @@ nsWindow::InitKeyEvent(WidgetKeyboardEve
     if (event.message == NS_KEY_PRESS &&
         key.UnicodeChar() && key.UnicodeChar() != key.BaseUnicodeChar()) {
         event.modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL | MODIFIER_META);
     }
 
     event.mIsRepeat =
         (event.message == NS_KEY_DOWN || event.message == NS_KEY_PRESS) &&
         (!!(key.Flags() & AKEY_EVENT_FLAG_LONG_PRESS) || !!key.RepeatCount());
-    event.location = key.DomKeyLocation();
+    event.location =
+        WidgetKeyboardEvent::ComputeLocationFromCodeValue(event.mCodeNameIndex);
     event.time = key.Time();
 
     if (gMenu)
         gMenuConsumed = true;
 }
 
 void
 nsWindow::HandleSpecialKey(AndroidGeckoEvent *ae)
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -207,16 +207,17 @@ public:
 private:
     const UserInputData& mData;
     sp<KeyCharacterMap> mKeyCharMap;
 
     char16_t mChar;
     char16_t mUnmodifiedChar;
 
     uint32_t mDOMKeyCode;
+    uint32_t mDOMKeyLocation;
     KeyNameIndex mDOMKeyNameIndex;
     CodeNameIndex mDOMCodeNameIndex;
     char16_t mDOMPrintableKeyValue;
 
     bool IsKeyPress() const
     {
         return mData.action == AKEY_EVENT_ACTION_DOWN;
     }
@@ -241,26 +242,31 @@ private:
     }
 
     void DispatchKeyDownEvent();
     void DispatchKeyUpEvent();
     nsEventStatus DispatchKeyEventInternal(uint32_t aEventMessage);
 };
 
 KeyEventDispatcher::KeyEventDispatcher(const UserInputData& aData,
-                                       KeyCharacterMap* aKeyCharMap) :
-    mData(aData), mKeyCharMap(aKeyCharMap), mChar(0), mUnmodifiedChar(0),
-    mDOMPrintableKeyValue(0)
+                                       KeyCharacterMap* aKeyCharMap)
+    : mData(aData)
+    , mKeyCharMap(aKeyCharMap)
+    , mChar(0)
+    , mUnmodifiedChar(0)
+    , mDOMPrintableKeyValue(0)
 {
     // XXX Printable key's keyCode value should be computed with actual
     //     input character.
     mDOMKeyCode = (mData.key.keyCode < (ssize_t)ArrayLength(kKeyMapping)) ?
         kKeyMapping[mData.key.keyCode] : 0;
     mDOMKeyNameIndex = GetKeyNameIndex(mData.key.keyCode);
     mDOMCodeNameIndex = GetCodeNameIndex(mData.key.scanCode);
+    mDOMKeyLocation =
+        WidgetKeyboardEvent::ComputeLocationFromCodeValue(mDOMCodeNameIndex);
 
     if (!mKeyCharMap.get()) {
         return;
     }
 
     mChar = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState);
     if (IsControlChar(mChar)) {
         mChar = 0;
@@ -303,17 +309,17 @@ KeyEventDispatcher::DispatchKeyEventInte
     event.isChar = !!event.charCode;
     event.mIsRepeat = IsRepeat();
     event.mKeyNameIndex = mDOMKeyNameIndex;
     if (mDOMPrintableKeyValue) {
         event.mKeyValue = mDOMPrintableKeyValue;
     }
     event.mCodeNameIndex = mDOMCodeNameIndex;
     event.modifiers = getDOMModifiers(mData.metaState);
-    event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE;
+    event.location = mDOMKeyLocation;
     event.time = mData.timeMs;
     return nsWindow::DispatchInputEvent(event);
 }
 
 void
 KeyEventDispatcher::Dispatch()
 {
     // XXX Even if unknown key is pressed, DOM key event should be