merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 05 May 2017 15:17:26 +0200
changeset 573311 9348b76977e833f108cf77dff75b0fab887a2fc1
parent 573301 8872ad4d52b6b517dc9d2a9f62c5c75ceda18790 (current diff)
parent 573310 0f95b12c599465afb36f64e268dd42bbbbafbfe1 (diff)
child 573312 b3bbc7bae111670ecb98487429c2d3c5acd0048f
child 573328 d5b45284dc344673efbdb6f704230172974e787a
child 573331 a190eb571ea1a6b6c25f54aea75a188f996b65ee
child 573358 a65a11468c776337663b1bdd5e43e15ed6ebcb71
child 573402 e4a4733f401461f8cb4fc12035dda76b8cb7361f
child 573408 68c9af262b110855d51c3ca46bfe271b6504667c
child 573409 88b88c4fd0898e3ea3e8a332c449a1b6a2695dc9
child 573428 e95e5825fd40928ceb553acc97022ef1fdb4232c
child 573480 65d7ac0e17922766f5527a0b5d0edd8c9b244c97
child 573498 3b6c8fcf9211cc45f5552dc14a46b79b6d42e025
push id57329
push userarmenzg@mozilla.com
push dateFri, 05 May 2017 13:42:20 +0000
reviewersmerge
milestone55.0a1
merge mozilla-inbound to mozilla-central a=merge
dom/base/nsDOMWindowUtils.cpp
gfx/layers/client/TextureClient.cpp
gfx/thebes/gfxTextRun.cpp
ipc/glue/ProtocolUtils.h
layout/tables/nsTablePainter.cpp
layout/tables/nsTablePainter.h
modules/libpref/init/all.js
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -790,20 +790,16 @@ NotificationController::WillRefresh(mozi
     if (!mDocument)
       return;
   }
 
   // Process invalidation list of the document after all accessible tree
   // modification are done.
   mDocument->ProcessInvalidationList();
 
-  // We cannot rely on DOM tree to keep aria-owns relations updated. Make
-  // a validation to remove dead links.
-  mDocument->ValidateARIAOwned();
-
   // Process relocation list.
   for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
     if (mRelocations[idx]->IsInDocument()) {
       mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
     }
   }
   mRelocations.Clear();
 
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1967,45 +1967,56 @@ DocAccessible::FireEventsOnInsertion(Acc
         break;
       }
     }
     while ((ancestor = ancestor->Parent()));
   }
 }
 
 void
-DocAccessible::ContentRemoved(Accessible* aContent)
+DocAccessible::ContentRemoved(Accessible* aChild)
 {
-  MOZ_DIAGNOSTIC_ASSERT(aContent->Parent(), "Unattached accessible from tree");
+  Accessible* parent = aChild->Parent();
+  MOZ_DIAGNOSTIC_ASSERT(parent, "Unattached accessible from tree");
 
 #ifdef A11Y_LOG
   logging::TreeInfo("process content removal", 0,
-                    "container", aContent->Parent(), "child", aContent, nullptr);
+                    "container", parent, "child", aChild, nullptr);
 #endif
 
-  TreeMutation mt(aContent->Parent());
-  mt.BeforeRemoval(aContent);
-  aContent->Parent()->RemoveChild(aContent);
-  UncacheChildrenInSubtree(aContent);
+  TreeMutation mt(parent);
+  mt.BeforeRemoval(aChild);
+
+  if (aChild->IsRelocated()) {
+    nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(parent);
+    MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+    owned->RemoveElement(aChild);
+    if (owned->Length() == 0) {
+      mARIAOwnsHash.Remove(parent);
+    }
+  }
+  parent->RemoveChild(aChild);
+  UncacheChildrenInSubtree(aChild);
+
   mt.Done();
 }
 
 void
 DocAccessible::ContentRemoved(nsIContent* aContentNode)
 {
   // If child node is not accessible then look for its accessible children.
   Accessible* acc = GetAccessible(aContentNode);
   if (acc) {
     ContentRemoved(acc);
   }
-  else {
-    TreeWalker walker(this, aContentNode);
-    while (Accessible* acc = walker.Next()) {
-      ContentRemoved(acc);
-    }
+
+  dom::AllChildrenIterator iter =
+    dom::AllChildrenIterator(aContentNode, nsIContent::eAllChildren, true);
+  while (nsIContent* childNode = iter.GetNextChild()) {
+    ContentRemoved(childNode);
   }
 }
 
 bool
 DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
 {
   if (!aElement->HasID())
     return false;
@@ -2023,60 +2034,16 @@ DocAccessible::RelocateARIAOwnedIfNeeded
       }
     }
   }
 
   return false;
 }
 
 void
-DocAccessible::ValidateARIAOwned()
-{
-  for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
-    Accessible* owner = it.Key();
-    nsTArray<RefPtr<Accessible> >* children = it.UserData();
-
-    // Owner is about to die, put children back if applicable.
-    if (owner != this &&
-        (!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
-         !owner->IsInDocument())) {
-      PutChildrenBack(children, 0);
-      it.Remove();
-      continue;
-    }
-
-    for (uint32_t idx = 0; idx < children->Length(); idx++) {
-      Accessible* child = children->ElementAt(idx);
-      if (!child->IsInDocument()) {
-        children->RemoveElementAt(idx);
-        idx--;
-        continue;
-      }
-
-      NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
-
-      // If DOM node doesn't have a frame anymore then shutdown its accessible.
-      if (child->Parent() && !child->GetFrame()) {
-        ContentRemoved(child);
-        children->RemoveElementAt(idx);
-        idx--;
-        continue;
-      }
-
-      NS_ASSERTION(child->Parent() == owner,
-                   "Illigally stolen ARIA owned child!");
-    }
-
-    if (children->Length() == 0) {
-      it.Remove();
-    }
-  }
-}
-
-void
 DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
 {
   nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
 
   MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
   MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
 
 #ifdef A11Y_LOG
@@ -2189,17 +2156,17 @@ DocAccessible::PutChildrenBack(nsTArray<
     int32_t idxInParent = -1;
     Accessible* origContainer = GetContainerAccessible(child->GetContent());
     if (origContainer) {
       TreeWalker walker(origContainer);
       if (walker.Seek(child->GetContent())) {
         Accessible* prevChild = walker.Prev();
         if (prevChild) {
           idxInParent = prevChild->IndexInParent() + 1;
-          MOZ_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
+          MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
           origContainer = prevChild->Parent();
         }
         else {
           idxInParent = 0;
         }
       }
     }
     MoveChild(child, origContainer, idxInParent);
@@ -2220,18 +2187,22 @@ DocAccessible::MoveChild(Accessible* aCh
 #ifdef A11Y_LOG
   logging::TreeInfo("move child", 0,
                     "old parent", curParent, "new parent", aNewParent,
                     "child", aChild, nullptr);
 #endif
 
   // If the child was taken from from an ARIA owns element.
   if (aChild->IsRelocated()) {
-    nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
-    children->RemoveElement(aChild);
+    nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(curParent);
+    MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+    owned->RemoveElement(aChild);
+    if (owned->Length() == 0) {
+      mARIAOwnsHash.Remove(curParent);
+    }
   }
 
   NotificationController::MoveGuard mguard(mNotificationController);
 
   if (curParent == aNewParent) {
     MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
     curParent->MoveChild(aIdxInParent, aChild);
 
@@ -2322,20 +2293,29 @@ DocAccessible::CacheChildrenInSubtree(Ac
 }
 
 void
 DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
 {
   aRoot->mStateFlags |= eIsNotInDocument;
   RemoveDependentIDsFor(aRoot);
 
+  nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(aRoot);
   uint32_t count = aRoot->ContentChildCount();
   for (uint32_t idx = 0; idx < count; idx++) {
     Accessible* child = aRoot->ContentChildAt(idx);
 
+    if (child->IsRelocated()) {
+      MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+      owned->RemoveElement(child);
+      if (owned->Length() == 0) {
+        mARIAOwnsHash.Remove(aRoot);
+      }
+    }
+
     // Removing this accessible from the document doesn't mean anything about
     // accessibles for subdocuments, so skip removing those from the tree.
     if (!child->IsDoc()) {
       UncacheChildrenInSubtree(child);
     }
   }
 
   if (aRoot->IsNodeMapEntry() &&
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -339,17 +339,17 @@ public:
    */
   void ContentInserted(nsIContent* aContainerNode,
                        nsIContent* aStartChildNode,
                        nsIContent* aEndChildNode);
 
   /**
    * Update the tree on content removal.
    */
-  void ContentRemoved(Accessible* aContent);
+  void ContentRemoved(Accessible* aAccessible);
   void ContentRemoved(nsIContent* aContentNode);
 
   /**
    * Updates accessible tree when rendered text is changed.
    */
   void UpdateText(nsIContent* aTextNode);
 
   /**
@@ -500,21 +500,16 @@ protected:
    *
    * While children are cached we may encounter the case there's no accessible
    * for referred content by related accessible. Store these related nodes to
    * invalidate their containers later.
    */
   void ProcessInvalidationList();
 
   /**
-   * Validates all aria-owns connections and updates the tree accordingly.
-   */
-  void ValidateARIAOwned();
-
-  /**
    * Steals or puts back accessible subtrees.
    */
   void DoARIAOwnsRelocation(Accessible* aOwner);
 
   /**
    * Moves children back under their original parents.
    */
   void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -42,17 +42,17 @@ function Buffer(subject, encoding /*, bu
 
   switch (type) {
     case 'number':
       // Create typed array of the given size if number.
       try {
         let buffer = new Uint8Array(subject > 0 ? Math.floor(subject) : 0);
         return buffer;
       } catch (e) {
-        if (/size and count too large/.test(e.message) ||
+        if (/invalid array length/.test(e.message) ||
             /invalid arguments/.test(e.message))
           throw new RangeError('Could not instantiate buffer: size of buffer may be too large');
         else
           throw new Error('Could not instantiate buffer');
       }
       break;
     case 'string':
       // If string encode it and use buffer for the returned Uint8Array
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -211,18 +211,16 @@ DEFAULT_TEST_PREFS = {
     'security.default_personal_cert': 'Select Automatically',
     'network.http.prompt-temp-redirect': False,
     'security.warn_viewing_mixed': False,
     'extensions.defaultProviders.enabled': True,
     'datareporting.policy.dataSubmissionPolicyBypassNotification': True,
     'layout.css.report_errors': True,
     'layout.css.grid.enabled': True,
     'layout.spammy_warnings.enabled': False,
-    # Make sure the disk cache doesn't get auto disabled
-    'network.http.bypass-cachelock-threshold': 200000,
     # Always use network provider for geolocation tests
     # so we bypass the OSX dialog raised by the corelocation provider
     'geo.provider.testing': True,
     # Background thumbnails in particular cause grief, and disabling thumbnails
     # in general can't hurt - we re-enable them when tests need them.
     'browser.pagethumbnails.capturing_disabled': True,
     # Indicate that the download panel has been shown once so that whichever
     # download test runs first doesn't show the popup inconsistently.
--- a/addon-sdk/source/test/preferences/test.json
+++ b/addon-sdk/source/test/preferences/test.json
@@ -26,17 +26,16 @@
   "security.default_personal_cert": "Select Automatically",
   "network.http.prompt-temp-redirect": false,
   "security.warn_viewing_mixed": false,
   "extensions.defaultProviders.enabled": true,
   "datareporting.policy.dataSubmissionPolicyBypassNotification": true,
   "layout.css.report_errors": true,
   "layout.css.grid.enabled": true,
   "layout.spammy_warnings.enabled": false,
-  "network.http.bypass-cachelock-threshold": 200000,
   "geo.provider.testing": true,
   "browser.pagethumbnails.capturing_disabled": true,
   "browser.download.panel.shown": true,
   "general.useragent.updates.enabled": false,
   "media.eme.enabled": true,
   "dom.ipc.tabs.shutdownTimeoutSecs": 0,
   "general.useragent.locale": "en-US",
   "intl.locale.matchOS": "en-US",
--- a/addon-sdk/source/test/test-xpcom.js
+++ b/addon-sdk/source/test/test-xpcom.js
@@ -140,16 +140,17 @@ function testRegister(assert, text) {
       newChannel : function(aURI, aLoadInfo) {
         var ios = Cc["@mozilla.org/network/io-service;1"].
                   getService(Ci.nsIIOService);
 
         var uri = ios.newURI("data:text/plain;charset=utf-8," + text);
         var channel = ios.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
 
         channel.originalURI = aURI;
+        aLoadInfo.resultPrincipalURI = aURI;
         return channel;
       },
       getURIFlags: function(aURI) {
         return Ci.nsIAboutModule.ALLOW_SCRIPT;
       }
     })
   });
 
--- a/browser/base/content/test/windows/browser_toolbariconcolor_restyles.js
+++ b/browser/base/content/test/windows/browser_toolbariconcolor_restyles.js
@@ -12,16 +12,26 @@ add_task(function* test_toolbar_element_
   // create a window and snapshot the elementsStyled
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
   yield new Promise(resolve => waitForFocus(resolve, win1));
 
   // create a 2nd window and snapshot the elementsStyled
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
   yield new Promise(resolve => waitForFocus(resolve, win2));
 
+  // Flush any pending styles before we take a measurement.
+  win1.getComputedStyle(win1.document.firstElementChild);
+  win2.getComputedStyle(win2.document.firstElementChild);
+
+  // Clear the focused element from each window so that when
+  // we raise them, the focus of the element doesn't cause an
+  // unrelated style flush.
+  Services.focus.clearFocus(win1);
+  Services.focus.clearFocus(win2);
+
   let utils1 = SpecialPowers.getDOMWindowUtils(win1);
   restyles.win1.initial = utils1.elementsRestyled;
 
   let utils2 = SpecialPowers.getDOMWindowUtils(win2);
   restyles.win2.initial = utils2.elementsRestyled;
 
   // switch back to 1st window, and snapshot elementsStyled
   Services.focus.activeWindow = win1;
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -167,37 +167,33 @@ AboutRedirector::NewChannel(nsIURI* aURI
         url.AssignASCII(redir.url);
       }
 
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), url);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
-      // channel which forces the channel owner to reflect the displayed
-      // URL rather then being the systemPrincipal.
+      // If tempURI links to an internal URI (chrome://, resource://)
+      // then set the result principal URL on the channel's load info.
+      // Otherwise, we leave it null which forces the channel principal
+      // to reflect the displayed URL rather than being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsLoadFlags loadFlags = isUIResource
-                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
-
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
-                                 aLoadInfo,
-                                 nullptr, // aLoadGroup
-                                 nullptr, // aCallbacks
-                                 loadFlags);
+                                 aLoadInfo);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      if (isUIResource) {
+        aLoadInfo->SetResultPrincipalURI(aURI);
+      }
       tempChannel->SetOriginalURI(aURI);
 
       NS_ADDREF(*result = tempChannel);
       return rv;
     }
   }
 
   return NS_ERROR_ILLEGAL_VALUE;
--- a/browser/components/feeds/FeedConverter.js
+++ b/browser/components/feeds/FeedConverter.js
@@ -248,16 +248,17 @@ FeedConverter.prototype = {
         // Store the result in the result service so that the display
         // page can access it.
         feedService.addFeedResult(result);
 
         // Now load the actual XUL document.
         let aboutFeedsURI = ios.newURI("about:feeds");
         chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
         chromeChannel.originalURI = result.uri;
+        loadInfo.resultPrincipalURI = result.uri;
 
         // carry the origin attributes from the channel that loaded the feed.
         chromeChannel.owner =
           Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI,
                                                                  loadInfo.originAttributes);
       } else {
         chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
       }
@@ -555,20 +556,22 @@ GenericProtocolHandler.prototype = {
     let inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI;
     let channel = Cc["@mozilla.org/network/io-service;1"].
                   getService(Ci.nsIIOService).
                   newChannelFromURIWithLoadInfo(inner, aLoadInfo);
 
     const schemeId = this._getTelemetrySchemeId();
     Services.telemetry.getHistogramById("FEED_PROTOCOL_USAGE").add(schemeId);
 
-    if (channel instanceof Components.interfaces.nsIHttpChannel)
+    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;
+    aLoadInfo.resultPrincipalURI = aUri;
     return channel;
   },
 
   QueryInterface(iid) {
     if (iid.equals(Ci.nsIProtocolHandler) ||
         iid.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
--- a/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
+++ b/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
@@ -21,16 +21,17 @@ let TestAboutPage = {
   },
 
   newChannel(aURI, aLoadInfo) {
     // about: page inception!
     let newURI = Services.io.newURI(SELFCHROMEURL);
     let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
                                                             aLoadInfo);
     channel.originalURI = aURI;
+    aLoadInfo.resultPrincipalURI = aURI;
     return channel;
   },
 
   createInstance(outer, iid) {
     if (outer != null) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(iid);
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.8.290
+Current extension version is: 1.8.314
 
-Taken from upstream commit: 60c232bc
+Taken from upstream commit: 3adda80f
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -998,16 +998,17 @@ PdfStreamConverter.prototype = {
             domWindow.frameElement.className === "previewPluginContentFrame";
           PdfJsTelemetry.onEmbed(isObjectEmbed);
         }
       }
     };
 
     // Keep the URL the same so the browser sees it as the same.
     channel.originalURI = aRequest.URI;
+    channel.loadInfo.resultPrincipalURI = aRequest.loadInfo.resultPrincipalURI;
     channel.loadGroup = aRequest.loadGroup;
     channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;
 
     // We can use the resource principal when data is fetched by the chrome,
     // e.g. useful for NoScript. Make make sure we reuse the origin attributes
     // from the request channel to keep isolation consistent.
     var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
                 .getService(Ci.nsIScriptSecurityManager);
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -987,17 +987,17 @@ var createObjectURL = function createObj
 function MessageHandler(sourceName, targetName, comObj) {
   this.sourceName = sourceName;
   this.targetName = targetName;
   this.comObj = comObj;
   this.callbackIndex = 1;
   this.postMessageTransfers = true;
   var callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
   var ah = this.actionHandler = Object.create(null);
-  this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
+  this._onComObjOnMessage = event => {
     var data = event.data;
     if (data.targetName !== this.sourceName) {
       return;
     }
     if (data.isReply) {
       var callbackId = data.callbackId;
       if (data.callbackId in callbacksCapabilities) {
         var callback = callbacksCapabilities[callbackId];
@@ -1038,17 +1038,17 @@ function MessageHandler(sourceName, targ
           });
         });
       } else {
         action[0].call(action[1], data.data);
       }
     } else {
       error('Unknown action from worker: ' + data.action);
     }
-  }.bind(this);
+  };
   comObj.addEventListener('message', this._onComObjOnMessage);
 }
 MessageHandler.prototype = {
   on(actionName, handler, scope) {
     var ah = this.actionHandler;
     if (ah[actionName]) {
       error('There is already an actionName called "' + actionName + '"');
     }
@@ -1220,24 +1220,24 @@ var DOMCMapReaderFactory = function DOMC
     this.isCompressed = params.isCompressed || false;
   }
   DOMCMapReaderFactory.prototype = {
     fetch(params) {
       var name = params.name;
       if (!name) {
         return Promise.reject(new Error('CMap name must be specified.'));
       }
-      return new Promise(function (resolve, reject) {
+      return new Promise((resolve, reject) => {
         var url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
         var request = new XMLHttpRequest();
         request.open('GET', url, true);
         if (this.isCompressed) {
           request.responseType = 'arraybuffer';
         }
-        request.onreadystatechange = function () {
+        request.onreadystatechange = () => {
           if (request.readyState !== XMLHttpRequest.DONE) {
             return;
           }
           if (request.status === 200 || request.status === 0) {
             var data;
             if (this.isCompressed && request.response) {
               data = new Uint8Array(request.response);
             } else if (!this.isCompressed && request.responseText) {
@@ -1247,19 +1247,19 @@ var DOMCMapReaderFactory = function DOMC
               resolve({
                 cMapData: data,
                 compressionType: this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE
               });
               return;
             }
           }
           reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url));
-        }.bind(this);
+        };
         request.send(null);
-      }.bind(this));
+      });
     }
   };
   return DOMCMapReaderFactory;
 }();
 var CustomStyle = function CustomStyleClosure() {
   var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
   var _cache = Object.create(null);
   function CustomStyle() {}
@@ -1593,34 +1593,32 @@ var LinkAnnotationElement = function Lin
           this._bindNamedAction(link, this.data.action);
         } else {
           this._bindLink(link, this.data.dest);
         }
       }
       this.container.appendChild(link);
       return this.container;
     },
-    _bindLink: function LinkAnnotationElement_bindLink(link, destination) {
-      var self = this;
+    _bindLink(link, destination) {
       link.href = this.linkService.getDestinationHash(destination);
-      link.onclick = function () {
+      link.onclick = () => {
         if (destination) {
-          self.linkService.navigateTo(destination);
+          this.linkService.navigateTo(destination);
         }
         return false;
       };
       if (destination) {
         link.className = 'internalLink';
       }
     },
-    _bindNamedAction: function LinkAnnotationElement_bindNamedAction(link, action) {
-      var self = this;
+    _bindNamedAction(link, action) {
       link.href = this.linkService.getAnchorUrl('');
-      link.onclick = function () {
-        self.linkService.executeNamedAction(action);
+      link.onclick = () => {
+        this.linkService.executeNamedAction(action);
         return false;
       };
       link.className = 'internalLink';
     }
   });
   return LinkAnnotationElement;
 }();
 var TextAnnotationElement = function TextAnnotationElementClosure() {
@@ -2077,17 +2075,17 @@ exports.AnnotationLayer = AnnotationLaye
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.build = exports.version = exports._UnsupportedManager = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.getDocument = undefined;
+exports.build = exports.version = exports._UnsupportedManager = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.getDocument = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
 var _dom_utils = __w_pdfjs_require__(1);
 
 var _font_loader = __w_pdfjs_require__(11);
 
 var _canvas = __w_pdfjs_require__(10);
@@ -2234,23 +2232,23 @@ var PDFDocumentLoadingTask = function PD
   }
   PDFDocumentLoadingTask.prototype = {
     get promise() {
       return this._capability.promise;
     },
     destroy() {
       this.destroyed = true;
       var transportDestroyed = !this._transport ? Promise.resolve() : this._transport.destroy();
-      return transportDestroyed.then(function () {
+      return transportDestroyed.then(() => {
         this._transport = null;
         if (this._worker) {
           this._worker.destroy();
           this._worker = null;
         }
-      }.bind(this));
+      });
     },
     then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
       return this.promise.then.apply(this.promise, arguments);
     }
   };
   return PDFDocumentLoadingTask;
 }();
 var PDFDataRangeTransport = function pdfDataRangeTransportClosure() {
@@ -2274,30 +2272,30 @@ var PDFDataRangeTransport = function pdf
     },
     onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
       var listeners = this._rangeListeners;
       for (var i = 0, n = listeners.length; i < n; ++i) {
         listeners[i](begin, chunk);
       }
     },
     onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
-      this._readyCapability.promise.then(function () {
+      this._readyCapability.promise.then(() => {
         var listeners = this._progressListeners;
         for (var i = 0, n = listeners.length; i < n; ++i) {
           listeners[i](loaded);
         }
-      }.bind(this));
+      });
     },
     onDataProgressiveRead: function PDFDataRangeTransport_onDataProgress(chunk) {
-      this._readyCapability.promise.then(function () {
+      this._readyCapability.promise.then(() => {
         var listeners = this._progressiveReadListeners;
         for (var i = 0, n = listeners.length; i < n; ++i) {
           listeners[i](chunk);
         }
-      }.bind(this));
+      });
     },
     transportReady: function PDFDataRangeTransport_transportReady() {
       this._readyCapability.resolve();
     },
     requestDataRange: function PDFDataRangeTransport_requestDataRange(begin, end) {
       throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
     },
     abort: function PDFDataRangeTransport_abort() {}
@@ -2426,56 +2424,53 @@ var PDFPageProxy = function PDFPageProxy
         };
         this.stats.time('Page Request');
         this.transport.messageHandler.send('RenderPageRequest', {
           pageIndex: this.pageNumber - 1,
           intent: renderingIntent,
           renderInteractiveForms: params.renderInteractiveForms === true
         });
       }
+      var complete = error => {
+        var i = intentState.renderTasks.indexOf(internalRenderTask);
+        if (i >= 0) {
+          intentState.renderTasks.splice(i, 1);
+        }
+        if (this.cleanupAfterRender) {
+          this.pendingCleanup = true;
+        }
+        this._tryCleanup();
+        if (error) {
+          internalRenderTask.capability.reject(error);
+        } else {
+          internalRenderTask.capability.resolve();
+        }
+        stats.timeEnd('Rendering');
+        stats.timeEnd('Overall');
+      };
       var internalRenderTask = new InternalRenderTask(complete, params, this.objs, this.commonObjs, intentState.operatorList, this.pageNumber, canvasFactory);
       internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print';
       if (!intentState.renderTasks) {
         intentState.renderTasks = [];
       }
       intentState.renderTasks.push(internalRenderTask);
       var renderTask = internalRenderTask.task;
       if (params.continueCallback) {
         (0, _util.deprecated)('render is used with continueCallback parameter');
         renderTask.onContinue = params.continueCallback;
       }
-      var self = this;
-      intentState.displayReadyCapability.promise.then(function pageDisplayReadyPromise(transparency) {
-        if (self.pendingCleanup) {
+      intentState.displayReadyCapability.promise.then(transparency => {
+        if (this.pendingCleanup) {
           complete();
           return;
         }
         stats.time('Rendering');
         internalRenderTask.initializeGraphics(transparency);
         internalRenderTask.operatorListChanged();
-      }, function pageDisplayReadPromiseError(reason) {
-        complete(reason);
-      });
-      function complete(error) {
-        var i = intentState.renderTasks.indexOf(internalRenderTask);
-        if (i >= 0) {
-          intentState.renderTasks.splice(i, 1);
-        }
-        if (self.cleanupAfterRender) {
-          self.pendingCleanup = true;
-        }
-        self._tryCleanup();
-        if (error) {
-          internalRenderTask.capability.reject(error);
-        } else {
-          internalRenderTask.capability.resolve();
-        }
-        stats.timeEnd('Rendering');
-        stats.timeEnd('Overall');
-      }
+      }, complete);
       return renderTask;
     },
     getOperatorList: function PDFPageProxy_getOperatorList() {
       function operatorListChanged() {
         if (intentState.operatorList.lastChunk) {
           intentState.opListReadCapability.resolve(intentState.operatorList);
           var i = intentState.renderTasks.indexOf(opListTask);
           if (i >= 0) {
@@ -2578,16 +2573,84 @@ var PDFPageProxy = function PDFPageProxy
       if (operatorListChunk.lastChunk) {
         intentState.receivingOperatorList = false;
         this._tryCleanup();
       }
     }
   };
   return PDFPageProxy;
 }();
+class LoopbackPort {
+  constructor(defer) {
+    this._listeners = [];
+    this._defer = defer;
+    this._deferred = Promise.resolve(undefined);
+  }
+  postMessage(obj, transfers) {
+    function cloneValue(value) {
+      if (typeof value !== 'object' || value === null) {
+        return value;
+      }
+      if (cloned.has(value)) {
+        return cloned.get(value);
+      }
+      var result;
+      var buffer;
+      if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
+        var transferable = transfers && transfers.indexOf(buffer) >= 0;
+        if (value === buffer) {
+          result = value;
+        } else if (transferable) {
+          result = new value.constructor(buffer, value.byteOffset, value.byteLength);
+        } else {
+          result = new value.constructor(value);
+        }
+        cloned.set(value, result);
+        return result;
+      }
+      result = (0, _util.isArray)(value) ? [] : {};
+      cloned.set(value, result);
+      for (var i in value) {
+        var desc,
+            p = value;
+        while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
+          p = Object.getPrototypeOf(p);
+        }
+        if (typeof desc.value === 'undefined' || typeof desc.value === 'function') {
+          continue;
+        }
+        result[i] = cloneValue(desc.value);
+      }
+      return result;
+    }
+    if (!this._defer) {
+      this._listeners.forEach(function (listener) {
+        listener.call(this, { data: obj });
+      }, this);
+      return;
+    }
+    var cloned = new WeakMap();
+    var e = { data: cloneValue(obj) };
+    this._deferred.then(() => {
+      this._listeners.forEach(function (listener) {
+        listener.call(this, e);
+      }, this);
+    });
+  }
+  addEventListener(name, listener) {
+    this._listeners.push(listener);
+  }
+  removeEventListener(name, listener) {
+    var i = this._listeners.indexOf(listener);
+    this._listeners.splice(i, 1);
+  }
+  terminate() {
+    this._listeners = [];
+  }
+}
 var PDFWorker = function PDFWorkerClosure() {
   var nextFakeWorkerId = 0;
   function getWorkerSrc() {
     if (typeof workerSrc !== 'undefined') {
       return workerSrc;
     }
     if ((0, _dom_utils.getDefaultSetting)('workerSrc')) {
       return (0, _dom_utils.getDefaultSetting)('workerSrc');
@@ -2604,84 +2667,16 @@ var PDFWorker = function PDFWorkerClosur
     var loader = fakeWorkerFilesLoader || function (callback) {
       _util.Util.loadScript(getWorkerSrc(), function () {
         callback(window.pdfjsDistBuildPdfWorker.WorkerMessageHandler);
       });
     };
     loader(fakeWorkerFilesLoadedCapability.resolve);
     return fakeWorkerFilesLoadedCapability.promise;
   }
-  function FakeWorkerPort(defer) {
-    this._listeners = [];
-    this._defer = defer;
-    this._deferred = Promise.resolve(undefined);
-  }
-  FakeWorkerPort.prototype = {
-    postMessage(obj, transfers) {
-      function cloneValue(value) {
-        if (typeof value !== 'object' || value === null) {
-          return value;
-        }
-        if (cloned.has(value)) {
-          return cloned.get(value);
-        }
-        var result;
-        var buffer;
-        if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
-          var transferable = transfers && transfers.indexOf(buffer) >= 0;
-          if (value === buffer) {
-            result = value;
-          } else if (transferable) {
-            result = new value.constructor(buffer, value.byteOffset, value.byteLength);
-          } else {
-            result = new value.constructor(value);
-          }
-          cloned.set(value, result);
-          return result;
-        }
-        result = (0, _util.isArray)(value) ? [] : {};
-        cloned.set(value, result);
-        for (var i in value) {
-          var desc,
-              p = value;
-          while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
-            p = Object.getPrototypeOf(p);
-          }
-          if (typeof desc.value === 'undefined' || typeof desc.value === 'function') {
-            continue;
-          }
-          result[i] = cloneValue(desc.value);
-        }
-        return result;
-      }
-      if (!this._defer) {
-        this._listeners.forEach(function (listener) {
-          listener.call(this, { data: obj });
-        }, this);
-        return;
-      }
-      var cloned = new WeakMap();
-      var e = { data: cloneValue(obj) };
-      this._deferred.then(function () {
-        this._listeners.forEach(function (listener) {
-          listener.call(this, e);
-        }, this);
-      }.bind(this));
-    },
-    addEventListener(name, listener) {
-      this._listeners.push(listener);
-    },
-    removeEventListener(name, listener) {
-      var i = this._listeners.indexOf(listener);
-      this._listeners.splice(i, 1);
-    },
-    terminate() {
-      this._listeners = [];
-    }
-  };
   function createCDNWrapper(url) {
     var wrapper = 'importScripts(\'' + url + '\');';
     return URL.createObjectURL(new Blob([wrapper]));
   }
   function PDFWorker(name, port) {
     this.name = name;
     this.destroyed = false;
     this._readyCapability = (0, _util.createPromiseCapability)();
@@ -2711,33 +2706,33 @@ var PDFWorker = function PDFWorkerClosur
       this._readyCapability.resolve();
     },
     _initialize: function PDFWorker_initialize() {
       if (!isWorkerDisabled && !(0, _dom_utils.getDefaultSetting)('disableWorker') && typeof Worker !== 'undefined') {
         var workerSrc = getWorkerSrc();
         try {
           var worker = new Worker(workerSrc);
           var messageHandler = new _util.MessageHandler('main', 'worker', worker);
-          var terminateEarly = function () {
+          var terminateEarly = () => {
             worker.removeEventListener('error', onWorkerError);
             messageHandler.destroy();
             worker.terminate();
             if (this.destroyed) {
               this._readyCapability.reject(new Error('Worker was destroyed'));
             } else {
               this._setupFakeWorker();
             }
-          }.bind(this);
-          var onWorkerError = function (event) {
+          };
+          var onWorkerError = () => {
             if (!this._webWorker) {
               terminateEarly();
             }
-          }.bind(this);
+          };
           worker.addEventListener('error', onWorkerError);
-          messageHandler.on('test', function PDFWorker_test(data) {
+          messageHandler.on('test', data => {
             worker.removeEventListener('error', onWorkerError);
             if (this.destroyed) {
               terminateEarly();
               return;
             }
             var supportTypedArray = data && data.supportTypedArray;
             if (supportTypedArray) {
               this._messageHandler = messageHandler;
@@ -2748,35 +2743,35 @@ var PDFWorker = function PDFWorkerClosur
               }
               this._readyCapability.resolve();
               messageHandler.send('configure', { verbosity: (0, _util.getVerbosityLevel)() });
             } else {
               this._setupFakeWorker();
               messageHandler.destroy();
               worker.terminate();
             }
-          }.bind(this));
+          });
           messageHandler.on('console_log', function (data) {
             console.log.apply(console, data);
           });
           messageHandler.on('console_error', function (data) {
             console.error.apply(console, data);
           });
-          messageHandler.on('ready', function (data) {
+          messageHandler.on('ready', data => {
             worker.removeEventListener('error', onWorkerError);
             if (this.destroyed) {
               terminateEarly();
               return;
             }
             try {
               sendTest();
             } catch (e) {
               this._setupFakeWorker();
             }
-          }.bind(this));
+          });
           var sendTest = function () {
             var postMessageTransfers = (0, _dom_utils.getDefaultSetting)('postMessageTransfers') && !isPostMessageTransfersDisabled;
             var testObj = new Uint8Array([postMessageTransfers ? 255 : 0]);
             try {
               messageHandler.send('test', testObj, [testObj.buffer]);
             } catch (ex) {
               (0, _util.info)('Cannot use postMessage transfers');
               testObj[0] = 0;
@@ -2791,31 +2786,31 @@ var PDFWorker = function PDFWorkerClosur
       }
       this._setupFakeWorker();
     },
     _setupFakeWorker: function PDFWorker_setupFakeWorker() {
       if (!isWorkerDisabled && !(0, _dom_utils.getDefaultSetting)('disableWorker')) {
         (0, _util.warn)('Setting up fake worker.');
         isWorkerDisabled = true;
       }
-      setupFakeWorkerGlobal().then(function (WorkerMessageHandler) {
+      setupFakeWorkerGlobal().then(WorkerMessageHandler => {
         if (this.destroyed) {
           this._readyCapability.reject(new Error('Worker was destroyed'));
           return;
         }
         var isTypedArraysPresent = Uint8Array !== Float32Array;
-        var port = new FakeWorkerPort(isTypedArraysPresent);
+        var port = new LoopbackPort(isTypedArraysPresent);
         this._port = port;
         var id = 'fake' + nextFakeWorkerId++;
         var workerHandler = new _util.MessageHandler(id + '_worker', id, port);
         WorkerMessageHandler.setup(workerHandler, port);
         var messageHandler = new _util.MessageHandler(id, id + '_worker', port);
         this._messageHandler = messageHandler;
         this._readyCapability.resolve();
-      }.bind(this));
+      });
     },
     destroy: function PDFWorker_destroy() {
       this.destroyed = true;
       if (this._webWorker) {
         this._webWorker.terminate();
         this._webWorker = null;
       }
       this._port = null;
@@ -2859,30 +2854,29 @@ var WorkerTransport = function WorkerTra
       var waitOn = [];
       this.pageCache.forEach(function (page) {
         if (page) {
           waitOn.push(page._destroy());
         }
       });
       this.pageCache = [];
       this.pagePromises = [];
-      var self = this;
       var terminated = this.messageHandler.sendWithPromise('Terminate', null);
       waitOn.push(terminated);
-      Promise.all(waitOn).then(function () {
-        self.fontLoader.clear();
-        if (self.pdfDataRangeTransport) {
-          self.pdfDataRangeTransport.abort();
-          self.pdfDataRangeTransport = null;
+      Promise.all(waitOn).then(() => {
+        this.fontLoader.clear();
+        if (this.pdfDataRangeTransport) {
+          this.pdfDataRangeTransport.abort();
+          this.pdfDataRangeTransport = null;
         }
-        if (self.messageHandler) {
-          self.messageHandler.destroy();
-          self.messageHandler = null;
+        if (this.messageHandler) {
+          this.messageHandler.destroy();
+          this.messageHandler = null;
         }
-        self.destroyCapability.resolve();
+        this.destroyCapability.resolve();
       }, this.destroyCapability.reject);
       return this.destroyCapability.promise;
     },
     setupMessageHandler: function WorkerTransport_setupMessageHandler() {
       var messageHandler = this.messageHandler;
       var loadingTask = this.loadingTask;
       var pdfDataRangeTransport = this.pdfDataRangeTransport;
       if (pdfDataRangeTransport) {
@@ -2986,19 +2980,20 @@ var WorkerTransport = function WorkerTra
                 }
               };
             }
             var font = new _font_loader.FontFaceObject(exportedData, {
               isEvalSuported: (0, _dom_utils.getDefaultSetting)('isEvalSupported'),
               disableFontFace: (0, _dom_utils.getDefaultSetting)('disableFontFace'),
               fontRegistry
             });
-            this.fontLoader.bind([font], function fontReady(fontObjs) {
+            var fontReady = fontObjs => {
               this.commonObjs.resolve(id, font);
-            }.bind(this));
+            };
+            this.fontLoader.bind([font], fontReady);
             break;
           case 'FontPath':
             this.commonObjs.resolve(id, data[2]);
             break;
           default:
             (0, _util.error)('Got unknown common object type ' + type);
         }
       }, this);
@@ -3135,24 +3130,24 @@ var WorkerTransport = function WorkerTra
     getPage: function WorkerTransport_getPage(pageNumber, capability) {
       if (!(0, _util.isInt)(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
         return Promise.reject(new Error('Invalid page request'));
       }
       var pageIndex = pageNumber - 1;
       if (pageIndex in this.pagePromises) {
         return this.pagePromises[pageIndex];
       }
-      var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex }).then(function (pageInfo) {
+      var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex }).then(pageInfo => {
         if (this.destroyed) {
           throw new Error('Transport destroyed');
         }
         var page = new PDFPageProxy(pageIndex, pageInfo, this);
         this.pageCache[pageIndex] = page;
         return page;
-      }.bind(this));
+      });
       this.pagePromises[pageIndex] = promise;
       return promise;
     },
     getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
       return this.messageHandler.sendWithPromise('GetPageIndex', { ref }).catch(function (reason) {
         return Promise.reject(new Error(reason));
       });
     },
@@ -3187,26 +3182,26 @@ var WorkerTransport = function WorkerTra
           metadata: results[1] ? new _metadata.Metadata(results[1]) : null
         };
       });
     },
     getStats: function WorkerTransport_getStats() {
       return this.messageHandler.sendWithPromise('GetStats', null);
     },
     startCleanup: function WorkerTransport_startCleanup() {
-      this.messageHandler.sendWithPromise('Cleanup', null).then(function endCleanup() {
+      this.messageHandler.sendWithPromise('Cleanup', null).then(() => {
         for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
           var page = this.pageCache[i];
           if (page) {
             page.cleanup();
           }
         }
         this.commonObjs.clear();
         this.fontLoader.clear();
-      }.bind(this));
+      });
     }
   };
   return WorkerTransport;
 }();
 var PDFObjects = function PDFObjectsClosure() {
   function PDFObjects() {
     this.objs = Object.create(null);
   }
@@ -3386,20 +3381,21 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.8.290';
-  exports.build = build = '60c232bc';
+  exports.version = version = '1.8.314';
+  exports.build = build = '3adda80f';
 }
 exports.getDocument = getDocument;
+exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 exports.version = version;
 exports.build = build;
 
@@ -3840,20 +3836,19 @@ var renderTextLayer = function renderTex
       var textItems = this._textContent.items;
       var textStyles = this._textContent.styles;
       for (var i = 0, len = textItems.length; i < len; i++) {
         appendText(this, textItems[i], textStyles);
       }
       if (!timeout) {
         render(this);
       } else {
-        var self = this;
-        this._renderTimer = setTimeout(function () {
-          render(self);
-          self._renderTimer = null;
+        this._renderTimer = setTimeout(() => {
+          render(this);
+          this._renderTimer = null;
         }, timeout);
       }
     },
     expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
       if (!this._enhanceTextSelection || !this._renderingDone) {
         return;
       }
       if (this._bounds !== null) {
@@ -4389,18 +4384,18 @@ var _text_layer = __w_pdfjs_require__(5)
 var _svg = __w_pdfjs_require__(4);
 
 var isWorker = typeof window === 'undefined';
 if (!_util.globalScope.PDFJS) {
   _util.globalScope.PDFJS = {};
 }
 var PDFJS = _util.globalScope.PDFJS;
 {
-  PDFJS.version = '1.8.290';
-  PDFJS.build = '60c232bc';
+  PDFJS.version = '1.8.314';
+  PDFJS.build = '3adda80f';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
@@ -4453,16 +4448,17 @@ PDFJS.postMessageTransfers = PDFJS.postM
 PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ? false : PDFJS.disableCreateObjectURL;
 PDFJS.disableWebGL = PDFJS.disableWebGL === undefined ? true : PDFJS.disableWebGL;
 PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? _dom_utils.LinkTarget.NONE : PDFJS.externalLinkTarget;
 PDFJS.externalLinkRel = PDFJS.externalLinkRel === undefined ? _dom_utils.DEFAULT_LINK_REL : PDFJS.externalLinkRel;
 PDFJS.isEvalSupported = PDFJS.isEvalSupported === undefined ? true : PDFJS.isEvalSupported;
 PDFJS.pdfjsNext = PDFJS.pdfjsNext === undefined ? false : PDFJS.pdfjsNext;
 ;
 PDFJS.getDocument = _api.getDocument;
+PDFJS.LoopbackPort = _api.LoopbackPort;
 PDFJS.PDFDataRangeTransport = _api.PDFDataRangeTransport;
 PDFJS.PDFWorker = _api.PDFWorker;
 PDFJS.hasCanvasTypedArrays = true;
 PDFJS.CustomStyle = _dom_utils.CustomStyle;
 PDFJS.LinkTarget = _dom_utils.LinkTarget;
 PDFJS.addLinkAttributes = _dom_utils.addLinkAttributes;
 PDFJS.getFilenameFromUrl = _dom_utils.getFilenameFromUrl;
 PDFJS.isExternalLinkTargetSet = _dom_utils.isExternalLinkTargetSet;
@@ -5728,17 +5724,17 @@ var CanvasGraphics = function CanvasGrap
           spacingLength = spacingDir * glyph * fontSize / 1000;
           this.ctx.translate(spacingLength, 0);
           current.x += spacingLength * textHScale;
           continue;
         }
         var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
         var operatorList = font.charProcOperatorList[glyph.operatorListId];
         if (!operatorList) {
-          (0, _util.warn)('Type3 character \"' + glyph.operatorListId + '\" is not available');
+          (0, _util.warn)(`Type3 character "${glyph.operatorListId}" is not available.`);
           continue;
         }
         this.processingType3 = glyph;
         this.save();
         ctx.scale(fontSize, fontSize);
         ctx.transform.apply(ctx, fontMatrix);
         this.executeOperatorList(operatorList);
         this.restore();
@@ -6703,29 +6699,30 @@ exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.290';
-var pdfjsBuild = '60c232bc';
+var pdfjsVersion = '1.8.314';
+var pdfjsBuild = '3adda80f';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
 exports.build = pdfjsDisplayAPI.build;
 exports.version = pdfjsDisplayAPI.version;
 exports.getDocument = pdfjsDisplayAPI.getDocument;
+exports.LoobpackPort = pdfjsDisplayAPI.LoopbackPort;
 exports.PDFDataRangeTransport = pdfjsDisplayAPI.PDFDataRangeTransport;
 exports.PDFWorker = pdfjsDisplayAPI.PDFWorker;
 exports.renderTextLayer = pdfjsDisplayTextLayer.renderTextLayer;
 exports.AnnotationLayer = pdfjsDisplayAnnotationLayer.AnnotationLayer;
 exports.CustomStyle = pdfjsDisplayDOMUtils.CustomStyle;
 exports.createPromiseCapability = pdfjsSharedUtil.createPromiseCapability;
 exports.PasswordResponses = pdfjsSharedUtil.PasswordResponses;
 exports.InvalidPDFException = pdfjsSharedUtil.InvalidPDFException;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -987,17 +987,17 @@ var createObjectURL = function createObj
 function MessageHandler(sourceName, targetName, comObj) {
   this.sourceName = sourceName;
   this.targetName = targetName;
   this.comObj = comObj;
   this.callbackIndex = 1;
   this.postMessageTransfers = true;
   var callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
   var ah = this.actionHandler = Object.create(null);
-  this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
+  this._onComObjOnMessage = event => {
     var data = event.data;
     if (data.targetName !== this.sourceName) {
       return;
     }
     if (data.isReply) {
       var callbackId = data.callbackId;
       if (data.callbackId in callbacksCapabilities) {
         var callback = callbacksCapabilities[callbackId];
@@ -1038,17 +1038,17 @@ function MessageHandler(sourceName, targ
           });
         });
       } else {
         action[0].call(action[1], data.data);
       }
     } else {
       error('Unknown action from worker: ' + data.action);
     }
-  }.bind(this);
+  };
   comObj.addEventListener('message', this._onComObjOnMessage);
 }
 MessageHandler.prototype = {
   on(actionName, handler, scope) {
     var ah = this.actionHandler;
     if (ah[actionName]) {
       error('There is already an actionName called "' + actionName + '"');
     }
@@ -4664,19 +4664,23 @@ var Lexer = function LexerClosure() {
         }
       } else if (ch === 0x2B) {
         ch = this.nextChar();
       }
       if (ch === 0x2E) {
         divideBy = 10;
         ch = this.nextChar();
       }
+      if (ch === 0x0A || ch === 0x0D) {
+        do {
+          ch = this.nextChar();
+        } while (ch === 0x0A || ch === 0x0D);
+      }
       if (ch < 0x30 || ch > 0x39) {
-        error('Invalid number: ' + String.fromCharCode(ch));
-        return 0;
+        error(`Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`);
       }
       var baseValue = ch - 0x30;
       var powerValue = 0;
       var powerValueSign = 1;
       while ((ch = this.nextChar()) >= 0) {
         if (0x30 <= ch && ch <= 0x39) {
           var currentDigit = ch - 0x30;
           if (eNotation) {
@@ -14551,25 +14555,25 @@ var ChunkedStreamManager = function Chun
             chunks = null;
             resolve(chunkData);
           } catch (e) {
             reject(e);
           }
         };
         rangeReader.read().then(readChunk, reject);
       });
-      promise.then(function (data) {
+      promise.then(data => {
         if (this.aborted) {
           return;
         }
         this.onReceiveData({
           chunk: data,
           begin
         });
-      }.bind(this));
+      });
     },
     requestAllChunks: function ChunkedStreamManager_requestAllChunks() {
       var missingChunks = this.stream.getMissingChunks();
       this._requestChunks(missingChunks);
       return this._loadedStreamCapability.promise;
     },
     _requestChunks: function ChunkedStreamManager_requestChunks(chunks) {
       var requestId = this.currRequestId++;
@@ -16431,17 +16435,17 @@ var getEncoding = coreEncodings.getEncod
 var getStdFontMap = coreStandardFonts.getStdFontMap;
 var getSerifFonts = coreStandardFonts.getSerifFonts;
 var getSymbolsFonts = coreStandardFonts.getSymbolsFonts;
 var getNormalizedUnicodes = coreUnicode.getNormalizedUnicodes;
 var reverseIfRtl = coreUnicode.reverseIfRtl;
 var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph;
 var getGlyphsUnicode = coreGlyphList.getGlyphsUnicode;
 var PartialEvaluator = function PartialEvaluatorClosure() {
-  var DefaultPartialEvaluatorOptions = {
+  const DefaultPartialEvaluatorOptions = {
     forceDataSchema: false,
     maxImageSize: -1,
     disableFontFace: false,
     disableNativeImageDecoder: false,
     ignoreErrors: false
   };
   function NativeImageDecoder(xref, resources, handler, forceDataSchema) {
     this.xref = xref;
@@ -16476,31 +16480,31 @@ var PartialEvaluator = function PartialE
   NativeImageDecoder.isDecodable = function NativeImageDecoder_isDecodable(image, xref, res) {
     var dict = image.dict;
     if (dict.has('DecodeParms') || dict.has('DP')) {
       return false;
     }
     var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
     return (cs.numComps === 1 || cs.numComps === 3) && cs.isDefaultDecode(dict.getArray('Decode', 'D'));
   };
-  function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options) {
+  function PartialEvaluator({ pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options = null }) {
     this.pdfManager = pdfManager;
     this.xref = xref;
     this.handler = handler;
     this.pageIndex = pageIndex;
     this.idFactory = idFactory;
     this.fontCache = fontCache;
     this.builtInCMapCache = builtInCMapCache;
     this.options = options || DefaultPartialEvaluatorOptions;
     this.fetchBuiltInCMap = name => {
       var cachedCMap = this.builtInCMapCache[name];
       if (cachedCMap) {
         return Promise.resolve(cachedCMap);
       }
-      return handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => {
+      return this.handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => {
         if (data.compressionType !== CMapCompressionType.NONE) {
           this.builtInCMapCache[name] = data;
         }
         return data;
       });
     };
   }
   var TIME_SLOT_DURATION_MS = 20;
@@ -16653,25 +16657,30 @@ var PartialEvaluator = function PartialE
         }
         if (smask && smask.backdrop) {
           colorSpace = colorSpace || ColorSpace.singletons.rgb;
           smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
         }
         operatorList.addOp(OPS.beginGroup, [groupOptions]);
       }
       operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
-      return this.getOperatorList(xobj, task, dict.get('Resources') || resources, operatorList, initialState).then(function () {
+      return this.getOperatorList({
+        stream: xobj,
+        task,
+        resources: dict.get('Resources') || resources,
+        operatorList,
+        initialState
+      }).then(function () {
         operatorList.addOp(OPS.paintFormXObjectEnd, []);
         if (group) {
           operatorList.addOp(OPS.endGroup, [groupOptions]);
         }
       });
     },
     buildPaintImageXObject: function PartialEvaluator_buildPaintImageXObject(resources, image, inline, operatorList, cacheKey, imageCache) {
-      var self = this;
       var dict = image.dict;
       var w = dict.get('Width', 'W');
       var h = dict.get('Height', 'H');
       if (!(w && isNum(w)) || !(h && isNum(h))) {
         warn('Image dimensions are missing, or not numbers.');
         return;
       }
       var maxImageSize = this.options.maxImageSize;
@@ -16715,24 +16724,24 @@ var PartialEvaluator = function PartialE
       args = [objId, w, h];
       if (useNativeImageDecoder && !softMask && !mask && image instanceof JpegStream && NativeImageDecoder.isSupported(image, this.xref, resources)) {
         operatorList.addOp(OPS.paintJpegXObject, args);
         this.handler.send('obj', [objId, this.pageIndex, 'JpegStream', image.getIR(this.options.forceDataSchema)]);
         return;
       }
       var nativeImageDecoder = null;
       if (useNativeImageDecoder && (image instanceof JpegStream || mask instanceof JpegStream || softMask instanceof JpegStream)) {
-        nativeImageDecoder = new NativeImageDecoder(self.xref, resources, self.handler, self.options.forceDataSchema);
-      }
-      PDFImage.buildImage(self.handler, self.xref, resources, image, inline, nativeImageDecoder).then(function (imageObj) {
+        nativeImageDecoder = new NativeImageDecoder(this.xref, resources, this.handler, this.options.forceDataSchema);
+      }
+      PDFImage.buildImage(this.handler, this.xref, resources, image, inline, nativeImageDecoder).then(imageObj => {
         var imgData = imageObj.createImageData(false);
-        self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData], [imgData.data.buffer]);
-      }).then(undefined, function (reason) {
+        this.handler.send('obj', [objId, this.pageIndex, 'Image', imgData], [imgData.data.buffer]);
+      }).catch(reason => {
         warn('Unable to decode image: ' + reason);
-        self.handler.send('obj', [objId, self.pageIndex, 'Image', null]);
+        this.handler.send('obj', [objId, this.pageIndex, 'Image', null]);
       });
       operatorList.addOp(OPS.paintImageXObject, args);
       if (cacheKey) {
         imageCache[cacheKey] = {
           fn: OPS.paintImageXObject,
           args
         };
       }
@@ -16756,111 +16765,114 @@ var PartialEvaluator = function PartialE
         smaskOptions.transferMap = transferMap;
       }
       return this.buildFormXObject(resources, smaskContent, smaskOptions, operatorList, task, stateManager.state.clone());
     },
     handleTilingType: function PartialEvaluator_handleTilingType(fn, args, resources, pattern, patternDict, operatorList, task) {
       var tilingOpList = new OperatorList();
       var resourcesArray = [patternDict.get('Resources'), resources];
       var patternResources = Dict.merge(this.xref, resourcesArray);
-      return this.getOperatorList(pattern, task, patternResources, tilingOpList).then(function () {
+      return this.getOperatorList({
+        stream: pattern,
+        task,
+        resources: patternResources,
+        operatorList: tilingOpList
+      }).then(function () {
         operatorList.addDependencies(tilingOpList.dependencies);
         operatorList.addOp(fn, getTilingPatternIR({
           fnArray: tilingOpList.fnArray,
           argsArray: tilingOpList.argsArray
         }, patternDict, args));
       });
     },
     handleSetFont: function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) {
       var fontName;
       if (fontArgs) {
         fontArgs = fontArgs.slice();
         fontName = fontArgs[0].name;
       }
-      var self = this;
-      return this.loadFont(fontName, fontRef, resources).then(function (translated) {
+      return this.loadFont(fontName, fontRef, resources).then(translated => {
         if (!translated.font.isType3Font) {
           return translated;
         }
-        return translated.loadType3Data(self, resources, operatorList, task).then(function () {
+        return translated.loadType3Data(this, resources, operatorList, task).then(function () {
           return translated;
-        }, function (reason) {
-          self.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font });
+        }).catch(reason => {
+          this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font });
           return new TranslatedFont('g_font_error', new ErrorFont('Type3 font load error: ' + reason), translated.font);
         });
-      }).then(function (translated) {
+      }).then(translated => {
         state.font = translated.font;
-        translated.send(self.handler);
+        translated.send(this.handler);
         return translated.loadedName;
       });
     },
     handleText: function PartialEvaluator_handleText(chars, state) {
       var font = state.font;
       var glyphs = font.charsToGlyphs(chars);
       var isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
       if (font.data && (isAddToPathSet || this.options.disableFontFace)) {
-        var buildPath = function (fontChar) {
+        var buildPath = fontChar => {
           if (!font.renderer.hasBuiltPath(fontChar)) {
             var path = font.renderer.getPathJs(fontChar);
             this.handler.send('commonobj', [font.loadedName + '_path_' + fontChar, 'FontPath', path]);
           }
-        }.bind(this);
+        };
         for (var i = 0, ii = glyphs.length; i < ii; i++) {
           var glyph = glyphs[i];
           buildPath(glyph.fontChar);
           var accent = glyph.accent;
           if (accent && accent.fontChar) {
             buildPath(accent.fontChar);
           }
         }
       }
       return glyphs;
     },
     setGState: function PartialEvaluator_setGState(resources, gState, operatorList, task, stateManager) {
       var gStateObj = [];
       var gStateKeys = gState.getKeys();
-      var self = this;
       var promise = Promise.resolve();
       for (var i = 0, ii = gStateKeys.length; i < ii; i++) {
-        var key = gStateKeys[i];
-        var value = gState.get(key);
+        let key = gStateKeys[i];
+        let value = gState.get(key);
         switch (key) {
           case 'Type':
             break;
           case 'LW':
           case 'LC':
           case 'LJ':
           case 'ML':
           case 'D':
           case 'RI':
           case 'FL':
           case 'CA':
           case 'ca':
             gStateObj.push([key, value]);
             break;
           case 'Font':
-            promise = promise.then(function () {
-              return self.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) {
+            promise = promise.then(() => {
+              return this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) {
                 operatorList.addDependency(loadedName);
                 gStateObj.push([key, [loadedName, value[1]]]);
               });
             });
             break;
           case 'BM':
             gStateObj.push([key, normalizeBlendMode(value)]);
             break;
           case 'SMask':
             if (isName(value, 'None')) {
               gStateObj.push([key, false]);
               break;
             }
             if (isDict(value)) {
-              promise = promise.then(function (dict) {
-                return self.handleSMask(dict, resources, operatorList, task, stateManager);
-              }.bind(this, value));
+              promise = promise.then(() => {
+                return this.handleSMask(value, resources, operatorList, task, stateManager);
+              });
               gStateObj.push([key, true]);
             } else {
               warn('Unsupported SMask type');
             }
             break;
           case 'OP':
           case 'op':
           case 'OPM':
@@ -16960,25 +16972,24 @@ var PartialEvaluator = function PartialE
       font.loadedName = 'g_' + this.pdfManager.docId + '_f' + fontID;
       font.translated = fontCapability.promise;
       var translatedPromise;
       try {
         translatedPromise = this.translateFont(preEvaluatedFont);
       } catch (e) {
         translatedPromise = Promise.reject(e);
       }
-      var self = this;
       translatedPromise.then(function (translatedFont) {
         if (translatedFont.fontType !== undefined) {
           var xrefFontStats = xref.stats.fontTypes;
           xrefFontStats[translatedFont.fontType] = true;
         }
         fontCapability.resolve(new TranslatedFont(font.loadedName, translatedFont, font));
-      }, function (reason) {
-        self.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font });
+      }).catch(reason => {
+        this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font });
         try {
           var descriptor = preEvaluatedFont.descriptor;
           var fontFile3 = descriptor && descriptor.get('FontFile3');
           var subtype = fontFile3 && fontFile3.get('Subtype');
           var fontType = getFontType(preEvaluatedFont.type, subtype && subtype.name);
           var xrefFontStats = xref.stats.fontTypes;
           xrefFontStats[fontType] = true;
         } catch (ex) {}
@@ -17015,25 +17026,26 @@ var PartialEvaluator = function PartialE
           operatorList.addOp(fn, pattern.getIR());
           return Promise.resolve();
         }
         return Promise.reject(new Error('Unknown PatternType: ' + typeNum));
       }
       operatorList.addOp(fn, args);
       return Promise.resolve();
     },
-    getOperatorList: function PartialEvaluator_getOperatorList(stream, task, resources, operatorList, initialState) {
+    getOperatorList({ stream, task, resources, operatorList, initialState = null }) {
+      resources = resources || Dict.empty;
+      initialState = initialState || new EvalState();
+      assert(operatorList, 'getOperatorList: missing "operatorList" parameter');
       var self = this;
       var xref = this.xref;
       var imageCache = Object.create(null);
-      assert(operatorList);
-      resources = resources || Dict.empty;
       var xobjs = resources.get('XObject') || Dict.empty;
       var patterns = resources.get('Pattern') || Dict.empty;
-      var stateManager = new StateManager(initialState || new EvalState());
+      var stateManager = new StateManager(initialState);
       var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
       var timeSlotManager = new TimeSlotManager();
       function closePendingRestoreOPS(argument) {
         for (var i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {
           operatorList.addOp(OPS.restore, []);
         }
       }
       return new Promise(function promiseBody(resolve, reject) {
@@ -17268,27 +17280,28 @@ var PartialEvaluator = function PartialE
           operatorList.addOp(fn, args);
         }
         if (stop) {
           next(deferred);
           return;
         }
         closePendingRestoreOPS();
         resolve();
-      }).catch(function (reason) {
+      }).catch(reason => {
         if (this.options.ignoreErrors) {
           this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.unknown });
           warn('getOperatorList - ignoring errors during task: ' + task.name);
           closePendingRestoreOPS();
           return;
         }
         throw reason;
-      }.bind(this));
-    },
-    getTextContent: function PartialEvaluator_getTextContent(stream, task, resources, stateManager, normalizeWhitespace, combineTextItems) {
+      });
+    },
+    getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false }) {
+      resources = resources || Dict.empty;
       stateManager = stateManager || new StateManager(new TextState());
       var WhitespaceRegexp = /\s/g;
       var textContent = {
         items: [],
         styles: Object.create(null)
       };
       var textContentItem = {
         initialized: false,
@@ -17307,17 +17320,16 @@ var PartialEvaluator = function PartialE
         transform: null,
         fontName: null
       };
       var SPACE_FACTOR = 0.3;
       var MULTI_SPACE_FACTOR = 1.5;
       var MULTI_SPACE_FACTOR_MAX = 4;
       var self = this;
       var xref = this.xref;
-      resources = xref.fetchIfRef(resources) || Dict.empty;
       var xobjs = null;
       var xobjsCache = Object.create(null);
       var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
       var textState;
       function ensureTextContentItem() {
         if (textContentItem.initialized) {
           return textContentItem;
         }
@@ -17655,17 +17667,24 @@ var PartialEvaluator = function PartialE
                 break;
               }
               var currentState = stateManager.state.clone();
               var xObjStateManager = new StateManager(currentState);
               var matrix = xobj.dict.getArray('Matrix');
               if (isArray(matrix) && matrix.length === 6) {
                 xObjStateManager.transform(matrix);
               }
-              next(self.getTextContent(xobj, task, xobj.dict.get('Resources') || resources, xObjStateManager, normalizeWhitespace, combineTextItems).then(function (formTextContent) {
+              next(self.getTextContent({
+                stream: xobj,
+                task,
+                resources: xobj.dict.get('Resources') || resources,
+                stateManager: xObjStateManager,
+                normalizeWhitespace,
+                combineTextItems
+              }).then(function (formTextContent) {
                 Util.appendToArray(textContent.items, formTextContent.items);
                 Util.extendObj(textContent.styles, formTextContent.styles);
                 xobjsCache.key = name;
                 xobjsCache.texts = formTextContent;
               }));
               return;
             case OPS.setGState:
               flushTextContentItem();
@@ -17689,24 +17708,24 @@ var PartialEvaluator = function PartialE
           }
         }
         if (stop) {
           next(deferred);
           return;
         }
         flushTextContentItem();
         resolve(textContent);
-      }).catch(function (reason) {
+      }).catch(reason => {
         if (this.options.ignoreErrors) {
           warn('getTextContent - ignoring errors during task: ' + task.name);
           flushTextContentItem();
           return textContent;
         }
         throw reason;
-      }.bind(this));
+      });
     },
     extractDataStructures: function PartialEvaluator_extractDataStructures(dict, baseDict, properties) {
       var xref = this.xref;
       var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
       var toUnicodePromise = toUnicode ? this.readToUnicode(toUnicode) : Promise.resolve(undefined);
       if (properties.composite) {
         var cidSystemInfo = dict.get('CIDSystemInfo');
         if (isDict(cidSystemInfo)) {
@@ -17772,20 +17791,20 @@ var PartialEvaluator = function PartialE
           }
         }
         properties.defaultEncoding = encoding;
       }
       properties.differences = differences;
       properties.baseEncodingName = baseEncodingName;
       properties.hasEncoding = !!baseEncodingName || differences.length > 0;
       properties.dict = dict;
-      return toUnicodePromise.then(function (toUnicode) {
+      return toUnicodePromise.then(toUnicode => {
         properties.toUnicode = toUnicode;
         return this.buildToUnicode(properties);
-      }.bind(this)).then(function (toUnicode) {
+      }).then(function (toUnicode) {
         properties.toUnicode = toUnicode;
         return properties;
       });
     },
     buildToUnicode: function PartialEvaluator_buildToUnicode(properties) {
       properties.hasIncludedToUnicodeMap = !!properties.toUnicode && properties.toUnicode.length > 0;
       if (properties.hasIncludedToUnicodeMap) {
         return Promise.resolve(properties.toUnicode);
@@ -18155,20 +18174,20 @@ var PartialEvaluator = function PartialE
             type,
             name: baseFontName,
             widths: metrics.widths,
             defaultWidth: metrics.defaultWidth,
             flags,
             firstChar: 0,
             lastChar: maxCharIndex
           };
-          return this.extractDataStructures(dict, dict, properties).then(function (properties) {
+          return this.extractDataStructures(dict, dict, properties).then(properties => {
             properties.widths = this.buildCharCodeToWidth(metrics.widths, properties);
             return new Font(baseFontName, null, properties);
-          }.bind(this));
+          });
         }
       }
       var firstChar = dict.get('FirstChar') || 0;
       var lastChar = dict.get('LastChar') || maxCharIndex;
       var fontName = descriptor.get('FontName');
       var baseFont = dict.get('BaseFont');
       if (isString(fontName)) {
         fontName = Name.get(fontName);
@@ -18236,25 +18255,25 @@ var PartialEvaluator = function PartialE
           useCMap: null
         }).then(function (cMap) {
           properties.cMap = cMap;
           properties.vertical = properties.cMap.vertical;
         });
       } else {
         cMapPromise = Promise.resolve(undefined);
       }
-      return cMapPromise.then(function () {
+      return cMapPromise.then(() => {
         return this.extractDataStructures(dict, baseDict, properties);
-      }.bind(this)).then(function (properties) {
+      }).then(properties => {
         this.extractWidths(dict, descriptor, properties);
         if (type === 'Type3') {
           properties.isType3Font = true;
         }
         return new Font(fontName.name, fontFile, properties);
-      }.bind(this));
+      });
     }
   };
   return PartialEvaluator;
 }();
 var TranslatedFont = function TranslatedFontClosure() {
   function TranslatedFont(loadedName, font, dict) {
     this.loadedName = loadedName;
     this.font = font;
@@ -18281,28 +18300,34 @@ var TranslatedFont = function Translated
       var type3Evaluator = evaluator.clone(type3Options);
       var translatedFont = this.font;
       var loadCharProcsPromise = Promise.resolve();
       var charProcs = this.dict.get('CharProcs');
       var fontResources = this.dict.get('Resources') || resources;
       var charProcKeys = charProcs.getKeys();
       var charProcOperatorList = Object.create(null);
       for (var i = 0, n = charProcKeys.length; i < n; ++i) {
-        loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
+        let key = charProcKeys[i];
+        loadCharProcsPromise = loadCharProcsPromise.then(function () {
           var glyphStream = charProcs.get(key);
           var operatorList = new OperatorList();
-          return type3Evaluator.getOperatorList(glyphStream, task, fontResources, operatorList).then(function () {
+          return type3Evaluator.getOperatorList({
+            stream: glyphStream,
+            task,
+            resources: fontResources,
+            operatorList
+          }).then(function () {
             charProcOperatorList[key] = operatorList.getIR();
             parentOperatorList.addDependencies(operatorList.dependencies);
-          }, function (reason) {
-            warn('Type3 font resource \"' + key + '\" is not available');
+          }).catch(function (reason) {
+            warn(`Type3 font resource "${key}" is not available.`);
             var operatorList = new OperatorList();
             charProcOperatorList[key] = operatorList.getIR();
           });
-        }.bind(this, charProcKeys[i]));
+        });
       }
       this.type3Loaded = loadCharProcsPromise.then(function () {
         translatedFont.charProcOperatorList = charProcOperatorList;
       });
       return this.type3Loaded;
     }
   };
   return TranslatedFont;
@@ -21599,32 +21624,30 @@ var Catalog = function CatalogClosure() 
       return shadow(this, 'javaScript', javaScript);
     },
     cleanup: function Catalog_cleanup() {
       this.pageKidsCountCache.clear();
       var promises = [];
       this.fontCache.forEach(function (promise) {
         promises.push(promise);
       });
-      return Promise.all(promises).then(function (translatedFonts) {
+      return Promise.all(promises).then(translatedFonts => {
         for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
           var font = translatedFonts[i].dict;
           delete font.translated;
         }
         this.fontCache.clear();
         this.builtInCMapCache = Object.create(null);
-      }.bind(this));
+      });
     },
     getPage: function Catalog_getPage(pageIndex) {
       if (!(pageIndex in this.pagePromises)) {
-        this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(function (a) {
-          var dict = a[0];
-          var ref = a[1];
+        this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(([dict, ref]) => {
           return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache);
-        }.bind(this));
+        });
       }
       return this.pagePromises[pageIndex];
     },
     getPageDict: function Catalog_getPageDict(pageIndex) {
       var capability = createPromiseCapability();
       var nodesToVisit = [this.catDict.getRaw('Pages')];
       var count,
           currentPageIndex = 0;
@@ -22659,26 +22682,26 @@ var ObjectLoader = function () {
           }
           if (foundMissingData) {
             nodesToRevisit.push(currentNode);
           }
         }
         addChildren(currentNode, nodesToVisit);
       }
       if (pendingRequests.length) {
-        this.xref.stream.manager.requestRanges(pendingRequests).then(function pendingRequestCallback() {
+        this.xref.stream.manager.requestRanges(pendingRequests).then(() => {
           nodesToVisit = nodesToRevisit;
           for (var i = 0; i < nodesToRevisit.length; i++) {
             var node = nodesToRevisit[i];
             if (isRef(node)) {
               this.refSet.remove(node);
             }
           }
           this._walk(nodesToVisit);
-        }.bind(this), this.capability.reject);
+        }, this.capability.reject);
         return;
       }
       this.refSet = null;
       this.capability.resolve();
     }
   };
   return ObjectLoader;
 }();
@@ -23591,17 +23614,17 @@ var PDFWorkerStream = function PDFWorker
   };
   return PDFWorkerStream;
 }();
 var PDFNetworkStream;
 function setPDFNetworkStreamClass(cls) {
   PDFNetworkStream = cls;
 }
 var WorkerMessageHandler = {
-  setup: function wphSetup(handler, port) {
+  setup(handler, port) {
     var testMessageProcessed = false;
     handler.on('test', function wphSetupTest(data) {
       if (testMessageProcessed) {
         return;
       }
       testMessageProcessed = true;
       if (!(data instanceof Uint8Array)) {
         handler.send('test', 'main', false);
@@ -23627,17 +23650,17 @@ var WorkerMessageHandler = {
     });
     handler.on('configure', function wphConfigure(data) {
       setVerbosityLevel(data.verbosity);
     });
     handler.on('GetDocRequest', function wphSetupDoc(data) {
       return WorkerMessageHandler.createDocumentHandler(data, port);
     });
   },
-  createDocumentHandler: function wphCreateDocumentHandler(docParams, port) {
+  createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new MessageHandler(workerHandlerName, docId, port);
@@ -23908,17 +23931,22 @@ var WorkerMessageHandler = {
     });
     handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
       var pageIndex = data.pageIndex;
       pdfManager.getPage(pageIndex).then(function (page) {
         var task = new WorkerTask('RenderPageRequest: page ' + pageIndex);
         startWorkerTask(task);
         var pageNum = pageIndex + 1;
         var start = Date.now();
-        page.getOperatorList(handler, task, data.intent, data.renderInteractiveForms).then(function (operatorList) {
+        page.getOperatorList({
+          handler,
+          task,
+          intent: data.intent,
+          renderInteractiveForms: data.renderInteractiveForms
+        }).then(function (operatorList) {
           finishWorkerTask(task);
           info('page=' + pageNum + ' - getOperatorList: time=' + (Date.now() - start) + 'ms, len=' + operatorList.totalLength);
         }, function (e) {
           finishWorkerTask(task);
           if (task.terminated) {
             return;
           }
           handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.unknown });
@@ -23950,17 +23978,22 @@ var WorkerMessageHandler = {
     }, this);
     handler.on('GetTextContent', function wphExtractText(data) {
       var pageIndex = data.pageIndex;
       return pdfManager.getPage(pageIndex).then(function (page) {
         var task = new WorkerTask('GetTextContent: page ' + pageIndex);
         startWorkerTask(task);
         var pageNum = pageIndex + 1;
         var start = Date.now();
-        return page.extractTextContent(handler, task, data.normalizeWhitespace, data.combineTextItems).then(function (textContent) {
+        return page.extractTextContent({
+          handler,
+          task,
+          normalizeWhitespace: data.normalizeWhitespace,
+          combineTextItems: data.combineTextItems
+        }).then(function (textContent) {
           finishWorkerTask(task);
           info('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms');
           return textContent;
         }, function (reason) {
           finishWorkerTask(task);
           if (task.terminated) {
             return;
           }
@@ -23990,25 +24023,28 @@ var WorkerMessageHandler = {
         handler = null;
       });
     });
     handler.on('Ready', function wphReady(data) {
       setupDoc(docParams);
       docParams = null;
     });
     return workerHandlerName;
+  },
+  initializeFromPort(port) {
+    var handler = new MessageHandler('worker', 'main', port);
+    WorkerMessageHandler.setup(handler, port);
+    handler.send('ready', null);
   }
 };
-function initializeWorker() {
-  var handler = new MessageHandler('worker', 'main', self);
-  WorkerMessageHandler.setup(handler, self);
-  handler.send('ready', null);
-}
-if (typeof window === 'undefined' && !isNodeJS()) {
-  initializeWorker();
+function isMessagePort(maybePort) {
+  return typeof maybePort.postMessage === 'function' && 'onmessage' in maybePort;
+}
+if (typeof window === 'undefined' && !isNodeJS() && typeof self !== 'undefined' && isMessagePort(self)) {
+  WorkerMessageHandler.initializeFromPort(self);
 }
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
 exports.WorkerTask = WorkerTask;
 exports.WorkerMessageHandler = WorkerMessageHandler;
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
@@ -24265,46 +24301,47 @@ var Annotation = function AnnotationClos
       if (!dict.has('C')) {
         this.data.color = null;
       }
       this.data.hasPopup = dict.has('Popup');
       this.data.title = stringToPDFString(dict.get('T') || '');
       this.data.contents = stringToPDFString(dict.get('Contents') || '');
     },
     loadResources: function Annotation_loadResources(keys) {
-      return new Promise(function (resolve, reject) {
-        this.appearance.dict.getAsync('Resources').then(function (resources) {
-          if (!resources) {
-            resolve();
-            return;
-          }
-          var objectLoader = new ObjectLoader(resources.map, keys, resources.xref);
-          objectLoader.load().then(function () {
-            resolve(resources);
-          }, reject);
-        }, reject);
-      }.bind(this));
+      return this.appearance.dict.getAsync('Resources').then(resources => {
+        if (!resources) {
+          return;
+        }
+        var objectLoader = new ObjectLoader(resources.map, keys, resources.xref);
+        return objectLoader.load().then(function () {
+          return resources;
+        });
+      });
     },
     getOperatorList: function Annotation_getOperatorList(evaluator, task, renderForms) {
       if (!this.appearance) {
         return Promise.resolve(new OperatorList());
       }
       var data = this.data;
       var appearanceDict = this.appearance.dict;
       var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']);
       var bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1];
       var matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0];
       var transform = getTransformMatrix(data.rect, bbox, matrix);
-      var self = this;
-      return resourcesPromise.then(function (resources) {
+      return resourcesPromise.then(resources => {
         var opList = new OperatorList();
         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
-        return evaluator.getOperatorList(self.appearance, task, resources, opList).then(function () {
+        return evaluator.getOperatorList({
+          stream: this.appearance,
+          task,
+          resources,
+          operatorList: opList
+        }).then(() => {
           opList.addOp(OPS.endAnnotation, []);
-          self.appearance.reset();
+          this.appearance.reset();
           return opList;
         });
       });
     }
   };
   return Annotation;
 }();
 var AnnotationBorderStyle = function AnnotationBorderStyleClosure() {
@@ -24459,17 +24496,22 @@ var TextWidgetAnnotation = function Text
       }
       if (this.appearance) {
         return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms);
       }
       if (!this.data.defaultAppearance) {
         return Promise.resolve(operatorList);
       }
       var stream = new Stream(stringToBytes(this.data.defaultAppearance));
-      return evaluator.getOperatorList(stream, task, this.fieldResources, operatorList).then(function () {
+      return evaluator.getOperatorList({
+        stream,
+        task,
+        resources: this.fieldResources,
+        operatorList
+      }).then(function () {
         return operatorList;
       });
     }
   });
   return TextWidgetAnnotation;
 }();
 var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() {
   function ButtonWidgetAnnotation(params) {
@@ -25805,44 +25847,53 @@ var Page = function PageClosure() {
         stream = new NullStream();
       }
       return stream;
     },
     loadResources: function Page_loadResources(keys) {
       if (!this.resourcesPromise) {
         this.resourcesPromise = this.pdfManager.ensure(this, 'resources');
       }
-      return this.resourcesPromise.then(function resourceSuccess() {
+      return this.resourcesPromise.then(() => {
         var objectLoader = new ObjectLoader(this.resources.map, keys, this.xref);
         return objectLoader.load();
-      }.bind(this));
-    },
-    getOperatorList: function Page_getOperatorList(handler, task, intent, renderInteractiveForms) {
-      var self = this;
-      var pdfManager = this.pdfManager;
-      var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []);
+      });
+    },
+    getOperatorList({ handler, task, intent, renderInteractiveForms }) {
+      var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream');
       var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']);
-      var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.builtInCMapCache, this.evaluatorOptions);
+      var partialEvaluator = new PartialEvaluator({
+        pdfManager: this.pdfManager,
+        xref: this.xref,
+        handler,
+        pageIndex: this.pageIndex,
+        idFactory: this.idFactory,
+        fontCache: this.fontCache,
+        builtInCMapCache: this.builtInCMapCache,
+        options: this.evaluatorOptions
+      });
       var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
-      var pageListPromise = dataPromises.then(function (data) {
-        var contentStream = data[0];
-        var opList = new OperatorList(intent, handler, self.pageIndex);
+      var pageListPromise = dataPromises.then(([contentStream]) => {
+        var opList = new OperatorList(intent, handler, this.pageIndex);
         handler.send('StartRenderPage', {
-          transparency: partialEvaluator.hasBlendModes(self.resources),
-          pageIndex: self.pageIndex,
+          transparency: partialEvaluator.hasBlendModes(this.resources),
+          pageIndex: this.pageIndex,
           intent
         });
-        return partialEvaluator.getOperatorList(contentStream, task, self.resources, opList).then(function () {
+        return partialEvaluator.getOperatorList({
+          stream: contentStream,
+          task,
+          resources: this.resources,
+          operatorList: opList
+        }).then(function () {
           return opList;
         });
       });
-      var annotationsPromise = pdfManager.ensure(this, 'annotations');
-      return Promise.all([pageListPromise, annotationsPromise]).then(function (datas) {
-        var pageOpList = datas[0];
-        var annotations = datas[1];
+      var annotationsPromise = this.pdfManager.ensure(this, 'annotations');
+      return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) {
         if (annotations.length === 0) {
           pageOpList.flush(true);
           return pageOpList;
         }
         var i,
             ii,
             opListPromises = [];
         for (i = 0, ii = annotations.length; i < ii; i++) {
@@ -25856,26 +25907,38 @@ var Page = function PageClosure() {
             pageOpList.addOpList(opLists[i]);
           }
           pageOpList.addOp(OPS.endAnnotations, []);
           pageOpList.flush(true);
           return pageOpList;
         });
       });
     },
-    extractTextContent: function Page_extractTextContent(handler, task, normalizeWhitespace, combineTextItems) {
-      var self = this;
-      var pdfManager = this.pdfManager;
-      var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []);
+    extractTextContent({ handler, task, normalizeWhitespace, combineTextItems }) {
+      var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream');
       var resourcesPromise = this.loadResources(['ExtGState', 'XObject', 'Font']);
       var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
-      return dataPromises.then(function (data) {
-        var contentStream = data[0];
-        var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions);
-        return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems);
+      return dataPromises.then(([contentStream]) => {
+        var partialEvaluator = new PartialEvaluator({
+          pdfManager: this.pdfManager,
+          xref: this.xref,
+          handler,
+          pageIndex: this.pageIndex,
+          idFactory: this.idFactory,
+          fontCache: this.fontCache,
+          builtInCMapCache: this.builtInCMapCache,
+          options: this.evaluatorOptions
+        });
+        return partialEvaluator.getTextContent({
+          stream: contentStream,
+          task,
+          resources: this.resources,
+          normalizeWhitespace,
+          combineTextItems
+        });
       });
     },
     getAnnotationsData: function Page_getAnnotationsData(intent) {
       var annotations = this.annotations;
       var annotationsData = [];
       for (var i = 0, n = annotations.length; i < n; ++i) {
         if (!intent || isAnnotationRenderable(annotations[i], intent)) {
           annotationsData.push(annotations[i].data);
@@ -27288,26 +27351,26 @@ var Font = function FontClosure() {
         if (/Wingdings/i.test(name)) {
           warn('Non-embedded Wingdings font, falling back to ZapfDingbats.');
         }
         this.toFontChar = buildToFontChar(ZapfDingbatsEncoding, getDingbatsGlyphsUnicode(), properties.differences);
       } else if (isStandardFont) {
         this.toFontChar = buildToFontChar(properties.defaultEncoding, getGlyphsUnicode(), properties.differences);
       } else {
         glyphsUnicodeMap = getGlyphsUnicode();
-        this.toUnicode.forEach(function (charCode, unicodeCharCode) {
+        this.toUnicode.forEach((charCode, unicodeCharCode) => {
           if (!this.composite) {
             glyphName = properties.differences[charCode] || properties.defaultEncoding[charCode];
             unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
             if (unicode !== -1) {
               unicodeCharCode = unicode;
             }
           }
           this.toFontChar[charCode] = unicodeCharCode;
-        }.bind(this));
+        });
       }
       this.loadedName = fontName.split('-')[0];
       this.loading = false;
       this.fontType = getFontType(type, subtype);
       return;
     }
     if (subtype === 'Type1C') {
       if (type !== 'Type1' && type !== 'MMType1') {
@@ -36558,18 +36621,18 @@ exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.290';
-var pdfjsBuild = '60c232bc';
+var pdfjsVersion = '1.8.314';
+var pdfjsBuild = '3adda80f';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 36 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
--- a/browser/extensions/pocket/content/AboutPocket.jsm
+++ b/browser/extensions/pocket/content/AboutPocket.jsm
@@ -35,16 +35,17 @@ AboutPage.prototype = {
     return this.uriFlags;
   },
 
   newChannel(aURI, aLoadInfo) {
     let newURI = Services.io.newURI(this.chromeURL);
     let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
                                                             aLoadInfo);
     channel.originalURI = aURI;
+    aLoadInfo.resultPrincipalURI = aURI;
 
     if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
       let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(aURI);
       channel.owner = principal;
     }
     return channel;
   },
 
--- a/build/moz.configure/android-ndk.configure
+++ b/build/moz.configure/android-ndk.configure
@@ -9,17 +9,22 @@ js_option('--with-android-ndk', nargs=1,
           help='location where the Android NDK can be found')
 
 js_option('--with-android-toolchain', nargs=1,
           help='location of the Android toolchain')
 
 js_option('--with-android-gnu-compiler-version', nargs=1,
           help='GNU compiler version to use')
 
-min_android_version = dependable(lambda: '9')
+@depends(target)
+def min_android_version(target):
+    if target.cpu in ['aarch64', 'x86_64', 'mips64']:
+        # 64-bit support was added in API 21.
+        return '21'
+    return '9'
 
 js_option('--with-android-version',
           nargs=1,
           help='android platform version',
           default=min_android_version)
 
 @depends('--with-android-version', min_android_version)
 @imports(_from='__builtin__', _import='ValueError')
@@ -47,16 +52,57 @@ def ndk(value, build_project):
         die('You must specify --with-android-ndk=/path/to/ndk when '
             'building mobile/android')
     if value:
         return value[0]
 
 set_config('ANDROID_NDK', ndk)
 add_old_configure_assignment('android_ndk', ndk)
 
+@depends(ndk)
+@checking('for android ndk version')
+@imports(_from='__builtin__', _import='open')
+def ndk_version(ndk):
+    if not ndk:
+        # Building 'js/src' for non-Android.
+        return
+    with open(os.path.join(ndk, 'source.properties'), 'r') as f:
+        for line in f:
+            if not line.startswith('Pkg.Revision'):
+                continue
+            (_, version) = line.split('=')
+            if version:
+                return version.strip()
+            die('Unexpected Pkg.Revision line in source.properties')
+    die('Cannot determine NDK version from source.properties')
+
+@depends(ndk_version)
+def ndk_major_version(ndk_version):
+    if not ndk_version:
+        # Building 'js/src' for non-Android.
+        return
+    (major, minor, revision) = ndk_version.split('.')
+    if major:
+        return major
+    die('Unexpected NDK version string: ' + ndk_version)
+
+set_config('ANDROID_NDK_MAJOR_VERSION', ndk_major_version);
+
+@depends(ndk_version)
+def ndk_minor_version(ndk_version):
+    if not ndk_version:
+        # Building 'js/src' for non-Android.
+        return
+    (major, minor, revision) = ndk_version.split('.')
+    if minor:
+        return minor
+    die('Unexpected NDK version string: ' + ndk_version)
+
+set_config('ANDROID_NDK_MINOR_VERSION', ndk_minor_version);
+
 @depends(target, android_version, ndk)
 @checking('for android platform directory')
 @imports(_from='os.path', _import='isdir')
 def android_platform(target, android_version, ndk):
     if target.os != 'Android':
         return
 
     if 'mips' in target.cpu:
--- a/chrome/nsChromeProtocolHandler.cpp
+++ b/chrome/nsChromeProtocolHandler.cpp
@@ -163,22 +163,21 @@ nsChromeProtocolHandler::NewChannel2(nsI
             file->GetNativePath(path);
             printf("Chrome file doesn't exist: %s\n", path.get());
         }
     }
 #endif
 
     // Make sure that the channel remembers where it was
     // originally loaded from.
-    nsLoadFlags loadFlags = 0;
-    result->GetLoadFlags(&loadFlags);
-    result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
     rv = result->SetOriginalURI(aURI);
     if (NS_FAILED(rv)) return rv;
 
+    aLoadInfo->SetResultPrincipalURI(aURI);
+
     // Get a system principal for content files and set the owner
     // property of the result
     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
     nsAutoCString path;
     rv = url->GetPath(path);
     if (StringBeginsWith(path, NS_LITERAL_CSTRING("/content/")))
     {
         nsCOMPtr<nsIScriptSecurityManager> securityManager =
--- a/devtools/client/framework/about-devtools-toolbox.js
+++ b/devtools/client/framework/about-devtools-toolbox.js
@@ -21,16 +21,20 @@ AboutURL.prototype = {
   classID: components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",
 
   QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
 
   newChannel: function (aURI, aLoadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
+
+    // Must set the result principal URI _after_ we've created the channel
+    // since the chrome protocol would overwrite it with a chrome:// URL.
+    aLoadInfo.resultPrincipalURI = aURI;
     return chan;
   },
 
   getURIFlags: function (aURI) {
     return nsIAboutModule.ALLOW_SCRIPT || nsIAboutModule.ENABLE_INDEXED_DB;
   }
 };
 
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -91,16 +91,20 @@ Converter.prototype = {
     this.data = "";
     this.uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
 
     // Sets the charset if it is available. (For documents loaded from the
     // filesystem, this is not set.)
     this.charset =
       request.QueryInterface(Ci.nsIChannel).contentCharset || "UTF-8";
 
+    // Let "save as" save the original JSON, not the viewer
+    request.QueryInterface(Ci.nsIWritablePropertyBag);
+    request.setProperty("contentType", "application/json");
+
     this.channel = request;
     this.channel.contentType = "text/html";
     this.channel.contentCharset = "UTF-8";
     // Because content might still have a reference to this window,
     // force setting it to a null principal to avoid it being same-
     // origin with (other) content.
     this.channel.loadInfo.resetPrincipalToInheritToNullPrincipal();
 
--- a/devtools/client/netmonitor/src/components/request-list-column-cause.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-cause.js
@@ -12,43 +12,43 @@ const {
 
 const { div } = DOM;
 
 const RequestListColumnCause = createClass({
   displayName: "RequestListColumnCause",
 
   propTypes: {
     item: PropTypes.object.isRequired,
-    onCauseBadgeClick: PropTypes.func.isRequired,
+    onCauseBadgeMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.cause !== nextProps.item.cause;
   },
 
   render() {
     let {
       item: { cause },
-      onCauseBadgeClick,
+      onCauseBadgeMouseDown,
     } = this.props;
 
     let causeType = "unknown";
     let causeHasStack = false;
 
     if (cause) {
       // Legacy server might send a numeric value. Display it as "unknown"
       causeType = typeof cause.type === "string" ? cause.type : "unknown";
       causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
     }
 
     return (
       div({ className: "requests-list-column requests-list-cause", title: causeType },
         causeHasStack && div({
           className: "requests-list-cause-stack",
-          onClick: onCauseBadgeClick,
+          onMouseDown: onCauseBadgeMouseDown,
         }, "JS"),
         causeType
       )
     );
   }
 });
 
 module.exports = RequestListColumnCause;
--- a/devtools/client/netmonitor/src/components/request-list-column-domain.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-domain.js
@@ -21,25 +21,25 @@ const UPDATED_DOMAIN_PROPS = [
   "urlDetails",
 ];
 
 const RequestListColumnDomain = createClass({
   displayName: "RequestListColumnDomain",
 
   propTypes: {
     item: PropTypes.object.isRequired,
-    onSecurityIconClick: PropTypes.func.isRequired,
+    onSecurityIconMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
   },
 
   render() {
-    let { item, onSecurityIconClick } = this.props;
+    let { item, onSecurityIconMouseDown } = this.props;
     let { remoteAddress, remotePort, securityState,
       urlDetails: { host, isLocal } } = item;
     let iconClassList = ["requests-security-state-icon"];
     let iconTitle;
     let title = host + (remoteAddress ?
       ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
 
     if (isLocal) {
@@ -49,17 +49,17 @@ const RequestListColumnDomain = createCl
       iconClassList.push(`security-state-${securityState}`);
       iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
     }
 
     return (
       div({ className: "requests-list-column requests-list-domain", title },
         div({
           className: iconClassList.join(" "),
-          onMouseDown: onSecurityIconClick,
+          onMouseDown: onSecurityIconMouseDown,
           title: iconTitle,
         }),
         host,
       )
     );
   }
 });
 
--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -18,33 +18,38 @@ const UPDATED_FILE_PROPS = [
   "urlDetails",
 ];
 
 const RequestListColumnFile = createClass({
   displayName: "RequestListColumnFile",
 
   propTypes: {
     item: PropTypes.object.isRequired,
+    onThumbnailMouseDown: PropTypes.func.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
   },
 
   render() {
-    let { responseContentDataUri, urlDetails } = this.props.item;
+    let {
+      item: { responseContentDataUri, urlDetails },
+      onThumbnailMouseDown
+    } = this.props;
 
     return (
       div({
         className: "requests-list-column requests-list-file",
         title: urlDetails.unicodeUrl,
       },
         img({
           className: "requests-list-icon",
           src: responseContentDataUri,
+          onMouseDown: onThumbnailMouseDown,
         }),
         urlDetails.baseNameWithQuery
       )
     );
   }
 });
 
 module.exports = RequestListColumnFile;
--- a/devtools/client/netmonitor/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -35,20 +35,21 @@ const RequestListContent = createClass({
   displayName: "RequestListContent",
 
   propTypes: {
     columns: PropTypes.object.isRequired,
     dispatch: PropTypes.func.isRequired,
     displayedRequests: PropTypes.object.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
     fromCache: PropTypes.bool,
-    onCauseBadgeClick: PropTypes.func.isRequired,
+    onCauseBadgeMouseDown: PropTypes.func.isRequired,
     onItemMouseDown: PropTypes.func.isRequired,
-    onSecurityIconClick: PropTypes.func.isRequired,
+    onSecurityIconMouseDown: PropTypes.func.isRequired,
     onSelectDelta: PropTypes.func.isRequired,
+    onThumbnailMouseDown: PropTypes.func.isRequired,
     scale: PropTypes.number,
     selectedRequestId: PropTypes.string,
   },
 
   componentWillMount() {
     const { dispatch } = this.props;
     this.contextMenu = new RequestListContextMenu({
       cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
@@ -219,19 +220,20 @@ const RequestListContent = createClass({
     this.shouldScrollBottom = false;
   },
 
   render() {
     const {
       columns,
       displayedRequests,
       firstRequestStartedMillis,
-      onCauseBadgeClick,
+      onCauseBadgeMouseDown,
       onItemMouseDown,
-      onSecurityIconClick,
+      onSecurityIconMouseDown,
+      onThumbnailMouseDown,
       selectedRequestId,
     } = this.props;
 
     return (
       div({ className: "requests-list-wrapper"},
         div({ className: "requests-list-table"},
           div({
             ref: "contentEl",
@@ -245,18 +247,19 @@ const RequestListContent = createClass({
               columns,
               item,
               index,
               isSelected: item.id === selectedRequestId,
               key: item.id,
               onContextMenu: this.onContextMenu,
               onFocusedNodeChange: this.onFocusedNodeChange,
               onMouseDown: () => onItemMouseDown(item.id),
-              onCauseBadgeClick: () => onCauseBadgeClick(item.cause),
-              onSecurityIconClick: () => onSecurityIconClick(item.securityState),
+              onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
+              onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
+              onThumbnailMouseDown: () => onThumbnailMouseDown(),
             }))
           )
         )
       )
     );
   },
 });
 
@@ -268,26 +271,33 @@ module.exports = connect(
     selectedRequestId: state.requests.selectedId,
     scale: getWaterfallScale(state),
   }),
   (dispatch) => ({
     dispatch,
     /**
      * A handler that opens the stack trace tab when a stack trace is available
      */
-    onCauseBadgeClick: (cause) => {
+    onCauseBadgeMouseDown: (cause) => {
       if (cause.stacktrace && cause.stacktrace.length > 0) {
         dispatch(Actions.selectDetailsPanelTab("stack-trace"));
       }
     },
     onItemMouseDown: (id) => dispatch(Actions.selectRequest(id)),
     /**
      * A handler that opens the security tab in the details view if secure or
      * broken security indicator is clicked.
      */
-    onSecurityIconClick: (securityState) => {
+    onSecurityIconMouseDown: (securityState) => {
       if (securityState && securityState !== "insecure") {
         dispatch(Actions.selectDetailsPanelTab("security"));
       }
     },
     onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
+    /**
+     * A handler that opens the response tab in the details view if
+     * the thumbnail is clicked.
+     */
+    onThumbnailMouseDown: () => {
+      dispatch(Actions.selectDetailsPanelTab("response"));
+    },
   }),
 )(RequestListContent);
--- a/devtools/client/netmonitor/src/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -71,21 +71,22 @@ const RequestListItem = createClass({
 
   propTypes: {
     columns: PropTypes.object.isRequired,
     item: PropTypes.object.isRequired,
     index: PropTypes.number.isRequired,
     isSelected: PropTypes.bool.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
     fromCache: PropTypes.bool,
-    onCauseBadgeClick: PropTypes.func.isRequired,
+    onCauseBadgeMouseDown: PropTypes.func.isRequired,
     onContextMenu: PropTypes.func.isRequired,
     onFocusedNodeChange: PropTypes.func,
     onMouseDown: PropTypes.func.isRequired,
-    onSecurityIconClick: PropTypes.func.isRequired,
+    onSecurityIconMouseDown: PropTypes.func.isRequired,
+    onThumbnailMouseDown: PropTypes.func.isRequired,
     waterfallWidth: PropTypes.number,
   },
 
   componentDidMount() {
     if (this.props.isSelected) {
       this.refs.listItem.focus();
     }
   },
@@ -110,18 +111,19 @@ const RequestListItem = createClass({
       columns,
       item,
       index,
       isSelected,
       firstRequestStartedMillis,
       fromCache,
       onContextMenu,
       onMouseDown,
-      onCauseBadgeClick,
-      onSecurityIconClick,
+      onCauseBadgeMouseDown,
+      onSecurityIconMouseDown,
+      onThumbnailMouseDown,
     } = this.props;
 
     let classList = ["request-list-item", index % 2 ? "odd" : "even"];
     isSelected && classList.push("selected");
     fromCache && classList.push("fromCache");
 
     return (
       div({
@@ -129,22 +131,23 @@ const RequestListItem = createClass({
         className: classList.join(" "),
         "data-id": item.id,
         tabIndex: 0,
         onContextMenu,
         onMouseDown,
       },
         columns.get("status") && RequestListColumnStatus({ item }),
         columns.get("method") && RequestListColumnMethod({ item }),
-        columns.get("file") && RequestListColumnFile({ item }),
+        columns.get("file") && RequestListColumnFile({ item, onThumbnailMouseDown }),
         columns.get("protocol") && RequestListColumnProtocol({ item }),
         columns.get("scheme") && RequestListColumnScheme({ item }),
-        columns.get("domain") && RequestListColumnDomain({ item, onSecurityIconClick }),
+        columns.get("domain") && RequestListColumnDomain({ item,
+                                                           onSecurityIconMouseDown }),
         columns.get("remoteip") && RequestListColumnRemoteIP({ item }),
-        columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeClick }),
+        columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
         columns.get("type") && RequestListColumnType({ item }),
         columns.get("cookies") && RequestListColumnCookies({ item }),
         columns.get("setCookies") && RequestListColumnSetCookies({ item }),
         columns.get("transferred") && RequestListColumnTransferredSize({ item }),
         columns.get("contentSize") && RequestListColumnContentSize({ item }),
         columns.get("waterfall") &&
           RequestListColumnWaterfall({ item, firstRequestStartedMillis }),
       )
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
@@ -2,16 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Basic tests for exporting Network panel content into HAR format.
  */
 add_task(function* () {
+  // Disable tcp fast open, because it is setting a response header indicator
+  // (bug 1352274). TCP Fast Open is not present on all platforms therefore the
+  // number of response headers will vary depending on the platform.
+  Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
   info("Starting test... ");
 
   let { gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
     "devtools/client/netmonitor/src/request-list-context-menu");
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -154,13 +154,14 @@ skip-if = true # Bug 1258809
 [browser_net_simple-request.js]
 [browser_net_sort-01.js]
 [browser_net_sort-02.js]
 [browser_net_statistics-01.js]
 [browser_net_statistics-02.js]
 [browser_net_status-codes.js]
 [browser_net_streaming-response.js]
 [browser_net_throttle.js]
+[browser_net_thumbnail-click.js]
 [browser_net_timeline_ticks.js]
 skip-if = true # TODO: fix the test
 [browser_net_timing-division.js]
 [browser_net_truncate.js]
 [browser_net_persistent_logs.js]
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -3,16 +3,21 @@
 
 "use strict";
 
 /**
  * Tests if requests render correct information in the menu UI.
  */
 
 function test() {
+  // Disable tcp fast open, because it is setting a response header indicator
+  // (bug 1352274). TCP Fast Open is not present on all platforms therefore the
+  // number of response headers will vary depending on the platform.
+  Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
+
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   initNetMonitor(SIMPLE_SJS).then(({ tab, monitor }) => {
     info("Starting test... ");
 
     let { document, gStore, windowRequire } = monitor.panelWin;
     let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_thumbnail-click.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test that clicking on the file thumbnail opens the response details tab.
+ */
+
+add_task(function* () {
+  let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
+  let { document } = monitor.panelWin;
+
+  yield performRequestsAndWait();
+
+  let wait = waitForDOM(document, "#response-panel");
+
+  let request = document.querySelectorAll(".request-list-item")[5];
+  let icon = request.querySelector(".requests-list-icon");
+
+  info("Clicking thumbnail of the sixth request.");
+  EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin);
+
+  yield wait;
+
+  ok(document.querySelector("#response-tab[aria-selected=true]"),
+     "Response tab is selected.");
+  ok(document.querySelector(".response-image-box"),
+     "Response image preview is shown.");
+
+  yield teardown(monitor);
+
+  function* performRequestsAndWait() {
+    let onAllEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
+    yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+      content.wrappedJSObject.performRequests();
+    });
+    yield onAllEvents;
+  }
+});
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -169,39 +169,35 @@ nsAboutRedirector::NewChannel(nsIURI* aU
 
   for (int i = 0; i < kRedirTotal; i++) {
     if (!strcmp(path.get(), kRedirMap[i].id)) {
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
-      // channel which forces the channel owner to reflect the displayed
-      // URL rather then being the systemPrincipal.
+      // If tempURI links to an internal URI (chrome://, resource://, about:)
+      // then set the result principal URL on the channel's load info.
+      // Otherwise, we leave it null which forces the channel principal
+      // to reflect the displayed URL rather than being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
       bool isAboutBlank = NS_IsAboutBlank(tempURI);
 
-      nsLoadFlags loadFlags = isUIResource || isAboutBlank
-                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
-
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
-                                 aLoadInfo,
-                                 nullptr, // aLoadGroup
-                                 nullptr, // aCallbacks
-                                 loadFlags);
+                                 aLoadInfo);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      if (isUIResource || isAboutBlank) {
+        aLoadInfo->SetResultPrincipalURI(aURI);
+      }
       tempChannel->SetOriginalURI(aURI);
 
       tempChannel.forget(aResult);
       return rv;
     }
   }
 
   NS_ERROR("nsAboutRedirector called for unknown case");
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11168,16 +11168,22 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   // Make sure to give the caller a channel if we managed to create one
   // This is important for correct error page/session history interaction
   if (aRequest) {
     NS_ADDREF(*aRequest = channel);
   }
 
   if (aOriginalURI) {
     channel->SetOriginalURI(aOriginalURI);
+    // The LOAD_REPLACE flag and its handling here will be removed as part
+    // of bug 1319110.  For now preserve its restoration here to not break
+    // any code expecting it being set specially on redirected channels.
+    // If the flag has originally been set to change result of
+    // NS_GetFinalChannelURI it won't have any effect and also won't cause
+    // any harm.
     if (aLoadReplace) {
       uint32_t loadFlags;
       channel->GetLoadFlags(&loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
       channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
     }
   } else {
     channel->SetOriginalURI(aURI);
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -101,28 +101,27 @@ PostMessageEvent::Run()
       return NS_OK;
 
     // Note: This is contrary to the spec with respect to file: URLs, which
     //       the spec groups into a single origin, but given we intentionally
     //       don't do that in other places it seems better to hold the line for
     //       now.  Long-term, we want HTML5 to address this so that we can
     //       be compliant while being safer.
     if (!targetPrin->Equals(mProvidedPrincipal)) {
+      MOZ_DIAGNOSTIC_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringFPD(mProvidedPrincipal->OriginAttributesRef(),
+                                                                            targetPrin->OriginAttributesRef()),
+                            "Unexpected postMessage call to a window with mismatched "
+                            "origin attributes");
+
       nsAutoString providedOrigin, targetOrigin;
       nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      MOZ_DIAGNOSTIC_ASSERT(providedOrigin != targetOrigin ||
-                            (mProvidedPrincipal->OriginAttributesRef() ==
-                              targetPrin->OriginAttributesRef()),
-                            "Unexpected postMessage call to a window with mismatched "
-                            "origin attributes");
-
       const char16_t* params[] = { providedOrigin.get(), targetOrigin.get() };
 
       nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
         NS_LITERAL_CSTRING("DOM Window"), sourceDocument,
         nsContentUtils::eDOM_PROPERTIES,
         "TargetPrincipalDoesNotMatch",
         params, ArrayLength(params));
 
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -216,17 +216,17 @@ TabGroup::FindItemWithName(const nsAStri
       }
     }
   }
 
   return NS_OK;
 }
 
 nsTArray<nsPIDOMWindowOuter*>
-TabGroup::GetTopLevelWindows()
+TabGroup::GetTopLevelWindows() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsTArray<nsPIDOMWindowOuter*> array;
 
   for (nsPIDOMWindowOuter* outerWindow : mWindows) {
     if (outerWindow->GetDocShell() &&
         !outerWindow->GetScriptableParentOrNull()) {
       array.AppendElement(outerWindow);
@@ -258,10 +258,22 @@ TabGroup::AbstractMainThreadForImpl(Task
   // We'll just go directly to the main thread.
   if (this == sChromeTabGroup || NS_WARN_IF(mLastWindowLeft)) {
     return AbstractThread::MainThread();
   }
 
   return SchedulerGroup::AbstractMainThreadForImpl(aCategory);
 }
 
+bool
+TabGroup::IsBackground() const
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  for (nsPIDOMWindowOuter* outerWindow : GetTopLevelWindows()) {
+    if (!outerWindow->IsBackground()) {
+      return false;
+    }
+  }
+  return true;
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -109,23 +109,27 @@ public:
   // It is illegal to pass in the special case-insensitive names "_blank",
   // "_self", "_parent" or "_top", as those should be handled elsewhere.
   nsresult
   FindItemWithName(const nsAString& aName,
                    nsIDocShellTreeItem* aRequestor,
                    nsIDocShellTreeItem* aOriginalRequestor,
                    nsIDocShellTreeItem** aFoundItem);
 
-  nsTArray<nsPIDOMWindowOuter*> GetTopLevelWindows();
+  nsTArray<nsPIDOMWindowOuter*> GetTopLevelWindows() const;
   const nsTArray<nsPIDOMWindowOuter*>& GetWindows() { return mWindows; }
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
   nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
+  // Returns true if all of the TabGroup's top-level windows are in
+  // the background.
+  bool IsBackground() const override;
+
 private:
   virtual AbstractThread*
   AbstractMainThreadForImpl(TaskCategory aCategory) override;
 
   TabGroup* AsTabGroup() override { return this; }
 
   void EnsureThrottledEventQueues();
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4246,16 +4246,29 @@ nsDOMWindowUtils::RemoveManuallyManagedS
   if (state.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   element->RemoveManuallyManagedStates(state);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::GetStorageUsage(nsIDOMStorage* aStorage, int64_t* aRetval)
+{
+  RefPtr<Storage> storage = static_cast<Storage*>(aStorage);
+  if (!storage) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aRetval = storage->GetOriginQuotaUsage();
+
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsTranslationNodeList)
 NS_IMPL_RELEASE(nsTranslationNodeList)
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -658,16 +658,17 @@ GK_ATOM(norolluponanchor, "norolluponanc
 GK_ATOM(nobr, "nobr")
 GK_ATOM(node, "node")
 GK_ATOM(nodefaultsrc, "nodefaultsrc")
 GK_ATOM(nodeSet, "node-set")
 GK_ATOM(noembed, "noembed")
 GK_ATOM(noframes, "noframes")
 GK_ATOM(nohref, "nohref")
 GK_ATOM(noisolation, "noisolation")
+GK_ATOM(nomodule, "nomodule")
 GK_ATOM(nonce, "nonce")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
 GK_ATOM(normal, "normal")
 GK_ATOM(normalizeSpace, "normalize-space")
 GK_ATOM(noscript, "noscript")
 GK_ATOM(noshade, "noshade")
 GK_ATOM(novalidate, "novalidate")
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -1597,17 +1597,17 @@ nsScriptLoader::ProcessScriptElement(nsI
   if (!mEnabled || !mDocument->IsScriptEnabled()) {
     return false;
   }
 
   NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");
 
   nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
 
-  // Step 12. Check that the script is not an eventhandler
+  // Step 13. Check that the script is not an eventhandler
   if (IsScriptEventHandler(scriptContent)) {
     return false;
   }
 
   JSVersion version = JSVERSION_DEFAULT;
 
   // Check the type attribute to determine language and version.
   // If type exists, it trumps the deprecated 'language='
@@ -1631,17 +1631,28 @@ nsScriptLoader::ProcessScriptElement(nsI
       if (!language.IsEmpty()) {
         if (!nsContentUtils::IsJavaScriptLanguage(language)) {
           return false;
         }
       }
     }
   }
 
-  // Step 14. in the HTML5 spec
+  // "In modern user agents that support module scripts, the script element with
+  // the nomodule attribute will be ignored".
+  // "The nomodule attribute must not be specified on module scripts (and will
+  // be ignored if it is)."
+  if (ModuleScriptsEnabled() &&
+      scriptKind == nsScriptKind::Classic &&
+      scriptContent->IsHTMLElement() &&
+      scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) {
+    return false;
+  }
+
+  // Step 15. and later in the HTML5 spec
   nsresult rv = NS_OK;
   RefPtr<nsScriptLoadRequest> request;
   if (aElement->GetScriptExternal()) {
     // external script
     nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
     if (!scriptURI) {
       // Asynchronously report the failure to create a URI object
       NS_DispatchToCurrentThread(
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4261,27 +4261,28 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
   }
 
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   virtual void SetText(const char16_t* aText, int32_t aLength, nsBidiDirection aDirection)
   {
     mFontgrp->UpdateUserFonts(); // ensure user font generation is current
     // adjust flags for current direction run
-    uint32_t flags = mTextRunFlags;
+    gfx::ShapedTextFlags flags = mTextRunFlags;
     if (aDirection == NSBIDI_RTL) {
-      flags |= gfxTextRunFactory::TEXT_IS_RTL;
+      flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
     } else {
-      flags &= ~gfxTextRunFactory::TEXT_IS_RTL;
+      flags &= ~gfx::ShapedTextFlags::TEXT_IS_RTL;
     }
     mTextRun = mFontgrp->MakeTextRun(aText,
                                      aLength,
                                      mDrawTarget,
                                      mAppUnitsPerDevPixel,
                                      flags,
+                                     nsTextFrameUtils::Flags(),
                                      mMissingFonts);
   }
 
   virtual nscoord GetWidth()
   {
     gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(
         mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS
                               : gfxFont::LOOSE_INK_EXTENTS, mDrawTarget);
@@ -4484,17 +4485,17 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
 
   // operation (fill or stroke)
   CanvasRenderingContext2D::TextDrawOperation mOp;
 
   // union of bounding boxes of all runs, needed for shadows
   gfxRect mBoundingBox;
 
   // flags to use when creating textrun, based on CSS style
-  uint32_t mTextRunFlags;
+  gfx::ShapedTextFlags mTextRunFlags;
 
   // true iff the bounding box should be measured
   bool mDoMeasureBoundingBox;
 };
 
 nsresult
 CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
                                             float aX,
@@ -4572,21 +4573,22 @@ CanvasRenderingContext2D::DrawOrMeasureT
   if (!IsFinite(aX) || !IsFinite(aY)) {
     return NS_OK;
   }
 
   CanvasBidiProcessor processor;
 
   // If we don't have a style context, we can't set up vertical-text flags
   // (for now, at least; perhaps we need new Canvas API to control this).
-  processor.mTextRunFlags = canvasStyle ?
-    nsLayoutUtils::GetTextRunFlagsForStyle(canvasStyle,
-                                           canvasStyle->StyleFont(),
-                                           canvasStyle->StyleText(),
-                                           0) : 0;
+  processor.mTextRunFlags = canvasStyle
+    ? nsLayoutUtils::GetTextRunFlagsForStyle(canvasStyle,
+                                             canvasStyle->StyleFont(),
+                                             canvasStyle->StyleText(),
+                                             0)
+    : gfx::ShapedTextFlags();
 
   GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
   processor.mPt = gfxPoint(aX, aY);
   processor.mDrawTarget =
     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 
   // If we don't have a target then we don't have a transform. A target won't
   // be needed in the case where we're measuring the text size. This allows
@@ -4670,21 +4672,21 @@ CanvasRenderingContext2D::DrawOrMeasureT
     baselineAnchor = -fontMetrics.emDescent;
     break;
   default:
     MOZ_CRASH("GFX: unexpected TextBaseline");
   }
 
   // We can't query the textRun directly, as it may not have been created yet;
   // so instead we check the flags that will be used to initialize it.
-  uint16_t runOrientation =
-    (processor.mTextRunFlags & gfxTextRunFactory::TEXT_ORIENT_MASK);
-  if (runOrientation != gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL) {
-    if (runOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED ||
-        runOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) {
+  gfx::ShapedTextFlags runOrientation =
+    (processor.mTextRunFlags & gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
+  if (runOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL) {
+    if (runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED ||
+        runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) {
       // Adjust to account for mTextRun being shaped using center baseline
       // rather than alphabetic.
       baselineAnchor -= (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
     }
     processor.mPt.x -= baselineAnchor;
   } else {
     processor.mPt.y += baselineAnchor;
   }
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4965,17 +4965,16 @@ skip-if = (os == 'android' || os == 'lin
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__fs-color-type-mismatch-color-buffer-type.html]
 fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__instanced-arrays.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__instanced-rendering-bug.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__out-of-bounds-index-buffers-after-copying.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__rendering-sampling-feedback-loop.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__rgb-format-support.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__uniform-block-buffer-size.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -208,18 +208,16 @@ fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__misc__views-with-offsets.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__rendering__blitframebuffer-outside-readbuffer.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__rendering__clear-func-buffer-type-match.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__rendering__fs-color-type-mismatch-color-buffer-type.html]
 fail-if = (os == 'mac') || (os == 'win')
-[generated/test_2_conformance2__rendering__instanced-rendering-bug.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__textures__misc__copy-texture-image.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__textures__misc__integer-cubemap-specification-order-bug.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__textures__misc__tex-srgb-mipmap.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__textures__video__tex-2d-r11f_g11f_b10f-rgb-float.html]
 fail-if = (os == 'mac') || (os == 'win')
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -275,16 +275,18 @@ FileReader::DoAsyncWait()
   return NS_OK;
 }
 
 nsresult
 FileReader::DoReadData(uint64_t aCount)
 {
   MOZ_ASSERT(mAsyncStream);
 
+  uint32_t bytesRead = 0;
+
   if (mDataFormat == FILE_AS_BINARY) {
     //Continuously update our binary string as data comes in
     uint32_t oldLen = mResult.Length();
     MOZ_ASSERT(mResult.Length() == mDataLen, "unexpected mResult length");
     if (uint64_t(oldLen) + aCount > UINT32_MAX)
       return NS_ERROR_OUT_OF_MEMORY;
     char16_t *buf = nullptr;
     mResult.GetMutableData(&buf, oldLen + aCount, fallible);
@@ -296,53 +298,46 @@ FileReader::DoReadData(uint64_t aCount)
     // with a nsIAsyncInputStream, in content process, we need to wrap a
     // nsIBufferedInputStream around it.
     if (!mBufferedStream) {
       rv = NS_NewBufferedInputStream(getter_AddRefs(mBufferedStream),
                                      mAsyncStream, 8192);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    uint32_t bytesRead = 0;
     rv = mBufferedStream->ReadSegments(ReadFuncBinaryString, buf + oldLen,
                                        aCount, &bytesRead);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(bytesRead == aCount, "failed to read data");
+    mResult.Truncate(oldLen + bytesRead);
   }
   else {
     CheckedInt<uint64_t> size = mDataLen;
     size += aCount;
 
     //Update memory buffer to reflect the contents of the file
     if (!size.isValid() ||
         // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
         size.value() > UINT32_MAX ||
         size.value() > mTotal) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    if (mDataFormat != FILE_AS_ARRAYBUFFER) {
-      mFileData = (char *) realloc(mFileData, mDataLen + aCount);
-      NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
-    }
+    MOZ_DIAGNOSTIC_ASSERT(mFileData);
+    MOZ_RELEASE_ASSERT((mDataLen + aCount) <= mTotal);
 
-    uint32_t bytesRead = 0;
-    MOZ_DIAGNOSTIC_ASSERT(mFileData);
     nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-
-    MOZ_ASSERT(bytesRead == aCount, "failed to read data");
   }
 
-  mDataLen += aCount;
+  mDataLen += bytesRead;
   return NS_OK;
 }
 
 // Helper methods
 
 void
 FileReader::ReadFileContent(Blob& aBlob,
                             const nsAString &aCharset,
@@ -411,18 +406,25 @@ FileReader::ReadFileContent(Blob& aBlob,
 
   MOZ_ASSERT(mAsyncStream);
 
   mTotal = mBlob->GetSize(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  if (mDataFormat == FILE_AS_ARRAYBUFFER) {
-    mFileData = js_pod_malloc<char>(mTotal);
+  // Binary Format doesn't need a post-processing of the data. Everything is
+  // written directly into mResult.
+  if (mDataFormat != FILE_AS_BINARY) {
+    if (mDataFormat == FILE_AS_ARRAYBUFFER) {
+      mFileData = js_pod_malloc<char>(mTotal);
+    } else {
+      mFileData = (char *) malloc(mTotal);
+    }
+
     if (!mFileData) {
       NS_WARNING("Preallocation failed for ReadFileData");
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
   }
 
   aRv = DoAsyncWait();
--- a/dom/file/ipc/IPCBlobInputStreamChild.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp
@@ -162,16 +162,17 @@ IPCBlobInputStreamChild::StreamNeeded(IP
 
   RefPtr<StreamNeededRunnable> runnable = new StreamNeededRunnable(this);
   mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
 mozilla::ipc::IPCResult
 IPCBlobInputStreamChild::RecvStreamReady(const OptionalIPCStream& aStream)
 {
+  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(!mPendingOperations.IsEmpty());
 
   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
 
   RefPtr<StreamReadyRunnable> runnable =
     new StreamReadyRunnable(mPendingOperations[0].mStream, stream);
   mPendingOperations[0].mEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
--- a/dom/file/ipc/tests/browser_ipcBlob.js
+++ b/dom/file/ipc/tests/browser_ipcBlob.js
@@ -166,8 +166,41 @@ add_task(function* test_CtoPtoC_bc_small
     });
   });
 
   ok(status, "CtoPtoC-blobURL: Data match!");
 
   yield BrowserTestUtils.removeTab(tab1);
   yield BrowserTestUtils.removeTab(tab2);
 });
+
+// Multipart Blob childA-parent-childB.
+add_task(function* test_CtoPtoC_multipart() {
+  let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+  let browser1 = gBrowser.getBrowserForTab(tab1);
+
+  let blob = yield ContentTask.spawn(browser1, null, function() {
+    return new Blob(["!"]);
+  });
+
+  ok(blob, "CtoPtoC-,ultipart: We have a blob!");
+  is(blob.size, "!".length, "CtoPtoC-multipart: The size matches");
+
+  let newBlob = new Blob(["world", blob]);
+
+  let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+  let browser2 = gBrowser.getBrowserForTab(tab2);
+
+  let status = yield ContentTask.spawn(browser2, newBlob, function(blob) {
+    return new Promise(resolve => {
+      let fr = new content.FileReader();
+      fr.readAsText(new Blob(["hello ", blob]));
+      fr.onloadend = function() {
+        resolve(fr.result == "hello world!");
+      }
+    });
+  });
+
+  ok(status, "CtoPtoC-multipart: Data match!");
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.removeTab(tab2);
+});
--- a/dom/file/nsHostObjectProtocolHandler.cpp
+++ b/dom/file/nsHostObjectProtocolHandler.cpp
@@ -859,16 +859,18 @@ nsHostObjectProtocolHandler::NewChannel2
   }
 
   uint64_t size = blobImpl->GetSize(rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   channel->SetOriginalURI(uri);
+  aLoadInfo->SetResultPrincipalURI(uri);
+
   channel->SetContentType(NS_ConvertUTF16toUTF8(contentType));
   channel->SetContentLength(size);
 
   channel.forget(result);
 
   return NS_OK;
 }
 
--- a/dom/html/HTMLScriptElement.cpp
+++ b/dom/html/HTMLScriptElement.cpp
@@ -215,16 +215,28 @@ HTMLScriptElement::SetAsync(bool aValue)
 
 void
 HTMLScriptElement::SetAsync(bool aValue, ErrorResult& rv)
 {
   mForceAsync = false;
   SetHTMLBoolAttr(nsGkAtoms::async, aValue, rv);
 }
 
+bool
+HTMLScriptElement::NoModule()
+{
+  return GetBoolAttr(nsGkAtoms::nomodule);
+}
+
+void
+HTMLScriptElement::SetNoModule(bool aValue, ErrorResult& aRv)
+{
+  SetHTMLBoolAttr(nsGkAtoms::nomodule, aValue, aRv);
+}
+
 nsresult
 HTMLScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify)
 {
   if (nsGkAtoms::async == aName && kNameSpaceID_None == aNamespaceID) {
     mForceAsync = false;
   }
   return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -85,16 +85,18 @@ public:
     GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
   }
   void SetIntegrity(const nsAString& aIntegrity, ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv);
   }
   bool Async();
   void SetAsync(bool aValue, ErrorResult& rv);
+  bool NoModule();
+  void SetNoModule(bool aValue, ErrorResult& rv);
 
 protected:
   virtual ~HTMLScriptElement();
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
   // nsScriptElement
   virtual bool HasScriptContent() override;
 };
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2407,16 +2407,17 @@ nsHTMLDocument::CreateAndAddWyciwygChann
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsLoadFlags loadFlags = 0;
     channel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
     channel->SetLoadFlags(loadFlags);
 
     channel->SetOriginalURI(wcwgURI);
+    loadInfo->SetResultPrincipalURI(wcwgURI);
 
     rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
   }
 
   return rv;
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/html/test/file_script_module.html
@@ -0,0 +1,42 @@
+<html>
+<body>
+  <script>
+// Helper methods.
+function ok(a, msg) {
+  parent.postMessage({ check: !!a, msg }, "*")
+}
+
+function is(a, b, msg) {
+  ok(a === b, msg);
+}
+
+function finish() {
+  parent.postMessage({ done: true }, "*");
+}
+  </script>
+
+  <script id="a" nomodule>42</script>
+  <script id="b">42</script>
+  <script>
+// Let's test the behavior of nomodule attribute and noModule getter/setter.
+var a = document.getElementById("a");
+is(a.noModule, true, "HTMLScriptElement with nomodule attribute has noModule set to true");
+a.removeAttribute("nomodule");
+is(a.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false");
+a.noModule = true;
+ok(a.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute");
+
+var b = document.getElementById("b");
+is(b.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false");
+b.noModule = true;
+ok(b.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute");
+  </script>
+
+  <script>var foo = 42;</script>
+  <script nomodule>foo = 43;</script>
+  <script>
+is(foo, 42, "nomodule HTMLScriptElements should not be executed in modern browsers");
+finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/test/file_script_nomodule.html
@@ -0,0 +1,32 @@
+<html>
+<body>
+  <script>
+// Helper methods.
+function ok(a, msg) {
+  parent.postMessage({ check: !!a, msg }, "*")
+}
+
+function is(a, b, msg) {
+  ok(a === b, msg);
+}
+
+function finish() {
+  parent.postMessage({ done: true }, "*");
+}
+  </script>
+
+  <script id="a" nomodule>42</script>
+  <script>
+// Let's test the behavior of nomodule attribute and noModule getter/setter.
+var a = document.getElementById("a");
+ok(!("noModule" in a), "When modules are disabled HTMLScriptElement.noModule is not defined");
+  </script>
+
+  <script>var foo = 42;</script>
+  <script nomodule>foo = 43;</script>
+  <script>
+is(foo, 43, "nomodule attribute is ignored when modules are disabled");
+finish();
+  </script>
+</body>
+</html>
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -603,8 +603,12 @@ skip-if = (os == 'android' || os == 'mac
 [test_allowMedia.html]
 [test_bug1292522_same_domain_with_different_port_number.html]
 [test_bug1295719_event_sequence_for_arrow_keys.html]
 skip-if = os == "android" # up/down arrow keys not supported on android
 [test_bug1295719_event_sequence_for_number_keys.html]
 [test_bug1310865.html]
 [test_bug1315146.html]
 [test_fakepath.html]
+[test_script_module.html]
+support-files =
+  file_script_module.html
+  file_script_nomodule.html
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_script_module.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for HTMLScriptElement with nomodule attribute</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+  <script>
+onmessage = (e) => {
+  if ("done" in e.data) {
+    next();
+  } else if ("check" in e.data) {
+    ok(e.data.check, e.data.msg);
+  } else {
+    ok(false, "Unknown message");
+  }
+}
+
+var tests = [
+  function() {
+    SpecialPowers.pushPrefEnv({"set":[["dom.moduleScripts.enabled", true]]})
+    .then(() => {
+      var ifr = document.createElement('iframe');
+      ifr.src = "file_script_module.html";
+      document.body.appendChild(ifr);
+    });
+  },
+
+  function() {
+    SpecialPowers.pushPrefEnv({"set":[["dom.moduleScripts.enabled", false]]})
+    .then(() => {
+      var ifr = document.createElement('iframe');
+      ifr.src = "file_script_nomodule.html";
+      document.body.appendChild(ifr);
+    });
+  },
+];
+
+SimpleTest.waitForExplicitFinish();
+next();
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+  </script>
+
+</body>
+</html>
--- a/dom/imptests/failures/html/typedarrays/test_constructors.html.json
+++ b/dom/imptests/failures/html/typedarrays/test_constructors.html.json
@@ -4,144 +4,45 @@
   "Constructing interface Uint8ClampedArray with no arguments should throw.": true,
   "Constructing interface Int16Array with no arguments should throw.": true,
   "Constructing interface Uint16Array with no arguments should throw.": true,
   "Constructing interface Int32Array with no arguments should throw.": true,
   "Constructing interface Uint32Array with no arguments should throw.": true,
   "Constructing interface Float32Array with no arguments should throw.": true,
   "Constructing interface Float64Array with no arguments should throw.": true,
   "Constructing interface ArrayBuffer with no arguments should throw.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Int8Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Int8Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Int8Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Int8Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int8Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Int8Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Int8Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Int8Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int8Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Uint8Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Uint8Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Uint8Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Uint8Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint8Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Uint8Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint8Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Uint8Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint8Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Uint8ClampedArray.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Uint8ClampedArray.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Uint8ClampedArray.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Uint8ClampedArray.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint8ClampedArray.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Uint8ClampedArray.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint8ClampedArray.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument null (14) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument false (15) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument true (16) should be interpreted as 1 for interface Uint8ClampedArray.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint8ClampedArray.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Int16Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Int16Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Int16Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Int16Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int16Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Int16Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Int16Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Int16Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int16Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Uint16Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Uint16Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Uint16Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Uint16Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint16Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Uint16Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint16Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Uint16Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint16Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Int32Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Int32Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Int32Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Int32Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Int32Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Int32Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Int32Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Int32Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Int32Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Uint32Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Uint32Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Uint32Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Uint32Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Uint32Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Uint32Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Uint32Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Uint32Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Uint32Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Float32Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Float32Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Float32Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Float32Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Float32Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Float32Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Float32Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Float32Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Float32Array.": true,
-  "The argument NaN (0) should be interpreted as 0 for interface Float64Array.": true,
   "The argument Infinity (1) should be interpreted as 0 for interface Float64Array.": true,
   "The argument -Infinity (2) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument -0.4 (5) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument -0.9 (6) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument 1.1 (7) should be interpreted as 1 for interface Float64Array.": true,
-  "The argument 2.9 (8) should be interpreted as 2 for interface Float64Array.": true,
   "The argument -4043309056 (10) should be interpreted as 251658240 for interface Float64Array.": true,
-  "The argument \"1\" (11) should be interpreted as 1 for interface Float64Array.": true,
-  "The argument \"1e2\" (12) should be interpreted as 100 for interface Float64Array.": true,
-  "The argument undefined (13) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument null (14) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument false (15) should be interpreted as 0 for interface Float64Array.": true,
-  "The argument true (16) should be interpreted as 1 for interface Float64Array.": true,
   "The argument object \"[object Object]\" (18) should be interpreted as 0 for interface Float64Array.": true
 }
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -43,16 +43,17 @@ interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsITranslationNodeList;
 interface nsIJSRAIIHelper;
 interface nsIContentPermissionRequest;
 interface nsIObserver;
+interface nsIDOMStorage;
 
 [scriptable, uuid(c471d440-004b-4c50-a6f2-747db5f443b6)]
 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
@@ -2001,16 +2002,24 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Removes the specified EventStates bits from the element.
    *
    * See above for the strings that can be passed for |state|.
    */
   void removeManuallyManagedState(in nsIDOMElement element,
                                   in AString state);
 
+  /**
+   * Returns usage data for a given storage object.
+   *
+   * @param aStorage
+   *    The storage object to get usage data for.
+   */
+  int64_t getStorageUsage(in nsIDOMStorage aStorage);
+
   // These consts are only for testing purposes.
   const long DEFAULT_MOUSE_POINTER_ID = 0;
   const long DEFAULT_PEN_POINTER_ID   = 1;
   const long DEFAULT_TOUCH_POINTER_ID = 2;
 
   // Match WidgetMouseEventBase::buttonType.
   const long MOUSE_BUTTON_LEFT_BUTTON   = 0;
   const long MOUSE_BUTTON_MIDDLE_BUTTON = 1;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2565,30 +2565,23 @@ ContentChild::DeallocPOfflineCacheUpdate
     static_cast<OfflineCacheUpdateChild*>(actor);
   NS_RELEASE(offlineCacheUpdate);
   return true;
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
-  nsTArray<const char*> featureArray;
-  for (size_t i = 0; i < params.features().Length(); ++i) {
-    featureArray.AppendElement(params.features()[i].get());
+  nsTArray<const char*> filterArray;
+  for (size_t i = 0; i < params.filters().Length(); ++i) {
+    filterArray.AppendElement(params.filters()[i].get());
   }
 
-  nsTArray<const char*> threadNameFilterArray;
-  for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
-    threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
-  }
-
-  profiler_start(params.entries(), params.interval(),
-                 featureArray.Elements(), featureArray.Length(),
-                 threadNameFilterArray.Elements(),
-                 threadNameFilterArray.Length());
+  profiler_start(params.entries(), params.interval(), params.features(),
+                 filterArray.Elements(), filterArray.Length());
 
  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvStopProfiler()
 {
   profiler_stop();
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -729,17 +729,19 @@ parent:
 
     async AccumulateMixedContentHSTS(URIParams aURI, bool aActive, bool aHasHSTSPriming,
                                      OriginAttributes aOriginAttributes);
 
     nested(inside_cpow) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
-    async PNecko();
+    // Giving high priority to prevent other messages sending before this one.
+    // See bug 1360549 for details.
+    prio(high) async PNecko();
 
     async PPrinting();
 
     async PChildToParentStream();
 
     async PSpeechSynthesis();
 
     nested(inside_cpow) async PStorage();
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1720,20 +1720,28 @@ NPObjWrapper_Resolve(JSContext *cx, JS::
 
   // no property or method
   return true;
 }
 
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
+  JS::AutoAssertGCCallback inCallback;
+
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (npobj) {
     if (sNPObjWrappers) {
-      sNPObjWrappers->Remove(npobj);
+      // If the sNPObjWrappers map contains an entry that refers to this
+      // wrapper, remove it.
+      auto entry =
+        static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+      if (entry && entry->mJSObj == obj) {
+        sNPObjWrappers->Remove(npobj);
+      }
     }
   }
 
   if (!sDelayedReleases)
     sDelayedReleases = new nsTArray<NPObject*>;
   sDelayedReleases->AppendElement(npobj);
 }
 
@@ -1902,23 +1910,33 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
   if (entry->mJSObj) {
-    // Found a live NPObject wrapper. It may not be in the same compartment
-    // as cx, so we need to wrap it before returning it.
-    JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
-    if (!JS_WrapObject(cx, &obj)) {
-      return nullptr;
+    // Found a NPObject wrapper. First check it is still alive.
+    JSObject* obj = entry->mJSObj;
+    if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
+      // The object is dead (finalization will happen at a later time). By the
+      // time we leave this function, this entry will either be updated with a
+      // new wrapper or removed if that fails. Clear it anyway to make sure
+      // nothing touches the dead object.
+      entry->mJSObj = nullptr;
+    } else {
+      // It may not be in the same compartment as cx, so we need to wrap it
+      // before returning it.
+      JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
+      if (!JS_WrapObject(cx, &obj)) {
+        return nullptr;
+      }
+      return obj;
     }
-    return obj;
   }
 
   entry->mNPObj = npobj;
   entry->mNpp = npp;
 
   uint32_t generation = sNPObjWrappers->Generation();
 
   // No existing JSObject, create one.
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2654,29 +2654,23 @@ void
 PluginModuleChild::ProcessNativeEvents() {
     CallProcessSomeEvents();    
 }
 #endif
 
 mozilla::ipc::IPCResult
 PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
-    nsTArray<const char*> featureArray;
-    for (size_t i = 0; i < params.features().Length(); ++i) {
-        featureArray.AppendElement(params.features()[i].get());
+    nsTArray<const char*> filterArray;
+    for (size_t i = 0; i < params.filters().Length(); ++i) {
+        filterArray.AppendElement(params.filters()[i].get());
     }
 
-    nsTArray<const char*> threadNameFilterArray;
-    for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
-        threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
-    }
-
-    profiler_start(params.entries(), params.interval(),
-                   featureArray.Elements(), featureArray.Length(),
-                   threadNameFilterArray.Elements(), threadNameFilterArray.Length());
+    profiler_start(params.entries(), params.interval(), params.features(),
+                   filterArray.Elements(), filterArray.Length());
 
     return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 PluginModuleChild::RecvStopProfiler()
 {
     profiler_stop();
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -67,16 +67,22 @@ Storage::~Storage()
 }
 
 /* virtual */ JSObject*
 Storage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
+int64_t
+Storage::GetOriginQuotaUsage() const
+{
+  return mCache->GetOriginQuotaUsage(this);
+}
+
 uint32_t
 Storage::GetLength(nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return 0;
   }
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -68,16 +68,18 @@ public:
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
+  int64_t GetOriginQuotaUsage() const;
+
   uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aRv);
 
   void Key(uint32_t aIndex, nsAString& aResult,
            nsIPrincipal& aSubjectPrincipal,
            ErrorResult& aRv);
 
   void GetItem(const nsAString& aKey, nsAString& aResult,
--- a/dom/storage/StorageCache.cpp
+++ b/dom/storage/StorageCache.cpp
@@ -535,16 +535,22 @@ StorageCache::CloneFrom(const StorageCac
   for (uint32_t i = 0; i < kDataSetCount; ++i) {
     for (auto it = aThat->mData[i].mKeys.ConstIter(); !it.Done(); it.Next()) {
       mData[i].mKeys.Put(it.Key(), it.UserData());
     }
     ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
   }
 }
 
+int64_t
+StorageCache::GetOriginQuotaUsage(const Storage* aStorage) const
+{
+  return mData[GetDataSetIndex(aStorage)].mOriginQuotaUsage;
+}
+
 // Defined in StorageManager.cpp
 extern bool
 PrincipalsEqual(nsIPrincipal* aObjectPrincipal,
                 nsIPrincipal* aSubjectPrincipal);
 
 bool
 StorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
 {
--- a/dom/storage/StorageCache.h
+++ b/dom/storage/StorageCache.h
@@ -104,16 +104,19 @@ protected:
 
 public:
   void Init(StorageManagerBase* aManager, bool aPersistent,
             nsIPrincipal* aPrincipal, const nsACString& aQuotaOriginScope);
 
   // Copies all data from the other storage.
   void CloneFrom(const StorageCache* aThat);
 
+  // Get size of per-origin data.
+  int64_t GetOriginQuotaUsage(const Storage* aStorage) const;
+
   // Starts async preload of this cache if it persistent and not loaded.
   void Preload();
 
   // The set of methods that are invoked by DOM storage web API.
   // We are passing the Storage object just to let the cache
   // read properties like mPrivate, mPrincipal and mSessionOnly.
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const Storage* aStorage, uint32_t* aRetval);
--- a/dom/tests/mochitest/sessionstorage/mochitest.ini
+++ b/dom/tests/mochitest/sessionstorage/mochitest.ini
@@ -11,8 +11,9 @@ support-files =
 [test_cookieSession.html]
 [test_sessionStorageBase.html]
 [test_sessionStorageBaseSessionOnly.html]
 [test_sessionStorageClone.html]
 skip-if = toolkit == 'android'
 [test_sessionStorageHttpHttps.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_sessionStorageReplace.html]
+[test_sessionStorageUsage.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/test_sessionStorageUsage.html
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>sessionStorage basic test</title>
+
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+function setup() {
+  sessionStorage.clear();
+  SimpleTest.executeSoon(startTest);
+}
+
+function startTest()
+{
+  var util = SpecialPowers.wrap(window)
+               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+               .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
+
+  // Check initial state.
+  is(sessionStorage.length, 0, "storage is empty");
+  is(util.getStorageUsage(sessionStorage), 0, "usage is zero");
+
+  // Add some data.
+  sessionStorage.setItem("one", "data");
+  var usage = util.getStorageUsage(sessionStorage);
+  ok(usage > 0, "storage contains data");
+
+  // Add some more data.
+  sessionStorage.setItem("two", "data");
+  ok(usage < util.getStorageUsage(sessionStorage), "storage size grew");
+
+  // Remove data.
+  sessionStorage.removeItem("two");
+  is(util.getStorageUsage(sessionStorage), usage, "storage size shrunk");
+
+  // Cleanup.
+  sessionStorage.clear();
+  is(sessionStorage.length, 0, "storage is empty");
+  is(util.getStorageUsage(sessionStorage), 0, "usage is zero");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="setup();">
+
+</body>
+</html>
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -9,16 +9,18 @@
  */
 
 [HTMLConstructor]
 interface HTMLScriptElement : HTMLElement {
   [SetterThrows]
   attribute DOMString src;
   [SetterThrows]
   attribute DOMString type;
+  [SetterThrows, Pref="dom.moduleScripts.enabled"]
+  attribute boolean noModule;
   [SetterThrows]
   attribute DOMString charset;
   [SetterThrows]
   attribute boolean async;
   [SetterThrows]
   attribute boolean defer;
   [SetterThrows]
   attribute DOMString? crossOrigin;
--- a/gfx/2d/NativeFontResourceFontconfig.cpp
+++ b/gfx/2d/NativeFontResourceFontconfig.cpp
@@ -48,14 +48,14 @@ NativeFontResourceFontconfig::Create(uin
     new NativeFontResourceFontconfig(Move(fontData), face);
   return resource.forget();
 }
 
 already_AddRefed<UnscaledFont>
 NativeFontResourceFontconfig::CreateUnscaledFont(uint32_t aIndex,
                                                  const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
 {
-  RefPtr<UnscaledFont> unscaledFont = new UnscaledFontFontconfig(mFace);
+  RefPtr<UnscaledFont> unscaledFont = new UnscaledFontFontconfig(mFace, this);
   return unscaledFont.forget();
 }
 
 } // gfx
 } // mozilla
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -237,23 +237,33 @@ UnscaledFontFontconfig::CreateScaledFont
                                          uint32_t aInstanceDataLength)
 {
   if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
     gfxWarning() << "Fontconfig scaled font instance data is truncated.";
     return nullptr;
   }
   const ScaledFontFontconfig::InstanceData *instanceData =
     reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(aInstanceData);
-  return ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize);
+  return ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize,
+                                                      mNativeFontResource.get());
+}
+
+static cairo_user_data_key_t sNativeFontResourceKey;
+
+static void
+ReleaseNativeFontResource(void* aData)
+{
+  static_cast<NativeFontResource*>(aData)->Release();
 }
 
 already_AddRefed<ScaledFont>
 ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
                                              UnscaledFontFontconfig* aUnscaledFont,
-                                             Float aSize)
+                                             Float aSize,
+                                             NativeFontResource* aNativeFontResource)
 {
   FcPattern* pattern = FcPatternCreate();
   if (!pattern) {
     gfxWarning() << "Failing initializing Fontconfig pattern for scaled font";
     return nullptr;
   }
   if (aUnscaledFont->GetFace()) {
     FcPatternAddFTFace(pattern, FC_FT_FACE, aUnscaledFont->GetFace());
@@ -266,16 +276,32 @@ ScaledFontFontconfig::CreateFromInstance
 
   cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern);
   if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
     gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
     FcPatternDestroy(pattern);
     return nullptr;
   }
 
+  if (aNativeFontResource) {
+    // Bug 1362117 - Cairo may keep the font face alive after the owning NativeFontResource
+    // was freed. To prevent this, we must bind the NativeFontResource to the font face so that
+    // it stays alive at least as long as the font face.
+    if (cairo_font_face_set_user_data(font,
+                                      &sNativeFontResourceKey,
+                                      aNativeFontResource,
+                                      ReleaseNativeFontResource) != CAIRO_STATUS_SUCCESS) {
+      gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
+      cairo_font_face_destroy(font);
+      FcPatternDestroy(pattern);
+      return nullptr;
+    }
+    aNativeFontResource->AddRef();
+  }
+
   cairo_matrix_t sizeMatrix;
   aInstanceData.SetupFontMatrix(&sizeMatrix);
 
   cairo_matrix_t identityMatrix;
   cairo_matrix_init_identity(&identityMatrix);
 
   cairo_font_options_t *fontOptions = cairo_font_options_create();
   aInstanceData.SetupFontOptions(fontOptions);
--- a/gfx/2d/ScaledFontFontconfig.h
+++ b/gfx/2d/ScaledFontFontconfig.h
@@ -61,17 +61,18 @@ private:
     uint8_t mLcdFilter;
     Float mScale;
     Float mSkew;
   };
 
   static already_AddRefed<ScaledFont>
     CreateFromInstanceData(const InstanceData& aInstanceData,
                            UnscaledFontFontconfig* aUnscaledFont,
-                           Float aSize);
+                           Float aSize,
+                           NativeFontResource* aNativeFontResource = nullptr);
 
   FcPattern* mPattern;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_ */
--- a/gfx/2d/UnscaledFontFreeType.h
+++ b/gfx/2d/UnscaledFontFreeType.h
@@ -68,26 +68,34 @@ public:
   explicit UnscaledFontFontconfig(FT_Face aFace,
                                   bool aOwnsFace = false)
     : UnscaledFontFreeType(aFace, aOwnsFace)
   {}
   explicit UnscaledFontFontconfig(const char* aFile,
                                   uint32_t aIndex = 0)
     : UnscaledFontFreeType(aFile, aIndex)
   {}
+  UnscaledFontFontconfig(FT_Face aFace,
+                         NativeFontResource* aNativeFontResource)
+    : UnscaledFontFreeType(aFace, false)
+    , mNativeFontResource(aNativeFontResource)
+  {}
 
   FontType GetType() const override { return FontType::FONTCONFIG; }
 
   static already_AddRefed<UnscaledFont>
     CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength);
 
   already_AddRefed<ScaledFont>
     CreateScaledFont(Float aGlyphSize,
                      const uint8_t* aInstanceData,
                      uint32_t aInstanceDataLength) override;
+
+private:
+  RefPtr<NativeFontResource> mNativeFontResource;
 };
 #endif
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_ */
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,14 +1,14 @@
-gfx/harfbuzz status as of 2017-03-10:
+gfx/harfbuzz status as of 2017-04-24:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
-Current version: 1.4.5
+Current version: 1.4.6
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
 
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -20,21 +20,23 @@ lib_LTLIBRARIES = libharfbuzz.la
 
 include Makefile.sources
 
 HBCFLAGS =
 HBLIBS =
 HBNONPCLIBS =
 HBDEPS =
 HBSOURCES =  $(HB_BASE_sources)
+HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 HBNODISTHEADERS = $(HB_NODIST_headers)
 
 if HAVE_OT
 HBSOURCES += $(HB_OT_sources)
+HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
 HBHEADERS += $(HB_OT_headers)
 endif
 
 if HAVE_FALLBACK
 HBSOURCES += $(HB_FALLBACK_sources)
 endif
 
 if HAVE_PTHREAD
@@ -246,29 +248,23 @@ use-table: gen-use-table.py IndicSyllabi
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) hb-ot-shape-complex-use-table.cc; false)
 
 built-sources: $(BUILT_SOURCES)
 
 .PHONY: unicode-tables arabic-table indic-table use-table built-sources
 
 RAGEL_GENERATED = \
-	$(srcdir)/hb-buffer-deserialize-json.hh \
-	$(srcdir)/hb-buffer-deserialize-text.hh \
-	$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-use-machine.hh \
+	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
+	$(patsubst %,$(srcdir)/%,$(HB_OT_RAGEL_GENERATED_sources)) \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
-	hb-buffer-deserialize-json.rl \
-	hb-buffer-deserialize-text.rl \
-	hb-ot-shape-complex-indic-machine.rl \
-	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-use-machine.rl \
+	$(HB_BASE_RAGEL_sources) \
+	$(HB_OT_RAGEL_sources) \
 	$(NULL)
 MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
 $(srcdir)/%.hh: $(srcdir)/%.rl
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
 noinst_PROGRAMS = \
 	main \
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -181,18 +181,20 @@ static const char canon_map[256] = {
 
 static bool
 lang_equal (hb_language_t  v1,
 	    const void    *v2)
 {
   const unsigned char *p1 = (const unsigned char *) v1;
   const unsigned char *p2 = (const unsigned char *) v2;
 
-  while (*p1 && *p1 == canon_map[*p2])
-    p1++, p2++;
+  while (*p1 && *p1 == canon_map[*p2]) {
+    p1++;
+    p2++;
+  }
 
   return *p1 == canon_map[*p2];
 }
 
 #if 0
 static unsigned int
 lang_hash (const void *key)
 {
@@ -662,17 +664,17 @@ parse_float (const char **pp, const char
   strncpy (buf, *pp, len);
   buf[len] = '\0';
 
   char *p = buf;
   char *pend = p;
   float v;
 
   errno = 0;
-  v = strtof (p, &pend);
+  v = strtod (p, &pend);
   if (errno || p == pend)
     return false;
 
   *pv = v;
   *pp += pend - p;
   return true;
 }
 
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -296,17 +296,17 @@ hb_bool_t
   ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count);
 
 #undef ALLOCATE_ARRAY
 
   memset (clusters, 0, sizeof (clusters[0]) * buffer->len);
 
   hb_codepoint_t *pg = gids;
   clusters[0].cluster = buffer->info[0].cluster;
-  float curradv = HB_DIRECTION_IS_BACKWARD(buffer->props.direction) ? gr_slot_origin_X(gr_seg_first_slot(seg)) : 0.;
+  float curradv = 0.;
   if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
     curradv = gr_slot_origin_X(gr_seg_first_slot(seg));
     clusters[0].advance = gr_seg_advance_X(seg) - curradv;
   }
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
     unsigned int before = gr_slot_before (is);
@@ -325,32 +325,31 @@ hb_bool_t
     {
       hb_graphite2_cluster_t *c = clusters + ci + 1;
       c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
       c->cluster = buffer->info[c->base_char].cluster;
       c->num_chars = before - c->base_char;
       c->base_glyph = ic;
       c->num_glyphs = 0;
       if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
-      {
-        ci++;
-        clusters[ci].advance = curradv - gr_slot_origin_X(is);
-      } else {
+        c->advance = curradv - gr_slot_origin_X(is);
+      else
         clusters[ci].advance = gr_slot_origin_X(is) - curradv;
-        ci++;
-      }
+      ci++;
       curradv = gr_slot_origin_X(is);
     }
     clusters[ci].num_glyphs++;
 
     if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
 	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
   }
 
-  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+    clusters[ci].advance += curradv;
+  else
     clusters[ci].advance = gr_seg_advance_X(seg) - curradv;
   ci++;
 
   for (unsigned int i = 0; i < ci; ++i)
   {
     for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
     {
       hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
@@ -361,21 +360,21 @@ hb_bool_t
   }
   buffer->len = glyph_count;
 
   unsigned int upem = hb_face_get_upem (face);
   float xscale = (float) font->x_scale / upem;
   float yscale = (float) font->y_scale / upem;
   yscale *= yscale / xscale;
   /* Positioning. */
+  int currclus = -1;
+  const hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
   if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
-    int currclus = -1;
-    const hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
     curradvx = 0;
     for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
     {
       pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
       pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
       if (info->cluster != currclus) {
         pPos->x_advance = info->var1.i32 * xscale;
         curradvx += pPos->x_advance;
@@ -384,33 +383,30 @@ hb_bool_t
         pPos->x_advance = 0.;
 
       pPos->y_advance = gr_slot_advance_Y (is, grface, NULL) * yscale;
       curradvy += pPos->y_advance;
     }
   }
   else
   {
-    int currclus = -1;
-    const hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
-    curradvx = gr_seg_advance_X(seg) * xscale;
+    curradvx = gr_seg_advance_X(seg);
     for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
     {
       if (info->cluster != currclus)
       {
         pPos->x_advance = info->var1.i32 * xscale;
-        if (currclus != -1) curradvx -= info[-1].var1.i32 * xscale;
+        curradvx -= pPos->x_advance;
         currclus = info->cluster;
       } else
-      pPos->x_advance = 0.;
+        pPos->x_advance = 0.;
 
       pPos->y_advance = gr_slot_advance_Y (is, grface, NULL) * yscale;
       curradvy -= pPos->y_advance;
-      pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx + pPos->x_advance;
+      pPos->x_offset = (gr_slot_origin_X (is) - info->var1.i32) * xscale - curradvx + pPos->x_advance;
       pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
     }
     hb_buffer_reverse_clusters (buffer);
   }
 
   if (feats) gr_featureval_destroy (feats);
   gr_seg_destroy (seg);
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -337,17 +337,17 @@ struct hb_apply_context_t :
     const void *match_data;
   };
 
   struct skipping_iterator_t
   {
     inline void init (hb_apply_context_t *c_, bool context_match = false)
     {
       c = c_;
-      match_glyph_data = NULL,
+      match_glyph_data = NULL;
       matcher.set_match_func (NULL, NULL);
       matcher.set_lookup_props (c->lookup_props);
       /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
       matcher.set_ignore_zwnj (context_match || c->table_index == 1);
       /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
       matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
       matcher.set_mask (context_match ? -1 : c->lookup_mask);
     }
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -96,20 +96,28 @@ hb_ot_layout_t *
       /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
       || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len)
       /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
       || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len)
       /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
       || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len)
       /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
       || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len)
+      /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (964 == gdef_len && 60072 == gpos_len && 23836 == gsub_len)
+      /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (976 == gdef_len && 61456 == gpos_len && 23832 == gsub_len)
       /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
       || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
       /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
       || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
+      /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (1006 == gdef_len && 61346 == gpos_len && 24576 == gsub_len)
+      /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (1018 == gdef_len && 62828 == gpos_len && 24572 == gsub_len)
       /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
       || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len)
       /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
       || (1018 == gdef_len && 62834 == gpos_len && 24572 == gsub_len)
       /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
       || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len)
       /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
       || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len)
@@ -120,16 +128,24 @@ hb_ot_layout_t *
       /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
       || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len)
       /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
       /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
       || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len)
       /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
       /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
       || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len)
+      /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
+      || (1058 == gdef_len && 11818 == gpos_len && 47032 == gsub_len)
+      /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
+      || (1046 == gdef_len && 12600 == gpos_len && 47030 == gsub_len)
+      /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
+      || (1058 == gdef_len && 16770 == gpos_len && 71796 == gsub_len)
+      /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
+      || (1046 == gdef_len && 17862 == gpos_len && 71790 == gsub_len)
       /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
       || (1046 == gdef_len && 17112 == gpos_len && 71788 == gsub_len)
       /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
       || (1058 == gdef_len && 17514 == gpos_len && 71794 == gsub_len)
       /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
       || (1330 == gdef_len && 57938 == gpos_len && 109904 == gsub_len)
       /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
       || (1330 == gdef_len && 58972 == gpos_len && 109904 == gsub_len)
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -108,17 +108,17 @@ struct hb_ot_map_t
     if (unlikely (stage == (unsigned int) -1)) {
       *plookups = NULL;
       *lookup_count = 0;
       return;
     }
     assert (stage <= stages[table_index].len);
     unsigned int start = stage ? stages[table_index][stage - 1].last_lookup : 0;
     unsigned int end   = stage < stages[table_index].len ? stages[table_index][stage].last_lookup : lookups[table_index].len;
-    *plookups = &lookups[table_index][start];
+    *plookups = end == start ? NULL : &lookups[table_index][start];
     *lookup_count = end - start;
   }
 
   HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const;
   template <typename Proxy>
   HB_INTERNAL inline void apply (const Proxy &proxy,
 				 const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -426,21 +426,22 @@ hb_shape_plan_matches (const hb_shape_pl
 	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
 
 static inline hb_bool_t
 hb_non_global_user_features_present (const hb_feature_t *user_features,
 				     unsigned int        num_user_features)
 {
-  while (num_user_features)
+  while (num_user_features) {
     if (user_features->start != 0 || user_features->end != (unsigned int) -1)
       return true;
-    else
-      num_user_features--, user_features++;
+    num_user_features--;
+    user_features++;
+  }
   return false;
 }
 
 static inline hb_bool_t
 hb_coords_present (const int *coords,
 		   unsigned int num_coords)
 {
   return num_coords != 0;
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 4
-#define HB_VERSION_MICRO 5
+#define HB_VERSION_MICRO 6
 
-#define HB_VERSION_STRING "1.4.5"
+#define HB_VERSION_STRING "1.4.6"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -384,29 +384,22 @@ GPUParent::RecvNotifyGpuObservers(const 
     obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvStartProfiler(const ProfilerInitParams& params)
 {
-  nsTArray<const char*> featureArray;
-  for (size_t i = 0; i < params.features().Length(); ++i) {
-    featureArray.AppendElement(params.features()[i].get());
+  nsTArray<const char*> filterArray;
+  for (size_t i = 0; i < params.filters().Length(); ++i) {
+    filterArray.AppendElement(params.filters()[i].get());
   }
-
-  nsTArray<const char*> threadNameFilterArray;
-  for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
-    threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
-  }
-  profiler_start(params.entries(), params.interval(),
-                 featureArray.Elements(), featureArray.Length(),
-                 threadNameFilterArray.Elements(),
-                 threadNameFilterArray.Length());
+  profiler_start(params.entries(), params.interval(), params.features(),
+                 filterArray.Elements(), filterArray.Length());
 
  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvStopProfiler()
 {
   profiler_stop();
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -97,17 +97,17 @@ ContentClient::EndPaint(nsTArray<Readbac
 }
 
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 
-  if (profiler_feature_active("displaylistdump")) {
+  if (profiler_feature_active(ProfilerFeature::DisplayListDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1369,17 +1369,18 @@ TextureClient::PrintInfo(std::stringstre
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("TextureClient (0x%p)", this).get();
   AppendToString(aStream, GetSize(), " [size=", "]");
   AppendToString(aStream, GetFormat(), " [format=", "]");
   AppendToString(aStream, mFlags, " [flags=", "]");
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     aStream << "\n" << pfx.get() << "Surface: ";
     RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
     if (dSurf) {
       aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
     }
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1392,17 +1392,17 @@ ClientMultiTiledLayerBuffer::Progressive
 }
 
 void
 TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
 
-  if (profiler_feature_active("displaylistdump")) {
+  if (profiler_feature_active(ProfilerFeature::DisplayListDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 void
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -831,17 +831,17 @@ LayerManagerComposite::Render(const nsIn
   bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
 
   // Set LayerScope begin/end frame
   LayerScopeAutoFrame frame(PR_Now());
 
   // Dump to console
   if (gfxPrefs::LayersDump()) {
     this->Dump(/* aSorted= */true);
-  } else if (profiler_feature_active("layersdump")) {
+  } else if (profiler_feature_active(ProfilerFeature::LayersDump)) {
     std::stringstream ss;
     Dump(ss);
     profiler_log(ss.str().c_str());
   }
 
   // Dump to LayerScope Viewer
   if (LayerScope::CheckSendable()) {
     // Create a LayersPacket, dump Layers into it and transfer the
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -403,17 +403,18 @@ TextureHost::PrintInfo(std::stringstream
   //       GetSize() and GetFormat() on it.
   if (Lock()) {
     AppendToString(aStream, GetSize(), " [size=", "]");
     AppendToString(aStream, GetFormat(), " [format=", "]");
     Unlock();
   }
   AppendToString(aStream, mFlags, " [flags=", "]");
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     aStream << "\n" << pfx.get() << "Surface: ";
     RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
     if (dSurf) {
       aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
     }
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -620,17 +620,18 @@ TiledContentHost::RenderLayerBuffer(Tile
 }
 
 void
 TiledContentHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get();
 
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 void
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -123,17 +123,20 @@ CrossProcessCompositorBridgeParent::Allo
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
 
   // If the widget has shutdown its compositor, we may not have had a chance yet
   // to unmap our layers id, and we could get here without a parent compositor.
   // In this case return an empty APZCTM.
   if (!state.mParent) {
+    // Note: we immediately call ClearTree since otherwise the APZCTM will
+    // retain a reference to itself, through the checkerboard observer.
     RefPtr<APZCTreeManager> temp = new APZCTreeManager();
+    temp->ClearTree();
     return new APZCTreeManagerParent(aLayersId, temp);
   }
 
   MOZ_ASSERT(!state.mApzcTreeManagerParent);
   state.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, state.mParent->GetAPZCTreeManager());
 
   return state.mApzcTreeManagerParent;
 }
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -33,50 +33,50 @@ public:
 
     AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
                 const char* aString, int32_t aLength)
     {
         mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
             reinterpret_cast<const uint8_t*>(aString), aLength,
             aDrawTarget,
             aMetrics->AppUnitsPerDevPixel(),
-            ComputeFlags(aMetrics),
+            ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
             nullptr);
     }
 
     AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
                 const char16_t* aString, int32_t aLength)
     {
         mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
             aString, aLength,
             aDrawTarget,
             aMetrics->AppUnitsPerDevPixel(),
-            ComputeFlags(aMetrics),
+            ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
             nullptr);
     }
 
     gfxTextRun *get() { return mTextRun.get(); }
     gfxTextRun *operator->() { return mTextRun.get(); }
 
 private:
-    static uint32_t ComputeFlags(nsFontMetrics* aMetrics) {
-        uint32_t flags = 0;
+    static gfx::ShapedTextFlags ComputeFlags(nsFontMetrics* aMetrics) {
+        gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
         if (aMetrics->GetTextRunRTL()) {
-            flags |= gfxTextRunFactory::TEXT_IS_RTL;
+            flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
         }
         if (aMetrics->GetVertical()) {
             switch (aMetrics->GetTextOrientation()) {
             case NS_STYLE_TEXT_ORIENTATION_MIXED:
-                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED;
+                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
                 break;
             case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
-                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
                 break;
             case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
-                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
                 break;
             }
         }
         return flags;
     }
 
     RefPtr<gfxTextRun> mTextRun;
 };
--- a/gfx/thebes/ContextStateTracker.cpp
+++ b/gfx/thebes/ContextStateTracker.cpp
@@ -9,17 +9,17 @@
 #include "ProfilerMarkerPayload.h"
 #endif
 
 namespace mozilla {
 
 void
 ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
 {
-  if (!profiler_feature_active("gpu")) {
+  if (!profiler_feature_active(ProfilerFeature::GPU)) {
     return;
   }
 
   if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
     return;
   }
 
   if (mSectionStack.Length() > 0) {
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -546,17 +546,17 @@ gfxDWriteFont::GetCairoScaledFont()
 }
 
 gfxFont::RunMetrics
 gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
                        uint32_t aStart, uint32_t aEnd,
                        BoundingBoxType aBoundingBoxType,
                        DrawTarget* aRefDrawTarget,
                        Spacing* aSpacing,
-                       uint16_t aOrientation)
+                       gfx::ShapedTextFlags aOrientation)
 {
     gfxFont::RunMetrics metrics =
         gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
                          aRefDrawTarget, aSpacing, aOrientation);
 
     // if aBoundingBoxType is LOOSE_INK_EXTENTS
     // and the underlying cairo font may be antialiased,
     // we can't trust Windows to have considered all the pixels
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -49,17 +49,17 @@ public:
     IDWriteFontFace *GetFontFace();
 
     /* override Measure to add padding for antialiasing */
     virtual RunMetrics Measure(const gfxTextRun *aTextRun,
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                DrawTarget *aDrawTargetForTightBoundingBox,
                                Spacing *aSpacing,
-                               uint16_t aOrientation) override;
+                               mozilla::gfx::ShapedTextFlags aOrientation) override;
 
     virtual bool ProvidesGlyphWidths() const override;
 
     virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
                                   uint16_t aGID) override;
 
     virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
     GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) override;
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2027,17 +2027,17 @@ gfxFont::DrawEmphasisMarks(const gfxText
             inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
         }
     }
 }
 
 void
 gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
               gfxPoint *aPt, const TextRunDrawParams& aRunParams,
-              uint16_t aOrientation)
+              gfx::ShapedTextFlags aOrientation)
 {
     NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
                  !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
 
     if (aStart >= aEnd) {
         return;
     }
@@ -2052,31 +2052,31 @@ gfxFont::Draw(const gfxTextRun *aTextRun
     if (!fontParams.scaledFont) {
         return;
     }
 
     fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
     fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
     fontParams.contextPaint = aRunParams.runContextPaint;
     fontParams.isVerticalFont =
-        aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+        aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
 
     bool sideways = false;
     gfxPoint origPt = *aPt;
     if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
         sideways = true;
         aRunParams.context->Save();
         gfxPoint p(aPt->x * aRunParams.devPerApp,
                    aPt->y * aRunParams.devPerApp);
         const Metrics& metrics = GetMetrics(eHorizontal);
         // Get a matrix we can use to draw the (horizontally-shaped) textrun
         // with 90-degree CW rotation.
         const gfxFloat
             rotation = (aOrientation ==
-                        gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
+                        gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
                        ? -M_PI / 2.0 : M_PI / 2.0;
         gfxMatrix mat =
             aRunParams.context->CurrentMatrix().
             Translate(p).     // translate origin for rotation
             Rotate(rotation). // turn 90deg CCW (sideways-left) or CW (*-right)
             Translate(-p);    // undo the translation
 
         // If we're drawing rotated horizontal text for an element styled
@@ -2187,17 +2187,17 @@ gfxFont::Draw(const gfxTextRun *aTextRun
     aRunParams.dt->SetTransform(oldMat);
     aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
 
     if (sideways) {
         aRunParams.context->Restore();
         // adjust updated aPt to account for the transform we were using
         gfxFloat advance = aPt->x - origPt.x;
         if (aOrientation ==
-            gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) {
+            gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) {
             *aPt = gfxPoint(origPt.x, origPt.y - advance);
         } else {
             *aPt = gfxPoint(origPt.x, origPt.y + advance);
         }
     }
 }
 
 bool
@@ -2285,17 +2285,17 @@ UnionRange(gfxFloat aX, gfxFloat* aDestM
 }
 
 // We get precise glyph extents if the textrun creator requested them, or
 // if the font is a user font --- in which case the author may be relying
 // on overflowing glyphs.
 static bool
 NeedsGlyphExtents(gfxFont *aFont, const gfxTextRun *aTextRun)
 {
-    return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
+    return (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX) ||
         aFont->GetFontEntry()->IsUserFont();
 }
 
 bool
 gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
                                const gfxTextRun* aTextRun)
 {
     if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized &&
@@ -2313,17 +2313,17 @@ gfxFont::IsSpaceGlyphInvisible(DrawTarge
 }
 
 gfxFont::RunMetrics
 gfxFont::Measure(const gfxTextRun *aTextRun,
                  uint32_t aStart, uint32_t aEnd,
                  BoundingBoxType aBoundingBoxType,
                  DrawTarget* aRefDrawTarget,
                  Spacing *aSpacing,
-                 uint16_t aOrientation)
+                 gfx::ShapedTextFlags aOrientation)
 {
     // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
     // and the underlying cairo font may be antialiased,
     // we need to create a copy in order to avoid getting cached extents.
     // This is only used by MathML layout at present.
     if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
         mAntialiasOption != kAntialiasNone) {
         if (!mNonAAFont) {
@@ -2336,17 +2336,17 @@ gfxFont::Measure(const gfxTextRun *aText
                                        TIGHT_HINTED_OUTLINE_EXTENTS,
                                        aRefDrawTarget, aSpacing, aOrientation);
         }
     }
 
     const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
     // Current position in appunits
     gfxFont::Orientation orientation =
-        aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT
+        aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
         ? eVertical : eHorizontal;
     const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
 
     gfxFloat baselineOffset = 0;
     if (aTextRun->UseCenterBaseline() && orientation == eHorizontal) {
         // For a horizontal font being used in vertical writing mode with
         // text-orientation:mixed, the overall metrics we're accumulating
         // will be aimed at a center baseline. But this font's metrics were
@@ -2566,17 +2566,17 @@ template<typename T>
 gfxShapedWord*
 gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
                        const T    *aText,
                        uint32_t    aLength,
                        uint32_t    aHash,
                        Script      aRunScript,
                        bool        aVertical,
                        int32_t     aAppUnitsPerDevUnit,
-                       uint32_t    aFlags,
+                       gfx::ShapedTextFlags aFlags,
                        RoundingFlags aRounding,
                        gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
 {
     // if the cache is getting too big, flush it and start over
     uint32_t wordCacheMaxEntries =
         gfxPlatform::GetPlatform()->WordCacheMaxEntries();
     if (mWordCache->Count() > wordCacheMaxEntries) {
         NS_WARNING("flushing shaped-word cache");
@@ -2664,17 +2664,17 @@ gfxFont::CacheHashEntry::KeyEquals(const
         const char16_t *s2end = s2 + aKey->mLength;
         while (s2 < s2end) {
             if (*s1++ != *s2++) {
                 return false;
             }
         }
         return true;
     }
-    NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
+    NS_ASSERTION(!(aKey->mFlags & gfx::ShapedTextFlags::TEXT_IS_8BIT) &&
                  !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
     return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
                         aKey->mLength * sizeof(char16_t)));
 }
 
 bool
 gfxFont::ShapeText(DrawTarget    *aDrawTarget,
                    const uint8_t *aText,
@@ -2867,17 +2867,17 @@ gfxFont::ShapeTextWithoutWordCache(DrawT
         // fragment was terminated by an invalid char: skip it,
         // unless it's a control char that we want to show as a hexbox,
         // but record where TAB or NEWLINE occur
         if (ch == '\t') {
             aTextRun->SetIsTab(aOffset + i);
         } else if (ch == '\n') {
             aTextRun->SetIsNewline(aOffset + i);
         } else if (IsInvalidControlChar(ch) &&
-            !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
+            !(aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
             if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
                 ShapeFragmentWithoutWordCache(aDrawTarget, aText + i,
                                               aOffset + i, 1,
                                               aScript, aVertical, aRounding,
                                               aTextRun);
             } else {
                 aTextRun->SetMissingGlyph(aOffset + i, ch, this);
             }
@@ -2961,23 +2961,23 @@ gfxFont::SplitAndInitTextRun(DrawTarget 
                                              aRunScript, aVertical,
                                              rounding, aTextRun);
         }
     }
 
     InitWordCache();
 
     // the only flags we care about for ShapedWord construction/caching
-    uint32_t flags = aTextRun->GetFlags();
-    flags &= (gfxTextRunFactory::TEXT_IS_RTL |
-              gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
-              gfxTextRunFactory::TEXT_USE_MATH_SCRIPT |
-              gfxTextRunFactory::TEXT_ORIENT_MASK);
+    gfx::ShapedTextFlags flags = aTextRun->GetFlags();
+    flags &= (gfx::ShapedTextFlags::TEXT_IS_RTL |
+              gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES |
+              gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT |
+              gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
     if (sizeof(T) == sizeof(uint8_t)) {
-        flags |= gfxTextRunFactory::TEXT_IS_8BIT;
+        flags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
     }
 
     uint32_t wordStart = 0;
     uint32_t hash = 0;
     bool wordIs8Bit = true;
     int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
 
     T nextCh = aString[0];
@@ -3013,60 +3013,61 @@ gfxFont::SplitAndInitTextRun(DrawTarget 
                                                     aRunScript,
                                                     aVertical,
                                                     rounding,
                                                     aTextRun);
             if (!ok) {
                 return false;
             }
         } else if (length > 0) {
-            uint32_t wordFlags = flags;
+            gfx::ShapedTextFlags wordFlags = flags;
             // in the 8-bit version of this method, TEXT_IS_8BIT was
             // already set as part of |flags|, so no need for a per-word
             // adjustment here
             if (sizeof(T) == sizeof(char16_t)) {
                 if (wordIs8Bit) {
-                    wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
+                    wordFlags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
                 }
             }
             gfxShapedWord* sw = GetShapedWord(aDrawTarget,
                                               aString + wordStart, length,
                                               hash, aRunScript, aVertical,
                                               appUnitsPerDevUnit,
                                               wordFlags, rounding, tp);
             if (sw) {
                 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
             } else {
                 return false; // failed, presumably out of memory?
             }
         }
 
         if (boundary) {
             // word was terminated by a space: add that to the textrun
-            uint16_t orientation = flags & gfxTextRunFactory::TEXT_ORIENT_MASK;
-            if (orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+            gfx::ShapedTextFlags orientation =
+                flags & gfx::ShapedTextFlags::TEXT_ORIENT_MASK;
+            if (orientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
                 orientation = aVertical ?
-                    gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT :
-                    gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+                    gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT :
+                    gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
             }
             if (boundary != ' ' ||
                 !aTextRun->SetSpaceGlyphIfSimple(this, aRunStart + i, ch,
                                                  orientation)) {
                 // Currently, the only "boundary" characters we recognize are
                 // space and no-break space, which are both 8-bit, so we force
                 // that flag (below). If we ever change IsBoundarySpace, we
                 // may need to revise this.
                 // Avoid tautological-constant-out-of-range-compare in 8-bit:
                 DebugOnly<char16_t> boundary16 = boundary;
                 NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
                 gfxShapedWord *sw =
                     GetShapedWord(aDrawTarget, &boundary, 1,
                                   gfxShapedWord::HashMix(0, boundary),
                                   aRunScript, aVertical, appUnitsPerDevUnit,
-                                  flags | gfxTextRunFactory::TEXT_IS_8BIT,
+                                  flags | gfx::ShapedTextFlags::TEXT_IS_8BIT,
                                   rounding, tp);
                 if (sw) {
                     aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
                 } else {
                     return false;
                 }
             }
             hash = 0;
@@ -3085,17 +3086,17 @@ gfxFont::SplitAndInitTextRun(DrawTarget 
         // word was terminated by an invalid char: skip it,
         // unless it's a control char that we want to show as a hexbox,
         // but record where TAB or NEWLINE occur
         if (ch == '\t') {
             aTextRun->SetIsTab(aRunStart + i);
         } else if (ch == '\n') {
             aTextRun->SetIsNewline(aRunStart + i);
         } else if (IsInvalidControlChar(ch) &&
-            !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
+            !(aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
             if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
                 ShapeFragmentWithoutWordCache(aDrawTarget, aString + i,
                                               aRunStart + i, 1,
                                               aRunScript, aVertical,
                                               rounding, aTextRun);
             } else {
                 aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
             }
@@ -3130,17 +3131,17 @@ gfxFont::SplitAndInitTextRun(DrawTarget 
 template<>
 bool
 gfxFont::InitFakeSmallCapsRun(DrawTarget     *aDrawTarget,
                               gfxTextRun     *aTextRun,
                               const char16_t *aText,
                               uint32_t        aOffset,
                               uint32_t        aLength,
                               uint8_t         aMatchType,
-                              uint16_t        aOrientation,
+                              gfx::ShapedTextFlags aOrientation,
                               Script          aScript,
                               bool            aSyntheticLower,
                               bool            aSyntheticUpper)
 {
     bool ok = true;
 
     RefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
     if (!smallCapsFont) {
@@ -3152,17 +3153,17 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget
         kNoChange,
         kUppercaseReduce,
         kUppercase
     };
 
     RunCaseAction runAction = kNoChange;
     uint32_t runStart = 0;
     bool vertical =
-        aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+        aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
 
     for (uint32_t i = 0; i <= aLength; ++i) {
         uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
                                      // a trailing surrogate as well as the
                                      // current code unit.
         RunCaseAction chAction = kNoChange;
         // Unless we're at the end, figure out what treatment the current
         // character will need.
@@ -3252,27 +3253,31 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget
                     // into the destination textrun but have to handle the
                     // mismatch of character positions.
                     gfxTextRunFactory::Parameters params = {
                         aDrawTarget, nullptr, nullptr, nullptr, 0,
                         aTextRun->GetAppUnitsPerDevUnit()
                     };
                     RefPtr<gfxTextRun> tempRun(
                         gfxTextRun::Create(&params, convertedString.Length(),
-                                           aTextRun->GetFontGroup(), 0));
+                                           aTextRun->GetFontGroup(),
+                                           gfx::ShapedTextFlags(), 
+                                           nsTextFrameUtils::Flags()));
                     tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
                     if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
                                                 convertedString.BeginReading(),
                                                 0, convertedString.Length(),
                                                 aScript, vertical)) {
                         ok = false;
                     } else {
                         RefPtr<gfxTextRun> mergedRun(
                             gfxTextRun::Create(&params, runLength,
-                                               aTextRun->GetFontGroup(), 0));
+                                               aTextRun->GetFontGroup(),
+                                               gfx::ShapedTextFlags(), 
+                                               nsTextFrameUtils::Flags()));
                         MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
                                                  charsToMergeArray.Elements(),
                                                  deletedCharsArray.Elements());
                         gfxTextRun::Range runRange(0, runLength);
                         aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
                                                     aOffset + runStart);
                     }
                 } else {
@@ -3303,17 +3308,17 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget
 template<>
 bool
 gfxFont::InitFakeSmallCapsRun(DrawTarget     *aDrawTarget,
                               gfxTextRun     *aTextRun,
                               const uint8_t  *aText,
                               uint32_t        aOffset,
                               uint32_t        aLength,
                               uint8_t         aMatchType,
-                              uint16_t        aOrientation,
+                              gfx::ShapedTextFlags aOrientation,
                               Script          aScript,
                               bool            aSyntheticLower,
                               bool            aSyntheticUpper)
 {
     NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
                                          aLength);
     return InitFakeSmallCapsRun(aDrawTarget, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
                                 aOffset, aLength, aMatchType, aOrientation,
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -215,39 +215,16 @@ struct gfxFontStyle {
             (featureSettings == other.featureSettings) &&
             (alternateValues == other.alternateValues) &&
             (featureValueLookup == other.featureValueLookup) &&
             (variationSettings == other.variationSettings) &&
             (languageOverride == other.languageOverride);
     }
 };
 
-struct gfxTextRange {
-    enum {
-        // flags for recording the kind of font-matching that was used
-        kFontGroup      = 0x0001,
-        kPrefsFallback  = 0x0002,
-        kSystemFallback = 0x0004
-    };
-    gfxTextRange(uint32_t aStart, uint32_t aEnd,
-                 gfxFont* aFont, uint8_t aMatchType,
-                 uint16_t aOrientation)
-        : start(aStart),
-          end(aEnd),
-          font(aFont),
-          matchType(aMatchType),
-          orientation(aOrientation)
-    { }
-    uint32_t Length() const { return end - start; }
-    uint32_t start, end;
-    RefPtr<gfxFont> font;
-    uint8_t matchType;
-    uint16_t orientation;
-};
-
 
 /**
  * Font cache design:
  * 
  * The mFonts hashtable contains most fonts, indexed by (gfxFontEntry*, style).
  * It does not add a reference to the fonts it contains.
  * When a font's refcount decreases to zero, instead of deleting it we
  * add it to our expiration tracker.
@@ -449,135 +426,126 @@ public:
         cumulative.fallbackSystem += current.fallbackSystem;
         cumulative.textrunConst += current.textrunConst;
         cumulative.textrunDestr += current.textrunDestr;
         cumulative.genericLookups += current.genericLookups;
         memset(&current, 0, sizeof(current));
     }
 };
 
+namespace mozilla {
+namespace gfx {
+// Flags that live in the gfxShapedText::mFlags field.
+// (Note that gfxTextRun has an additional mFlags2 field for use
+// by textrun clients like nsTextFrame.)
+enum class ShapedTextFlags : uint16_t {
+    /**
+     * When set, the text string pointer used to create the text run
+     * is guaranteed to be available during the lifetime of the text run.
+     */
+    TEXT_IS_PERSISTENT           = 0x0001,
+    /**
+     * When set, the text is RTL.
+     */
+    TEXT_IS_RTL                  = 0x0002,
+    /**
+     * When set, spacing is enabled and the textrun needs to call GetSpacing
+     * on the spacing provider.
+     */
+    TEXT_ENABLE_SPACING          = 0x0004,
+    /**
+     * When set, the text has no characters above 255 and it is stored
+     * in the textrun in 8-bit format.
+     */
+    TEXT_IS_8BIT                 = 0x0008,
+    /**
+     * When set, GetHyphenationBreaks may return true for some character
+     * positions, otherwise it will always return false for all characters.
+     */
+    TEXT_ENABLE_HYPHEN_BREAKS    = 0x0010,
+    /**
+     * When set, the RunMetrics::mBoundingBox field will be initialized
+     * properly based on glyph extents, in particular, glyph extents that
+     * overflow the standard font-box (the box defined by the ascent, descent
+     * and advance width of the glyph). When not set, it may just be the
+     * standard font-box even if glyphs overflow.
+     */
+    TEXT_NEED_BOUNDING_BOX       = 0x0020,
+    /**
+     * When set, optional ligatures are disabled. Ligatures that are
+     * required for legible text should still be enabled.
+     */
+    TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0040,
+    /**
+     * When set, the textrun should favour speed of construction over
+     * quality. This may involve disabling ligatures and/or kerning or
+     * other effects.
+     */
+    TEXT_OPTIMIZE_SPEED          = 0x0080,
+    /**
+     * When set, the textrun should discard control characters instead of
+     * turning them into hexboxes.
+     */
+    TEXT_HIDE_CONTROL_CHARACTERS = 0x0100,
+
+    /**
+     * nsTextFrameThebes sets these, but they're defined here rather than
+     * in nsTextFrameUtils.h because ShapedWord creation/caching also needs
+     * to check the _INCOMING flag
+     */
+    TEXT_TRAILING_ARABICCHAR     = 0x0200,
+    /**
+     * When set, the previous character for this textrun was an Arabic
+     * character.  This is used for the context detection necessary for
+     * bidi.numeral implementation.
+     */
+    TEXT_INCOMING_ARABICCHAR     = 0x0400,
+
+    /**
+     * Set if the textrun should use the OpenType 'math' script.
+     */
+    TEXT_USE_MATH_SCRIPT         = 0x0800,
+
+    /**
+     * Field for orientation of the textrun and glyphs within it.
+     * Possible values of the TEXT_ORIENT_MASK field:
+     *   TEXT_ORIENT_HORIZONTAL
+     *   TEXT_ORIENT_VERTICAL_UPRIGHT
+     *   TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
+     *   TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
+     *   TEXT_ORIENT_VERTICAL_MIXED
+     * For all VERTICAL settings, the x and y coordinates of glyph
+     * positions are exchanged, so that simple advances are vertical.
+     *
+     * The MIXED value indicates vertical textRuns for which the CSS
+     * text-orientation property is 'mixed', but is never used for
+     * individual glyphRuns; it will be resolved to either UPRIGHT
+     * or SIDEWAYS_RIGHT according to the UTR50 properties of the
+     * characters, and separate glyphRuns created for the resulting
+     * glyph orientations.
+     */
+    TEXT_ORIENT_MASK                    = 0x7000,
+    TEXT_ORIENT_HORIZONTAL              = 0x0000,
+    TEXT_ORIENT_VERTICAL_UPRIGHT        = 0x1000,
+    TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT = 0x2000,
+    TEXT_ORIENT_VERTICAL_MIXED          = 0x3000,
+    TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT  = 0x4000,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ShapedTextFlags)
+} // namespace gfx
+} // namespace mozilla
+
 class gfxTextRunFactory {
     // Used by stylo
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxTextRunFactory)
 
 public:
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
-    // Flags in the mask 0xFFFF0000 are reserved for textrun clients
-    // Flags in the mask 0x0000F000 are reserved for per-platform fonts
-    // Flags in the mask 0x00000FFF are set by the textrun creator.
-    enum {
-        CACHE_TEXT_FLAGS    = 0xF0000000,
-        USER_TEXT_FLAGS     = 0x0FFF0000,
-        TEXTRUN_TEXT_FLAGS  = 0x0000FFFF,
-        SETTABLE_FLAGS      = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS,
-
-        /**
-         * When set, the text string pointer used to create the text run
-         * is guaranteed to be available during the lifetime of the text run.
-         */
-        TEXT_IS_PERSISTENT           = 0x0001,
-        /**
-         * When set, the text is known to be all-ASCII (< 128).
-         */
-        TEXT_IS_ASCII                = 0x0002,
-        /**
-         * When set, the text is RTL.
-         */
-        TEXT_IS_RTL                  = 0x0004,
-        /**
-         * When set, spacing is enabled and the textrun needs to call GetSpacing
-         * on the spacing provider.
-         */
-        TEXT_ENABLE_SPACING          = 0x0008,
-        /**
-         * When set, GetHyphenationBreaks may return true for some character
-         * positions, otherwise it will always return false for all characters.
-         */
-        TEXT_ENABLE_HYPHEN_BREAKS    = 0x0010,
-        /**
-         * When set, the text has no characters above 255 and it is stored
-         * in the textrun in 8-bit format.
-         */
-        TEXT_IS_8BIT                 = 0x0020,
-        /**
-         * When set, the RunMetrics::mBoundingBox field will be initialized
-         * properly based on glyph extents, in particular, glyph extents that
-         * overflow the standard font-box (the box defined by the ascent, descent
-         * and advance width of the glyph). When not set, it may just be the
-         * standard font-box even if glyphs overflow.
-         */
-        TEXT_NEED_BOUNDING_BOX       = 0x0040,
-        /**
-         * When set, optional ligatures are disabled. Ligatures that are
-         * required for legible text should still be enabled.
-         */
-        TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
-        /**
-         * When set, the textrun should favour speed of construction over
-         * quality. This may involve disabling ligatures and/or kerning or
-         * other effects.
-         */
-        TEXT_OPTIMIZE_SPEED          = 0x0100,
-        /**
-         * For internal use by the memory reporter when accounting for
-         * storage used by textruns.
-         * Because the reporter may visit each textrun multiple times while
-         * walking the frame trees and textrun cache, it needs to mark
-         * textruns that have been seen so as to avoid multiple-accounting.
-         */
-        TEXT_RUN_SIZE_ACCOUNTED      = 0x0200,
-        /**
-         * When set, the textrun should discard control characters instead of
-         * turning them into hexboxes.
-         */
-        TEXT_HIDE_CONTROL_CHARACTERS = 0x0400,
-
-        /**
-         * Field for orientation of the textrun and glyphs within it.
-         * Possible values of the TEXT_ORIENT_MASK field:
-         *   TEXT_ORIENT_HORIZONTAL
-         *   TEXT_ORIENT_VERTICAL_UPRIGHT
-         *   TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
-         *   TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
-         *   TEXT_ORIENT_VERTICAL_MIXED
-         * For all VERTICAL settings, the x and y coordinates of glyph
-         * positions are exchanged, so that simple advances are vertical.
-         *
-         * The MIXED value indicates vertical textRuns for which the CSS
-         * text-orientation property is 'mixed', but is never used for
-         * individual glyphRuns; it will be resolved to either UPRIGHT
-         * or SIDEWAYS_RIGHT according to the UTR50 properties of the
-         * characters, and separate glyphRuns created for the resulting
-         * glyph orientations.
-         */
-        TEXT_ORIENT_MASK                    = 0xF000,
-        TEXT_ORIENT_HORIZONTAL              = 0x0000,
-        TEXT_ORIENT_VERTICAL_UPRIGHT        = 0x1000,
-        TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT = 0x2000,
-        TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT  = 0x4000,
-        TEXT_ORIENT_VERTICAL_MIXED          = 0x8000,
-
-        /**
-         * nsTextFrameThebes sets these, but they're defined here rather than
-         * in nsTextFrameUtils.h because ShapedWord creation/caching also needs
-         * to check the _INCOMING flag
-         */
-        TEXT_TRAILING_ARABICCHAR = 0x20000000,
-        /**
-         * When set, the previous character for this textrun was an Arabic
-         * character.  This is used for the context detection necessary for
-         * bidi.numeral implementation.
-         */
-        TEXT_INCOMING_ARABICCHAR = 0x40000000,
-
-        // Set if the textrun should use the OpenType 'math' script.
-        TEXT_USE_MATH_SCRIPT = 0x80000000,
-    };
-
     /**
      * This record contains all the parameters needed to initialize a textrun.
      */
     struct Parameters {
         // Shape text params suggesting where the textrun will be rendered
         DrawTarget   *mDrawTarget;
         // Pointer to arbitrary user data (which should outlive the textrun)
         void         *mUserData;
@@ -593,16 +561,39 @@ public:
         int32_t       mAppUnitsPerDevUnit;
     };
 
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxTextRunFactory();
 };
 
+struct gfxTextRange {
+    enum {
+        // flags for recording the kind of font-matching that was used
+        kFontGroup      = 0x0001,
+        kPrefsFallback  = 0x0002,
+        kSystemFallback = 0x0004
+    };
+    gfxTextRange(uint32_t aStart, uint32_t aEnd,
+                 gfxFont* aFont, uint8_t aMatchType,
+                 mozilla::gfx::ShapedTextFlags aOrientation)
+        : start(aStart),
+          end(aEnd),
+          font(aFont),
+          matchType(aMatchType),
+          orientation(aOrientation)
+    { }
+    uint32_t Length() const { return end - start; }
+    uint32_t start, end;
+    RefPtr<gfxFont> font;
+    uint8_t matchType;
+    mozilla::gfx::ShapedTextFlags orientation;
+};
+
 /**
  * gfxFontShaper
  *
  * This class implements text shaping (character to glyph mapping and
  * glyph layout). There is a gfxFontShaper subclass for each text layout
  * technology (uniscribe, core text, harfbuzz,....) we support.
  *
  * The shaper is responsible for setting up glyph data in gfxTextRuns.
@@ -686,17 +677,17 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gf
  * their entirety rather than using cached words, because there may be layout
  * features that depend on the inter-word spaces).
  */
 class gfxShapedText
 {
 public:
     typedef mozilla::unicode::Script Script;
 
-    gfxShapedText(uint32_t aLength, uint32_t aFlags,
+    gfxShapedText(uint32_t aLength, mozilla::gfx::ShapedTextFlags aFlags,
                   int32_t aAppUnitsPerDevUnit)
         : mLength(aLength)
         , mFlags(aFlags)
         , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
     { }
 
     virtual ~gfxShapedText() { }
 
@@ -963,57 +954,61 @@ public:
                                 const char16_t *aString,
                                 uint32_t         aLength);
     // In 8-bit text, there won't actually be any clusters, but we still need
     // the space-marking functionality.
     void SetupClusterBoundaries(uint32_t       aOffset,
                                 const uint8_t *aString,
                                 uint32_t       aLength);
 
-    uint32_t GetFlags() const {
+    mozilla::gfx::ShapedTextFlags GetFlags() const {
         return mFlags;
     }
 
     bool IsVertical() const {
-        return (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK) !=
-                gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL;
+        return (GetFlags() & mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_MASK) !=
+                mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL;
     }
 
     bool UseCenterBaseline() const {
-        uint32_t orient = GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK;
-        return orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED ||
-               orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+        mozilla::gfx::ShapedTextFlags orient =
+            GetFlags() & mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_MASK;
+        return orient == mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED ||
+               orient == mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
 
     bool IsRightToLeft() const {
-        return (GetFlags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
+        return (GetFlags() & mozilla::gfx::ShapedTextFlags::TEXT_IS_RTL) ==
+               mozilla::gfx::ShapedTextFlags::TEXT_IS_RTL;
     }
 
     bool IsSidewaysLeft() const {
-        return (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK) ==
-               gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
+        return (GetFlags() & mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_MASK) ==
+               mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
     }
 
     // Return true if the logical inline direction is reversed compared to
     // normal physical coordinates (i.e. if it is leftwards or upwards)
     bool IsInlineReversed() const {
         return IsSidewaysLeft() != IsRightToLeft();
     }
 
     gfxFloat GetDirection() const {
         return IsInlineReversed() ? -1.0f : 1.0f;
     }
 
     bool DisableLigatures() const {
         return (GetFlags() &
-                gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
+                mozilla::gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES) ==
+               mozilla::gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;
     }
 
     bool TextIs8Bit() const {
-        return (GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
+        return (GetFlags() & mozilla::gfx::ShapedTextFlags::TEXT_IS_8BIT) ==
+               mozilla::gfx::ShapedTextFlags::TEXT_IS_8BIT;
     }
 
     int32_t GetAppUnitsPerDevUnit() const {
         return mAppUnitsPerDevUnit;
     }
 
     uint32_t GetLength() const {
         return mLength;
@@ -1156,19 +1151,19 @@ protected:
     };
 
     mozilla::UniquePtr<DetailedGlyphStore>   mDetailedGlyphs;
 
     // Number of char16_t characters and CompressedGlyph glyph records
     uint32_t                        mLength;
 
     // Shaping flags (direction, ligature-suppression)
-    uint32_t                        mFlags;
+    mozilla::gfx::ShapedTextFlags   mFlags;
 
-    int32_t                         mAppUnitsPerDevUnit;
+    uint16_t                        mAppUnitsPerDevUnit;
 };
 
 /*
  * gfxShapedWord: an individual (space-delimited) run of text shaped with a
  * particular font, without regard to external context.
  *
  * The glyph data is copied into gfxTextRuns as needed from the cache of
  * ShapedWords associated with each gfxFont instance.
@@ -1185,17 +1180,17 @@ public:
     // so caller must check for success.
     //
     // This does NOT perform shaping, so the returned word contains no
     // glyph data; the caller must call gfxFont::ShapeText() with appropriate
     // parameters to set up the glyphs.
     static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
                                  Script aRunScript,
                                  int32_t aAppUnitsPerDevUnit,
-                                 uint32_t aFlags,
+                                 mozilla::gfx::ShapedTextFlags aFlags,
                                  gfxFontShaper::RoundingFlags aRounding) {
         NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
                      "excessive length for gfxShapedWord!");
 
         // Compute size needed including the mCharacterGlyphs array
         // and a copy of the original text
         uint32_t size =
             offsetof(gfxShapedWord, mCharGlyphsStorage) +
@@ -1209,25 +1204,25 @@ public:
         return new (storage) gfxShapedWord(aText, aLength, aRunScript,
                                            aAppUnitsPerDevUnit, aFlags,
                                            aRounding);
     }
 
     static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength,
                                  Script aRunScript,
                                  int32_t aAppUnitsPerDevUnit,
-                                 uint32_t aFlags,
+                                 mozilla::gfx::ShapedTextFlags aFlags,
                                  gfxFontShaper::RoundingFlags aRounding) {
         NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
                      "excessive length for gfxShapedWord!");
 
         // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
         // then we convert the text to an 8-bit version and call the 8-bit
         // Create function instead.
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+        if (aFlags & mozilla::gfx::ShapedTextFlags::TEXT_IS_8BIT) {
             nsAutoCString narrowText;
             LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
                                     narrowText);
             return Create((const uint8_t*)(narrowText.BeginReading()),
                           aLength, aRunScript, aAppUnitsPerDevUnit, aFlags,
                           aRounding);
         }
 
@@ -1296,32 +1291,34 @@ public:
 
 private:
     // so that gfxTextRun can share our DetailedGlyphStore class
     friend class gfxTextRun;
 
     // Construct storage for a ShapedWord, ready to receive glyph data
     gfxShapedWord(const uint8_t *aText, uint32_t aLength,
                   Script aRunScript,
-                  int32_t aAppUnitsPerDevUnit, uint32_t aFlags,
+                  int32_t aAppUnitsPerDevUnit,
+                  mozilla::gfx::ShapedTextFlags aFlags,
                   gfxFontShaper::RoundingFlags aRounding)
-        : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
+        : gfxShapedText(aLength, aFlags | mozilla::gfx::ShapedTextFlags::TEXT_IS_8BIT,
                         aAppUnitsPerDevUnit)
         , mScript(aRunScript)
         , mRounding(aRounding)
         , mAgeCounter(0)
     {
         memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
         uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
         memcpy(text, aText, aLength * sizeof(uint8_t));
     }
 
     gfxShapedWord(const char16_t *aText, uint32_t aLength,
                   Script aRunScript,
-                  int32_t aAppUnitsPerDevUnit, uint32_t aFlags,
+                  int32_t aAppUnitsPerDevUnit,
+                  mozilla::gfx::ShapedTextFlags aFlags,
                   gfxFontShaper::RoundingFlags aRounding)
         : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
         , mScript(aRunScript)
         , mRounding(aRounding)
         , mAgeCounter(0)
     {
         memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
         char16_t *text = reinterpret_cast<char16_t*>(&mCharGlyphsStorage[aLength]);
@@ -1647,17 +1644,17 @@ public:
      * .dt  Moz2D DrawTarget to which we're drawing
      *
      * Callers guarantee:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
      */
     void Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
               gfxPoint *aPt, const TextRunDrawParams& aRunParams,
-              uint16_t aOrientation);
+              mozilla::gfx::ShapedTextFlags aOrientation);
 
     /**
      * Draw the emphasis marks for the given text run. Its prerequisite
      * and output are similiar to the method Draw().
      * @param aPt the baseline origin of the emphasis marks.
      * @param aParams some drawing parameters, see EmphasisMarkDrawParams.
      */
     void DrawEmphasisMarks(const gfxTextRun* aShapedText, gfxPoint* aPt,
@@ -1684,17 +1681,18 @@ public:
      * The default implementation just uses font metrics and aTextRun's
      * advances, and assumes no characters fall outside the font box. In
      * general this is insufficient, because that assumption is not always true.
      */
     virtual RunMetrics Measure(const gfxTextRun *aTextRun,
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                DrawTarget* aDrawTargetForTightBoundingBox,
-                               Spacing *aSpacing, uint16_t aOrientation);
+                               Spacing *aSpacing,
+                               mozilla::gfx::ShapedTextFlags aOrientation);
     /**
      * Line breaks have been changed at the beginning and/or end of a substring
      * of the text. Reshaping may be required; glyph updating is permitted.
      * @return true if anything was changed, false otherwise
      */
     bool NotifyLineBreaksChanged(gfxTextRun *aTextRun,
                                    uint32_t aStart, uint32_t aLength)
     { return false; }
@@ -1755,17 +1753,17 @@ public:
 
     template<typename T>
     bool InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
                               gfxTextRun *aTextRun,
                               const T    *aText,
                               uint32_t    aOffset,
                               uint32_t    aLength,
                               uint8_t     aMatchType,
-                              uint16_t    aOrientation,
+                              mozilla::gfx::ShapedTextFlags aOrientation,
                               Script      aScript,
                               bool        aSyntheticLower,
                               bool        aSyntheticUpper);
 
     // call the (virtual) InitTextRun method to do glyph generation/shaping,
     // limiting the length of text passed by processing the run in multiple
     // segments if necessary
     template<typename T>
@@ -1782,17 +1780,17 @@ public:
     template<typename T>
     gfxShapedWord* GetShapedWord(DrawTarget *aDrawTarget,
                                  const T *aText,
                                  uint32_t aLength,
                                  uint32_t aHash,
                                  Script aRunScript,
                                  bool aVertical,
                                  int32_t aAppUnitsPerDevUnit,
-                                 uint32_t aFlags,
+                                 mozilla::gfx::ShapedTextFlags aFlags,
                                  RoundingFlags aRounding,
                                  gfxTextPerfMetrics *aTextPerf);
 
     // Ensure the ShapedWord cache is initialized. This MUST be called before
     // any attempt to use GetShapedWord().
     void InitWordCache() {
         if (!mWordCache) {
             mWordCache = mozilla::MakeUnique<nsTHashtable<CacheHashEntry>>();
@@ -2037,56 +2035,58 @@ protected:
     RefPtr<gfxFontEntry> mFontEntry;
 
     struct CacheHashKey {
         union {
             const uint8_t   *mSingle;
             const char16_t *mDouble;
         }                mText;
         uint32_t         mLength;
-        uint32_t         mFlags;
+        mozilla::gfx::ShapedTextFlags mFlags;
         Script           mScript;
         int32_t          mAppUnitsPerDevUnit;
         PLDHashNumber    mHashKey;
         bool             mTextIs8Bit;
         RoundingFlags    mRounding;
 
         CacheHashKey(const uint8_t *aText, uint32_t aLength,
                      uint32_t aStringHash,
                      Script aScriptCode, int32_t aAppUnitsPerDevUnit,
-                     uint32_t aFlags, RoundingFlags aRounding)
+                     mozilla::gfx::ShapedTextFlags aFlags,
+                     RoundingFlags aRounding)
             : mLength(aLength),
               mFlags(aFlags),
               mScript(aScriptCode),
               mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
               mHashKey(aStringHash
                            + static_cast<int32_t>(aScriptCode)
                            + aAppUnitsPerDevUnit * 0x100
-                           + aFlags * 0x10000
+                           + uint16_t(aFlags) * 0x10000
                            + int(aRounding)),
               mTextIs8Bit(true),
               mRounding(aRounding)
         {
-            NS_ASSERTION(aFlags & gfxTextRunFactory::TEXT_IS_8BIT,
+            NS_ASSERTION(aFlags & mozilla::gfx::ShapedTextFlags::TEXT_IS_8BIT,
                          "8-bit flag should have been set");
             mText.mSingle = aText;
         }
 
         CacheHashKey(const char16_t *aText, uint32_t aLength,
                      uint32_t aStringHash,
                      Script aScriptCode, int32_t aAppUnitsPerDevUnit,
-                     uint32_t aFlags, RoundingFlags aRounding)
+                     mozilla::gfx::ShapedTextFlags aFlags,
+                     RoundingFlags aRounding)
             : mLength(aLength),
               mFlags(aFlags),
               mScript(aScriptCode),
               mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
               mHashKey(aStringHash
                            + static_cast<int32_t>(aScriptCode)
                            + aAppUnitsPerDevUnit * 0x100
-                           + aFlags * 0x10000
+                           + uint16_t(aFlags) * 0x10000
                            + int(aRounding)),
               mTextIs8Bit(false),
               mRounding(aRounding)
         {
             // We can NOT assert that TEXT_IS_8BIT is false in aFlags here,
             // because this might be an 8bit-only word from a 16-bit textrun,
             // in which case the text we're passed is still in 16-bit form,
             // and we'll have to use an 8-to-16bit comparison in KeyEquals.
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -136,17 +136,17 @@ gfxGDIFont::SetupCairoFont(DrawTarget* a
 }
 
 gfxFont::RunMetrics
 gfxGDIFont::Measure(const gfxTextRun *aTextRun,
                     uint32_t aStart, uint32_t aEnd,
                     BoundingBoxType aBoundingBoxType,
                     DrawTarget *aRefDrawTarget,
                     Spacing *aSpacing,
-                    uint16_t aOrientation)
+                    gfx::ShapedTextFlags aOrientation)
 {
     gfxFont::RunMetrics metrics =
         gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
                          aRefDrawTarget, aSpacing, aOrientation);
 
     // if aBoundingBoxType is LOOSE_INK_EXTENTS
     // and the underlying cairo font may be antialiased,
     // we can't trust Windows to have considered all the pixels
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -37,17 +37,17 @@ public:
     virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
 
     /* override Measure to add padding for antialiasing */
     virtual RunMetrics Measure(const gfxTextRun *aTextRun,
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                DrawTarget *aDrawTargetForTightBoundingBox,
                                Spacing *aSpacing,
-                               uint16_t aOrientation) override;
+                               mozilla::gfx::ShapedTextFlags aOrientation) override;
 
     /* required for MathML to suppress effects of ClearType "padding" */
     mozilla::UniquePtr<gfxFont>
     CopyWithAntialiasOption(AntialiasOption anAAOption) override;
 
     // If the font has a cmap table, we handle it purely with harfbuzz;
     // but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
     virtual bool ProvidesGetGlyph() const override {
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1477,17 +1477,17 @@ gfxHarfBuzzShaper::ShapeText(DrawTarget 
     hb_buffer_t *buffer = hb_buffer_create();
     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
 
     hb_buffer_set_direction(buffer,
                             aVertical ? HB_DIRECTION_TTB :
                                         (isRightToLeft ? HB_DIRECTION_RTL :
                                                          HB_DIRECTION_LTR));
     hb_script_t scriptTag;
-    if (aShapedText->GetFlags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
+    if (aShapedText->GetFlags() & gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT) {
         scriptTag = sMathScript;
     } else {
         scriptTag = GetHBScriptUsedForShaping(aScript);
     }
     hb_buffer_set_script(buffer, scriptTag);
 
     hb_language_t language;
     if (style->languageOverride) {
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -194,17 +194,17 @@ gfxMacFont::SetupCairoFont(DrawTarget* a
 }
 
 gfxFont::RunMetrics
 gfxMacFont::Measure(const gfxTextRun *aTextRun,
                     uint32_t aStart, uint32_t aEnd,
                     BoundingBoxType aBoundingBoxType,
                     DrawTarget *aRefDrawTarget,
                     Spacing *aSpacing,
-                    uint16_t aOrientation)
+                    gfx::ShapedTextFlags aOrientation)
 {
     gfxFont::RunMetrics metrics =
         gfxFont::Measure(aTextRun, aStart, aEnd,
                          aBoundingBoxType, aRefDrawTarget, aSpacing,
                          aOrientation);
 
     // if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add
     // a pixel column each side of the bounding box in case of antialiasing "bleed"
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -34,17 +34,17 @@ public:
     bool SetupCairoFont(DrawTarget* aDrawTarget) override;
 
     /* override Measure to add padding for antialiasing */
     RunMetrics Measure(const gfxTextRun *aTextRun,
                        uint32_t aStart, uint32_t aEnd,
                        BoundingBoxType aBoundingBoxType,
                        DrawTarget *aDrawTargetForTightBoundingBox,
                        Spacing *aSpacing,
-                       uint16_t aOrientation) override;
+                       mozilla::gfx::ShapedTextFlags aOrientation) override;
 
     // We need to provide hinted (non-linear) glyph widths if using a font
     // with embedded color bitmaps (Apple Color Emoji), as Core Text renders
     // the glyphs with non-linear scaling at small pixel sizes.
     bool ProvidesGlyphWidths() const override {
         return mVariationFont ||
                mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
     }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2040,17 +2040,17 @@ static void ShutdownCMS()
     gCMSInitialized = false;
 }
 
 // default SetupClusterBoundaries, based on Unicode properties;
 // platform subclasses may override if they wish
 void
 gfxPlatform::SetupClusterBoundaries(gfxTextRun *aTextRun, const char16_t *aString)
 {
-    if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
+    if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_IS_8BIT) {
         // 8-bit text doesn't have clusters.
         // XXX is this true in all languages???
         // behdad: don't think so.  Czech for example IIRC has a
         // 'ch' grapheme.
         // jfkthame: but that's not expected to behave as a grapheme cluster
         // for selection/editing/etc.
         return;
     }
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -106,17 +106,17 @@ AccountStorageForTextRun(gfxTextRun *aTe
     gTextRunStorage += bytes*aSign;
     gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
 }
 #endif
 
 static bool
 NeedsGlyphExtents(gfxTextRun *aTextRun)
 {
-    if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
+    if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX)
         return true;
     uint32_t numRuns;
     const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
     for (uint32_t i = 0; i < numRuns; ++i) {
         if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
             return true;
     }
     return false;
@@ -140,34 +140,40 @@ gfxTextRun::AllocateStorageForTextRun(si
     memset(reinterpret_cast<char*>(storage) + aSize, 0,
            aLength * sizeof(CompressedGlyph));
 
     return storage;
 }
 
 already_AddRefed<gfxTextRun>
 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
-                   uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
+                   uint32_t aLength, gfxFontGroup *aFontGroup,
+                   gfx::ShapedTextFlags aFlags, 
+                   nsTextFrameUtils::Flags aFlags2)
 {
     void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
     if (!storage) {
         return nullptr;
     }
 
     RefPtr<gfxTextRun> result = new (storage) gfxTextRun(aParams, aLength,
-                                                         aFontGroup, aFlags);
+                                                         aFontGroup,
+                                                         aFlags, aFlags2);
     return result.forget();
 }
 
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
-                       uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
+                       uint32_t aLength, gfxFontGroup *aFontGroup,
+                       gfx::ShapedTextFlags aFlags,
+                       nsTextFrameUtils::Flags aFlags2)
     : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
     , mSingleGlyphRun()
     , mUserData(aParams->mUserData)
     , mFontGroup(aFontGroup)
+    , mFlags2(aFlags2)
     , mReleasedFontGroup(false)
     , mHasGlyphRunArray(false)
     , mShapingState(eShapingState_Normal)
 {
     NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
     NS_ADDREF(mFontGroup);
 
 #ifndef RELEASE_OR_BETA
@@ -192,17 +198,18 @@ gfxTextRun::gfxTextRun(const gfxTextRunF
 
 gfxTextRun::~gfxTextRun()
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     AccountStorageForTextRun(this, -1);
 #endif
 #ifdef DEBUG
     // Make it easy to detect a dead text run
-    mFlags = 0xFFFFFFFF;
+    mFlags = ~gfx::ShapedTextFlags();
+    mFlags2 = ~nsTextFrameUtils::Flags();
 #endif
 
     if (mHasGlyphRunArray) {
         mGlyphRunArray.~nsTArray<GlyphRun>();
     } else {
         mSingleGlyphRun.mFont = nullptr;
     }
 
@@ -313,17 +320,17 @@ gfxTextRun::ComputeLigatureData(Range aP
         // We need to clip before the part if any cluster is drawn before
         // this part.
         result.mClipBeforePart = partClusterIndex > 0;
         // We need to clip after the part if any cluster is drawn after
         // this part.
         result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
     }
 
-    if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
+    if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
         gfxFont::Spacing spacing;
         if (aPartRange.start == result.mRange.start) {
             aProvider->GetSpacing(
                 Range(aPartRange.start, aPartRange.start + 1), &spacing);
             result.mPartWidth += spacing.mBefore;
         }
         if (aPartRange.end == result.mRange.end) {
             aProvider->GetSpacing(
@@ -385,17 +392,17 @@ GetAdjustedSpacing(const gfxTextRun *aTe
 }
 
 bool
 gfxTextRun::GetAdjustedSpacingArray(Range aRange, PropertyProvider *aProvider,
                                     Range aSpacingRange,
                                     nsTArray<PropertyProvider::Spacing>*
                                         aSpacing) const
 {
-    if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
+    if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING))
         return false;
     if (!aSpacing->AppendElements(aRange.Length()))
         return false;
     auto spacingOffset = aSpacingRange.start - aRange.start;
     memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
     GetAdjustedSpacing(this, aSpacingRange, aProvider,
                        aSpacing->Elements() + spacingOffset);
     memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
@@ -421,17 +428,18 @@ gfxTextRun::ShrinkToLigatureBoundaries(R
             --aRange->end;
         }
     }
 }
 
 void
 gfxTextRun::DrawGlyphs(gfxFont *aFont, Range aRange, gfxPoint *aPt,
                        PropertyProvider *aProvider, Range aSpacingRange,
-                       TextRunDrawParams& aParams, uint16_t aOrientation) const
+                       TextRunDrawParams& aParams,
+                       gfx::ShapedTextFlags aOrientation) const
 {
     AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
                                                aSpacingRange, &spacingBuffer);
     aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
     aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
 }
 
@@ -458,17 +466,17 @@ ClipPartialLigature(const gfxTextRun* aT
         }
     }
 }
 
 void
 gfxTextRun::DrawPartialLigature(gfxFont *aFont, Range aRange,
                                 gfxPoint *aPt, PropertyProvider *aProvider,
                                 TextRunDrawParams& aParams,
-                                uint16_t aOrientation) const
+                                gfx::ShapedTextFlags aOrientation) const
 {
     if (aRange.start >= aRange.end) {
         return;
     }
 
     // Draw partial ligature. We hack this by clipping the ligature.
     LigatureData data = ComputeLigatureData(aRange, aProvider);
     gfxRect clipExtents = aParams.context->GetClipExtents();
@@ -761,33 +769,33 @@ gfxTextRun::DrawEmphasisMarks(gfxContext
 }
 
 void
 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
                                     gfxFont::BoundingBoxType aBoundingBoxType,
                                     DrawTarget* aRefDrawTarget,
                                     PropertyProvider *aProvider,
                                     Range aSpacingRange,
-                                    uint16_t aOrientation,
+                                    gfx::ShapedTextFlags aOrientation,
                                     Metrics *aMetrics) const
 {
     AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
                                                aSpacingRange, &spacingBuffer);
     Metrics metrics = aFont->Measure(this, aRange.start, aRange.end,
                                      aBoundingBoxType, aRefDrawTarget,
                                      haveSpacing ? spacingBuffer.Elements() : nullptr,
                                      aOrientation);
     aMetrics->CombineWith(metrics, IsRightToLeft());
 }
 
 void
 gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
     gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
-    PropertyProvider *aProvider, uint16_t aOrientation,
+    PropertyProvider *aProvider, gfx::ShapedTextFlags aOrientation,
     Metrics *aMetrics) const
 {
     if (aRange.start >= aRange.end)
         return;
 
     // Measure partial ligature. We hack this by clipping the metrics in the
     // same way we clip the drawing.
     LigatureData data = ComputeLigatureData(aRange, aProvider);
@@ -944,27 +952,28 @@ gfxTextRun::BreakAndMeasureText(uint32_t
 {
     aMaxLength = std::min(aMaxLength, GetLength() - aStart);
 
     NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
 
     Range bufferRange(aStart, aStart +
         std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
     PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
-    bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
+    bool haveSpacing = aProvider &&
+        !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING);
     if (haveSpacing) {
         GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
     }
     AutoTArray<HyphenType, 4096> hyphenBuffer;
     HyphenationState wordState;
     wordState.mostRecentBoundary = aStart;
     bool haveHyphenation = aProvider &&
         (aProvider->GetHyphensOption() == StyleHyphens::Auto ||
          (aProvider->GetHyphensOption() == StyleHyphens::Manual &&
-          (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
+          !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
     if (haveHyphenation) {
         if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
             aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements());
             if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
                 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
                                          &wordState);
             }
         } else {
@@ -1219,17 +1228,17 @@ gfxTextRun::GetAdvanceWidth(Range aRange
                                     aProvider);
 
     if (aSpacing) {
         aSpacing->mBefore = aSpacing->mAfter = 0;
     }
 
     // Account for all remaining spacing here. This is more efficient than
     // processing it along with the glyphs.
-    if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
+    if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
         uint32_t i;
         AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
         if (spacingBuffer.AppendElements(aRange.Length())) {
             GetAdjustedSpacing(this, ligatureRange, aProvider,
                                spacingBuffer.Elements());
             for (i = 0; i < ligatureRange.Length(); ++i) {
                 PropertyProvider::Spacing *space = &spacingBuffer[i];
                 result += space->mBefore + space->mAfter;
@@ -1284,20 +1293,20 @@ gfxTextRun::FindFirstGlyphRunContaining(
     NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
                  "Hmm, something went wrong, aOffset should have been found");
     return start;
 }
 
 nsresult
 gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
                         uint32_t aUTF16Offset, bool aForceNewRun,
-                        uint16_t aOrientation)
+                        gfx::ShapedTextFlags aOrientation)
 {
     NS_ASSERTION(aFont, "adding glyph run for null font!");
-    NS_ASSERTION(aOrientation != gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED,
+    NS_ASSERTION(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
                  "mixed orientation should have been resolved");
     if (!aFont) {
         return NS_OK;
     }
     if (!mHasGlyphRunArray) {
         // We don't currently have an array.
         if (!mSingleGlyphRun.mFont) {
             // This is the first glyph run: just store it directly.
@@ -1387,17 +1396,17 @@ gfxTextRun::SortGlyphRuns()
 
     AutoTArray<GlyphRun,16> runs(Move(mGlyphRunArray));
     GlyphRunOffsetComparator comp;
     runs.Sort(comp);
 
     // Now copy back, coalescing adjacent glyph runs that have the same font
     mGlyphRunArray.Clear();
     gfxFont* prevFont = nullptr;
-    uint16_t prevOrient = 0;
+    gfx::ShapedTextFlags prevOrient = gfx::ShapedTextFlags();
     DebugOnly<uint32_t> prevOffset = 0;
     for (auto& run : runs) {
         // a GlyphRun with the same font and orientation as the previous can
         // just be skipped; the last GlyphRun will cover its character range.
         MOZ_ASSERT(run.mFont != nullptr);
         if (prevFont == nullptr ||
             run.mFont != prevFont || run.mOrientation != prevOrient) {
             // If two fonts have the same character offset, Sort() will have
@@ -1588,30 +1597,31 @@ gfxTextRun::ClearGlyphsAndCharacters()
     ResetGlyphRuns();
     memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
            mLength * sizeof(CompressedGlyph));
     mDetailedGlyphs = nullptr;
 }
 
 void
 gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
-                          uint32_t aCharIndex, uint16_t aOrientation)
+                          uint32_t aCharIndex,
+                          gfx::ShapedTextFlags aOrientation)
 {
     if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
         return;
     }
 
     aFont->InitWordCache();
     static const uint8_t space = ' ';
-    uint32_t flags = gfxTextRunFactory::TEXT_IS_8BIT |
-                     gfxTextRunFactory::TEXT_IS_ASCII |
-                     gfxTextRunFactory::TEXT_IS_PERSISTENT |
-                     aOrientation;
+    gfx::ShapedTextFlags
+        flags = gfx::ShapedTextFlags::TEXT_IS_8BIT |
+                gfx::ShapedTextFlags::TEXT_IS_PERSISTENT |
+                aOrientation;
     bool vertical =
-        (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0;
+        !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
     gfxFontShaper::RoundingFlags roundingFlags =
         aFont->GetRoundOffsetsToPixels(aDrawTarget);
     gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget,
                                              &space, 1,
                                              gfxShapedWord::HashMix(0, ' '),
                                              Script::LATIN,
                                              vertical,
                                              mAppUnitsPerDevUnit,
@@ -1622,25 +1632,26 @@ gfxTextRun::SetSpaceGlyph(gfxFont* aFont
         AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
                     aOrientation);
         CopyGlyphDataFrom(sw, aCharIndex);
     }
 }
 
 bool
 gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
-                                  char16_t aSpaceChar, uint16_t aOrientation)
+                                  char16_t aSpaceChar,
+                                  gfx::ShapedTextFlags aOrientation)
 {
     uint32_t spaceGlyph = aFont->GetSpaceGlyph();
     if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
         return false;
     }
 
     gfxFont::Orientation fontOrientation =
-        (aOrientation & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) ?
+        (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) ?
             gfxFont::eVertical : gfxFont::eHorizontal;
     uint32_t spaceWidthAppUnits =
         NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth *
                    mAppUnitsPerDevUnit);
     if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
         return false;
     }
 
@@ -2164,36 +2175,40 @@ gfxFontGroup::IsInvalidChar(char16_t ch)
         return true;
     }
     return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
              (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
             IsBidiControl(ch));
 }
 
 already_AddRefed<gfxTextRun>
-gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
+gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams,
+                               gfx::ShapedTextFlags aFlags,
+                               nsTextFrameUtils::Flags aFlags2)
 {
-    aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
-    return gfxTextRun::Create(aParams, 0, this, aFlags);
+    aFlags |= ShapedTextFlags::TEXT_IS_8BIT | ShapedTextFlags::TEXT_IS_PERSISTENT;
+    return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2);
 }
 
 already_AddRefed<gfxTextRun>
-gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
+gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams,
+                               gfx::ShapedTextFlags aFlags,
+                               nsTextFrameUtils::Flags aFlags2)
 {
-    aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
+    aFlags |= ShapedTextFlags::TEXT_IS_8BIT | ShapedTextFlags::TEXT_IS_PERSISTENT;
 
     RefPtr<gfxTextRun> textRun =
-        gfxTextRun::Create(aParams, 1, this, aFlags);
+        gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2);
     if (!textRun) {
         return nullptr;
     }
 
-    uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
-    if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
-        orientation = TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+    gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
+    if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
+        orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
     }
 
     gfxFont *font = GetFirstValidFont();
     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
@@ -2222,27 +2237,29 @@ gfxFontGroup::MakeSpaceTextRun(const Par
     // Note that the gfxGlyphExtents glyph bounds storage for the font will
     // always contain an entry for the font's space glyph, so we don't have
     // to call FetchGlyphExtents here.
     return textRun.forget();
 }
 
 already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
-                               const Parameters *aParams, uint32_t aFlags)
+                               const Parameters *aParams,
+                               gfx::ShapedTextFlags aFlags,
+                               nsTextFrameUtils::Flags aFlags2)
 {
     RefPtr<gfxTextRun> textRun =
-        gfxTextRun::Create(aParams, aLength, this, aFlags);
+        gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
     if (!textRun) {
         return nullptr;
     }
 
-    uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
-    if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
-        orientation = TEXT_ORIENT_VERTICAL_UPRIGHT;
+    gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
+    if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
+        orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
     textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
                          orientation);
     return textRun.forget();
 }
 
 already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
@@ -2250,22 +2267,24 @@ gfxFontGroup::MakeHyphenTextRun(DrawTarg
 {
     // only use U+2010 if it is supported by the first font in the group;
     // it's better to use ASCII '-' from the primary font than to fall back to
     // U+2010 from some other, possibly poorly-matching face
     static const char16_t hyphen = 0x2010;
     gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
     if (font->HasCharacter(hyphen)) {
         return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
-                           gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
+                           ShapedTextFlags::TEXT_IS_PERSISTENT,
+                           nsTextFrameUtils::Flags(), nullptr);
     }
 
     static const uint8_t dash = '-';
     return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
-                       gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
+                       ShapedTextFlags::TEXT_IS_PERSISTENT,
+                       nsTextFrameUtils::Flags(), nullptr);
 }
 
 gfxFloat
 gfxFontGroup::GetHyphenWidth(const gfxTextRun::PropertyProvider* aProvider)
 {
     if (mHyphenWidth < 0) {
         RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
         if (dt) {
@@ -2275,67 +2294,71 @@ gfxFontGroup::GetHyphenWidth(const gfxTe
             mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
         }
     }
     return mHyphenWidth;
 }
 
 already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
-                          const Parameters *aParams, uint32_t aFlags,
+                          const Parameters *aParams,
+                          gfx::ShapedTextFlags aFlags,
+                          nsTextFrameUtils::Flags aFlags2,
                           gfxMissingFontRecorder *aMFR)
 {
     if (aLength == 0) {
-        return MakeEmptyTextRun(aParams, aFlags);
+        return MakeEmptyTextRun(aParams, aFlags, aFlags2);
     }
     if (aLength == 1 && aString[0] == ' ') {
-        return MakeSpaceTextRun(aParams, aFlags);
+        return MakeSpaceTextRun(aParams, aFlags, aFlags2);
     }
 
-    aFlags |= TEXT_IS_8BIT;
+    aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
 
     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
-        return MakeBlankTextRun(aLength, aParams, aFlags);
+        return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
     }
 
     RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
-                                                    aFlags);
+                                                    aFlags, aFlags2);
     if (!textRun) {
         return nullptr;
     }
 
     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
     return textRun.forget();
 }
 
 already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
-                          const Parameters *aParams, uint32_t aFlags,
+                          const Parameters *aParams,
+                          gfx::ShapedTextFlags aFlags, 
+                          nsTextFrameUtils::Flags aFlags2,
                           gfxMissingFontRecorder *aMFR)
 {
     if (aLength == 0) {
-        return MakeEmptyTextRun(aParams, aFlags);
+        return MakeEmptyTextRun(aParams, aFlags, aFlags2);
     }
     if (aLength == 1 && aString[0] == ' ') {
-        return MakeSpaceTextRun(aParams, aFlags);
+        return MakeSpaceTextRun(aParams, aFlags, aFlags2);
     }
     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
-        return MakeBlankTextRun(aLength, aParams, aFlags);
+        return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
     }
 
     RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
-                                                    aFlags);
+                                                    aFlags, aFlags2);
     if (!textRun) {
         return nullptr;
     }
 
     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
@@ -2356,17 +2379,17 @@ gfxFontGroup::InitTextRun(DrawTarget* aD
     // in case we're converting Western to Hindi/Arabic digits
     int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
     UniquePtr<char16_t[]> transformedString;
     if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
         // scan the string for numerals that may need to be transformed;
         // if we find any, we'll make a local copy here and use that for
         // font matching and glyph generation/shaping
         bool prevIsArabic =
-            (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
+            !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR);
         for (uint32_t i = 0; i < aLength; ++i) {
             char16_t origCh = aString[i];
             char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
             if (newCh != origCh) {
                 if (!transformedString) {
                     transformedString = MakeUnique<char16_t[]>(aLength);
                     if (sizeof(T) == sizeof(char16_t)) {
                         memcpy(transformedString.get(), aString, i * sizeof(char16_t));
@@ -2536,26 +2559,26 @@ gfxFontGroup::InitScriptRun(DrawTarget* 
         UpdateUserFonts();
     }
 
     gfxFont *mainFont = GetFirstValidFont();
 
     uint32_t runStart = 0;
     AutoTArray<gfxTextRange,3> fontRanges;
     ComputeRanges(fontRanges, aString, aLength, aRunScript,
-                  aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK);
+                  aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK);
     uint32_t numRanges = fontRanges.Length();
     bool missingChars = false;
 
     for (uint32_t r = 0; r < numRanges; r++) {
         const gfxTextRange& range = fontRanges[r];
         uint32_t matchedLength = range.Length();
         gfxFont *matchedFont = range.font;
         bool vertical =
-            range.orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+            range.orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
         // create the glyph run for this range
         if (matchedFont && mStyle.noFallbackVariantFeatures) {
             // common case - just do glyph layout and record the
             // resulting positioned glyphs
             aTextRun->AddGlyphRun(matchedFont, range.matchType,
                                   aOffset + runStart, (matchedLength > 0),
                                   range.orientation);
             if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
@@ -2743,23 +2766,24 @@ gfxFontGroup::InitScriptRun(DrawTarget* 
     }
 
     if (aMFR && missingChars) {
         aMFR->RecordScript(aRunScript);
     }
 }
 
 gfxTextRun *
-gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
+gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
+                                 gfx::ShapedTextFlags aFlags,
                                  LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
 {
-    MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK),
+    MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK),
                "flags here should only be used to specify orientation");
     if (mCachedEllipsisTextRun &&
-        (mCachedEllipsisTextRun->GetFlags() & TEXT_ORIENT_MASK) == aFlags &&
+        (mCachedEllipsisTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags &&
         mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
         return mCachedEllipsisTextRun.get();
     }
 
     // Use a Unicode ellipsis if the font supports it,
     // otherwise use three ASCII periods as fallback.
     gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
     nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
@@ -2769,17 +2793,18 @@ gfxFontGroup::GetEllipsisTextRun(int32_t
                             ArrayLength(kASCIIPeriodsChar) - 1);
 
     RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
     Parameters params = {
         refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
     };
     mCachedEllipsisTextRun =
         MakeTextRun(ellipsis.get(), ellipsis.Length(), &params,
-                    aFlags | TEXT_IS_PERSISTENT, nullptr);
+                    aFlags | ShapedTextFlags::TEXT_IS_PERSISTENT,
+                    nsTextFrameUtils::Flags(), nullptr);
     if (!mCachedEllipsisTextRun) {
         return nullptr;
     }
     // don't let the presence of a cached ellipsis textrun prolong the
     // fontgroup's life
     mCachedEllipsisTextRun->ReleaseFontGroup();
     return mCachedEllipsisTextRun.get();
 }
@@ -3072,17 +3097,18 @@ gfxFontGroup::FindFontForChar(uint32_t a
     *aMatchType = gfxTextRange::kSystemFallback;
     font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
     return font.forget();
 }
 
 template<typename T>
 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
                                  const T *aString, uint32_t aLength,
-                                 Script aRunScript, uint16_t aOrientation)
+                                 Script aRunScript,
+                                 gfx::ShapedTextFlags aOrientation)
 {
     NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
     NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
 
     uint32_t prevCh = 0;
     uint32_t nextCh = aString[0];
     if (sizeof(T) == sizeof(char16_t)) {
         if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) &&
@@ -3146,28 +3172,28 @@ void gfxFontGroup::ComputeRanges(nsTArra
             } else if (matchType == gfxTextRange::kSystemFallback) {
                 mTextPerf->current.fallbackSystem++;
             }
         }
 #endif
 
         prevCh = ch;
 
-        uint16_t orient = aOrientation;
-        if (aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+        ShapedTextFlags orient = aOrientation;
+        if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
             // For CSS text-orientation:mixed, we need to resolve orientation
             // on a per-character basis using the UTR50 orientation property.
             switch (GetVerticalOrientation(ch)) {
             case VERTICAL_ORIENTATION_U:
             case VERTICAL_ORIENTATION_Tr:
             case VERTICAL_ORIENTATION_Tu:
-                orient = TEXT_ORIENT_VERTICAL_UPRIGHT;
+                orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
                 break;
             case VERTICAL_ORIENTATION_R:
-                orient = TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+                orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
                 break;
             }
         }
 
         if (lastRangeIndex == -1) {
             // first char ==> make a new range
             aRanges.AppendElement(gfxTextRange(0, 1, font, matchType, orient));
             lastRangeIndex++;
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -15,16 +15,17 @@
 #include "nsTArray.h"
 #include "gfxSkipChars.h"
 #include "gfxPlatform.h"
 #include "mozilla/MemoryReporting.h"
 #include "DrawMode.h"
 #include "harfbuzz/hb.h"
 #include "nsUnicodeScriptCodes.h"
 #include "nsColor.h"
+#include "nsTextFrameUtils.h"
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 class gfxContext;
 class gfxFontGroup;
 class gfxUserFontEntry;
@@ -426,43 +427,40 @@ public:
                                  bool aCanWordWrap,
                                  gfxBreakPriority *aBreakPriority);
 
     // Utility getters
 
     void *GetUserData() const { return mUserData; }
     void SetUserData(void *aUserData) { mUserData = aUserData; }
 
-    void SetFlagBits(uint32_t aFlags) {
-      NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
-                   "Only user flags should be mutable");
-      mFlags |= aFlags;
+    void SetFlagBits(nsTextFrameUtils::Flags aFlags) {
+      mFlags2 |= aFlags;
     }
-    void ClearFlagBits(uint32_t aFlags) {
-      NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
-                   "Only user flags should be mutable");
-      mFlags &= ~aFlags;
+    void ClearFlagBits(nsTextFrameUtils::Flags aFlags) {
+      mFlags2 &= ~aFlags;
     }
     const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
     gfxFontGroup *GetFontGroup() const { return mFontGroup; }
 
 
     // Call this, don't call "new gfxTextRun" directly. This does custom
     // allocation and initialization
     static already_AddRefed<gfxTextRun>
     Create(const gfxTextRunFactory::Parameters *aParams,
            uint32_t aLength, gfxFontGroup *aFontGroup,
-           uint32_t aFlags);
+           mozilla::gfx::ShapedTextFlags aFlags,
+           nsTextFrameUtils::Flags aFlags2);
 
     // The text is divided into GlyphRuns as necessary. (In the vast majority
     // of cases, a gfxTextRun contains just a single GlyphRun.)
     struct GlyphRun {
         RefPtr<gfxFont> mFont; // never null in a valid GlyphRun
         uint32_t        mCharacterOffset; // into original UTF16 string
-        uint16_t        mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
+        mozilla::gfx::ShapedTextFlags mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
         uint8_t         mMatchType;
     };
 
     class GlyphRunIterator {
     public:
         GlyphRunIterator(const gfxTextRun *aTextRun, Range aRange)
           : mTextRun(aTextRun)
           , mStartOffset(aRange.start)
@@ -513,25 +511,28 @@ public:
      * previously added run uses the same font.  If glyph runs are
      * added out of strictly increasing aStartCharIndex order (via
      * force), then SortGlyphRuns must be called after all glyph runs
      * are added before any further operations are performed with this
      * TextRun.
      */
     nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
                          uint32_t aStartCharIndex, bool aForceNewRun,
-                         uint16_t aOrientation);
+                         mozilla::gfx::ShapedTextFlags aOrientation);
     void ResetGlyphRuns()
     {
         if (mHasGlyphRunArray) {
-            mGlyphRunArray.~nsTArray<GlyphRun>();
-            mHasGlyphRunArray = false;
-        } else {
-            mSingleGlyphRun.mFont = nullptr;
+            MOZ_ASSERT(mGlyphRunArray.Length() > 1);
+            // Discard all but the first GlyphRun...
+            mGlyphRunArray.TruncateLength(1);
+            // ...and then convert to the single-run representation.
+            ConvertFromGlyphRunArray();
         }
+        // Clear out the one remaining GlyphRun.
+        mSingleGlyphRun.mFont = nullptr;
     }
     void SortGlyphRuns();
     void SanitizeGlyphRuns();
 
     const CompressedGlyph* GetCharacterGlyphs() const final {
         MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
         return mCharacterGlyphs;
     }
@@ -539,33 +540,35 @@ public:
         MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
         return mCharacterGlyphs;
     }
 
     // clean out results from shaping in progress, used for fallback scenarios
     void ClearGlyphsAndCharacters();
 
     void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
-                       uint32_t aCharIndex, uint16_t aOrientation);
+                       uint32_t aCharIndex,
+                       mozilla::gfx::ShapedTextFlags aOrientation);
 
     // Set the glyph data for the given character index to the font's
     // space glyph, IF this can be done as a "simple" glyph record
     // (not requiring a DetailedGlyph entry). This avoids the need to call
     // the font shaper and go through the shaped-word cache for most spaces.
     //
     // The parameter aSpaceChar is the original character code for which
     // this space glyph is being used; if this is U+0020, we need to record
     // that it could be trimmed at a run edge, whereas other kinds of space
     // (currently just U+00A0) would not be trimmable/breakable.
     //
     // Returns true if it was able to set simple glyph data for the space;
     // if it returns false, the caller needs to fall back to some other
     // means to create the necessary (detailed) glyph data.
     bool SetSpaceGlyphIfSimple(gfxFont *aFont, uint32_t aCharIndex,
-                               char16_t aSpaceChar, uint16_t aOrientation);
+                               char16_t aSpaceChar, 
+                               mozilla::gfx::ShapedTextFlags aOrientation);
 
     // Record the positions of specific characters that layout may need to
     // detect in the textrun, even though it doesn't have an explicit copy
     // of the original text. These are recorded using flag bits in the
     // CompressedGlyph record; if necessary, we convert "simple" glyph records
     // to "complex" ones as the Tab and Newline flags are not present in
     // simple CompressedGlyph records.
     void SetIsTab(uint32_t aIndex) {
@@ -634,33 +637,37 @@ public:
 
     // return storage used by this run, for memory reporter;
     // nsTransformedTextRun needs to override this as it holds additional data
     virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
       MOZ_MUST_OVERRIDE;
     virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
       MOZ_MUST_OVERRIDE;
 
+    nsTextFrameUtils::Flags GetFlags2() const {
+        return mFlags2;
+    }
+
     // Get the size, if it hasn't already been gotten, marking as it goes.
     size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)  {
-        if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
+        if (mFlags2 & nsTextFrameUtils::Flags::TEXT_RUN_SIZE_ACCOUNTED) {
             return 0;
         }
-        mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+        mFlags2 |= nsTextFrameUtils::Flags::TEXT_RUN_SIZE_ACCOUNTED;
         return SizeOfIncludingThis(aMallocSizeOf);
     }
     void ResetSizeOfAccountingFlags() {
-        mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+        mFlags2 &= ~nsTextFrameUtils::Flags::TEXT_RUN_SIZE_ACCOUNTED;
     }
 
     // shaping state - for some font features, fallback is required that
     // affects the entire run. for example, fallback for one script/font
     // portion of a textrun requires fallback to be applied to the entire run
 
-    enum ShapingState {
+    enum ShapingState : uint8_t {
         eShapingState_Normal,                 // default state
         eShapingState_ShapingWithFeature,     // have shaped with feature
         eShapingState_ShapingWithFallback,    // have shaped with fallback
         eShapingState_Aborted,                // abort initial iteration
         eShapingState_ForceFallbackFeature    // redo with fallback forced on
     };
 
     ShapingState GetShapingState() const { return mShapingState; }
@@ -693,17 +700,19 @@ public:
 protected:
     /**
      * Create a textrun, and set its mCharacterGlyphs to point immediately
      * after the base object; this is ONLY used in conjunction with placement
      * new, after allocating a block large enough for the glyph records to
      * follow the base textrun object.
      */
     gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
-               uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags);
+               uint32_t aLength, gfxFontGroup *aFontGroup,
+               mozilla::gfx::ShapedTextFlags aFlags,
+               nsTextFrameUtils::Flags aFlags2);
 
     /**
      * Helper for the Create() factory method to allocate the required
      * glyph storage for a textrun object with the basic size aSize,
      * plus room for aLength glyph records.
      */
     static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
 
@@ -739,44 +748,45 @@ private:
     // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
     LigatureData ComputeLigatureData(Range aPartRange,
                                      PropertyProvider *aProvider) const;
     gfxFloat ComputePartialLigatureWidth(Range aPartRange,
                                          PropertyProvider *aProvider) const;
     void DrawPartialLigature(gfxFont *aFont, Range aRange,
                              gfxPoint *aPt, PropertyProvider *aProvider,
                              TextRunDrawParams& aParams,
-                             uint16_t aOrientation) const;
+                             mozilla::gfx::ShapedTextFlags aOrientation) const;
     // Advance aRange.start to the start of the nearest ligature, back
     // up aRange.end to the nearest ligature end; may result in
     // aRange->start == aRange->end.
     void ShrinkToLigatureBoundaries(Range* aRange) const;
     // result in appunits
     gfxFloat GetPartialLigatureWidth(Range aRange,
                                      PropertyProvider *aProvider) const;
     void AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
                                           gfxFont::BoundingBoxType aBoundingBoxType,
                                           DrawTarget* aRefDrawTarget,
                                           PropertyProvider *aProvider,
-                                          uint16_t aOrientation,
+                                          mozilla::gfx::ShapedTextFlags aOrientation,
                                           Metrics *aMetrics) const;
 
     // **** measurement helper ****
     void AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
                                  gfxFont::BoundingBoxType aBoundingBoxType,
                                  DrawTarget* aRefDrawTarget,
                                  PropertyProvider *aProvider,
                                  Range aSpacingRange,
-                                 uint16_t aOrientation,
+                                 mozilla::gfx::ShapedTextFlags aOrientation,
                                  Metrics *aMetrics) const;
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, Range aRange, gfxPoint *aPt,
                     PropertyProvider *aProvider, Range aSpacingRange,
-                    TextRunDrawParams& aParams, uint16_t aOrientation) const;
+                    TextRunDrawParams& aParams,
+                    mozilla::gfx::ShapedTextFlags aOrientation) const;
 
     // The textrun holds either a single GlyphRun -or- an array;
     // the flag mHasGlyphRunArray tells us which is present.
     union {
         GlyphRun           mSingleGlyphRun;
         nsTArray<GlyphRun> mGlyphRunArray;
     };
 
@@ -797,16 +807,18 @@ private:
         mHasGlyphRunArray = false;
     }
 
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed on creation, but our reference
                                   // may be released by ReleaseFontGroup()
     gfxSkipChars      mSkipChars;
 
+    nsTextFrameUtils::Flags mFlags2; // additional flags (see also gfxShapedText::mFlags)
+
     bool              mSkipDrawing; // true if the font group we used had a user font
                                     // download that's in progress, so we should hide text
                                     // until the download completes (or timeout fires)
     bool              mReleasedFontGroup; // we already called NS_RELEASE on
                                           // mFontGroup, so don't do it again
     bool              mHasGlyphRunArray; // whether we're using an array or
                                          // just storing a single glyphrun
 
@@ -852,45 +864,50 @@ public:
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
     virtual already_AddRefed<gfxTextRun>
     MakeTextRun(const char16_t *aString, uint32_t aLength,
-                const Parameters *aParams, uint32_t aFlags,
+                const Parameters *aParams,
+                mozilla::gfx::ShapedTextFlags aFlags,
+                nsTextFrameUtils::Flags aFlags2,
                 gfxMissingFontRecorder *aMFR);
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
     virtual already_AddRefed<gfxTextRun>
     MakeTextRun(const uint8_t *aString, uint32_t aLength,
-                const Parameters *aParams, uint32_t aFlags,
+                const Parameters *aParams,
+                mozilla::gfx::ShapedTextFlags aFlags,
+                nsTextFrameUtils::Flags aFlags2,
                 gfxMissingFontRecorder *aMFR);
 
     /**
      * Textrun creation helper for clients that don't want to pass
      * a full Parameters record.
      */
     template<typename T>
     already_AddRefed<gfxTextRun>
     MakeTextRun(const T* aString, uint32_t aLength,
                 DrawTarget* aRefDrawTarget,
                 int32_t aAppUnitsPerDevUnit,
-                uint32_t aFlags,
+                mozilla::gfx::ShapedTextFlags aFlags,
+                nsTextFrameUtils::Flags aFlags2,
                 gfxMissingFontRecorder *aMFR)
     {
         gfxTextRunFactory::Parameters params = {
             aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
         };
-        return MakeTextRun(aString, aLength, &params, aFlags, aMFR);
+        return MakeTextRun(aString, aLength, &params, aFlags, aFlags2, aMFR);
     }
 
     // Get the (possibly-cached) width of the hyphen character.
     gfxFloat GetHyphenWidth(const gfxTextRun::PropertyProvider* aProvider);
 
     /**
      * Make a text run representing a single hyphen character.
      * This will use U+2010 HYPHEN if available in the first font,
@@ -961,31 +978,33 @@ public:
     public:
       virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
     };
     // The gfxFontGroup keeps ownership of this textrun.
     // It is only guaranteed to exist until the next call to GetEllipsisTextRun
     // (which might use a different appUnitsPerDev value or flags) for the font
     // group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
     // Get it/use it/forget it :) - don't keep a reference that might go stale.
-    gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
+    gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
+                                   mozilla::gfx::ShapedTextFlags aFlags,
                                    LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
 
 protected:
     // search through pref fonts for a character, return nullptr if no matching pref font
     already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh);
 
     already_AddRefed<gfxFont>
         WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
                                     Script aRunScript);
 
     template<typename T>
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
                        const T *aString, uint32_t aLength,
-                       Script aRunScript, uint16_t aOrientation);
+                       Script aRunScript,
+                       mozilla::gfx::ShapedTextFlags aOrientation);
 
     class FamilyFace {
     public:
         FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
                        mNeedsBold(false), mFontCreated(false),
                        mLoading(false), mInvalid(false),
                        mCheckForFallbackFaces(false)
         { }
@@ -1153,24 +1172,29 @@ protected:
                                           // download to complete (or fallback
                                           // timer to fire)
 
     /**
      * Textrun creation short-cuts for special cases where we don't need to
      * call a font shaper to generate glyphs.
      */
     already_AddRefed<gfxTextRun>
-    MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
+    MakeEmptyTextRun(const Parameters *aParams,
+                     mozilla::gfx::ShapedTextFlags aFlags,
+                     nsTextFrameUtils::Flags aFlags2);
 
     already_AddRefed<gfxTextRun>
-    MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
+    MakeSpaceTextRun(const Parameters *aParams,
+                     mozilla::gfx::ShapedTextFlags aFlags,
+                     nsTextFrameUtils::Flags aFlags2);
 
     already_AddRefed<gfxTextRun>
     MakeBlankTextRun(uint32_t aLength, const Parameters *aParams,
-                     uint32_t aFlags);
+                     mozilla::gfx::ShapedTextFlags aFlags,
+                     nsTextFrameUtils::Flags aFlags2);
 
     // Initialize the list of fonts
     void BuildFontList();
 
     // Get the font at index i within the fontlist.
     // Will initiate userfont load if not already loaded.
     // May return null if userfont not loaded or if font invalid
     virtual gfxFont* GetFontAt(int32_t i, uint32_t aCh = 0x20);
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -430,45 +430,52 @@ FrameAnimator::RequestRefresh(AnimationS
   MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid);
 
   return ret;
 }
 
 LookupResult
 FrameAnimator::GetCompositedFrame(AnimationState& aState)
 {
+  LookupResult result =
+    SurfaceCache::Lookup(ImageKey(mImage),
+                         RasterSurfaceKey(mSize,
+                                          DefaultSurfaceFlags(),
+                                          PlaybackType::eAnimated));
+
   if (aState.mCompositedFrameInvalid) {
     MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
     MOZ_ASSERT(aState.GetHasBeenDecoded());
     MOZ_ASSERT(!aState.GetIsCurrentlyDecoded());
-    return LookupResult(MatchType::NOT_FOUND);
+    if (result.Type() == MatchType::NOT_FOUND) {
+      return result;
+    }
+    return LookupResult(MatchType::PENDING);
   }
 
   // If we have a composited version of this frame, return that.
   if (mLastCompositedFrameIndex >= 0 &&
       (uint32_t(mLastCompositedFrameIndex) == aState.mCurrentAnimationFrameIndex)) {
     return LookupResult(DrawableSurface(mCompositingFrame->DrawableRef()),
                         MatchType::EXACT);
   }
 
   // Otherwise return the raw frame. DoBlend is required to ensure that we only
   // hit this case if the frame is not paletted and doesn't require compositing.
-  LookupResult result =
-    SurfaceCache::Lookup(ImageKey(mImage),
-                         RasterSurfaceKey(mSize,
-                                          DefaultSurfaceFlags(),
-                                          PlaybackType::eAnimated));
   if (!result) {
     return result;
   }
 
   // Seek to the appropriate frame. If seeking fails, it means that we couldn't
   // get the frame we're looking for; treat this as if the lookup failed.
   if (NS_FAILED(result.Surface().Seek(aState.mCurrentAnimationFrameIndex))) {
-    return LookupResult(MatchType::NOT_FOUND);
+    if (result.Type() == MatchType::NOT_FOUND) {
+      return result;
+    }
+    return LookupResult(MatchType::PENDING);
   }
 
   MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
              "About to return a paletted frame");
 
   return result;
 }
 
--- a/image/test/reftest/downscaling/huge-1.html
+++ b/image/test/reftest/downscaling/huge-1.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html><meta charset=utf-8>
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<body style="margin: 0px; overflow: hidden">
+<body style="margin: 0px">
   <script>
     let args = location.search.substring(1).split(',');
     document.write(`<img src="${args[0]}" width="${args[1]}" height="${args[2]}">`);
   </script>
 </body>
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -10,16 +10,17 @@
 #include "BroadcastChannelChild.h"
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasChild.h"
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/SchedulerGroup.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
@@ -542,16 +543,28 @@ BackgroundChildImpl::AllocPGamepadTestCh
 bool
 BackgroundChildImpl::DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<dom::GamepadTestChannelChild*>(aActor);
   return true;
 }
 
+#ifdef EARLY_BETA_OR_EARLIER
+void
+BackgroundChildImpl::OnChannelReceivedMessage(const Message& aMsg)
+{
+  if (aMsg.type() == layout::PVsync::MessageType::Msg_Notify__ID) {
+    // Not really necessary to look at the message payload, it will be
+    // <0.5ms away from TimeStamp::Now()
+    SchedulerGroup::MarkVsyncReceived();
+  }
+}
+#endif
+
 } // namespace ipc
 } // namespace mozilla
 
 mozilla::ipc::IPCResult
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -192,16 +192,21 @@ protected:
   virtual bool
   DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor) override;
 
   virtual PGamepadTestChannelChild*
   AllocPGamepadTestChannelChild() override;
 
   virtual bool
   DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor) override;
+
+#ifdef EARLY_BETA_OR_EARLIER
+  virtual void
+  OnChannelReceivedMessage(const Message& aMsg) override;
+#endif
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -1083,16 +1083,18 @@ void
 MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (MaybeInterceptSpecialIOMessage(aMsg))
         return;
 
+    mListener->OnChannelReceivedMessage(aMsg);
+
     // Regardless of the Interrupt stack, if we're awaiting a sync reply,
     // we know that it needs to be immediately handled to unblock us.
     if (aMsg.is_sync() && aMsg.is_reply()) {
         IPC_LOG("Received reply seqno=%d xid=%d", aMsg.seqno(), aMsg.transaction_id());
 
         if (aMsg.seqno() == mTimedOutMessageSeqno) {
             // Drop the message, but allow future sync messages to be sent.
             IPC_LOG("Received reply to timedout message; igoring; xid=%d", mTimedOutMessageSeqno);
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -380,16 +380,17 @@ public:
     GetMessageEventTarget(const Message& aMsg);
 
     already_AddRefed<nsIEventTarget>
     GetActorEventTarget(IProtocol* aActor);
 
     virtual nsIEventTarget*
     GetActorEventTarget();
 
+    virtual void OnChannelReceivedMessage(const Message& aMsg) {}
 protected:
     // Override this method in top-level protocols to change the event target
     // for a new actor (and its sub-actors).
     virtual already_AddRefed<nsIEventTarget>
     GetConstructedEventTarget(const Message& aMsg) { return nullptr; }
 
     // Override this method in top-level protocols to change the event target
     // for specific messages.
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -78,18 +78,18 @@ using mozilla::RangedPtr;
 #if !ENABLE_INTL_API
 
 /*
  * When the Internationalization API isn't enabled, we also shouldn't link
  * against ICU. However, we still want to compile this code in order to prevent
  * bit rot. The following stub implementations for ICU functions make this
  * possible. The functions using them should never be called, so they assert
  * and return error codes. Signatures adapted from ICU header files locid.h,
- * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h; see the ICU
- * directory for license.
+ * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h, uloc.h;
+ * see the ICU directory for license.
  */
 
 namespace {
 
 enum UErrorCode {
     U_ZERO_ERROR,
     U_BUFFER_OVERFLOW_ERROR,
 };
@@ -826,16 +826,22 @@ u_strToLower(UChar* dest, int32_t destCa
 
 int32_t
 u_strToUpper(UChar* dest, int32_t destCapacity, const UChar* src, int32_t srcLength,
              const char* locale, UErrorCode* pErrorCode)
 {
     MOZ_CRASH("u_strToUpper: Intl API disabled");
 }
 
+const char*
+uloc_toUnicodeLocaleType(const char* keyword, const char* value)
+{
+    MOZ_CRASH("uloc_toUnicodeLocaleType: Intl API disabled");
+}
+
 } // anonymous namespace
 
 #endif
 
 
 /******************** Common to Intl constructors ********************/
 
 static bool
@@ -882,42 +888,40 @@ LegacyIntlInitialize(JSContext* cx, Hand
         return false;
 
     MOZ_ASSERT(result.isObject(), "Legacy Intl object initializer must return an object");
     return true;
 }
 
 // CountAvailable and GetAvailable describe the signatures used for ICU API
 // to determine available locales for various functionality.
-typedef int32_t
-(* CountAvailable)();
-
-typedef const char*
-(* GetAvailable)(int32_t localeIndex);
+using CountAvailable = int32_t (*)();
+using GetAvailable = const char* (*)(int32_t localeIndex);
 
 static bool
 intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
                       GetAvailable getAvailable, MutableHandleValue result)
 {
     RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
     if (!locales)
         return false;
 
 #if ENABLE_INTL_API
+    RootedAtom a(cx);
     uint32_t count = countAvailable();
     RootedValue t(cx, BooleanValue(true));
     for (uint32_t i = 0; i < count; i++) {
         const char* locale = getAvailable(i);
         auto lang = DuplicateString(cx, locale);
         if (!lang)
             return false;
         char* p;
         while ((p = strchr(lang.get(), '_')))
             *p = '-';
-        RootedAtom a(cx, Atomize(cx, lang.get(), strlen(lang.get())));
+        a = Atomize(cx, lang.get(), strlen(lang.get()));
         if (!a)
             return false;
         if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
                             JSPROP_ENUMERATE))
         {
             return false;
         }
     }
@@ -1206,45 +1210,37 @@ js::intl_availableCollations(JSContext* 
 
     uint32_t index = 0;
 
     // The first element of the collations array must be |null| per
     // ES2017 Intl, 10.2.3 Internal Slots.
     if (!DefineElement(cx, collations, index++, NullHandleValue))
         return false;
 
+    RootedString jscollation(cx);
+    RootedValue element(cx);
     for (uint32_t i = 0; i < count; i++) {
         const char* collation = uenum_next(values, nullptr, &status);
         if (U_FAILURE(status)) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
             return false;
         }
 
         // Per ECMA-402, 10.2.3, we don't include standard and search:
         // "The values 'standard' and 'search' must not be used as elements in
         // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
         // array."
         if (equal(collation, "standard") || equal(collation, "search"))
             continue;
 
-        // ICU returns old-style keyword values; map them to BCP 47 equivalents
-        // (see http://bugs.icu-project.org/trac/ticket/9620).
-        if (equal(collation, "dictionary"))
-            collation = "dict";
-        else if (equal(collation, "gb2312han"))
-            collation = "gb2312";
-        else if (equal(collation, "phonebook"))
-            collation = "phonebk";
-        else if (equal(collation, "traditional"))
-            collation = "trad";
-
-        RootedString jscollation(cx, JS_NewStringCopyZ(cx, collation));
+        // ICU returns old-style keyword values; map them to BCP 47 equivalents.
+        jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
         if (!jscollation)
             return false;
-        RootedValue element(cx, StringValue(jscollation));
+        element = StringValue(jscollation);
         if (!DefineElement(cx, collations, index++, element))
             return false;
     }
 
     args.rval().setObject(*collations);
     return true;
 }
 
@@ -2673,29 +2669,26 @@ js::intl_DateTimeFormat_availableLocales
 
     RootedValue result(cx);
     if (!intl_availableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
         return false;
     args.rval().set(result);
     return true;
 }
 
-// ICU returns old-style keyword values; map them to BCP 47 equivalents
-// (see http://bugs.icu-project.org/trac/ticket/9620).
-static const char*
-bcp47CalendarName(const char* icuName)
+struct CalendarAlias
 {
-    if (equal(icuName, "ethiopic-amete-alem"))
-        return "ethioaa";
-    if (equal(icuName, "gregorian"))
-        return "gregory";
-    if (equal(icuName, "islamic-civil"))
-        return "islamicc";
-    return icuName;
-}
+    const char* const calendar;
+    const char* const alias;
+};
+
+const CalendarAlias calendarAliases[] = {
+    { "islamic-civil", "islamicc" },
+    { "ethioaa", "ethiopic-amete-alem" }
+};
 
 bool
 js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
@@ -2718,17 +2711,18 @@ js::intl_availableCalendars(JSContext* c
         ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
 
         const char* calendar = ucal_getType(cal, &status);
         if (U_FAILURE(status)) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
             return false;
         }
 
-        jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
+        // ICU returns old-style keyword values; map them to BCP 47 equivalents
+        jscalendar = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
         if (!jscalendar)
             return false;
     }
 
     RootedValue element(cx, StringValue(jscalendar));
     if (!DefineElement(cx, calendars, index++, element))
         return false;
 
@@ -2748,22 +2742,37 @@ js::intl_availableCalendars(JSContext* c
 
     for (; count > 0; count--) {
         const char* calendar = uenum_next(values, nullptr, &status);
         if (U_FAILURE(status)) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
             return false;
         }
 
-        jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
+        // ICU returns old-style keyword values; map them to BCP 47 equivalents
+        calendar = uloc_toUnicodeLocaleType("ca", calendar);
+
+        jscalendar = JS_NewStringCopyZ(cx, calendar);
         if (!jscalendar)
             return false;
         element = StringValue(jscalendar);
         if (!DefineElement(cx, calendars, index++, element))
             return false;
+
+        // ICU doesn't return calendar aliases, append them here.
+        for (const auto& calendarAlias : calendarAliases) {
+            if (equal(calendar, calendarAlias.calendar)) {
+                jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
+                if (!jscalendar)
+                    return false;
+                element = StringValue(jscalendar);
+                if (!DefineElement(cx, calendars, index++, element))
+                    return false;
+            }
+        }
     }
 
     args.rval().setObject(*calendars);
     return true;
 }
 
 template<typename Char>
 static constexpr Char
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -377,17 +377,17 @@ function CanonicalizeLanguageTag(locale)
     // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
     // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
     locale = callFunction(std_String_toLowerCase, locale);
 
     // Handle mappings for complete tags.
     if (hasOwn(locale, langTagMappings))
         return langTagMappings[locale];
 
-    var subtags = StringSplitString(ToString(locale), "-");
+    var subtags = StringSplitString(locale, "-");
     var i = 0;
 
     // Handle the standard part: All subtags before the first singleton or "x".
     // "zh-nan-hans-bu-variant2-variant1"
     while (i < subtags.length) {
         var subtag = subtags[i];
 
         // If we reach the start of an extension sequence or private use part,
@@ -925,20 +925,17 @@ function LookupMatcher(availableLocales,
     }
 
     var result = new Record();
     if (availableLocale !== undefined) {
         result.locale = availableLocale;
         if (locale !== noExtensionsLocale) {
             var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
             var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale);
-            var extension = extensionMatch[0];
-            var extensionIndex = extensionMatch.index;
-            result.extension = extension;
-            result.extensionIndex = extensionIndex;
+            result.extension = extensionMatch[0];
         }
     } else {
         result.locale = DefaultLocale();
     }
     return result;
 }
 
 
@@ -953,16 +950,89 @@ function LookupMatcher(availableLocales,
  */
 function BestFitMatcher(availableLocales, requestedLocales) {
     // this implementation doesn't have anything better
     return LookupMatcher(availableLocales, requestedLocales);
 }
 
 
 /**
+ * Returns the Unicode extension value subtags for the requested key subtag.
+ *
+ * NOTE: PR to add UnicodeExtensionValue to ECMA-402 isn't yet written.
+ */
+function UnicodeExtensionValue(extension, key) {
+    assert(typeof extension === "string", "extension is a string value");
+    assert(function() {
+        var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
+        var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, extension);
+        return extensionMatch !== null && extensionMatch[0] === extension;
+    }(), "extension is a Unicode extension subtag");
+    assert(typeof key === "string", "key is a string value");
+    assert(key.length === 2, "key is a Unicode extension key subtag");
+
+    // Step 1.
+    var size = extension.length;
+
+    // Step 2.
+    var searchValue = "-" + key + "-";
+
+    // Step 3.
+    var pos = callFunction(std_String_indexOf, extension, searchValue);
+
+    // Step 4.
+    if (pos !== -1) {
+        // Step 4.a.
+        var start = pos + 4;
+
+        // Step 4.b.
+        var end = start;
+
+        // Step 4.c.
+        var k = start;
+
+        // Steps 4.d-e.
+        while (true) {
+            // Step 4.e.i.
+            var e = callFunction(std_String_indexOf, extension, "-", k);
+
+            // Step 4.e.ii.
+            var len = e === -1 ? size - k : e - k;
+
+            // Step 4.e.iii.
+            if (len === 2)
+                break;
+
+            // Step 4.e.iv.
+            if (e === -1) {
+                end = size;
+                break;
+            }
+
+            // Step 4.e.v.
+            end = e;
+            k = e + 1;
+        }
+
+        // Step 4.f.
+        return callFunction(String_substring, extension, start, end);
+    }
+
+    // Step 5.
+    searchValue = "-" + key;
+
+    // Steps 6-7.
+    if (callFunction(std_String_endsWith, extension, searchValue))
+        return "";
+
+    // Step 8 (implicit).
+}
+
+
+/**
  * Compares a BCP 47 language priority list against availableLocales and
  * determines the best available language to meet the request. Options specified
  * through Unicode extension subsequences are negotiated separately, taking the
  * caller's relevant extensions and locale data as well as client-provided
  * options into consideration.
  *
  * Spec: ECMAScript Internationalization API Specification, 9.2.5.
  */
@@ -973,135 +1043,131 @@ function ResolveLocale(availableLocales,
     var matcher = options.localeMatcher;
     var r = (matcher === "lookup")
             ? LookupMatcher(availableLocales, requestedLocales)
             : BestFitMatcher(availableLocales, requestedLocales);
 
     // Step 4.
     var foundLocale = r.locale;
 
-    // Step 5.a.
+    // Step 5 (Not applicable in this implementation).
     var extension = r.extension;
-    var extensionIndex, extensionSubtags, extensionSubtagsLength;
-
-    // Step 5.
-    if (extension !== undefined) {
-        // Step 5.b.
-        extensionIndex = r.extensionIndex;
-
-        // Steps 5.d-e.
-        extensionSubtags = StringSplitString(ToString(extension), "-");
-        extensionSubtagsLength = extensionSubtags.length;
-    }
 
     // Steps 6-7.
     var result = new Record();
     result.dataLocale = foundLocale;
 
     // Step 8.
     var supportedExtension = "-u";
 
-    // Steps 9-11.
+    // Steps 9-12.
     var i = 0;
     var len = relevantExtensionKeys.length;
     var foundLocaleData;
     if (len > 0) {
         // In this implementation, localeData is a function, not an object.
-        // Step 11.b.
+        // Step 12.b.
         foundLocaleData = localeData(foundLocale);
     }
     while (i < len) {
-        // Step 11.a.
+        // Step 12.a.
         var key = relevantExtensionKeys[i];
 
-        // Step 11.c.
+        // Step 12.c.
         var keyLocaleData = foundLocaleData[key];
 
         // Locale data provides default value.
-        // Step 11.d.
+        // Step 12.d.
         var value = keyLocaleData[0];
+        assert(typeof value === "string" || value === null, "unexpected locale data value");
 
         // Locale tag may override.
 
-        // Step 11.e.
+        // Step 12.e.
         var supportedExtensionAddition = "";
 
-        // Step 11.f is implemented by Utilities.js.
-
-        var valuePos;
-
-        // Step 11.g.
-        if (extensionSubtags !== undefined) {
-            // Step 11.g.i.
-            var keyPos = callFunction(ArrayIndexOf, extensionSubtags, key);
-
-            // Step 11.g.ii.
-            if (keyPos !== -1) {
-                // Step 11.g.ii.1.
-                if (keyPos + 1 < extensionSubtagsLength &&
-                    extensionSubtags[keyPos + 1].length > 2)
-                {
-                    // Step 11.g.ii.1.a.
-                    var requestedValue = extensionSubtags[keyPos + 1];
-
-                    // Step 11.g.ii.1.b.
-                    valuePos = callFunction(ArrayIndexOf, keyLocaleData, requestedValue);
-
-                    // Step 11.g.ii.1.c.
-                    if (valuePos !== -1) {
+        // Step 12.f.
+        if (extension !== undefined) {
+            // NB: The step annotations don't yet match the ES2017 Intl draft,
+            // 94045d234762ad107a3d09bb6f7381a65f1a2f9b, because the PR to add
+            // the new UnicodeExtensionValue abstract operation still needs to
+            // be written.
+
+            // Step 12.f.i.
+            var requestedValue = UnicodeExtensionValue(extension, key);
+
+            // Step 12.f.ii.
+            if (requestedValue !== undefined) {
+                // Step 12.f.ii.1.
+                if (requestedValue !== "") {
+                    // Step 12.f.ii.1.a.
+                    if (callFunction(ArrayIndexOf, keyLocaleData, requestedValue) !== -1) {
                         value = requestedValue;
                         supportedExtensionAddition = "-" + key + "-" + value;
                     }
                 } else {
-                    // Step 11.g.ii.2.
+                    // Step 12.f.ii.2.
 
                     // According to the LDML spec, if there's no type value,
                     // and true is an allowed value, it's used.
-
-                    // Step 11.g.ii.2.a.
-                    valuePos = callFunction(ArrayIndexOf, keyLocaleData, "true");
-
-                    // Step 11.g.ii.2.b.
-                    if (valuePos !== -1)
+                    if (callFunction(ArrayIndexOf, keyLocaleData, "true") !== -1)
                         value = "true";
                 }
             }
         }
 
         // Options override all.
 
-        // Step 11.h.i.
+        // Step 12.g.i.
         var optionsValue = options[key];
 
-        // Step 11.h, 11.h.ii.
+        // Step 12.g, 12.g.ii.
         if (optionsValue !== undefined &&
+            optionsValue !== value &&
             callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1)
         {
-            // Step 11.h.ii.1.
-            if (optionsValue !== value) {
-                value = optionsValue;
-                supportedExtensionAddition = "";
-            }
+            value = optionsValue;
+            supportedExtensionAddition = "";
         }
 
-        // Steps 11.i-k.
+        // Steps 12.h-j.
         result[key] = value;
         supportedExtension += supportedExtensionAddition;
         i++;
     }
 
-    // Step 12.
+    // Step 13.
     if (supportedExtension.length > 2) {
-        var preExtension = callFunction(String_substring, foundLocale, 0, extensionIndex);
-        var postExtension = callFunction(String_substring, foundLocale, extensionIndex);
-        foundLocale = preExtension + supportedExtension + postExtension;
+        assert(!callFunction(std_String_startsWith, foundLocale, "x-"),
+               "unexpected privateuse-only locale returned from ICU");
+
+        // Step 13.a.
+        var privateIndex = callFunction(std_String_indexOf, foundLocale, "-x-");
+
+        // Steps 13.b-c.
+        if (privateIndex === -1) {
+            foundLocale += supportedExtension;
+        } else {
+            var preExtension = callFunction(String_substring, foundLocale, 0, privateIndex);
+            var postExtension = callFunction(String_substring, foundLocale, privateIndex);
+            foundLocale = preExtension + supportedExtension + postExtension;
+        }
+
+        // Step 13.d.
+        assert(IsStructurallyValidLanguageTag(foundLocale), "invalid locale after concatenation");
+
+        // Step 13.e (Not required in this implementation, because we don't
+        // canonicalize Unicode extension subtags).
+        assert(foundLocale === CanonicalizeLanguageTag(foundLocale), "same locale with extension");
     }
 
-    // Steps 13-14.
+    // Step 14.
     result.locale = foundLocale;
+
+    // Step 15.
     return result;
 }
 
 
 /**
  * Returns the subset of requestedLocales for which availableLocales has a
  * matching (possibly fallback) locale. Locales appear in the same order in the
  * returned list as in the input list.
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3221,16 +3221,18 @@ ASTSerializer::propertyName(ParseNode* p
 bool
 ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
 {
     if (pn->isKind(PNK_MUTATEPROTO)) {
         RootedValue val(cx);
         return expression(pn->pn_kid, &val) &&
                builder.prototypeMutation(val, &pn->pn_pos, dst);
     }
+    if (pn->isKind(PNK_SPREAD))
+        return expression(pn, dst);
 
     PropKind kind;
     switch (pn->getOp()) {
       case JSOP_INITPROP:
         kind = PROP_INIT;
         break;
 
       case JSOP_INITPROP_GETTER:
@@ -3341,16 +3343,26 @@ ASTSerializer::objectPattern(ParseNode* 
 {
     MOZ_ASSERT(pn->isKind(PNK_OBJECT));
 
     NodeVector elts(cx);
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
+        if (propdef->isKind(PNK_SPREAD)) {
+            RootedValue target(cx);
+            RootedValue spread(cx);
+            if (!pattern(propdef->pn_kid, &target))
+                return false;
+            if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
+                return false;
+            elts.infallibleAppend(spread);
+            continue;
+        }
         LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP));
 
         RootedValue key(cx);
         ParseNode* target;
         if (propdef->isKind(PNK_MUTATEPROTO)) {
             RootedValue pname(cx, StringValue(cx->names().proto));
             if (!builder.literal(pname, &propdef->pn_pos, &key))
                 return false;
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -151,16 +151,82 @@ ErrorWrongTypeArg(JSContext* cx, unsigne
 
 static inline bool
 ErrorBadIndex(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
     return false;
 }
 
+/* Non-standard: convert and range check an index value for SIMD operations.
+ *
+ *   1. numericIndex = ToNumber(argument)            (may throw TypeError)
+ *   2. intIndex = ToInteger(numericIndex)
+ *   3. if intIndex != numericIndex throw RangeError
+ *
+ * This function additionally bounds the range to the non-negative contiguous
+ * integers:
+ *
+ *   4. if intIndex < 0 or intIndex > 2^53 throw RangeError
+ *
+ * Return true and set |*index| to the integer value if |argument| is a valid
+ * array index argument. Otherwise report an TypeError or RangeError and return
+ * false.
+ *
+ * The returned index will always be in the range 0 <= *index <= 2^53.
+ */
+static bool
+NonStandardToIndex(JSContext* cx, HandleValue v, uint64_t* index)
+{
+    // Fast common case.
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        if (i >= 0) {
+            *index = i;
+            return true;
+        }
+    }
+
+    // Slow case. Use ToNumber() to coerce. This may throw a TypeError.
+    double d;
+    if (!ToNumber(cx, v, &d))
+        return false;
+
+    // Check that |d| is an integer in the valid range.
+    //
+    // Not all floating point integers fit in the range of a uint64_t, so we
+    // need a rough range check before the real range check in our caller. We
+    // could limit indexes to UINT64_MAX, but this would mean that our callers
+    // have to be very careful about integer overflow. The contiguous integer
+    // floating point numbers end at 2^53, so make that our upper limit. If we
+    // ever support arrays with more than 2^53 elements, this will need to
+    // change.
+    //
+    // Reject infinities, NaNs, and numbers outside the contiguous integer range
+    // with a RangeError.
+
+    // Write relation so NaNs throw a RangeError.
+    if (!(0 <= d && d <= (uint64_t(1) << 53))) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+        return false;
+    }
+
+    // Check that d is an integer, throw a RangeError if not.
+    // Note that this conversion could invoke undefined behaviour without the
+    // range check above.
+    uint64_t i(d);
+    if (d != double(i)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+        return false;
+    }
+
+    *index = i;
+    return true;
+}
+
 template<typename T>
 static SimdTypeDescr*
 GetTypeDescr(JSContext* cx)
 {
     RootedGlobalObject global(cx, cx->global());
     return GlobalObject::getOrCreateSimdTypeDescr(cx, global, T::type);
 }
 
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -231,14 +231,77 @@ function GetInternalError(msg) {
         return e;
     }
     assert(false, "the catch block should've returned from this function.");
 }
 
 // To be used when a function is required but calling it shouldn't do anything.
 function NullFunction() {}
 
+// Object Rest/Spread Properties proposal
+// Abstract operation: CopyDataProperties (target, source, excluded)
+function CopyDataProperties(target, source, excluded) {
+    // Step 1.
+    assert(IsObject(target), "target is an object");
+
+    // Step 2.
+    assert(IsObject(excluded), "excluded is an object");
+
+    // Steps 3, 6.
+    if (source === undefined || source === null)
+        return;
+
+    // Step 4.a.
+    source = ToObject(source);
+
+    // Step 4.b.
+    var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
+
+    // Step 5.
+    for (var index = 0; index < keys.length; index++) {
+        var key = keys[index];
+
+        // We abbreviate this by calling propertyIsEnumerable which is faster
+        // and returns false for not defined properties.
+        if (!hasOwn(key, excluded) && callFunction(std_Object_propertyIsEnumerable, source, key))
+            _DefineDataProperty(target, key, source[key]);
+    }
+
+    // Step 6 (Return).
+}
+
+// Object Rest/Spread Properties proposal
+// Abstract operation: CopyDataProperties (target, source, excluded)
+function CopyDataPropertiesUnfiltered(target, source) {
+    // Step 1.
+    assert(IsObject(target), "target is an object");
+
+    // Step 2 (Not applicable).
+
+    // Steps 3, 6.
+    if (source === undefined || source === null)
+        return;
+
+    // Step 4.a.
+    source = ToObject(source);
+
+    // Step 4.b.
+    var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
+
+    // Step 5.
+    for (var index = 0; index < keys.length; index++) {
+        var key = keys[index];
+
+        // We abbreviate this by calling propertyIsEnumerable which is faster
+        // and returns false for not defined properties.
+        if (callFunction(std_Object_propertyIsEnumerable, source, key))
+            _DefineDataProperty(target, key, source[key]);
+    }
+
+    // Step 6 (Return).
+}
+
 /*************************************** Testing functions ***************************************/
 function outer() {
     return function inner() {
         return "foo";
     }
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2509,16 +2509,19 @@ BytecodeEmitter::emitCall(JSOp op, uint1
     return emit3(op, ARGC_HI(argc), ARGC_LO(argc));
 }
 
 bool
 BytecodeEmitter::emitDupAt(unsigned slotFromTop)
 {
     MOZ_ASSERT(slotFromTop < unsigned(stackDepth));
 
+    if (slotFromTop == 0)
+        return emit1(JSOP_DUP);
+
     if (slotFromTop >= JS_BIT(24)) {
         reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
         return false;
     }
 
     ptrdiff_t off;
     if (!emitN(JSOP_DUPAT, 3, &off))
         return false;
@@ -5782,23 +5785,18 @@ BytecodeEmitter::emitDestructuringOpsArr
                 return false;
             if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ ITER TRUE *LREF UNDEF
                 return false;
 
             if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER *LREF
                 return false;
         }
 
-        if (emitted) {
-            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
-                return false;
-        } else {
-            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER *LREF ITER
-                return false;
-        }
+        if (!emitDupAt(emitted))                                  // ... OBJ ITER *LREF ITER
+            return false;
         if (!emitIteratorNext(pattern))                           // ... OBJ ITER *LREF RESULT
             return false;
         if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT RESULT
             return false;
         if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER *LREF RESULT DONE
             return false;
 
         if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT DONE DONE
@@ -5883,90 +5881,218 @@ BytecodeEmitter::emitComputedPropertyNam
 bool
 BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav)
 {
     MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
     MOZ_ASSERT(pattern->isArity(PN_LIST));
 
     MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS
 
-    if (!emitRequireObjectCoercible())                            // ... RHS
-        return false;
+    if (!emit1(JSOP_CHECKOBJCOERCIBLE))                           // ... RHS
+        return false;
+
+    bool needsRestPropertyExcludedSet = pattern->pn_count > 1 &&
+                                        pattern->last()->isKind(PNK_SPREAD);
+    if (needsRestPropertyExcludedSet) {
+        if (!emitDestructuringObjRestExclusionSet(pattern))       // ... RHS SET
+            return false;
+
+        if (!emit1(JSOP_SWAP))                                    // ... SET RHS
+            return false;
+    }
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
         ParseNode* subpattern;
-        if (member->isKind(PNK_MUTATEPROTO))
+        if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD))
             subpattern = member->pn_kid;
         else
             subpattern = member->pn_right;
+
         ParseNode* lhs = subpattern;
+        MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN));
         if (lhs->isKind(PNK_ASSIGN))
             lhs = lhs->pn_left;
 
         size_t emitted;
-        if (!emitDestructuringLHSRef(lhs, &emitted))              // ... RHS *LREF
+        if (!emitDestructuringLHSRef(lhs, &emitted))              // ... *SET RHS *LREF
             return false;
 
         // Duplicate the value being destructured to use as a reference base.
-        if (emitted) {
-            if (!emitDupAt(emitted))                              // ... RHS *LREF RHS
-                return false;
-        } else {
-            if (!emit1(JSOP_DUP))                                 // ... RHS RHS
-                return false;
+        if (!emitDupAt(emitted))                                  // ... *SET RHS *LREF RHS
+            return false;
+
+        if (member->isKind(PNK_SPREAD)) {
+            if (!updateSourceCoordNotes(member->pn_pos.begin))
+                return false;
+
+            if (!emitNewInit(JSProto_Object))                     // ... *SET RHS *LREF RHS TARGET
+                return false;
+            if (!emit1(JSOP_DUP))                                 // ... *SET RHS *LREF RHS TARGET TARGET
+                return false;
+            if (!emit2(JSOP_PICK, 2))                             // ... *SET RHS *LREF TARGET TARGET RHS
+                return false;
+
+            if (needsRestPropertyExcludedSet) {
+                if (!emit2(JSOP_PICK, emitted + 4))               // ... RHS *LREF TARGET TARGET RHS SET
+                    return false;
+            }
+
+            CopyOption option = needsRestPropertyExcludedSet
+                                ? CopyOption::Filtered
+                                : CopyOption::Unfiltered;
+            if (!emitCopyDataProperties(option))                  // ... RHS *LREF TARGET
+                return false;
+
+            // Destructure TARGET per this member's lhs.
+            if (!emitSetOrInitializeDestructuring(lhs, flav))     // ... RHS
+                return false;
+
+            MOZ_ASSERT(member == pattern->last(), "Rest property is always last");
+            break;
         }
 
         // Now push the property name currently being matched, which is the
         // current property name "label" on the left of a colon in the object
         // initialiser.
         bool needsGetElem = true;
 
         if (member->isKind(PNK_MUTATEPROTO)) {
-            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... RHS *LREF PROP
+            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... *SET RHS *LREF PROP
                 return false;
             needsGetElem = false;
         } else {
             MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
 
             ParseNode* key = member->pn_left;
             if (key->isKind(PNK_NUMBER)) {
-                if (!emitNumberOp(key->pn_dval))                  // ... RHS *LREF RHS KEY
+                if (!emitNumberOp(key->pn_dval))                  // ... *SET RHS *LREF RHS KEY
                     return false;
             } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
-                PropertyName* name = key->pn_atom->asPropertyName();
-
-                // The parser already checked for atoms representing indexes and
-                // used PNK_NUMBER instead, but also watch for ids which TI treats
-                // as indexes for simplification of downstream analysis.
-                jsid id = NameToId(name);
-                if (id != IdToTypeId(id)) {
-                    if (!emitTree(key))                           // ... RHS *LREF RHS KEY
+                if (!emitAtomOp(key->pn_atom, JSOP_GETPROP))      // ... *SET RHS *LREF PROP
+                    return false;
+                needsGetElem = false;
+            } else {
+                if (!emitComputedPropertyName(key))               // ... *SET RHS *LREF RHS KEY
+                    return false;
+
+                // Add the computed property key to the exclusion set.
+                if (needsRestPropertyExcludedSet) {
+                    if (!emitDupAt(emitted + 3))                  // ... SET RHS *LREF RHS KEY SET
                         return false;
-                } else {
-                    if (!emitAtomOp(name, JSOP_GETPROP))          // ... RHS *LREF PROP
+                    if (!emitDupAt(1))                            // ... SET RHS *LREF RHS KEY SET KEY
+                        return false;
+                    if (!emit1(JSOP_UNDEFINED))                   // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED
                         return false;
-                    needsGetElem = false;
+                    if (!emit1(JSOP_INITELEM))                    // ... SET RHS *LREF RHS KEY SET
+                        return false;
+                    if (!emit1(JSOP_POP))                         // ... SET RHS *LREF RHS KEY
+                        return false;
                 }
-            } else {
-                if (!emitComputedPropertyName(key))               // ... RHS *LREF RHS KEY
-                    return false;
             }
         }
 
         // Get the property value if not done already.
-        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... RHS *LREF PROP
+        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... *SET RHS *LREF PROP
             return false;
 
         if (subpattern->isKind(PNK_ASSIGN)) {
-            if (!emitDefault(subpattern->pn_right, lhs))          // ... RHS *LREF VALUE
+            if (!emitDefault(subpattern->pn_right, lhs))          // ... *SET RHS *LREF VALUE
                 return false;
         }
 
         // Destructure PROP per this member's lhs.
-        if (!emitSetOrInitializeDestructuring(subpattern, flav))  // ... RHS
+        if (!emitSetOrInitializeDestructuring(subpattern, flav))  // ... *SET RHS
+            return false;
+    }
+
+    return true;
+}
+
+bool
+BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern)
+{
+    MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
+    MOZ_ASSERT(pattern->isArity(PN_LIST));
+    MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD));
+
+    ptrdiff_t offset = this->offset();
+    if (!emitNewInit(JSProto_Object))
+        return false;
+
+    // Try to construct the shape of the object as we go, so we can emit a
+    // JSOP_NEWOBJECT with the final shape instead.
+    // In the case of computed property names and indices, we cannot fix the
+    // shape at bytecode compile time. When the shape cannot be determined,
+    // |obj| is nulled out.
+
+    // No need to do any guessing for the object kind, since we know the upper
+    // bound of how many properties we plan to have.
+    gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1);
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
+    if (!obj)
+        return false;
+
+    RootedAtom pnatom(cx);
+    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
+        if (member->isKind(PNK_SPREAD))
+            break;
+
+        bool isIndex = false;
+        if (member->isKind(PNK_MUTATEPROTO)) {
+            pnatom.set(cx->names().proto);
+        } else {
+            ParseNode* key = member->pn_left;
+            if (key->isKind(PNK_NUMBER)) {
+                if (!emitNumberOp(key->pn_dval))
+                    return false;
+                isIndex = true;
+            } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
+                pnatom.set(key->pn_atom);
+            } else {
+                // Otherwise this is a computed property name which needs to
+                // be added dynamically.
+                obj.set(nullptr);
+                continue;
+            }
+        }
+
+        // Initialize elements with |undefined|.
+        if (!emit1(JSOP_UNDEFINED))
+            return false;
+
+        if (isIndex) {
+            obj.set(nullptr);
+            if (!emit1(JSOP_INITELEM))
+                return false;
+        } else {
+            uint32_t index;
+            if (!makeAtomIndex(pnatom, &index))
+                return false;
+
+            if (obj) {
+                MOZ_ASSERT(!obj->inDictionaryMode());
+                Rooted<jsid> id(cx, AtomToId(pnatom));
+                if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr,
+                                          JSPROP_ENUMERATE))
+                {
+                    return false;
+                }
+                if (obj->inDictionaryMode())
+                    obj.set(nullptr);
+            }
+
+            if (!emitIndex32(JSOP_INITPROP, index))
+                return false;
+        }
+    }
+
+    if (obj) {
+        // The object survived and has a predictable shape: update the
+        // original bytecode.
+        if (!replaceNewInitWithNewObject(obj, offset))
             return false;
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav)
@@ -6791,48 +6917,59 @@ BytecodeEmitter::emitWith(ParseNode* pn)
 
     if (!emitTree(pn->pn_right))
         return false;
 
     return emitterScope.leave(this);
 }
 
 bool
-BytecodeEmitter::emitRequireObjectCoercible()
-{
-    // For simplicity, handle this in self-hosted code, at cost of 13 bytes of
-    // bytecode versus 1 byte for a dedicated opcode.  As more places need this
-    // behavior, we may want to reconsider this tradeoff.
-
-#ifdef DEBUG
-    auto depth = this->stackDepth;
-#endif
-    MOZ_ASSERT(depth > 0);                 // VAL
-    if (!emit1(JSOP_DUP))                  // VAL VAL
-        return false;
-
-    // Note that "intrinsic" is a misnomer: we're calling a *self-hosted*
-    // function that's not an intrinsic!  But it nonetheless works as desired.
-    if (!emitAtomOp(cx->names().RequireObjectCoercible,
-                    JSOP_GETINTRINSIC))    // VAL VAL REQUIREOBJECTCOERCIBLE
-    {
-        return false;
-    }
-    if (!emit1(JSOP_UNDEFINED))            // VAL VAL REQUIREOBJECTCOERCIBLE UNDEFINED
-        return false;
-    if (!emit2(JSOP_PICK, 2))              // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL
-        return false;
-    if (!emitCall(JSOP_CALL_IGNORES_RV, 1))// VAL IGNORED
+BytecodeEmitter::emitCopyDataProperties(CopyOption option)
+{
+    DebugOnly<int32_t> depth = this->stackDepth;
+
+    uint32_t argc;
+    if (option == CopyOption::Filtered) {
+        MOZ_ASSERT(depth > 2);                 // TARGET SOURCE SET
+        argc = 3;
+
+        if (!emitAtomOp(cx->names().CopyDataProperties,
+                        JSOP_GETINTRINSIC))    // TARGET SOURCE SET COPYDATAPROPERTIES
+        {
+            return false;
+        }
+    } else {
+        MOZ_ASSERT(depth > 1);                 // TARGET SOURCE
+        argc = 2;
+
+        if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered,
+                        JSOP_GETINTRINSIC))    // TARGET SOURCE COPYDATAPROPERTIES
+        {
+            return false;
+        }
+    }
+
+    if (!emit1(JSOP_UNDEFINED))                // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED
+        return false;
+    if (!emit2(JSOP_PICK, argc + 1))           // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET
+        return false;
+    if (!emit2(JSOP_PICK, argc + 1))           // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE
+        return false;
+    if (option == CopyOption::Filtered) {
+        if (!emit2(JSOP_PICK, argc + 1))       // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET
+            return false;
+    }
+    if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED
         return false;
     checkTypeSet(JSOP_CALL_IGNORES_RV);
 
-    if (!emit1(JSOP_POP))                  // VAL
-        return false;
-
-    MOZ_ASSERT(depth == this->stackDepth);
+    if (!emit1(JSOP_POP))                      // -
+        return false;
+
+    MOZ_ASSERT(depth - int(argc) == this->stackDepth);
     return true;
 }
 
 bool
 BytecodeEmitter::emitIterator()
 {
     // Convert iterable to iterator.
     if (!emit1(JSOP_DUP))                                         // OBJ OBJ
@@ -9715,16 +9852,32 @@ BytecodeEmitter::emitPropertyList(ParseN
             if (!emitTree(propdef->pn_kid))
                 return false;
             objp.set(nullptr);
             if (!emit1(JSOP_MUTATEPROTO))
                 return false;
             continue;
         }
 
+        if (propdef->isKind(PNK_SPREAD)) {
+            MOZ_ASSERT(type == ObjectLiteral);
+
+            if (!emit1(JSOP_DUP))
+                return false;
+
+            if (!emitTree(propdef->pn_kid))
+                return false;
+
+            if (!emitCopyDataProperties(CopyOption::Unfiltered))
+                return false;
+
+            objp.set(nullptr);
+            continue;
+        }
+
         bool extraPop = false;
         if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
             extraPop = true;
             if (!emit1(JSOP_DUP2))
                 return false;
             if (!emit1(JSOP_POP))
                 return false;
         }
@@ -9738,26 +9891,16 @@ BytecodeEmitter::emitPropertyList(ParseN
             isIndex = true;
         } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
             // EmitClass took care of constructor already.
             if (type == ClassBody && key->pn_atom == cx->names().constructor &&
                 !propdef->as<ClassMethod>().isStatic())
             {
                 continue;
             }
-
-            // The parser already checked for atoms representing indexes and
-            // used PNK_NUMBER instead, but also watch for ids which TI treats
-            // as indexes for simpliciation of downstream analysis.
-            jsid id = NameToId(key->pn_atom->asPropertyName());
-            if (id != IdToTypeId(id)) {
-                if (!emitTree(key))
-                    return false;
-                isIndex = true;
-            }
         } else {
             if (!emitComputedPropertyName(key))
                 return false;
             isIndex = true;
         }
 
         /* Emit code for the property initializer. */
         if (!emitTree(propdef->pn_right))
@@ -9828,18 +9971,17 @@ BytecodeEmitter::emitPropertyList(ParseN
             if (!makeAtomIndex(key->pn_atom, &index))
                 return false;
 
             if (objp) {
                 MOZ_ASSERT(type == ObjectLiteral);
                 MOZ_ASSERT(!IsHiddenInitOp(op));
                 MOZ_ASSERT(!objp->inDictionaryMode());
                 Rooted<jsid> id(cx, AtomToId(key->pn_atom));
-                RootedValue undefinedValue(cx, UndefinedValue());
-                if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr,
+                if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr,
                                           JSPROP_ENUMERATE))
                 {
                     return false;
                 }
                 if (objp->inDictionaryMode())
                     objp.set(nullptr);
             }
 
@@ -9872,51 +10014,61 @@ BytecodeEmitter::emitObject(ParseNode* p
      * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
      * a new object and defining (in source order) each property on the object
      * (or mutating the object's [[Prototype]], in the case of __proto__).
      */
     ptrdiff_t offset = this->offset();
     if (!emitNewInit(JSProto_Object))
         return false;
 
-    /*
-     * Try to construct the shape of the object as we go, so we can emit a
-     * JSOP_NEWOBJECT with the final shape instead.
-     */
-    RootedPlainObject obj(cx);
-    // No need to do any guessing for the object kind, since we know exactly
-    // how many properties we plan to have.
+    // Try to construct the shape of the object as we go, so we can emit a
+    // JSOP_NEWOBJECT with the final shape instead.
+    // In the case of computed property names and indices, we cannot fix the
+    // shape at bytecode compile time. When the shape cannot be determined,
+    // |obj| is nulled out.
+
+    // No need to do any guessing for the object kind, since we know the upper
+    // bound of how many properties we plan to have.
     gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count);
-    obj = NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject);
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
     if (!obj)
         return false;
 
     if (!emitPropertyList(pn, &obj, ObjectLiteral))
         return false;
 
     if (obj) {
-        /*
-         * The object survived and has a predictable shape: update the original
-         * bytecode.
-         */
-        ObjectBox* objbox = parser.newObjectBox(obj);
-        if (!objbox)
-            return false;
-
-        static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
-                      "newinit and newobject must have equal length to edit in-place");
-
-        uint32_t index = objectList.add(objbox);
-        jsbytecode* code = this->code(offset);
-        code[0] = JSOP_NEWOBJECT;
-        code[1] = jsbytecode(index >> 24);
-        code[2] = jsbytecode(index >> 16);
-        code[3] = jsbytecode(index >> 8);
-        code[4] = jsbytecode(index);
-    }
+        // The object survived and has a predictable shape: update the original
+        // bytecode.
+        if (!replaceNewInitWithNewObject(obj, offset))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
+{
+    ObjectBox* objbox = parser.newObjectBox(obj);
+    if (!objbox)
+        return false;
+
+    static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
+                  "newinit and newobject must have equal length to edit in-place");
+
+    uint32_t index = objectList.add(objbox);
+    jsbytecode* code = this->code(offset);
+
+    MOZ_ASSERT(code[0] == JSOP_NEWINIT);
+    code[0] = JSOP_NEWOBJECT;
+    code[1] = jsbytecode(index >> 24);
+    code[2] = jsbytecode(index >> 16);
+    code[3] = jsbytecode(index >> 8);
+    code[4] = jsbytecode(index);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitArrayComp(ParseNode* pn)
 {
     if (!emitNewInit(JSProto_Array))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -548,16 +548,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
     MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op);
     MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
     MOZ_MUST_USE bool emitRegExp(uint32_t index);
 
     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn);
 
+    MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
+
     MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn);
 
     MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
                                        PropListType type);
 
     // To catch accidental misuse, emitUint16Operand/emit3 assert that they are
     // not used to unconditionally emit JSOP_GETLOCAL. Variable access should
     // instead be emitted using EmitVarOp. In special cases, when the caller
@@ -684,32 +686,41 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitDestructuringLHSRef(ParseNode* target, size_t* emitted);
 
     // emitSetOrInitializeDestructuring assumes the lhs expression's reference
     // and the to-be-destructured value has been pushed on the stack.  It emits
     // code to destructure a single lhs expression (either a name or a compound
     // []/{} expression).
     MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
 
+    // emitDestructuringObjRestExclusionSet emits the property exclusion set
+    // for the rest-property in an object pattern.
+    MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern);
+
     // emitDestructuringOps assumes the to-be-destructured value has been
     // pushed on the stack and emits code to destructure each part of a [] or
     // {} lhs expression.
     MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
     MOZ_MUST_USE bool emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav);
     MOZ_MUST_USE bool emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav);
 
     typedef bool
     (*DestructuringDeclEmitter)(BytecodeEmitter* bce, ParseNode* pn);
 
     template <typename NameEmitter>
     MOZ_MUST_USE bool emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitter emitName);
 
-    // Throw a TypeError if the value atop the stack isn't convertible to an
-    // object, with no overall effect on the stack.
-    MOZ_MUST_USE bool emitRequireObjectCoercible();
+    enum class CopyOption {
+        Filtered, Unfiltered
+    };
+
+    // Calls either the |CopyDataProperties| or the
+    // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or
+    // two in the latter case) elements from the stack.
+    MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option);
 
     // emitIterator expects the iterable to already be on the stack.
     // It will replace that stack value with the corresponding iterator
     MOZ_MUST_USE bool emitIterator();
 
     MOZ_MUST_USE bool emitAsyncIterator();
 
     // Pops iterator from the top of the stack. Pushes the result of |.next()|
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -1343,25 +1343,18 @@ FoldElement(JSContext* cx, ParseNode** n
             name = atom->asPropertyName();
         }
     }
 
     // If we don't have a name, we can't optimize to getprop.
     if (!name)
         return true;
 
-    // Also don't optimize if the name doesn't map directly to its id for TI's
-    // purposes.
-    if (NameToId(name) != IdToTypeId(NameToId(name)))
-        return true;
-
     // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
     // to a property access (like expr.foo) that optimizes better downstream.
-    // Don't bother with this for names that TI considers to be indexes, to
-    // simplify downstream analysis.
     ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
     if (!dottedAccess)
         return false;
     dottedAccess->setInParens(node->isInParens());
     ReplaceNode(nodePtr, dottedAccess);
 
     // If we've replaced |expr["prop"]| with |expr.prop|, we can now free the
     // |"prop"| and |expr["prop"]| nodes -- but not the |expr| node that we're
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -272,20 +272,19 @@ class FullParseHandlerBase
         if (!elision)
             return false;
         literal->append(elision);
         literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
         return true;
     }
 
     MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) {
-        TokenPos pos(begin, inner->pn_pos.end);
-        ParseNode* spread = new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, inner);
+        ParseNode* spread = newSpread(begin, inner);
         if (!spread)
-            return null();
+            return false;
         literal->append(spread);
         literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
         return true;
     }
 
     void addArrayElement(ParseNode* literal, ParseNode* element) {
         if (!element->isConstant())
             literal->pn_xflags |= PNX_NONCONST;
@@ -366,16 +365,28 @@ class FullParseHandlerBase
         setListFlag(literal, PNX_NONCONST);
         ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
         if (!propdef)
             return false;
         literal->append(propdef);
         return true;
     }
 
+    MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) {
+        MOZ_ASSERT(literal->isKind(PNK_OBJECT));
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
+        setListFlag(literal, PNX_NONCONST);
+        ParseNode* spread = newSpread(begin, inner);
+        if (!spread)
+            return false;
+        literal->append(spread);
+        return true;
+    }
+
     MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
                                                 JSOp op)
     {
         MOZ_ASSERT(literal->isArity(PN_LIST));
         MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
                    key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
                    key->isKind(PNK_STRING) ||
                    key->isKind(PNK_COMPUTED_NAME));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4402,94 +4402,113 @@ Parser<ParseHandler, CharT>::objectBindi
     Node literal = handler.newObjectLiteral(begin);
     if (!literal)
         return null();
 
     Maybe<DeclarationKind> declKind = Some(kind);
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
-        if (!tokenStream.getToken(&tt))
+        if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
-        TokenPos namePos = pos();
-
-        tokenStream.ungetToken();
-
-        PropertyType propType;
-        Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
-        if (!propName)
-            return null();
-
-        if (propType == PropertyType::Normal) {
-            // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
-
-            if (!tokenStream.getToken(&tt, TokenStream::Operand))
-                return null();
-
-            Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
-            if (!binding)
-                return null();
-
-            bool hasInitializer;
-            if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
-                return null();
-
-            Node bindingExpr = hasInitializer
-                               ? bindingInitializer(binding, kind, yieldHandling)
-                               : binding;
-            if (!bindingExpr)
+        if (tt == TOK_TRIPLEDOT) {
+            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+            uint32_t begin = pos().begin;
+
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
                 return null();
 
-            if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
-                return null();
-        } else if (propType == PropertyType::Shorthand) {
-            // Handle e.g., |var {x, y} = o| as destructuring shorthand
-            // for |var {x: x, y: y} = o|.
-            MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
-
-            Node binding = bindingIdentifier(kind, yieldHandling);
-            if (!binding)
-                return null();
-
-            if (!handler.addShorthand(literal, propName, binding))
+            Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+            if (!inner)
                 return null();
-        } else if (propType == PropertyType::CoverInitializedName) {
-            // Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand
-            // with default values.
-            MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
-
-            Node binding = bindingIdentifier(kind, yieldHandling);
-            if (!binding)
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_ASSIGN);
-
-            Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
-            if (!bindingExpr)
-                return null();
-
-            if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+
+            if (!handler.addSpreadProperty(literal, begin, inner))
                 return null();
         } else {
-            errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
-            return null();
-        }
-
-        if (!tokenStream.getToken(&tt))
-            return null();
-        if (tt == TOK_RC)
+            TokenPos namePos = tokenStream.nextToken().pos;
+
+            PropertyType propType;
+            Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
+            if (!propName)
+                return null();
+
+            if (propType == PropertyType::Normal) {
+                // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
+
+                if (!tokenStream.getToken(&tt, TokenStream::Operand))
+                    return null();
+
+                Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+                if (!binding)
+                    return null();
+
+                bool hasInitializer;
+                if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
+                    return null();
+
+                Node bindingExpr = hasInitializer
+                                   ? bindingInitializer(binding, kind, yieldHandling)
+                                   : binding;
+                if (!bindingExpr)
+                    return null();
+
+                if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+                    return null();
+            } else if (propType == PropertyType::Shorthand) {
+                // Handle e.g., |var {x, y} = o| as destructuring shorthand
+                // for |var {x: x, y: y} = o|.
+                MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+                Node binding = bindingIdentifier(kind, yieldHandling);
+                if (!binding)
+                    return null();
+
+                if (!handler.addShorthand(literal, propName, binding))
+                    return null();
+            } else if (propType == PropertyType::CoverInitializedName) {
+                // Handle e.g., |var {x=1, y=2} = o| as destructuring
+                // shorthand with default values.
+                MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+                Node binding = bindingIdentifier(kind, yieldHandling);
+                if (!binding)
+                    return null();
+
+                tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+                Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
+                if (!bindingExpr)
+                    return null();
+
+                if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+                    return null();
+            } else {
+                errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
+                return null();
+            }
+        }
+
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
+            return null();
+        if (!matched)
             break;
-        if (tt != TOK_COMMA) {
-            reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin);
-            return null();
-        }
-    }
+        if (tt == TOK_TRIPLEDOT) {
+            error(JSMSG_REST_WITH_COMMA);
+            return null();
+        }
+    }
+
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
+                                     reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+                                                          JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
@@ -9774,183 +9793,207 @@ Parser<ParseHandler, CharT>::objectLiter
         return null();
 
     bool seenPrototypeMutation = false;
     bool seenCoverInitializedName = false;
     Maybe<DeclarationKind> declKind = Nothing();
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
-        if (!tokenStream.getToken(&tt))
+        if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
-        TokenPos namePos = pos();
-
-        tokenStream.ungetToken();
-
-        PropertyType propType;
-        Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
-        if (!propName)
-            return null();
-
-        if (propType == PropertyType::Normal) {
-            TokenPos exprPos;
-            if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
+        if (tt == TOK_TRIPLEDOT) {
+            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+            uint32_t begin = pos().begin;
+
+            TokenPos innerPos;
+            if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
+                return null();
+
+            Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+                                    possibleError);
+            if (!inner)
+                return null();
+            if (possibleError)
+                checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
+            if (!handler.addSpreadProperty(literal, begin, inner))
+                return null();
+        } else {
+            TokenPos namePos = tokenStream.nextToken().pos;
+
+            PropertyType propType;
+            Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
+            if (!propName)
                 return null();
 
-            Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
-                                       possibleError);
-            if (!propExpr)
-                return null();
-
-            handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
-
-            if (possibleError)
-                checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
-
-            if (foldConstants && !FoldConstants(context, &propExpr, this))
-                return null();
-
-            if (propAtom == context->names().proto) {
-                if (seenPrototypeMutation) {
-                    // Directly report the error when we're not in a
-                    // destructuring context.
+            if (propType == PropertyType::Normal) {
+                TokenPos exprPos;
+                if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
+                    return null();
+
+                Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+                                           possibleError);
+                if (!propExpr)
+                    return null();
+
+                handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
+
+                if (possibleError)
+                    checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
+
+                if (foldConstants && !FoldConstants(context, &propExpr, this))
+                    return null();
+
+                if (propAtom == context->names().proto) {
+                    if (seenPrototypeMutation) {
+                        // Directly report the error when we're not in a
+                        // destructuring context.
+                        if (!possibleError) {
+                            errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
+                            return null();
+                        }
+
+                        // Otherwise delay error reporting until we've
+                        // determined whether or not we're destructuring.
+                        possibleError->setPendingExpressionErrorAt(namePos,
+                                                                   JSMSG_DUPLICATE_PROTO_PROPERTY);
+                    }
+                    seenPrototypeMutation = true;
+
+                    // Note: this occurs *only* if we observe TOK_COLON!  Only
+                    // __proto__: v mutates [[Prototype]].  Getters, setters,
+                    // method/generator definitions, computed property name
+                    // versions of all of these, and shorthands do not.
+                    if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
+                        return null();
+                } else {
+                    if (!handler.isConstant(propExpr))
+                        handler.setListFlag(literal, PNX_NONCONST);
+
+                    if (!handler.addPropertyDefinition(literal, propName, propExpr))
+                        return null();
+                }
+            } else if (propType == PropertyType::Shorthand) {
+                /*
+                 * Support, e.g., |({x, y} = o)| as destructuring shorthand
+                 * for |({x: x, y: y} = o)|, and |var o = {x, y}| as
+                 * initializer shorthand for |var o = {x: x, y: y}|.
+                 */
+                Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+                if (!name)
+                    return null();
+
+                Node nameExpr = identifierReference(name);
+                if (!nameExpr)
+                    return null();
+
+                if (possibleError)
+                    checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
+
+                if (!handler.addShorthand(literal, propName, nameExpr))
+                    return null();
+            } else if (propType == PropertyType::CoverInitializedName) {
+                /*
+                 * Support, e.g., |({x=1, y=2} = o)| as destructuring
+                 * shorthand with default values, as per ES6 12.14.5
+                 */
+                Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+                if (!name)
+                    return null();
+
+                Node lhs = identifierReference(name);
+                if (!lhs)
+                    return null();
+
+                tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+                if (!seenCoverInitializedName) {
+                    // "shorthand default" or "CoverInitializedName" syntax is
+                    // only valid in the case of destructuring.
+                    seenCoverInitializedName = true;
+
                     if (!possibleError) {
-                        errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
+                        // Destructuring defaults are definitely not allowed
+                        // in this object literal, because of something the
+                        // caller knows about the preceding code. For example,
+                        // maybe the preceding token is an operator:
+                        // |x + {y=z}|.
+                        error(JSMSG_COLON_AFTER_ID);
                         return null();
                     }
 
-                    // Otherwise delay error reporting until we've determined
-                    // whether or not we're destructuring.
-                    possibleError->setPendingExpressionErrorAt(namePos,
-                                                               JSMSG_DUPLICATE_PROTO_PROPERTY);
+                    // Here we set a pending error so that later in the parse,
+                    // once we've determined whether or not we're
+                    // destructuring, the error can be reported or ignored
+                    // appropriately.
+                    possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
                 }
-                seenPrototypeMutation = true;
-
-                // Note: this occurs *only* if we observe TOK_COLON!  Only
-                // __proto__: v mutates [[Prototype]].  Getters, setters,
-                // method/generator definitions, computed property name
-                // versions of all of these, and shorthands do not.
-                if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
+
+                if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
+                    // |chars| is "arguments" or "eval" here.
+                    if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
+                        return null();
+                }
+
+                Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+                if (!rhs)
                     return null();
-            } else {
-                if (!handler.isConstant(propExpr))
-                    handler.setListFlag(literal, PNX_NONCONST);
+
+                handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+
+                Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+                if (!propExpr)
+                    return null();
 
                 if (!handler.addPropertyDefinition(literal, propName, propExpr))
                     return null();
-            }
-        } else if (propType == PropertyType::Shorthand) {
-            /*
-             * Support, e.g., |({x, y} = o)| as destructuring shorthand
-             * for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer
-             * shorthand for |var o = {x: x, y: y}|.
-             */
-            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
-            if (!name)
-                return null();
-
-            Node nameExpr = identifierReference(name);
-            if (!nameExpr)
-                return null();
-
-            if (possibleError)
-                checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
-
-            if (!handler.addShorthand(literal, propName, nameExpr))
-                return null();
-        } else if (propType == PropertyType::CoverInitializedName) {
-            /*
-             * Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand
-             * with default values, as per ES6 12.14.5
-             */
-            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
-            if (!name)
-                return null();
-
-            Node lhs = identifierReference(name);
-            if (!lhs)
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_ASSIGN);
-
-            if (!seenCoverInitializedName) {
-                // "shorthand default" or "CoverInitializedName" syntax is only
-                // valid in the case of destructuring.
-                seenCoverInitializedName = true;
-
-                if (!possibleError) {
-                    // Destructuring defaults are definitely not allowed in this object literal,
-                    // because of something the caller knows about the preceding code.
-                    // For example, maybe the preceding token is an operator: `x + {y=z}`.
-                    error(JSMSG_COLON_AFTER_ID);
+            } else {
+                RootedAtom funName(context);
+                if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+                    funName = propAtom;
+
+                    if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
+                        funName = prefixAccessorName(propType, propAtom);
+                        if (!funName)
+                            return null();
+                    }
+                }
+
+                Node fn = methodDefinition(namePos.begin, propType, funName);
+                if (!fn)
                     return null();
-                }
-
-                // Here we set a pending error so that later in the parse, once we've
-                // determined whether or not we're destructuring, the error can be
-                // reported or ignored appropriately.
-                possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
-            }
-
-            if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
-                // |chars| is "arguments" or "eval" here.
-                if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
+
+                handler.checkAndSetIsDirectRHSAnonFunction(fn);
+
+                JSOp op = JSOpFromPropertyType(propType);
+                if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
                     return null();
-            }
-
-            Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
-            if (!rhs)
-                return null();
-
-            handler.checkAndSetIsDirectRHSAnonFunction(rhs);
-
-            Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
-            if (!propExpr)
-                return null();
-
-            if (!handler.addPropertyDefinition(literal, propName, propExpr))
-                return null();
-        } else {
-            RootedAtom funName(context);
-            if (!tokenStream.isCurrentTokenType(TOK_RB)) {
-                funName = propAtom;
-
-                if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
-                    funName = prefixAccessorName(propType, propAtom);
-                    if (!funName)
-                        return null();
+
+                if (possibleError) {
+                    possibleError->setPendingDestructuringErrorAt(namePos,
+                                                                  JSMSG_BAD_DESTRUCT_TARGET);
                 }
             }
-
-            Node fn = methodDefinition(namePos.begin, propType, funName);
-            if (!fn)
-                return null();
-
-            handler.checkAndSetIsDirectRHSAnonFunction(fn);
-
-            JSOp op = JSOpFromPropertyType(propType);
-            if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
-                return null();
-
-            if (possibleError)
-                possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET);
-        }
-
-        if (!tokenStream.getToken(&tt))
-            return null();
-        if (tt == TOK_RC)
+        }
+
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
+            return null();
+        if (!matched)
             break;
-        if (tt != TOK_COMMA) {
-            reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos);
-            return null();
-        }
-    }
+        if (tt == TOK_TRIPLEDOT && possibleError)
+            possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
+    }
+
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
+                                     reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+                                                          JSMSG_CURLY_OPENED, openedPos));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -290,16 +290,17 @@ class SyntaxParseHandlerBase
 
     Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
 
     MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
+    MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
     MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
     Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
     Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
     Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
 
     // Statements
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -28,17 +28,17 @@ class AutoLockHelperThreadState;
 class VerifyPreTracer;
 
 namespace gc {
 
 typedef Vector<ZoneGroup*, 4, SystemAllocPolicy> ZoneGroupVector;
 using BlackGrayEdgeVector = Vector<TenuredCell*, 0, SystemAllocPolicy>;
 
 class AutoMaybeStartBackgroundAllocation;
-template <typename Task> class AutoRunGCSweepTask;
+class AutoRunParallelTask;
 class AutoTraceSession;
 class MarkingValidator;
 struct MovingTracer;
 
 enum IncrementalProgress
 {
     NotFinished = 0,
     Finished
@@ -982,17 +982,17 @@ class GCRuntime
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
     void beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     void groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool findInterZoneEdges();
     void getNextSweepGroup();
     void endMarkingSweepGroup();
-    void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock);
+    void beginSweepingSweepGroup();
     bool shouldReleaseObservedTypes();
     void sweepDebuggerOnMainThread(FreeOp* fop);
     void sweepJitDataOnMainThread(FreeOp* fop);
     void endSweepingSweepGroup();
     IncrementalProgress performSweepActions(SliceBudget& sliceBudget,
                                             AutoLockForExclusiveAccess& lock);
     static IncrementalProgress sweepTypeInformation(GCRuntime* gc, FreeOp* fop, Zone* zone,
                                                     SliceBudget& budget, AllocKind kind);
@@ -1225,17 +1225,17 @@ class GCRuntime
     ActiveThreadData<size_t> sweepActionIndex;
     ActiveThreadData<bool> abortSweepAfterCurrentGroup;
 
     /*
      * Concurrent sweep infrastructure.
      */
     void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
     void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
-    template <typename Task> friend class AutoRunGCSweepTask;
+    friend class AutoRunParallelTask;
 
     /*
      * List head of arenas allocated during the sweep phase.
      */
     ActiveThreadData<Arena*> arenasAllocatedDuringSweep;
 
     /*
      * Incremental compacting state.
--- a/js/src/gdb/mozilla/unwind.py
+++ b/js/src/gdb/mozilla/unwind.py
@@ -35,18 +35,16 @@ def debug(something):
     pass
 
 # Maps frametype enum base names to corresponding class.
 SizeOfFramePrefix = {
     'JitFrame_IonJS': 'ExitFrameLayout',
     'JitFrame_BaselineJS': 'JitFrameLayout',
     'JitFrame_BaselineStub': 'BaselineStubFrameLayout',
     'JitFrame_IonStub': 'JitStubFrameLayout',
-    # Technically EntryFrameLayout, but that doesn't wind up in the
-    # debuginfo because there are no uses of it.
     'JitFrame_Entry': 'JitFrameLayout',
     'JitFrame_Rectifier': 'RectifierFrameLayout',
     'JitFrame_IonAccessorIC': 'IonAccessorICFrameLayout',
     'JitFrame_Exit': 'ExitFrameLayout',
     'JitFrame_Bailout': 'JitFrameLayout',
 }
 
 # All types and symbols that we need are attached to an object that we
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/bug1357048.js
@@ -0,0 +1,21 @@
+// |jit-test|
+load(libdir + "asm.js");
+
+function runTest() {
+    var code = USE_ASM + `
+        function f() {
+            var x = 0;
+            var y = 0;
+            x = (((0x77777777 - 0xcccccccc) | 0) % -1) | 1;
+            y = (((0x7FFFFFFF + 0x7FFFFFFF) | 0) % -1) | 0;
+            return (x + y) | 0;
+        }
+        return f;
+    `;
+
+    assertEq(asmLink(asmCompile(code))(), 1);
+}
+
+runTest();
+setARMHwCapFlags('vfp');
+runTest();
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -64,18 +64,18 @@ assertAsmTypeFail('glob', USE_ASM + 'var
 assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), null);
 assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {});
 assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {imul:Math.imul});
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), {Math:{imul:Math.imul}})(2,3), 6);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'), this)(8,4), 32);
 
 var module = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f');
 assertAsmLinkAlwaysFail(module, null, null);
-assertAsmLinkAlwaysFail(module, this, null, null);
-assertAsmLinkAlwaysFail(module, this, null, null);
+assertAsmLinkFail(module, this, null, null);
+assertAsmLinkFail(module, this, null, null);
 assertAsmLinkAlwaysFail(module, this, null, new ArrayBuffer(1));
 assertAsmLinkFail(module, this, null, new ArrayBuffer(4));
 assertAsmLinkFail(module, this, null, new ArrayBuffer(100));
 assertAsmLinkFail(module, this, null, new ArrayBuffer(4092));
 assertAsmLinkFail(module, this, null, new ArrayBuffer(64000));
 assertAsmLinkFail(module, this, null, new ArrayBuffer(BUF_MIN+4));
 assertAsmLinkDeprecated(module, this, null, new ArrayBuffer(4096));
 assertAsmLinkDeprecated(module, this, null, new ArrayBuffer(2*4096));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug1360390.js
@@ -0,0 +1,13 @@
+load(libdir + "asm.js");
+
+var code = `
+    "use asm";
+    var ff = foreign.ff;
+    function f(x) {
+        x = +x
+        return ~~x + (ff(3 ^ 9 / 7), 1) & 1;
+    }
+    return f;
+`;
+
+assertEq(asmLink(asmCompile("b", "foreign", code), 0, { ff: decodeURIComponent })(Infinity), 1);
--- a/js/src/jit-test/tests/basic/bug1292858.js
+++ b/js/src/jit-test/tests/basic/bug1292858.js
@@ -1,47 +1,47 @@
 var caughtInvalidArguments = false;
 var a = -1
 try {
     var buf = new Uint8ClampedArray(a);
     throw new Error("didn't throw");
 } catch (e) {
-    assertEq(e instanceof TypeError, true,
-             "expected TypeError, instead threw: " + e);
+    assertEq(e instanceof RangeError, true,
+             "expected RangeError, instead threw: " + e);
     caughtInvalidArguments = true;
 }
 assertEq(caughtInvalidArguments, true);
 
 var caughtInvalidArguments = false;
 var i = 0;
 while (true) {
     i = (i + 1) | 0;
     var a = inIon() ? -1 : 300;
     try {
         var buf = new Uint8ClampedArray(a);
         assertEq(buf.length, 300);
     } catch (e) {
         assertEq(a, -1);
-        assertEq(e instanceof TypeError, true,
-                "expected TypeError, instead threw: " + e);
+        assertEq(e instanceof RangeError, true,
+                "expected RangeError, instead threw: " + e);
         caughtInvalidArguments = true;
         break;
     }
 }
 assertEq(caughtInvalidArguments, true);
 
 var caughtInvalidArguments = false;
 var i = 0;
 while (true) {
     i = (i + 1) | 0;
     var a = inIon() ? -1 : 0;
     try {
         var buf = new Uint8ClampedArray(a);
         assertEq(buf.length, 0);
     } catch (e) {
         assertEq(a, -1);
-        assertEq(e instanceof TypeError, true,
-                "expected TypeError, instead threw: " + e);
+        assertEq(e instanceof RangeError, true,
+                "expected RangeError, instead threw: " + e);
         caughtInvalidArguments = true;
         break;
     }
 }
 assertEq(caughtInvalidArguments, true);
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -178,32 +178,20 @@ check_one("(new foo.x(...))",
 
 check_one("[...].foo",
           function() { [undefined].foo(); },
           " is not a function");
 check_one("[...].foo",
           function() { [undefined, ...[]].foo(); },
           " is not a function");
 
-// Manual testing for this case: the only way to trigger an error is *not* on
-// an attempted property access during destructuring, and the error message
-// invoking ToObject(null) is different: "can't convert {0} to object".
-try
-{
-  (function() {
-    var [{x}] = [null, {}];
-   })();
-  throw new Error("didn't throw");
-}
-catch (e)
-{
-  assertEq(e instanceof TypeError, true,
-           "expected TypeError, got " + e);
-  assertEq(e.message, "can't convert null to object");
-}
+check_one("[...][Symbol.iterator](...).next(...).value",
+          function () { var [{x}] = [null, {}]; }, " is null");
+check_one("[...][Symbol.iterator](...).next(...).value",
+          function () { var [{x}] = [void 0, {}]; }, " is undefined");
 
 try {
   (function() {
     "use strict";
     var o = [];
     Object.freeze(o);
     o[0] = "foo";
   }());
--- a/js/src/jit-test/tests/debug/wasm-12.js
+++ b/js/src/jit-test/tests/debug/wasm-12.js
@@ -2,17 +2,17 @@
 
 if (!wasmIsSupported())
   quit();
 
 var g = newGlobal();
 g.eval(`
 function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
 o = initWasm('(module (func) (export "" 0))');
-o = initWasm('(module (func) (func) (export "" 1))');
+o2 = initWasm('(module (func) (func) (export "" 1))');
 `);
 
 function isWasm(script) { return script.format === "wasm"; }
 
 function isValidWasmURL(url) {
    // The URLs will have the following format:
    //   wasm: [<uri-econded-filename-of-host> ":"] <64-bit-hash>
    return /^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(url);
--- a/js/src/jit-test/tests/ion/bug1298354.js
+++ b/js/src/jit-test/tests/ion/bug1298354.js
@@ -1,14 +1,14 @@
 // |jit-test| error: ReferenceError
 
 new Function(`
   while (true) {
     try {
-        var buf = new Uint8ClampedArray(a);
+        var buf = new Uint8ClampedArray(-1);
     } catch (e) {
         break;
     }
   }
   var caughtInvalidArguments = false;
   while (true) {
     var a = inIon() ? -true.get : 0;
     while (x > 7 & 0) {}
--- a/js/src/jit-test/tests/parser/arrow-rest.js
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -34,25 +34,25 @@ function f(... ...a) =>
 // destructuring parameter
 
 testThrow(`
 ([... ...a)=>
 `, 6);
 
 testThrow(`
 ({...a)=>
-`, 2);
+`, 6);
 
 testThrow(`
 function f([... ...a)=>
 `, 16);
 
 testThrow(`
 function f({...a)=>
-`, 12);
+`, 16);
 
 // arrow
 
 testThrow(`
 x => ...a)=>
 `, 5);
 
 // template literal
@@ -62,17 +62,17 @@ testThrow("`${ ...a)=>}`", 4);
 // destructing assignment
 
 testThrow(`
 var [... ...a)=>
 `, 9);
 
 testThrow(`
 var {...a)=>
-`, 5);
+`, 9);
 
 // initializer
 
 testThrow(`
 var [a] = ...a)=>
 `, 10);
 
 testThrow(`
--- a/js/src/jit-test/tests/wasm/spec/jsapi.js
+++ b/js/src/jit-test/tests/wasm/spec/jsapi.js
@@ -679,18 +679,17 @@ function assertCompileError(args, err) {
 }
 
 assertCompileError([], TypeError);
 assertCompileError([undefined], TypeError);
 assertCompileError([1], TypeError);
 assertCompileError([{}], TypeError);
 assertCompileError([new Uint8Array()], CompileError);
 assertCompileError([new ArrayBuffer()], CompileError);
-// TODO typed array ctors must coerce their argument
-//assertCompileError([new Uint8Array("hi!")], CompileError);
+assertCompileError([new Uint8Array("hi!")], CompileError);
 assertCompileError([new ArrayBuffer("hi!")], CompileError);
 
 num_tests = 1;
 function assertCompileSuccess(bytes) {
     promise_test(() => {
         return WebAssembly.compile(bytes)
         .then(module => {
             assert_equals(module instanceof Module, true);
@@ -710,53 +709,54 @@ test(() => {
 }, "'WebAssembly.instantiate' data property");
 
 test(() => {
     const instantiateDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'instantiate');
     const instantiate = WebAssembly.instantiate;
     assert_equals(instantiate, instantiateDesc.value);
     assert_equals(instantiate.length, 1);
     assert_equals(instantiate.name, "instantiate");
+    var instantiateErrorTests = 1;
     function assertInstantiateError(args, err) {
         promise_test(() => {
             return instantiate(...args)
                 .then(m => {
                     throw null;
                 })
                 .catch(error => {
                     assert_equals(error instanceof err, true);
                     assert_equals(Boolean(error.stack.match("jsapi.js")), true);
                 })
-        }, 'unexpected success in assertInstantiateError');
+        }, `unexpected success in assertInstantiateError ${instantiateErrorTests++}`);
     }
     var scratch_memory = new WebAssembly.Memory({initial:1});
     var scratch_table = new WebAssembly.Table({element:"anyfunc", initial:1, maximum:1});
     assertInstantiateError([], TypeError);
     assertInstantiateError([undefined], TypeError);
     assertInstantiateError([1], TypeError);
     assertInstantiateError([{}], TypeError);
     assertInstantiateError([new Uint8Array()], CompileError);
     assertInstantiateError([new ArrayBuffer()], CompileError);
-    // TODO typed array ctors must coerce their argument
-    //assertInstantiateError([new Uint8Array("hi!")], CompileError);
+    assertInstantiateError([new Uint8Array("hi!")], CompileError);
     assertInstantiateError([new ArrayBuffer("hi!")], CompileError);
     assertInstantiateError([importingModule], TypeError);
     assertInstantiateError([importingModule, null], TypeError);
     assertInstantiateError([importingModuleBinary, null], TypeError);
     assertInstantiateError([emptyModule, null], TypeError);
     assertInstantiateError([importingModuleBinary, null], TypeError);
     assertInstantiateError([importingModuleBinary, undefined], TypeError);
     assertInstantiateError([importingModuleBinary, {}], TypeError);
     assertInstantiateError([importingModuleBinary, {"":{g:()=>{}}}], LinkError);
     assertInstantiateError([importingModuleBinary, {t:{f:()=>{}}}], TypeError);
     assertInstantiateError([complexImportingModuleBinary, null], TypeError);
     assertInstantiateError([complexImportingModuleBinary, undefined], TypeError);
     assertInstantiateError([complexImportingModuleBinary, {}], TypeError);
     assertInstantiateError([complexImportingModuleBinary, {"c": {"d": scratch_memory}}], TypeError);
 
+    var instantiateSuccessTests = 1;
     function assertInstantiateSuccess(module, imports) {
         promise_test(()=> {
             return instantiate(module, imports)
                 .then(result => {
                     if (module instanceof Module) {
                         assert_equals(result instanceof Instance, true);
                     } else {
                         assert_equals(result.module instanceof Module, true);
@@ -765,17 +765,17 @@ test(() => {
                         assert_equals(desc.writable, true);
                         assert_equals(desc.enumerable, true);
                         assert_equals(desc.configurable, true);
                         desc = Object.getOwnPropertyDescriptor(result, 'instance');
                         assert_equals(desc.writable, true);
                         assert_equals(desc.enumerable, true);
                         assert_equals(desc.configurable, true);
                     }
-                })}, 'unexpected failure in assertInstantiateSuccess');
+                })}, `unexpected failure in assertInstantiateSuccess ${instantiateSuccessTests++}`);
     }
     assertInstantiateSuccess(emptyModule);
     assertInstantiateSuccess(emptyModuleBinary);
     assertInstantiateSuccess(emptyModuleBinary.buffer);
     assertInstantiateSuccess(importingModule, {"":{f:()=>{}}});
     assertInstantiateSuccess(importingModuleBinary, {"":{f:()=>{}}});
     assertInstantiateSuccess(importingModuleBinary.buffer, {"":{f:()=>{}}});
     assertInstantiateSuccess(complexImportingModuleBinary, {
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -585,28 +585,22 @@ BacktrackingAllocator::buildLivenessInfo
                     new(alloc().fallible()) CallRange(outputOf(*ins), outputOf(*ins).next());
                 if (!callRange)
                     return false;
 
                 callRangesList.pushFront(callRange);
                 if (!callRanges.insert(callRange))
                     return false;
             }
-            DebugOnly<bool> hasDoubleDef = false;
-            DebugOnly<bool> hasFloat32Def = false;
+
             for (size_t i = 0; i < ins->numDefs(); i++) {
                 LDefinition* def = ins->getDef(i);
                 if (def->isBogusTemp())
                     continue;
-#ifdef DEBUG
-                if (def->type() == LDefinition::DOUBLE)
-                    hasDoubleDef = true;
-                if (def->type() == LDefinition::FLOAT32)
-                    hasFloat32Def = true;
-#endif
+
                 CodePosition from = outputOf(*ins);
 
                 if (def->policy() == LDefinition::MUST_REUSE_INPUT) {
                     // MUST_REUSE_INPUT is implemented by allocating an output
                     // register and moving the input to it. Register hints are
                     // used to avoid unnecessary moves. We give the input an
                     // LUse::ANY policy to avoid allocating a register for the
                     // input.
@@ -641,18 +635,17 @@ BacktrackingAllocator::buildLivenessInfo
                             if (use->isFixedRegister()) {
                                 if (GetFixedRegister(vreg(use).def(), use) == reg)
                                     from = outputOf(*ins);
                             }
                         }
                     }
                 }
 
-                CodePosition to =
-                    ins->isCall() ? outputOf(*ins) : outputOf(*ins).next();
+                CodePosition to = ins->isCall() ? outputOf(*ins) : outputOf(*ins).next();
 
                 if (!vreg(temp).addInitialRange(alloc(), from, to))
                     return false;
                 vreg(temp).setInitialDefinition(from);
             }
 
             DebugOnly<bool> hasUseRegister = false;
             DebugOnly<bool> hasUseRegisterAtStart = false;
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -90,20 +90,21 @@ ScriptFromCalleeToken(CalleeToken token)
 
 // In between every two frames lies a small header describing both frames. This
 // header, minimally, contains a returnAddress word and a descriptor word. The
 // descriptor describes the size and type of the previous frame, whereas the
 // returnAddress describes the address the newer frame (the callee) will return
 // to. The exact mechanism in which frames are laid out is architecture
 // dependent.
 //
-// Two special frame types exist. Entry frames begin an ion activation, and
-// therefore there is exactly one per activation of jit::Cannon. Exit frames
-// are necessary to leave JIT code and enter C++, and thus, C++ code will
-// always begin iterating from the topmost exit frame.
+// Two special frame types exist:
+// - Entry frames begin a JitActivation, and therefore there is exactly one
+// per activation of EnterIon or EnterBaseline. These reuse JitFrameLayout.
+// - Exit frames are necessary to leave JIT code and enter C++, and thus,
+// C++ code will always begin iterating from the topmost exit frame.
 
 class LSafepoint;
 
 // Two-tuple that lets you look up the safepoint entry given the
 // displacement of a call instruction within the JIT code.
 class SafepointIndex
 {
     // The displacement is the distance from the first byte of the JIT'd code
@@ -433,25 +434,16 @@ class JitFrameLayout : public CommonFram
     // or LArgument.
     uintptr_t* slotRef(SafepointSlotEntry where);
 
     static inline size_t Size() {
         return sizeof(JitFrameLayout);
     }
 };
 
-// this is the layout of the frame that is used when we enter Ion code from platform ABI code
-class EntryFrameLayout : public JitFrameLayout
-{
-  public:
-    static inline size_t Size() {
-        return sizeof(EntryFrameLayout);
-    }
-};
-
 class RectifierFrameLayout : public JitFrameLayout
 {
   public:
     static inline size_t Size() {
         return sizeof(RectifierFrameLayout);
     }
 };
 
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1183,16 +1183,28 @@ template <size_t Defs, size_t Operands, 
 class LCallInstructionHelper : public LInstructionHelper<Defs, Operands, Temps>
 {
   public:
     virtual bool isCall() const {
         return true;
     }
 };
 
+template <size_t Defs, size_t Temps>
+class LBinaryCallInstructionHelper : public LCallInstructionHelper<Defs, 2, Temps>
+{
+  public:
+    const LAllocation* lhs() {
+        return this->getOperand(0);
+    }
+    const LAllocation* rhs() {
+        return this->getOperand(1);
+    }
+};
+
 class LRecoverInfo : public TempObject
 {
   public:
     typedef Vector<MNode*, 2, JitAllocPolicy> Instructions;
 
   private:
     // List of instructions needed to recover the stack frames.
     // Outer frames are stored before inner frames.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1914,18 +1914,16 @@ LIRGenerator::visitMod(MMod* ins)
         return;
     }
 
     if (ins->specialization() == MIRType::Double) {
         MOZ_ASSERT(ins->type() == MIRType::Double);
         MOZ_ASSERT(ins->lhs()->type() == MIRType::Double);
         MOZ_ASSERT(ins->rhs()->type() == MIRType::Double);
 
-        gen->setPerformsCall();
-
         // Ion does an unaligned ABI call and thus needs a temp register. Wasm
         // doesn't.
         LDefinition maybeTemp = gen->compilingWasm()
                                 ? LDefinition::BogusTemp()
                                 : tempFixed(CallTempReg0);
 
         // Note: useRegisterAtStart is safe here, the temp is not a FP register.
         LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()),
@@ -2261,24 +2259,24 @@ LIRGenerator::visitTruncateToInt32(MTrun
         break;
 
       case MIRType::Int32:
       case MIRType::Boolean:
         redefine(truncate, opd);
         break;
 
       case MIRType::Double:
-        // May call into JS::ToInt32().
-        gen->setPerformsCall();
+        // May call into JS::ToInt32() on the slow OOL path.
+        gen->setNeedsStaticStackAlignment();
         lowerTruncateDToInt32(truncate);
         break;
 
       case MIRType::Float32:
-        // May call into JS::ToInt32().
-        gen->setPerformsCall();
+        // May call into JS::ToInt32() on the slow OOL path.
+        gen->setNeedsStaticStackAlignment();
         lowerTruncateFToInt32(truncate);
         break;
 
       default:
         // Objects might be effectful. Symbols throw.
         // Strings are complicated - we don't handle them yet.
         MOZ_CRASH("unexpected type");
     }
@@ -3702,20 +3700,19 @@ LIRGenerator::visitStoreFixedSlot(MStore
     }
 }
 
 void
 LIRGenerator::visitGetNameCache(MGetNameCache* ins)
 {
     MOZ_ASSERT(ins->envObj()->type() == MIRType::Object);
 
-    // Set the performs-call flag so that we don't omit the overrecursed check.
-    // This is necessary because the cache can attach a scripted getter stub
-    // that calls this script recursively.
-    gen->setPerformsCall();
+    // Emit an overrecursed check: this is necessary because the cache can
+    // attach a scripted getter stub that calls this script recursively.
+    gen->setNeedsOverrecursedCheck();
 
     LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()), temp());
     defineBox(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue* ins)
@@ -3733,20 +3730,19 @@ LIRGenerator::visitGetPropertyCache(MGet
 
     MDefinition* id = ins->idval();
     MOZ_ASSERT(id->type() == MIRType::String ||
                id->type() == MIRType::Symbol ||
                id->type() == MIRType::Int32 ||
                id->type() == MIRType::Value);
 
     if (ins->monitoredResult()) {
-        // Set the performs-call flag so that we don't omit the overrecursed
-        // check. This is necessary because the cache can attach a scripted
-        // getter stub that calls this script recursively.
-        gen->setPerformsCall();
+        // Emit an overrecursed check: this is necessary because the cache can
+        // attach a scripted getter stub that calls this script recursively.
+        gen->setNeedsOverrecursedCheck();
     }
 
     // If this is a GETPROP, the id is a constant string. Allow passing it as a