merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 04 Mar 2016 11:49:58 +0100
changeset 323098 33d36bf6ca0c9d9c22cadf6d8223fa6e1418b62c
parent 322968 3b21d7cfcf4324d5733da2ce10fd9b525c64b22c (current diff)
parent 323097 7c6642a759eebe9ae74c9cef9f67e55d9477b994 (diff)
child 323099 e7319545eb3819da67ffe1d4233022ae71e3a9a1
child 323120 92ce3f765f055e650df3cf21412ce92f6605ea8e
child 323236 365dff9e6e1fdc49c111765fbfc6518dfc4c55ea
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
33d36bf6ca0c / 47.0a1 / 20160304030206 / files
nightly linux64
33d36bf6ca0c / 47.0a1 / 20160304030206 / files
nightly mac
33d36bf6ca0c / 47.0a1 / 20160304030206 / files
nightly win32
33d36bf6ca0c / 47.0a1 / 20160304030206 / files
nightly win64
33d36bf6ca0c / 47.0a1 / 20160304030206 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/plugins/base/android/ANPOpenGL.cpp
dom/tests/mochitest/bugs/test_bug406375.html
dom/tests/mochitest/bugs/test_bug437361.html
dom/tests/mochitest/bugs/test_bug504862.html
gfx/cairo/cairo/src/Makefile.in
gfx/thebes/Makefile.in
intl/unicharutil/util/standalone/moz.build
layout/reftests/w3c-css/submitted/flexbox/flexbox-table-fixup-001a.xhtml
layout/reftests/w3c-css/submitted/flexbox/flexbox-table-fixup-001b.xhtml
security/manager/.eslintrc
testing/marionette/harness/marionette/tests/unit/test_import_script_reuse_window.py
testing/talos/talos/post_file.py
toolkit/components/telemetry/Histograms.json
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -11,16 +11,17 @@
 #include "nsCoreUtils.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsAttrName.h"
 #include "nsWhitespaceTokenizer.h"
 
 #include "mozilla/BinarySearch.h"
+#include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::a11y::aria;
 
 static const uint32_t kGenericAccType = 0;
 
 /**
@@ -856,22 +857,20 @@ struct RoleComparator
   int operator()(const nsRoleMapEntry& aEntry) const {
     return Compare(mRole, aEntry.ARIARoleString());
   }
 };
 
 }
 
 nsRoleMapEntry*
-aria::GetRoleMap(nsINode* aNode)
+aria::GetRoleMap(dom::Element* aEl)
 {
-  nsIContent* content = nsCoreUtils::GetRoleContent(aNode);
   nsAutoString roles;
-  if (!content ||
-      !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
+  if (!aEl || !aEl->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
       roles.IsEmpty()) {
     // We treat role="" as if the role attribute is absent (per aria spec:8.1.1)
     return nullptr;
   }
 
   nsWhitespaceTokenizer tokenizer(roles);
   while (tokenizer.hasMoreTokens()) {
     // Do a binary search through table for the next role in role list
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -205,17 +205,17 @@ extern nsRoleMapEntry gEmptyRoleMap;
 /**
  * Get the role map entry for a given DOM node. This will use the first
  * ARIA role if the role attribute provides a space delimited list of roles.
  *
  * @param aNode  [in] the DOM node to get the role map entry for
  * @return        a pointer to the role map entry for the ARIA role, or nullptr
  *                if none
  */
-nsRoleMapEntry* GetRoleMap(nsINode* aNode);
+nsRoleMapEntry* GetRoleMap(dom::Element* aEl);
 
 /**
  * Return accessible state from ARIA universal states applied to the given
  * element.
  */
 uint64_t UniversalStatesFor(mozilla::dom::Element* aElement);
 
 /**
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -126,35 +126,37 @@ nsAccUtils::GetLevelForXULContainerItem(
     parentContainer.swap(container);
   }
 
   return level;
 }
 
 void
 nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
-                                       nsIContent *aStartContent,
-                                       nsIContent *aTopContent)
+                                       nsIContent* aStartContent,
+                                       dom::Element* aTopEl)
 {
   nsAutoString live, relevant, busy;
-  nsIContent *ancestor = aStartContent;
+  nsIContent* ancestor = aStartContent;
   while (ancestor) {
 
     // container-relevant attribute
     if (relevant.IsEmpty() &&
         HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
         ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant))
       SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
 
     // container-live, and container-live-role attributes
     if (live.IsEmpty()) {
-      nsRoleMapEntry* role = aria::GetRoleMap(ancestor);
+      nsRoleMapEntry* role = nullptr;
+      if (ancestor->IsElement()) {
+        role = aria::GetRoleMap(ancestor->AsElement());
+      }
       if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
-        ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live,
-                          live);
+        ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
       } else if (role) {
         GetLiveAttrValue(role->liveAttRule, live);
       }
       if (!live.IsEmpty()) {
         SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
         if (role) {
           SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
                      role->ARIARoleString());
@@ -170,22 +172,22 @@ nsAccUtils::SetLiveContainerAttributes(n
     }
 
     // container-busy attribute
     if (busy.IsEmpty() &&
         HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
         ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
       SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
 
-    if (ancestor == aTopContent)
+    if (ancestor == aTopEl)
       break;
 
     ancestor = ancestor->GetParent();
     if (!ancestor)
-      ancestor = aTopContent; // Use <body>/<frameset>
+      ancestor = aTopEl; // Use <body>/<frameset>
   }
 }
 
 bool
 nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
 {
   NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
 
--- a/accessible/base/nsAccUtils.h
+++ b/accessible/base/nsAccUtils.h
@@ -80,18 +80,18 @@ public:
   /**
    * Set container-foo live region attributes for the given node.
    *
    * @param aAttributes    where to store the attributes
    * @param aStartContent  node to start from
    * @param aTopContent    node to end at
    */
   static void SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
-                                         nsIContent *aStartContent,
-                                         nsIContent *aTopContent);
+                                         nsIContent* aStartContent,
+                                         mozilla::dom::Element* aTopEl);
 
   /**
    * Any ARIA property of type boolean or NMTOKEN is undefined if the ARIA
    * property is not present, or is "" or "undefined". Do not call 
    * this method for properties of type string, decimal, IDREF or IDREFS.
    * 
    * Return true if the ARIA property is defined, otherwise false
    */
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -137,17 +137,17 @@ MustBeAccessible(nsIContent* aContent, D
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible constructors
 
 static Accessible*
 New_HTMLLink(nsIContent* aContent, Accessible* aContext)
 {
   // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
   // see closed bug 494807.
-  nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent);
+  nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent->AsElement());
   if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
       roleMapEntry->role != roles::LINK) {
     return new HyperTextAccessibleWrap(aContent, aContext->Document());
   }
 
   return new HTMLLinkAccessible(aContent, aContext->Document());
 }
 
@@ -1123,21 +1123,21 @@ nsAccessibilityService::GetOrCreateAcces
                                               frame->GetParent()).IsEmpty()) {
       if (aIsSubtreeHidden)
         *aIsSubtreeHidden = true;
 
       return nullptr;
     }
 
     newAcc = new HyperTextAccessibleWrap(content, document);
-    document->BindToDocument(newAcc, aria::GetRoleMap(aNode));
+    document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
     return newAcc;
   }
 
-  nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode);
+  nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
 
   // If the element is focusable or global ARIA attribute is applied to it or
   // it is referenced by ARIA relationship then treat role="presentation" on
   // the element as the role is not there.
   if (roleMapEntry &&
       (roleMapEntry->Is(nsGkAtoms::presentation) ||
        roleMapEntry->Is(nsGkAtoms::none))) {
     if (!MustBeAccessible(content, document))
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -218,17 +218,17 @@ nsCoreUtils::GetDOMNodeFromDOMPoint(nsIN
     // from the given DOM point is used as result node.
     if (aOffset != childCount)
       return aNode->GetChildAt(aOffset);
   }
 
   return aNode;
 }
 
-nsIContent*
+dom::Element*
 nsCoreUtils::GetRoleContent(nsINode *aNode)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
   if (!content) {
     nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
     if (doc) {
       nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(aNode));
       if (htmlDoc) {
@@ -237,17 +237,17 @@ nsCoreUtils::GetRoleContent(nsINode *aNo
         content = do_QueryInterface(bodyElement);
       }
       else {
         return doc->GetDocumentElement();
       }
     }
   }
 
-  return content;
+  return (content && content->IsElement()) ? content->AsElement() : nullptr;
 }
 
 bool
 nsCoreUtils::IsAncestorOf(nsINode *aPossibleAncestorNode,
                           nsINode *aPossibleDescendantNode,
                           nsINode *aRootNode)
 {
   NS_ENSURE_TRUE(aPossibleAncestorNode && aPossibleDescendantNode, false);
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -112,17 +112,17 @@ public:
    * Return the nsIContent* to check for ARIA attributes on -- this may not
    * always be the DOM node for the accessible. Specifically, for doc
    * accessibles, it is not the document node, but either the root element or
    * <body> in HTML.
    *
    * @param aNode  [in] DOM node for the accessible that may be affected by ARIA
    * @return        the nsIContent which may have ARIA markup
    */
-  static nsIContent* GetRoleContent(nsINode *aNode);
+  static mozilla::dom::Element* GetRoleContent(nsINode *aNode);
 
   /**
    * Is the first passed in node an ancestor of the second?
    * Note: A node is not considered to be the ancestor of itself.
    *
    * @param  aPossibleAncestorNode   [in] node to test for ancestor-ness of
    *                                   aPossibleDescendantNode
    * @param  aPossibleDescendantNode [in] node to test for descendant-ness of
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1458,20 +1458,20 @@ DocAccessible::DoInitialUpdate()
   if (nsCoreUtils::IsTabDocument(mDocumentNode))
     mDocFlags |= eTabDocument;
 
   mLoadState |= eTreeConstructed;
 
   // The content element may be changed before the initial update and then we
   // miss the notification (since content tree change notifications are ignored
   // prior to initial update). Make sure the content element is valid.
-  nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
-  if (contentElm) {
-    mContent = contentElm;
-    SetRoleMapEntry(aria::GetRoleMap(mContent));
+  dom::Element* rootEl = nsCoreUtils::GetRoleContent(mDocumentNode);
+  if (rootEl) {
+    mContent = rootEl;
+    SetRoleMapEntry(aria::GetRoleMap(rootEl));
   }
 
   // Build initial tree.  Since its the initial tree there's no group info to
   // invalidate.
   AutoTreeMutation mut(this, false);
   CacheChildrenInSubtree(this);
 
   // Fire reorder event after the document tree is constructed. Note, since
@@ -1695,20 +1695,20 @@ DocAccessible::ProcessContentInserted(Ac
 
     Accessible* container =
       GetContainerAccessible(aInsertedContent->ElementAt(idx));
     if (container != aContainer)
       continue;
 
     if (container == this) {
       // If new root content has been inserted then update it.
-      nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
-      if (rootContent != mContent) {
-        mContent = rootContent;
-        SetRoleMapEntry(aria::GetRoleMap(mContent));
+      dom::Element* rootEl = nsCoreUtils::GetRoleContent(mDocumentNode);
+      if (rootEl != mContent) {
+        mContent = rootEl;
+        SetRoleMapEntry(aria::GetRoleMap(rootEl));
       }
 
       // Continue to update the tree even if we don't have root content.
       // For example, elements may be inserted under the document element while
       // there is no HTML body element.
     }
 
     // We have a DOM/layout change under the container accessible, and its tree
--- a/accessible/html/HTMLImageMapAccessible.cpp
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -104,21 +104,20 @@ HTMLImageMapAccessible::UpdateChildAreas
     RemoveChild(area);
     treeChanged = true;
   }
 
   // Insert new areas into the tree.
   uint32_t areaElmCount = imageMapObj->AreaCount();
   for (uint32_t idx = 0; idx < areaElmCount; idx++) {
     nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
-
     Accessible* area = mChildren.SafeElementAt(idx);
     if (!area || area->GetContent() != areaContent) {
       RefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc);
-      mDoc->BindToDocument(area, aria::GetRoleMap(areaContent));
+      mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement()));
 
       if (!InsertChildAt(idx, area)) {
         mDoc->UnbindFromDocument(area);
         break;
       }
 
       if (aDoFireEvents) {
         RefPtr<AccShowEvent> event = new AccShowEvent(area);
--- a/browser/base/content/test/general/browser_registerProtocolHandler_notification.js
+++ b/browser/base/content/test/general/browser_registerProtocolHandler_notification.js
@@ -14,26 +14,30 @@ function test() {
         let notification = notificationBox.getNotificationWithValue(notificationValue);
         return notification;
     },
     function() {
 
         let notificationBox = window.gBrowser.getNotificationBox();
         let notification = notificationBox.getNotificationWithValue(notificationValue);
         ok(notification, "Notification box should be displayed");
+        if (notification == null) {
+            finish();
+            return;
+        }
         is(notification.type, "info", "We expect this notification to have the type of 'info'.");
         isnot(notification.image, null, "We expect this notification to have an icon.");
 
         let buttons = notification.getElementsByClassName("notification-button-default");
         is(buttons.length, 1, "We expect see one default button.");
 
         buttons = notification.getElementsByClassName("notification-button");
         is(buttons.length, 1, "We expect see one button.");
 
         let button = buttons[0];
         isnot(button.label, null, "We expect the add button to have a label.");
         todo_isnot(button.accesskey, null, "We expect the add button to have a accesskey.");
 
         finish();
-    }, 100);
+    }, "Still can not get notification after retry 100 times.", 100);
 
     window.gBrowser.selectedBrowser.loadURI(testURI);
 }
--- a/browser/base/content/test/general/browser_ssl_error_reports.js
+++ b/browser/base/content/test/general/browser_ssl_error_reports.js
@@ -61,16 +61,17 @@ function* testSendReportAutomatically(te
 
   // Add a tab and wait until it's loaded.
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
   let browser = tab.linkedBrowser;
 
   // Load the page and wait for the error report submission.
   let promiseStatus = createReportResponseStatusPromise(URL_REPORTS + suffix);
   browser.loadURI(testURL);
+  yield promiseErrorPageLoaded(browser);
 
   ok(!isErrorStatus(yield promiseStatus),
      "SSL error report submitted successfully");
 
   // Check that we loaded the right error page.
   yield checkErrorPage(browser, errorURISuffix);
 
   // Cleanup.
--- a/browser/base/content/test/general/browser_urlbarDecode.js
+++ b/browser/base/content/test/general/browser_urlbarDecode.js
@@ -79,19 +79,19 @@ function* checkInput(inputStr) {
     input: inputStr,
   };
   for (let key in params) {
     params[key] = encodeURIComponent(params[key]);
   }
   let expectedURL = "moz-action:" + type + "," + JSON.stringify(params);
   Assert.equal(item.getAttribute("url"), expectedURL, "url");
 
-  Assert.equal(item.getAttribute("title"), inputStr, "title");
+  Assert.equal(item.getAttribute("title"), inputStr.replace("\\","/"), "title");
   Assert.equal(item.getAttribute("text"), inputStr, "text");
 
   let itemTypeStr = item.getAttribute("type");
   let itemTypes = itemTypeStr.split(" ").sort();
   Assert.equal(itemTypes.toString(),
                ["action", "heuristic", "visiturl"].toString(),
                "type");
 
-  Assert.equal(item._title.textContent, "Visit " + inputStr, "Visible title");
+  Assert.equal(item._title.textContent, "Visit " + inputStr.replace("\\","/"), "Visible title");
 }
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -90,20 +90,21 @@ function closeToolbarCustomizationUI(aCa
   aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() {
     aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded);
     executeSoon(aCallback);
   });
 
   aBrowserWin.gCustomizeMode.exit();
 }
 
-function waitForCondition(condition, nextTest, errorMsg) {
+function waitForCondition(condition, nextTest, errorMsg, retryTimes) {
+  retryTimes = typeof retryTimes !== 'undefined' ?  retryTimes : 30;
   var tries = 0;
   var interval = setInterval(function() {
-    if (tries >= 30) {
+    if (tries >= retryTimes) {
       ok(false, errorMsg);
       moveOn();
     }
     var conditionPassed;
     try {
       conditionPassed = condition();
     } catch (e) {
       ok(false, e + "\n" + e.stack);
--- a/browser/base/content/test/plugins/browser_blocking.js
+++ b/browser/base/content/test/plugins/browser_blocking.js
@@ -337,22 +337,22 @@ add_task(function* () {
 
   // Since the plugin notification is dismissed by default, reshow it.
   yield promiseForNotificationShown(notification);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED,
      "Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED");
 
-  let result = ContentTask.spawn(gTestBrowser, {}, function* () {
+  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     return objLoadingContent.activated;
   });
-  ok(result, "Plugin should be activated.");
+  ok(!result, "Plugin should not be activated.");
 
   const testUrl = "http://test.url.com/";
 
   let firstPanelChild = PopupNotifications.panel.firstChild;
   let infoLink = document.getAnonymousElementByAttribute(firstPanelChild, "anonid",
     "click-to-play-plugins-notification-link");
   is(infoLink.href, testUrl,
     "Test 26, the notification URL needs to match the infoURL from the blocklist file.");
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -355,17 +355,17 @@ add_task(function* () {
 
   result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let overlayRect = mainBox.getBoundingClientRect();
     return overlayRect.width == 200 && overlayRect.height == 200;
   });
-  ok(result, "Test 20b, Waited too long for plugin to become visible");
+  ok(result, "Test 20c, plugin should have overlay dims of 200px");
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 20b, plugin should not be activated");
 
   ok(notification.dismissed, "Test 20c, Doorhanger should start out dismissed");
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
@@ -381,23 +381,23 @@ add_task(function* () {
 
   let condition = () => !notification.dismissed && !!PopupNotifications.panel.firstChild;
   yield promiseForCondition(condition);
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(pluginInfo.activated, "Test 20c, plugin should be activated");
 
-  result = ContentTask.spawn(gTestBrowser, {}, function* () {
+  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     return overlayRect.width == 0 && overlayRect.height == 0;
   });
-  ok(result, "Test 20c, plugin should have overlay dims of 200px");
+  ok(result, "Test 20c, plugin should have overlay dims of 0px");
 
   clearAllPluginPermissions();
 });
 
 // Test having multiple different types of plugin on one page
 add_task(function* () {
   // contains three plugins, application/x-test, application/x-second-test x 2
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_two_types.html");
@@ -461,17 +461,17 @@ add_task(function* () {
 
   notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21b, Should have a click-to-play notification");
 
   yield promiseForNotificationShown(notification);
 
   ok(notification.options.pluginData.size == 2, "Test 21c, Should have one type of plugin in the notification");
 
-  let result = ContentTask.spawn(gTestBrowser, {}, function* () {
+  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     return overlayRect.width == 0 && overlayRect.height == 0;
   });
   ok(result, "Test 21c, plugin should have overlay dims of 0px");
 
   ids = ["secondtestA", "secondtestB"];
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -10,55 +10,86 @@ Cu.import("resource://gre/modules/XPCOMU
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
 // Map[Extension -> Map[ID -> MenuItem]]
 // Note: we want to enumerate all the menu items so
 // this cannot be a weak map.
-var contextMenuMap = new Map();
+var gContextMenuMap = new Map();
 
 // Map[Extension -> MenuItem]
-var rootItems = new Map();
+var gRootItems = new Map();
 
 // Not really used yet, will be used for event pages.
-var onClickedCallbacksMap = new WeakMap();
+var gOnClickedCallbacksMap = new WeakMap();
 
 // If id is not specified for an item we use an integer.
-var nextID = 0;
+var gNextMenuItemID = 0;
 
+// Used to assign unique names to radio groups.
+var gNextRadioGroupID = 0;
+
+// The max length of a menu item's label.
 var gMaxLabelLength = 64;
 
 // When a new contextMenu is opened, this function is called and
 // we populate the |xulMenu| with all the items from extensions
 // to be displayed. We always clear all the items again when
 // popuphidden fires.
-var menuBuilder = {
+var gMenuBuilder = {
   build: function(contextData) {
     let xulMenu = contextData.menu;
     xulMenu.addEventListener("popuphidden", this);
     this.xulMenu = xulMenu;
-    for (let [, root] of rootItems) {
+    for (let [, root] of gRootItems) {
       let rootElement = this.buildElementWithChildren(root, contextData);
       if (!rootElement.childNodes.length) {
         // If the root has no visible children, there is no reason to show
         // the root menu item itself either.
         continue;
       }
       rootElement.setAttribute("ext-type", "top-level-menu");
       rootElement = this.removeTopLevelMenuIfNeeded(rootElement);
+
+      // Display the extension icon on the root element.
+      if (root.extension.manifest.icons) {
+        let parentWindow = contextData.menu.ownerDocument.defaultView;
+        let extension = root.extension;
+
+        let url = IconDetails.getURL(extension.manifest.icons, parentWindow, extension, 16 /* size */);
+        let resolvedURL = root.extension.baseURI.resolve(url);
+
+        if (rootElement.localName == "menu") {
+          rootElement.setAttribute("class", "menu-iconic");
+        } else if (rootElement.localName == "menuitem") {
+          rootElement.setAttribute("class", "menuitem-iconic");
+        }
+        rootElement.setAttribute("image", resolvedURL);
+      }
+
       xulMenu.appendChild(rootElement);
       this.itemsToCleanUp.add(rootElement);
     }
   },
 
   buildElementWithChildren(item, contextData) {
     let element = this.buildSingleElement(item, contextData);
+    let groupName;
     for (let child of item.children) {
+      if (child.type == "radio" && !child.groupName) {
+        if (!groupName) {
+          groupName = `webext-radio-group-${gNextRadioGroupID++}`;
+        }
+        child.groupName = groupName;
+      } else {
+        groupName = null;
+      }
+
       if (child.enabledForContext(contextData)) {
         let childElement = this.buildElementWithChildren(child, contextData);
         // Here element must be a menu element and its first child
         // is a menupopup, we have to append its children to this
         // menupopup.
         element.firstChild.appendChild(childElement);
       }
     }
@@ -90,18 +121,17 @@ var menuBuilder = {
       element = doc.createElement("menuitem");
     }
 
     return this.customizeElement(element, item, contextData);
   },
 
   createMenuElement(doc, item) {
     let element = doc.createElement("menu");
-    // Menu elements need to have a menupopup child for
-    // its menu items.
+    // Menu elements need to have a menupopup child for its menu items.
     let menupopup = doc.createElement("menupopup");
     element.appendChild(menupopup);
     return element;
   },
 
   customizeElement(element, item, contextData) {
     let label = item.title;
     if (label) {
@@ -115,21 +145,47 @@ var menuBuilder = {
           selection = selection.substring(0, maxSelectionLength - 3) + "...";
         }
         label = label.replace(/%s/g, selection);
       }
 
       element.setAttribute("label", label);
     }
 
+    if (item.type == "checkbox") {
+      element.setAttribute("type", "checkbox");
+      if (item.checked) {
+        element.setAttribute("checked", "true");
+      }
+    } else if (item.type == "radio") {
+      element.setAttribute("type", "radio");
+      element.setAttribute("name", item.groupName);
+      if (item.checked) {
+        element.setAttribute("checked", "true");
+      }
+    }
+
     if (!item.enabled) {
-      element.setAttribute("disabled", true);
+      element.setAttribute("disabled", "true");
     }
 
     element.addEventListener("command", event => {  // eslint-disable-line mozilla/balanced-listeners
+      if (item.type == "checkbox") {
+        item.checked = !item.checked;
+      } else if (item.type == "radio") {
+        // Deselect all radio items in the current radio group.
+        for (let child of item.parent.children) {
+          if (child.type == "radio" && child.groupName == item.groupName) {
+            child.checked = false;
+          }
+        }
+        // Select the clicked radio item.
+        item.checked = true;
+      }
+
       item.tabManager.addActiveTabPermission();
       if (item.onclick) {
         let clickData = item.getClickData(contextData, event);
         runSafe(item.extContext, item.onclick, clickData);
       }
     });
 
     return element;
@@ -149,17 +205,17 @@ var menuBuilder = {
     this.itemsToCleanUp.clear();
   },
 
   itemsToCleanUp: new Set(),
 };
 
 function contextMenuObserver(subject, topic, data) {
   subject = subject.wrappedJSObject;
-  menuBuilder.build(subject);
+  gMenuBuilder.build(subject);
 }
 
 function getContexts(contextData) {
   let contexts = new Set(["all"]);
 
   contexts.add("page");
 
   if (contextData.inFrame) {
@@ -198,17 +254,17 @@ function MenuItem(extension, extContext,
   this.extContext = extContext;
   this.children = [];
   this.parent = null;
   this.tabManager = TabManager.for(extension);
 
   this.setDefaults();
   this.setProps(createProperties);
   if (!this.hasOwnProperty("_id")) {
-    this.id = nextID++;
+    this.id = gNextMenuItemID++;
   }
   // If the item is not the root and has no parent
   // it must be a child of the root.
   if (!isRoot && !this.parent) {
     this.root.addChild(this);
   }
 }
 
@@ -229,42 +285,42 @@ MenuItem.prototype = {
     if (createProperties.targetUrlPatterns != null) {
       this.targetUrlMatchPattern = new MatchPattern(this.targetUrlPatterns);
     }
   },
 
   setDefaults() {
     this.setProps({
       type: "normal",
-      checked: "false",
+      checked: false,
       contexts: ["all"],
-      enabled: "true",
+      enabled: true,
     });
   },
 
   set id(id) {
     if (this.hasOwnProperty("_id")) {
       throw new Error("Id of a MenuItem cannot be changed");
     }
-    let isIdUsed = contextMenuMap.get(this.extension).has(id);
+    let isIdUsed = gContextMenuMap.get(this.extension).has(id);
     if (isIdUsed) {
       throw new Error("Id already exists");
     }
     this._id = id;
   },
 
   get id() {
     return this._id;
   },
 
   ensureValidParentId(parentId) {
     if (parentId === undefined) {
       return;
     }
-    let menuMap = contextMenuMap.get(this.extension);
+    let menuMap = gContextMenuMap.get(this.extension);
     if (!menuMap.has(parentId)) {
       throw new Error("Could not find any MenuItem with id: " + parentId);
     }
     for (let item = menuMap.get(parentId); item; item = item.parent) {
       if (item === this) {
         throw new Error("MenuItem cannot be an ancestor (or self) of its new parent.");
       }
     }
@@ -275,17 +331,17 @@ MenuItem.prototype = {
 
     if (this.parent) {
       this.parent.detachChild(this);
     }
 
     if (parentId === undefined) {
       this.root.addChild(this);
     } else {
-      let menuMap = contextMenuMap.get(this.extension);
+      let menuMap = gContextMenuMap.get(this.extension);
       menuMap.get(parentId).addChild(this);
     }
   },
 
   get parentId() {
     return this.parent ? this.parent.id : undefined;
   },
 
@@ -303,39 +359,39 @@ MenuItem.prototype = {
       throw new Error("Child MenuItem not found, it cannot be removed.");
     }
     this.children.splice(idx, 1);
     child.parent = null;
   },
 
   get root() {
     let extension = this.extension;
-    if (!rootItems.has(extension)) {
+    if (!gRootItems.has(extension)) {
       let root = new MenuItem(extension, this.context,
                               {title: extension.name},
                               /* isRoot = */ true);
-      rootItems.set(extension, root);
+      gRootItems.set(extension, root);
     }
 
-    return rootItems.get(extension);
+    return gRootItems.get(extension);
   },
 
   remove() {
     if (this.parent) {
       this.parent.detachChild(this);
     }
     let children = this.children.slice(0);
     for (let child of children) {
       child.remove();
     }
 
-    let menuMap = contextMenuMap.get(this.extension);
+    let menuMap = gContextMenuMap.get(this.extension);
     menuMap.delete(this.id);
     if (this.root == this) {
-      rootItems.delete(this.extension);
+      gRootItems.delete(this.extension);
     }
   },
 
   getClickData(contextData, event) {
     let mediaType;
     if (contextData.onVideo) {
       mediaType = "video";
     }
@@ -389,79 +445,79 @@ MenuItem.prototype = {
       // TODO: double check if mediaURL is always set when we need it
       return false;
     }
 
     return true;
   },
 };
 
-var extCount = 0;
+var gExtensionCount = 0;
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
-  contextMenuMap.set(extension, new Map());
-  rootItems.delete(extension);
-  if (++extCount == 1) {
+  gContextMenuMap.set(extension, new Map());
+  gRootItems.delete(extension);
+  if (++gExtensionCount == 1) {
     Services.obs.addObserver(contextMenuObserver,
                              "on-build-contextmenu",
                              false);
   }
 });
 
 extensions.on("shutdown", (type, extension) => {
-  contextMenuMap.delete(extension);
-  if (--extCount == 0) {
+  gContextMenuMap.delete(extension);
+  if (--gExtensionCount == 0) {
     Services.obs.removeObserver(contextMenuObserver,
                                 "on-build-contextmenu");
   }
 });
 /* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerSchemaAPI("contextMenus", "contextMenus", (extension, context) => {
   return {
     contextMenus: {
       create: function(createProperties, callback) {
         let menuItem = new MenuItem(extension, context, createProperties);
-        contextMenuMap.get(extension).set(menuItem.id, menuItem);
+        gContextMenuMap.get(extension).set(menuItem.id, menuItem);
         if (callback) {
           runSafe(context, callback);
         }
         return menuItem.id;
       },
 
       update: function(id, updateProperties) {
-        let menuItem = contextMenuMap.get(extension).get(id);
+        let menuItem = gContextMenuMap.get(extension).get(id);
         if (menuItem) {
           menuItem.setProps(updateProperties);
         }
         return Promise.resolve();
       },
 
       remove: function(id) {
-        let menuItem = contextMenuMap.get(extension).get(id);
+        let menuItem = gContextMenuMap.get(extension).get(id);
         if (menuItem) {
           menuItem.remove();
         }
         return Promise.resolve();
       },
 
       removeAll: function() {
-        let root = rootItems.get(extension);
+        let root = gRootItems.get(extension);
         if (root) {
           root.remove();
         }
         return Promise.resolve();
       },
 
       // TODO: implement this once event pages are ready.
       onClicked: new EventManager(context, "contextMenus.onClicked", fire => {
         let callback = menuItem => {
           fire(menuItem.data);
         };
 
-        onClickedCallbacksMap.set(extension, callback);
+        gOnClickedCallbacksMap.set(extension, callback);
         return () => {
-          onClickedCallbacksMap.delete(extension);
+          gOnClickedCallbacksMap.delete(extension);
         };
       }).api(),
     },
   };
 });
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -97,20 +97,20 @@ global.IconDetails = {
       extension.manifestError(`Invalid icon data: ${e}`);
     }
 
     return result;
   },
 
   // Returns the appropriate icon URL for the given icons object and the
   // screen resolution of the given window.
-  getURL(icons, window, extension) {
+  getURL(icons, window, extension, size = 18) {
     const DEFAULT = "chrome://browser/content/extension.svg";
 
-    return AddonManager.getPreferredIconURL({icons: icons}, 18, window) || DEFAULT;
+    return AddonManager.getPreferredIconURL({icons: icons}, size, window) || DEFAULT;
   },
 
   convertImageDataToPNG(imageData, context) {
     let document = context.contentWindow.document;
     let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = imageData.width;
     canvas.height = imageData.height;
     canvas.getContext("2d").putImageData(imageData, 0, 0);
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -1,175 +1,310 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 /* globals content */
 /* eslint-disable mozilla/no-cpows-in-tests */
-
 add_task(function* () {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
     "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
 
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["contextMenus"],
     },
 
     background: function() {
       // A generic onclick callback function.
       function genericOnClick(info) {
-        browser.test.sendMessage("menuItemClick", JSON.stringify(info));
+        browser.test.sendMessage("menuItemClick", info);
       }
 
-      browser.contextMenus.create({contexts: ["all"], type: "separator"});
+      browser.contextMenus.create({
+        contexts: ["all"],
+        type: "separator",
+      });
 
       let contexts = ["page", "selection", "image"];
       for (let i = 0; i < contexts.length; i++) {
         let context = contexts[i];
         let title = context;
-        browser.contextMenus.create({title: title, contexts: [context], id: "ext-" + context,
-                                     onclick: genericOnClick});
+        browser.contextMenus.create({
+          title: title,
+          contexts: [context],
+          id: "ext-" + context,
+          onclick: genericOnClick,
+        });
         if (context == "selection") {
           browser.contextMenus.update("ext-selection", {
             title: "selection is: '%s'",
             onclick: (info) => {
               browser.contextMenus.removeAll();
               genericOnClick(info);
             },
           });
         }
       }
 
-      let parent = browser.contextMenus.create({title: "parent"});
-      browser.contextMenus.create(
-        {title: "child1", parentId: parent, onclick: genericOnClick});
-      let child2 = browser.contextMenus.create(
-        {title: "child2", parentId: parent, onclick: genericOnClick});
+      let parent = browser.contextMenus.create({
+        title: "parent",
+      });
+      browser.contextMenus.create({
+        title: "child1",
+        parentId: parent,
+        onclick: genericOnClick,
+      });
+      let child2 = browser.contextMenus.create({
+        title: "child2",
+        parentId: parent,
+        onclick: genericOnClick,
+      });
+
+      let parentToDel = browser.contextMenus.create({
+        title: "parentToDel",
+      });
+      browser.contextMenus.create({
+        title: "child1",
+        parentId: parentToDel,
+        onclick: genericOnClick,
+      });
+      browser.contextMenus.create({
+        title: "child2",
+        parentId: parentToDel,
+        onclick: genericOnClick,
+      });
+      browser.contextMenus.remove(parentToDel);
+
+      browser.contextMenus.create({
+        title: "radio-group-1",
+        type: "radio",
+        checked: true,
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
 
-      let parentToDel = browser.contextMenus.create({title: "parentToDel"});
-      browser.contextMenus.create(
-        {title: "child1", parentId: parentToDel, onclick: genericOnClick});
-      browser.contextMenus.create(
-        {title: "child2", parentId: parentToDel, onclick: genericOnClick});
-      browser.contextMenus.remove(parentToDel);
+      browser.contextMenus.create({
+        title: "Checkbox",
+        type: "checkbox",
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
+
+      browser.contextMenus.create({
+        title: "radio-group-2",
+        type: "radio",
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
+
+      browser.contextMenus.create({
+        title: "radio-group-2",
+        type: "radio",
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
+
+      browser.contextMenus.create({
+        type: "separator",
+      });
+
+      browser.contextMenus.create({
+        title: "Checkbox",
+        type: "checkbox",
+        checked: true,
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
+
+      browser.contextMenus.create({
+        title: "Checkbox",
+        type: "checkbox",
+        contexts: ["page"],
+        onclick: genericOnClick,
+      });
 
       browser.contextMenus.update(parent, {parentId: child2}).then(
         () => {
           browser.test.notifyFail();
         },
         () => {
           browser.test.notifyPass();
-        });
+        }
+      );
     },
   });
 
-  let expectedClickInfo;
-  function checkClickInfo(info) {
-    info = JSON.parse(info);
-    for (let i in expectedClickInfo) {
-      is(info[i], expectedClickInfo[i],
-         "click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + info[i]);
-    }
-    is(expectedClickInfo.pageSrc, info.tab.url);
-  }
-
   yield extension.startup();
   yield extension.awaitFinish();
 
-  // Bring up context menu
-  let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
-  let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#img1",
-    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
-  yield popupShownPromise;
+  let contentAreaContextMenu;
+
+  function getTop() {
+    contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
+    let items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
+    is(items.length, 1, "top level item was found (context=selection)");
+    let topItem = items[0];
+    return topItem.childNodes[0];
+  }
+
+  function* openExtensionMenu() {
+    contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
+    let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#img1", {
+      type: "contextmenu",
+      button: 2,
+    }, gBrowser.selectedBrowser);
+    yield popupShownPromise;
+
+    popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
+    EventUtils.synthesizeMouseAtCenter(getTop(), {});
+    yield popupShownPromise;
+  }
+
+  function* closeContextMenu(itemToSelect, expectedClickInfo) {
+    function checkClickInfo(info) {
+      for (let i of Object.keys(expectedClickInfo)) {
+        is(info[i], expectedClickInfo[i],
+           "click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + info[i]);
+      }
+      is(expectedClickInfo.pageSrc, info.tab.url);
+    }
+    let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
+    EventUtils.synthesizeMouseAtCenter(itemToSelect, {});
+    let clickInfo = yield extension.awaitMessage("menuItemClick");
+    if (expectedClickInfo) {
+      checkClickInfo(clickInfo);
+    }
+    yield popupHiddenPromise;
+  }
+
+  function confirmRadioGroupStates(expectedStates) {
+    let top = getTop();
+
+    let radioItems = top.getElementsByAttribute("type", "radio");
+    let radioGroup1 = top.getElementsByAttribute("label", "radio-group-1");
+    let radioGroup2 = top.getElementsByAttribute("label", "radio-group-2");
+
+    is(radioItems.length, 3, "there should be 3 radio items in the context menu");
+    is(radioGroup1.length, 1, "the first radio group should only have 1 radio item");
+    is(radioGroup2.length, 2, "the second radio group should only have 2 radio items");
+
+    is(radioGroup1[0].hasAttribute("checked"), expectedStates[0], `radio item 1 has state (checked=${expectedStates[0]})`);
+    is(radioGroup2[0].hasAttribute("checked"), expectedStates[1], `radio item 2 has state (checked=${expectedStates[1]})`);
+    is(radioGroup2[1].hasAttribute("checked"), expectedStates[2], `radio item 3 has state (checked=${expectedStates[2]})`);
+  }
+
+  function confirmCheckboxStates(expectedStates) {
+    let checkboxItems = getTop().getElementsByAttribute("type", "checkbox");
+
+    is(checkboxItems.length, 3, "there should be 3 checkbox items in the context menu");
+
+    is(checkboxItems[0].hasAttribute("checked"), expectedStates[0], `checkbox item 1 has state (checked=${expectedStates[0]})`);
+    is(checkboxItems[1].hasAttribute("checked"), expectedStates[1], `checkbox item 2 has state (checked=${expectedStates[1]})`);
+    is(checkboxItems[2].hasAttribute("checked"), expectedStates[2], `checkbox item 3 has state (checked=${expectedStates[2]})`);
+  }
+
+  yield openExtensionMenu();
 
   // Check some menu items
-  let items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
-  is(items.length, 1, "top level item was found (context=image)");
-  let topItem = items.item(0);
-  let top = topItem.childNodes[0];
-
-  items = top.getElementsByAttribute("label", "image");
+  let top = getTop();
+  let items = top.getElementsByAttribute("label", "image");
   is(items.length, 1, "contextMenu item for image was found (context=image)");
-  let image = items.item(0);
+  let image = items[0];
 
   items = top.getElementsByAttribute("label", "selection-edited");
   is(items.length, 0, "contextMenu item for selection was not found (context=image)");
 
   items = top.getElementsByAttribute("label", "parentToDel");
   is(items.length, 0, "contextMenu item for removed parent was not found (context=image)");
 
   items = top.getElementsByAttribute("label", "parent");
   is(items.length, 1, "contextMenu item for parent was found (context=image)");
 
-  is(items.item(0).childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
+  is(items[0].childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
 
-  // Click on ext-image item and check the results
-  let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
-  expectedClickInfo = {
+  // Click on ext-image item and check the click results
+  yield closeContextMenu(image, {
     menuItemId: "ext-image",
     mediaType: "image",
     srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
     pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
-  };
-  top.openPopup(topItem, "end_before", 0, 0, true, false);
-  EventUtils.synthesizeMouseAtCenter(image, {});
-  let clickInfo = yield extension.awaitMessage("menuItemClick");
-  checkClickInfo(clickInfo);
-  yield popupHiddenPromise;
+  });
+
+  // Test radio groups
+  yield openExtensionMenu();
+  confirmRadioGroupStates([true, false, false]);
+  items = getTop().getElementsByAttribute("type", "radio");
+  yield closeContextMenu(items[1]);
+
+  yield openExtensionMenu();
+  confirmRadioGroupStates([true, true, false]);
+  items = getTop().getElementsByAttribute("type", "radio");
+  yield closeContextMenu(items[2]);
+
+  yield openExtensionMenu();
+  confirmRadioGroupStates([true, false, true]);
+  items = getTop().getElementsByAttribute("type", "radio");
+  yield closeContextMenu(items[0]);
+
+  yield openExtensionMenu();
+  confirmRadioGroupStates([true, false, true]);
+
+  // Test checkboxes
+  items = getTop().getElementsByAttribute("type", "checkbox");
+  confirmCheckboxStates([false, true, false]);
+  yield closeContextMenu(items[0]);
+
+  yield openExtensionMenu();
+  confirmCheckboxStates([true, true, false]);
+  items = getTop().getElementsByAttribute("type", "checkbox");
+  yield closeContextMenu(items[2]);
+
+  yield openExtensionMenu();
+  confirmCheckboxStates([true, true, true]);
+  items = getTop().getElementsByAttribute("type", "checkbox");
+  yield closeContextMenu(items[0]);
+
+  yield openExtensionMenu();
+  confirmCheckboxStates([false, true, true]);
+  items = getTop().getElementsByAttribute("type", "checkbox");
+  yield closeContextMenu(items[2]);
 
   // Select some text
   yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
     let doc = content.document;
     let range = doc.createRange();
     let selection = content.getSelection();
     selection.removeAllRanges();
     let textNode = doc.getElementById("img1").previousSibling;
     range.setStart(textNode, 0);
     range.setEnd(textNode, 100);
     selection.addRange(range);
   });
 
   // Bring up context menu again
-  popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
-  yield BrowserTestUtils.synthesizeMouse(null, 1, 1,
-    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
-  yield popupShownPromise;
-
-  items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
-  is(items.length, 1, "top level item was found (context=selection)");
-  top = items.item(0).childNodes[0];
+  yield openExtensionMenu();
 
   // Check some menu items
+  top = getTop();
   items = top.getElementsByAttribute("label", "selection is: 'just some text 123456789012345678901234567890...'");
   is(items.length, 1, "contextMenu item for selection was found (context=selection)");
-  let selectionItem = items.item(0);
+  let selectionItem = items[0];
 
   items = top.getElementsByAttribute("label", "selection");
   is(items.length, 0, "contextMenu item label update worked (context=selection)");
 
-  popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
-  expectedClickInfo = {
+  yield closeContextMenu(selectionItem, {
     menuItemId: "ext-selection",
     pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
     selectionText: "just some text 1234567890123456789012345678901234567890123456789012345678901234567890123456789012",
-  };
-  top.openPopup(topItem, "end_before", 0, 0, true, false);
-  EventUtils.synthesizeMouseAtCenter(selectionItem, {});
-  clickInfo = yield extension.awaitMessage("menuItemClick");
-  checkClickInfo(clickInfo);
-  yield popupHiddenPromise;
-
-  popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#img1",
-    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
-  yield popupShownPromise;
+  });
 
   items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
   is(items.length, 0, "top level item was not found (after removeAll()");
 
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab1);
 });
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -259,17 +259,19 @@ var SessionHistoryInternal = {
    * @param tabData
    *        The tabdata including all history entries.
    */
   restore: function (docShell, tabData) {
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory;
 
     if ("userContextId" in tabData) {
-      docShell.setUserContextId(tabData.userContextId);
+      let attrs = docShell.getOriginAttributes();
+      attrs.userContextId = tabData.userContextId;
+      docShell.setOriginAttributes(attrs);
     }
 
     if (history.count > 0) {
       history.PurgeHistory(history.count);
     }
     history.QueryInterface(Ci.nsISHistoryInternal);
 
     let idMap = { used: {} };
--- a/browser/config/mozconfigs/win32/beta
+++ b/browser/config/mozconfigs/win32/beta
@@ -8,11 +8,9 @@ fi
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
-. "$topsrcdir/build/mozconfig.rust"
-
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -1,12 +1,10 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 ac_add_options --enable-profiling
 ac_add_options --enable-verify-mar
 
 ac_add_options --with-branding=browser/branding/nightly
 
-. "$topsrcdir/build/mozconfig.rust"
-
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -14,11 +14,9 @@ mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
 # defines.sh during the beta cycle
 export BUILDING_RELEASE=1
 
-. "$topsrcdir/build/mozconfig.rust"
-
 . "$topsrcdir/build/mozconfig.common.override"
new file mode 100644
--- /dev/null
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+
+ // The amount of people to be part of e10s, in %
+const TEST_THRESHOLD = {
+  "beta"    : 50,
+  "release" : 0,
+};
+
+const PREF_COHORT_SAMPLE       = "e10s.rollout.cohortSample";
+const PREF_COHORT_NAME         = "e10s.rollout.cohort";
+const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
+const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
+const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
+const PREF_TOGGLE_E10S         = "browser.tabs.remote.autostart.2";
+
+
+function startup() {
+  // In theory we only need to run this once (on install()), but
+  // it's better to also run it on every startup. If the user has
+  // made manual changes to the prefs, this will keep the data
+  // reported more accurate.
+  // It's also fine (and preferred) to just do it here on startup
+  // (instead of observing prefs), because e10s takes a restart
+  // to take effect, so we keep the data based on how it was when
+  // the session started.
+  defineCohort();
+}
+
+function install() {
+  defineCohort();
+}
+
+let cohortDefinedOnThisSession = false;
+
+function defineCohort() {
+  // Avoid running twice when it was called by install() first
+  if (cohortDefinedOnThisSession) {
+    return;
+  }
+  cohortDefinedOnThisSession = true;
+
+  let updateChannel = UpdateUtils.getUpdateChannel(false);
+  if (!(updateChannel in TEST_THRESHOLD)) {
+    setCohort("unsupportedChannel");
+    return;
+  }
+
+  let userOptedOut = optedOut();
+  let userOptedIn = optedIn();
+  let testGroup = (getUserSample() < TEST_THRESHOLD[updateChannel]);
+
+  if (userOptedOut) {
+    setCohort("optedOut");
+  } else if (userOptedIn) {
+    setCohort("optedIn");
+  } else if (testGroup) {
+    setCohort("test");
+    Preferences.set(PREF_TOGGLE_E10S, true);
+  } else {
+    setCohort("control");
+    Preferences.reset(PREF_TOGGLE_E10S);
+  }
+}
+
+function shutdown(data, reason) {
+}
+
+function uninstall() {
+}
+
+function getUserSample() {
+  let existingVal = Preferences.get(PREF_COHORT_SAMPLE, undefined);
+  if (typeof(existingVal) == "number") {
+    return existingVal;
+  }
+
+  let val = Math.floor(Math.random() * 100);
+  Preferences.set(PREF_COHORT_SAMPLE, val);
+  return val;
+}
+
+function setCohort(cohortName) {
+  Preferences.set(PREF_COHORT_NAME, cohortName);
+}
+
+function optedIn() {
+  return Preferences.get(PREF_E10S_OPTED_IN, false) ||
+         Preferences.get(PREF_E10S_FORCE_ENABLED, false);
+}
+
+function optedOut() {
+  // Users can also opt-out by toggling back the pref to false.
+  // If they reset the pref instead they might be re-enabled if
+  // they are still part of the threshold.
+  return Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
+         (Preferences.isSet(PREF_TOGGLE_E10S) &&
+          Preferences.get(PREF_TOGGLE_E10S) == false);
+}
+
copy from browser/extensions/pocket/install.rdf.in
copy to browser/extensions/e10srollout/install.rdf.in
--- a/browser/extensions/pocket/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -4,28 +4,28 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
-    <em:id>firefox@getpocket.com</em:id>
+    <em:id>e10srollout@mozilla.org</em:id>
     <em:version>1.0</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
 
     <!-- Target Application this theme can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>@FIREFOX_VERSION@</em:minVersion>
         <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
       </Description>
     </em:targetApplication>
 
     <!-- Front End MetaData -->
-    <em:name>Pocket</em:name>
-    <em:description>When you find something you want to view later, put it in Pocket.</em:description>
+    <em:name>Multi-process staged rollout</em:name>
+    <em:description>Staged rollout of Firefox multi-process feature.</em:description>
   </Description>
 </RDF>
copy from browser/extensions/pocket/moz.build
copy to browser/extensions/e10srollout/moz.build
--- a/browser/extensions/pocket/moz.build
+++ b/browser/extensions/e10srollout/moz.build
@@ -1,19 +1,13 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIRS += ['locales']
-
-FINAL_TARGET_FILES.features['firefox@getpocket.com'] += [
+FINAL_TARGET_FILES.features['e10srollout@mozilla.org'] += [
   'bootstrap.js'
 ]
 
-FINAL_TARGET_PP_FILES.features['firefox@getpocket.com'] += [
+FINAL_TARGET_PP_FILES.features['e10srollout@mozilla.org'] += [
   'install.rdf.in'
 ]
-
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
-
-JAR_MANIFESTS += ['jar.mn']
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,11 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
+    'e10srollout',
     'loop',
     'pdfjs',
     'pocket',
 ]
--- a/build/clang-plugin/tests/TestHeapClass.cpp
+++ b/build/clang-plugin/tests/TestHeapClass.cpp
@@ -1,18 +1,18 @@
 #define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
 #define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
 
 #include <stddef.h>
 
 struct MOZ_HEAP_CLASS Heap {
   int i;
   Heap() {}
-  MOZ_IMPLICIT Heap(int i) {}
-  Heap(int i, int j) {}
+  MOZ_IMPLICIT Heap(int a) {}
+  Heap(int a, int b) {}
   void *operator new(size_t x) throw() { return 0; }
   void *operator new(size_t blah, char *buffer) { return buffer; }
 };
 
 template <class T>
 struct MOZ_HEAP_CLASS TemplateClass {
   T i;
 };
--- a/build/clang-plugin/tests/TestNonTemporaryClass.cpp
+++ b/build/clang-plugin/tests/TestNonTemporaryClass.cpp
@@ -1,18 +1,18 @@
 #define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class")))
 #define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
 
 #include <stddef.h>
 
 struct MOZ_NON_TEMPORARY_CLASS NonTemporary {
   int i;
   NonTemporary() {}
-  MOZ_IMPLICIT NonTemporary(int i) {}
-  NonTemporary(int i, int j) {}
+  MOZ_IMPLICIT NonTemporary(int a) {}
+  NonTemporary(int a, int b) {}
   void *operator new(size_t x) throw() { return 0; }
   void *operator new(size_t blah, char *buffer) { return buffer; }
 };
 
 template <class T>
 struct MOZ_NON_TEMPORARY_CLASS TemplateClass {
   T i;
 };
--- a/build/util/count_ctors.py
+++ b/build/util/count_ctors.py
@@ -1,11 +1,13 @@
+
 #!/usr/bin/python
+import json
+import re
 import subprocess
-import re
 import sys
 
 def count_ctors(filename):
     proc = subprocess.Popen(
         ['readelf', '-W', '-S', filename], stdout=subprocess.PIPE)
 
     # Some versions of ld produce both .init_array and .ctors.  So we have
     # to check for both.
@@ -42,11 +44,20 @@ def count_ctors(filename):
 
     # We didn't find anything; somebody switched initialization mechanisms on
     # us, or the binary is completely busted.  Complain either way.
     print >>sys.stderr, "Couldn't find .init_array or .ctors in", filename
     sys.exit(1)
 
 if __name__ == '__main__':
     for f in sys.argv[1:]:
-        output = {"framework": {"name": "build_metrics"}, "suites": [{"name": "compiler_metrics", "subtests": [{"name": "num_constructors", "value": count_ctors(f)}] } ]}
-        print "PERFHERDER_DATA: %s" % output
+        perfherder_data = {
+            "framework": {"name": "build_metrics"},
+            "suites": [{
+                "name": "compiler_metrics",
+                "subtests": [{
+                    "name": "num_constructors",
+                    "value": count_ctors(f)
+                }]}
+            ]
+        }
+        print "PERFHERDER_DATA: %s" % json.dumps(perfherder_data)
 
--- a/client.mk
+++ b/client.mk
@@ -33,16 +33,26 @@
 #   MOZ_UNIFY_BDATE      - Set to use the same bdate for each project in
 #                          MOZ_BUILD_PROJECTS
 #
 #######################################################################
 # Defines
 
 comma := ,
 
+ifdef MACH
+ifndef NO_BUILDSTATUS_MESSAGES
+define BUILDSTATUS
+@echo 'BUILDSTATUS $1'
+
+endef
+endif
+endif
+
+
 CWD := $(CURDIR)
 ifneq (1,$(words $(CWD)))
 $(error The mozilla directory cannot be located in a path with spaces.)
 endif
 
 ifeq "$(CWD)" "/"
 CWD   := /.
 endif
@@ -220,22 +230,33 @@ build_and_deploy: build package install
 everything: clean build
 
 ####################################
 # Profile-Guided Optimization
 #  This is up here, outside of the MOZ_CURRENT_PROJECT logic so that this
 #  is usable in multi-pass builds, where you might not have a runnable
 #  application until all the build passes and postflight scripts have run.
 profiledbuild::
+	$(call BUILDSTATUS,TIERS pgo_profile_generate pgo_package pgo_profile pgo_clobber pgo_profile_use)
+	$(call BUILDSTATUS,TIER_START pgo_profile_generate)
 	$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1 CREATE_MOZCONFIG_JSON=
+	$(call BUILDSTATUS,TIER_FINISH pgo_profile_generate)
+	$(call BUILDSTATUS,TIER_START pgo_package)
 	$(MAKE) -C $(OBJDIR) package MOZ_PGO_INSTRUMENTED=1 MOZ_INTERNAL_SIGNING_FORMAT= MOZ_EXTERNAL_SIGNING_FORMAT=
 	rm -f $(OBJDIR)/jarlog/en-US.log
+	$(call BUILDSTATUS,TIER_FINISH pgo_package)
+	$(call BUILDSTATUS,TIER_START pgo_profile)
 	MOZ_PGO_INSTRUMENTED=1 JARLOG_FILE=jarlog/en-US.log EXTRA_TEST_ARGS=10 $(MAKE) -C $(OBJDIR) pgo-profile-run
+	$(call BUILDSTATUS,TIER_FINISH pgo_profile)
+	$(call BUILDSTATUS,TIER_START pgo_clobber)
 	$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild CREATE_MOZCONFIG_JSON=
+	$(call BUILDSTATUS,TIER_FINISH pgo_clobber)
+	$(call BUILDSTATUS,TIER_START pgo_profile_use)
 	$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1 CREATE_MOZCONFIG_JSON=
+	$(call BUILDSTATUS,TIER_FINISH pgo_profile_use)
 
 #####################################################
 # Build date unification
 
 ifdef MOZ_UNIFY_BDATE
 ifndef MOZ_BUILD_DATE
 ifdef MOZ_BUILD_PROJECTS
 MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/build/variables.py buildid_header | awk '{print $$3}')
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -55,16 +55,20 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
 # -DSQLITE_ENABLE_LOCKING_STYLE=1 to help with AFP folders
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 1
 
 # sqlite defaults this to on on __APPLE_ but it breaks on newer iOS SDKs
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
     DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 0
 
+# Thunderbird needs the 2-argument version of fts3_tokenizer()
+if CONFIG['MOZ_THUNDERBIRD'] or CONFIG['MOZ_SUITE']:
+    DEFINES['SQLITE_ENABLE_FTS3_TOKENIZER'] = 1
+
 # Turn on SQLite's assertions in debug builds.
 if CONFIG['MOZ_DEBUG']:
     DEFINES['SQLITE_DEBUG'] = 1
 
 if CONFIG['OS_TARGET'] == 'Android':
     # default to user readable only to fit Android security model
     DEFINES['SQLITE_DEFAULT_FILE_PERMISSIONS'] = '0600'
 
--- a/devtools/client/responsivedesign/test/browser.ini
+++ b/devtools/client/responsivedesign/test/browser.ini
@@ -2,16 +2,16 @@
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   touch.html
 
 [browser_responsive_cmd.js]
 [browser_responsivecomputedview.js]
-skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s
+skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1252201 - Docshell leak on win/osx debug e10s
 [browser_responsiveruleview.js]
-skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s
+skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1252201 - Docshell leak on win/osx debug e10s
 [browser_responsiveui.js]
 [browser_responsiveui_touch.js]
 [browser_responsiveuiaddcustompreset.js]
 [browser_responsive_devicewidth.js]
 [browser_responsiveui_customuseragent.js]
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -63,17 +63,17 @@ support-files =
 [browser_styleeditor_highlight-selector.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
 [browser_styleeditor_inline_friendly_names.js]
 [browser_styleeditor_loading.js]
 [browser_styleeditor_media_sidebar.js]
 [browser_styleeditor_media_sidebar_links.js]
-skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s
+skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1252201 - Docshell leak on win/osx debug e10s
 [browser_styleeditor_media_sidebar_sourcemaps.js]
 [browser_styleeditor_missing_stylesheet.js]
 [browser_styleeditor_navigate.js]
 [browser_styleeditor_new.js]
 [browser_styleeditor_nostyle.js]
 [browser_styleeditor_opentab.js]
 [browser_styleeditor_pretty.js]
 [browser_styleeditor_private_perwindowpb.js]
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -345,17 +345,16 @@ tags = trackingprotection
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
 [browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
 [browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
 [browser_console_history_persist.js]
 [browser_webconsole_output_01.js]
-skip-if = e10s # Bug 1042253 - webconsole e10s tests
 [browser_webconsole_output_02.js]
 [browser_webconsole_output_03.js]
 [browser_webconsole_output_04.js]
 [browser_webconsole_output_05.js]
 [browser_webconsole_output_06.js]
 [browser_webconsole_output_dom_elements_01.js]
 [browser_webconsole_output_dom_elements_02.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
--- a/devtools/client/webconsole/test/browser_webconsole_output_01.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_01.js
@@ -12,21 +12,16 @@
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
 
 // Test the webconsole output for various types of objects.
 
 const TEST_URI = "data:text/html;charset=utf8,test for console output - 01";
 
 var {DebuggerServer} = require("devtools/server/main");
 
-var LONG_STRING_LENGTH = DebuggerServer.LONG_STRING_LENGTH;
-var LONG_STRING_INITIAL_LENGTH = DebuggerServer.LONG_STRING_INITIAL_LENGTH;
-DebuggerServer.LONG_STRING_LENGTH = 100;
-DebuggerServer.LONG_STRING_INITIAL_LENGTH = 50;
-
 var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a");
 var initialString = longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
 
 var inputTests = [
   // 0
   {
     input: "'hello \\nfrom \\rthe \\\"string world!'",
     output: "\"hello \nfrom \rthe \"string world!\"",
@@ -109,21 +104,16 @@ var inputTests = [
   },
 ];
 
 longString = initialString = null;
 
 function test() {
   requestLongerTimeout(2);
 
-  registerCleanupFunction(() => {
-    DebuggerServer.LONG_STRING_LENGTH = LONG_STRING_LENGTH;
-    DebuggerServer.LONG_STRING_INITIAL_LENGTH = LONG_STRING_INITIAL_LENGTH;
-  });
-
   Task.spawn(function*() {
     let {tab} = yield loadTab(TEST_URI);
     let hud = yield openConsole(tab);
     return checkOutputForInputs(hud, inputTests);
   }).then(finishUp);
 }
 
 function finishUp() {
--- a/devtools/client/webconsole/test/browser_webconsole_promise.js
+++ b/devtools/client/webconsole/test/browser_webconsole_promise.js
@@ -4,51 +4,32 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Bug 1148759 - Test the webconsole can display promises inside objects.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,test for console and promises";
 
-var {DebuggerServer} = require("devtools/server/main");
-
-var LONG_STRING_LENGTH = DebuggerServer.LONG_STRING_LENGTH;
-var LONG_STRING_INITIAL_LENGTH = DebuggerServer.LONG_STRING_INITIAL_LENGTH;
-DebuggerServer.LONG_STRING_LENGTH = 100;
-DebuggerServer.LONG_STRING_INITIAL_LENGTH = 50;
-
-var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a");
-var initialString = longString.substring(0,
-  DebuggerServer.LONG_STRING_INITIAL_LENGTH);
-
 var inputTests = [
   // 0
   {
     input: "({ x: Promise.resolve() })",
     output: "Object { x: Promise }",
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object"
   },
 ];
 
-longString = initialString = null;
-
 function test() {
   requestLongerTimeout(2);
 
-  registerCleanupFunction(() => {
-    DebuggerServer.LONG_STRING_LENGTH = LONG_STRING_LENGTH;
-    DebuggerServer.LONG_STRING_INITIAL_LENGTH = LONG_STRING_INITIAL_LENGTH;
-  });
-
   Task.spawn(function*() {
     let {tab} = yield loadTab(TEST_URI);
     let hud = yield openConsole(tab);
     return checkOutputForInputs(hud, inputTests);
   }).then(finishUp);
 }
 
 function finishUp() {
-  longString = initialString = inputTests = null;
   finishTest();
 }
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -1472,17 +1472,17 @@ function checkOutputForInputs(hud, input
 
   function* checkInput(entry) {
     yield checkConsoleLog(entry);
     yield checkPrintOutput(entry);
     yield checkJSEval(entry);
   }
 
   function* checkConsoleLog(entry) {
-    info("Logging: " + entry.input);
+    info("Logging");
     hud.jsterm.clearOutput();
     hud.jsterm.execute("console.log(" + entry.input + ")");
 
     let consoleOutput = "consoleOutput" in entry ?
                         entry.consoleOutput : entry.output;
 
     let [result] = yield waitForMessages({
       webconsole: hud,
@@ -1496,40 +1496,40 @@ function checkOutputForInputs(hud, input
 
     let msg = [...result.matched][0];
 
     if (entry.consoleLogClick) {
       yield checkObjectClick(entry, msg);
     }
 
     if (typeof entry.inspectorIcon == "boolean") {
-      info("Checking Inspector Link: " + entry.input);
+      info("Checking Inspector Link");
       yield checkLinkToInspector(entry.inspectorIcon, msg);
     }
   }
 
   function checkPrintOutput(entry) {
-    info("Printing: " + entry.input);
+    info("Printing");
     hud.jsterm.clearOutput();
     hud.jsterm.execute("print(" + entry.input + ")");
 
     let printOutput = entry.printOutput || entry.output;
 
     return waitForMessages({
       webconsole: hud,
       messages: [{
         name: "print() output: " + printOutput,
         text: printOutput,
         category: CATEGORY_OUTPUT,
       }],
     });
   }
 
   function* checkJSEval(entry) {
-    info("Evaluating: " + entry.input);
+    info("Evaluating");
     hud.jsterm.clearOutput();
     hud.jsterm.execute(entry.input);
 
     let evalOutput = entry.evalOutput || entry.output;
 
     let [result] = yield waitForMessages({
       webconsole: hud,
       messages: [{
@@ -1545,17 +1545,17 @@ function checkOutputForInputs(hud, input
     }
     if (typeof entry.inspectorIcon == "boolean") {
       info("Checking Inspector Link: " + entry.input);
       yield checkLinkToInspector(entry.inspectorIcon, msg);
     }
   }
 
   function* checkObjectClick(entry, msg) {
-    info("Clicking: " + entry.input);
+    info("Clicking");
     let body;
     if (entry.getClickableNode) {
       body = entry.getClickableNode(msg);
     } else {
       body = msg.querySelector(".message-body a") ||
              msg.querySelector(".message-body");
     }
     ok(body, "the message body");
@@ -1590,17 +1590,17 @@ function checkOutputForInputs(hud, input
       container.removeEventListener("TabOpen", entry._onTabOpen, true);
       entry._onTabOpen = null;
     }
 
     yield promise.resolve(null);
   }
 
   function onVariablesViewOpen(entry, {resolve, reject}, event, view, options) {
-    info("Variables view opened: " + entry.input);
+    info("Variables view opened");
     let label = entry.variablesViewLabel || entry.output;
     if (typeof label == "string" && options.label != label) {
       return;
     }
     if (label instanceof RegExp && !label.test(options.label)) {
       return;
     }
 
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -217,55 +217,16 @@ nsDefaultURIFixup::GetFixupURIInfo(const
     // NB: FileURIFixup only returns a URI if it had to fix the protocol to
     // do so, so passing in file:///foo/bar will not hit this path:
     if (uri) {
       uri.swap(info->mFixedURI);
       info->mPreferredURI = info->mFixedURI;
       info->mFixupChangedProtocol = true;
       return NS_OK;
     }
-
-#if defined(XP_WIN)
-    // Not a file URL, so translate '\' to '/' for convenience in the common
-    // protocols. E.g. catch
-    //
-    //   http:\\broken.com\address
-    //   http:\\broken.com/blah
-    //   broken.com\blah
-    //
-    // Code will also do partial fix up the following urls
-    //
-    //   http:\\broken.com\address/somewhere\image.jpg (stops at first forward slash)
-    //   http:\\broken.com\blah?arg=somearg\foo.jpg (stops at question mark)
-    //   http:\\broken.com#odd\ref (stops at hash)
-    //
-    if (scheme.IsEmpty() ||
-        scheme.LowerCaseEqualsLiteral("http") ||
-        scheme.LowerCaseEqualsLiteral("https") ||
-        scheme.LowerCaseEqualsLiteral("ftp")) {
-      // Walk the string replacing backslashes with forward slashes until
-      // the end is reached, or a question mark, or a hash, or a forward
-      // slash. The forward slash test is to stop before trampling over
-      // URIs which legitimately contain a mix of both forward and
-      // backward slashes.
-      nsAutoCString::iterator start;
-      nsAutoCString::iterator end;
-      uriString.BeginWriting(start);
-      uriString.EndWriting(end);
-      while (start != end) {
-        if (*start == '?' || *start == '#' || *start == '/') {
-          break;
-        }
-        if (*start == '\\') {
-          *start = '/';
-        }
-        ++start;
-      }
-    }
-#endif
   }
 
   if (!sInitializedPrefCaches) {
     // Check if we want to fix up common scheme typos.
     rv = Preferences::AddBoolVarCache(&sFixTypos,
                                       "browser.fixup.typo.scheme",
                                       sFixTypos);
     MOZ_ASSERT(NS_SUCCEEDED(rv),
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -800,20 +800,18 @@ nsDocShell::nsDocShell()
 #ifdef DEBUG
   , mInEnsureScriptEnv(false)
 #endif
   , mAffectPrivateSessionLifetime(true)
   , mInvisible(false)
   , mHasLoadedNonBlankURI(false)
   , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
   , mBlankTiming(false)
-  , mFrameType(eFrameTypeRegular)
+  , mFrameType(FRAME_TYPE_REGULAR)
   , mIsInIsolatedMozBrowser(false)
-  , mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
-  , mUserContextId(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID)
   , mParentCharsetSource(0)
   , mJSRunToCompletionDepth(0)
 {
   mHistoryID = ++gDocshellIDCounter;
   if (gDocShellCount++ == 0) {
     NS_ASSERTION(sURIFixup == nullptr,
                  "Huh, sURIFixup not null in first nsDocShell ctor!");
 
@@ -4004,17 +4002,16 @@ nsDocShell::AddChild(nsIDocShellTreeItem
     childDocShell->SetUseGlobalHistory(true);
   }
 
   if (aChild->ItemType() != mItemType) {
     return NS_OK;
   }
 
   aChild->SetTreeOwner(mTreeOwner);
-  childDocShell->SetUserContextId(mUserContextId);
   childDocShell->SetIsInIsolatedMozBrowserElement(mIsInIsolatedMozBrowser);
 
   nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
   if (!childAsDocShell) {
     return NS_OK;
   }
 
   // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
@@ -9546,17 +9543,17 @@ nsDocShell::JustStartedNetworkLoad()
   return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
 }
 
 nsresult
 nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
                                         nsIPrincipal** aResult)
 {
   PrincipalOriginAttributes attrs;
-  attrs.InheritFromDocShellToDoc(GetOriginAttributes(), aReferrer);
+  attrs.InheritFromDocShellToDoc(mOriginAttributes, aReferrer);
   nsCOMPtr<nsIPrincipal> prin =
     BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
   prin.forget(aResult);
 
   return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
@@ -13845,202 +13842,154 @@ unsigned long nsDocShell::gNumberOfDocSh
 
 NS_IMETHODIMP
 nsDocShell::GetCanExecuteScripts(bool* aResult)
 {
   *aResult = mCanExecuteScripts;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDocShell::SetIsApp(uint32_t aOwnAppId)
-{
-  mOwnOrContainingAppId = aOwnAppId;
-  if (aOwnAppId != nsIScriptSecurityManager::NO_APP_ID &&
-      aOwnAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-    mFrameType = eFrameTypeApp;
-  } else {
-    mFrameType = eFrameTypeRegular;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::SetIsBrowserInsideApp(uint32_t aContainingAppId)
-{
-  mOwnOrContainingAppId = aContainingAppId;
-  mFrameType = eFrameTypeBrowser;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg)
-{
-  mSignedPkg = aSignedPkg;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::SetUserContextId(uint32_t aUserContextId)
-{
-  mUserContextId = aUserContextId;
-
-  nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
-  while (iter.HasMore()) {
-    nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
-    if (!docshell || docshell->ItemType() != ItemType()) {
-      continue;
-    }
-
-    docshell->SetUserContextId(aUserContextId);
-  }
-
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::SetFrameType(uint32_t aFrameType)
+{
+  mFrameType = aFrameType;
+  return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetFrameType(uint32_t* aFrameType)
+{
+  *aFrameType = mFrameType;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsApp(bool* aIsApp)
 {
-  *aIsApp = (mFrameType == eFrameTypeApp);
+  *aIsApp = (mFrameType == FRAME_TYPE_APP);
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsMozBrowserOrApp(bool* aIsMozBrowserOrApp)
 {
-  switch (mFrameType) {
-    case eFrameTypeRegular:
-      *aIsMozBrowserOrApp = false;
-      break;
-    case eFrameTypeBrowser:
-    case eFrameTypeApp:
-      *aIsMozBrowserOrApp = true;
-      break;
-  }
-
-  return NS_OK;
-}
-
-nsDocShell::FrameType
+  *aIsMozBrowserOrApp = (mFrameType != FRAME_TYPE_REGULAR);
+  return NS_OK;
+}
+
+uint32_t
 nsDocShell::GetInheritedFrameType()
 {
-  if (mFrameType != eFrameTypeRegular) {
+  if (mFrameType != FRAME_TYPE_REGULAR) {
     return mFrameType;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   GetSameTypeParent(getter_AddRefs(parentAsItem));
 
   nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
   if (!parent) {
-    return eFrameTypeRegular;
+    return FRAME_TYPE_REGULAR;
   }
 
   return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsIsolatedMozBrowserElement(bool* aIsIsolatedMozBrowserElement)
 {
-  bool result = mFrameType == eFrameTypeBrowser && mIsInIsolatedMozBrowser;
+  bool result = mFrameType == FRAME_TYPE_BROWSER && mIsInIsolatedMozBrowser;
   *aIsIsolatedMozBrowserElement = result;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
 {
   MOZ_ASSERT(!mIsInIsolatedMozBrowser ||
-             (GetInheritedFrameType() == eFrameTypeBrowser),
+             (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
              "Isolated mozbrowser should only be true inside browser frames");
-  bool result = (GetInheritedFrameType() == eFrameTypeBrowser) &&
+  bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
                 mIsInIsolatedMozBrowser;
   *aIsInIsolatedMozBrowserElement = result;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::SetIsInIsolatedMozBrowserElement(bool aIsInIsolatedMozBrowserElement)
 {
   mIsInIsolatedMozBrowser = aIsInIsolatedMozBrowserElement;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsInMozBrowserOrApp(bool* aIsInMozBrowserOrApp)
 {
-  switch (GetInheritedFrameType()) {
-    case eFrameTypeRegular:
-      *aIsInMozBrowserOrApp = false;
-      break;
-    case eFrameTypeBrowser:
-    case eFrameTypeApp:
-      *aIsInMozBrowserOrApp = true;
-      break;
-  }
-
+  *aIsInMozBrowserOrApp = (GetInheritedFrameType() != FRAME_TYPE_REGULAR);
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetAppId(uint32_t* aAppId)
 {
-  if (mOwnOrContainingAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-    *aAppId = mOwnOrContainingAppId;
+  if (mOriginAttributes.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    *aAppId = mOriginAttributes.mAppId;
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocShell> parent;
   GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
 
   if (!parent) {
     *aAppId = nsIScriptSecurityManager::NO_APP_ID;
     return NS_OK;
   }
 
   return parent->GetAppId(aAppId);
 }
 
-DocShellOriginAttributes
-nsDocShell::GetOriginAttributes()
-{
-  DocShellOriginAttributes attrs;
-  RefPtr<nsDocShell> parent = GetParentDocshell();
-  if (parent) {
-    nsCOMPtr<nsIPrincipal> parentPrin = parent->GetDocument()->NodePrincipal();
-    PrincipalOriginAttributes poa = BasePrincipal::Cast(parentPrin)->OriginAttributesRef();
-    attrs.InheritFromDocToChildDocShell(poa);
-  } else {
-    // This is the topmost docshell, so we get the mSignedPkg attribute if it is
-    // set before.
-    attrs.mSignedPkg = mSignedPkg;
-  }
-
-  if (mOwnOrContainingAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-    attrs.mAppId = mOwnOrContainingAppId;
-  }
-
-  attrs.mUserContextId = mUserContextId;
-  attrs.mInIsolatedMozBrowser = mIsInIsolatedMozBrowser;
-
-  return attrs;
-}
-
+// Implements nsILoadContext.originAttributes
 NS_IMETHODIMP
 nsDocShell::GetOriginAttributes(JS::MutableHandle<JS::Value> aVal)
 {
   JSContext* cx = nsContentUtils::GetCurrentJSContext();
   MOZ_ASSERT(cx);
 
-  bool ok = ToJSValue(cx, GetOriginAttributes(), aVal);
+  return GetOriginAttributes(cx, aVal);
+}
+
+// Implements nsIDocShell.GetOriginAttributes()
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JSContext* aCx,
+                                JS::MutableHandle<JS::Value> aVal)
+{
+  bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
+void
+nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs)
+{
+  mOriginAttributes = aAttrs;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
+                                JSContext* aCx)
+{
+  DocShellOriginAttributes attrs;
+  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  SetOriginAttributes(attrs);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
 {
   uint32_t appId = nsIDocShell::GetAppId();
   if (appId != nsIScriptSecurityManager::NO_APP_ID &&
       appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     nsCOMPtr<nsIAppsService> appsService =
       do_GetService(APPS_SERVICE_CONTRACTID);
@@ -14221,17 +14170,17 @@ nsDocShell::ShouldPrepareForIntercept(ns
       if (isThirdPartyURI) {
         return NS_OK;
       }
     }
   }
 
   if (aIsNonSubresourceRequest) {
     PrincipalOriginAttributes attrs;
-    attrs.InheritFromDocShellToDoc(GetOriginAttributes(), aURI);
+    attrs.InheritFromDocShellToDoc(mOriginAttributes, aURI);
     *aShouldIntercept = swm->IsAvailable(attrs, aURI);
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = GetDocument();
   if (!doc) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -14275,17 +14224,17 @@ nsDocShell::ChannelIntercepted(nsIInterc
 
   bool isReload = mLoadType & LOAD_CMD_RELOAD;
 
   nsCOMPtr<nsIURI> uri;
   rv = channel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrincipalOriginAttributes attrs;
-  attrs.InheritFromDocShellToDoc(GetOriginAttributes(), uri);
+  attrs.InheritFromDocShellToDoc(mOriginAttributes, uri);
 
   ErrorResult error;
   swm->DispatchFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel,
                           isReload, isSubresourceLoad, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -265,17 +265,23 @@ public:
   void NotifyAsyncPanZoomStopped();
 
   void SetInFrameSwap(bool aInSwap)
   {
     mInFrameSwap = aInSwap;
   }
   bool InFrameSwap();
 
-  mozilla::DocShellOriginAttributes GetOriginAttributes();
+  const mozilla::DocShellOriginAttributes&
+  GetOriginAttributes()
+  {
+    return mOriginAttributes;
+  }
+
+  void SetOriginAttributes(const mozilla::DocShellOriginAttributes& aAttrs);
 
   void GetInterceptedDocumentId(nsAString& aId)
   {
     aId = mInterceptedDocumentId;
   }
 
 private:
   // An observed docshell wrapper is created when recording markers is enabled.
@@ -759,39 +765,32 @@ public:
   };
 
 protected:
   bool JustStartedNetworkLoad();
 
   nsresult CreatePrincipalFromReferrer(nsIURI* aReferrer,
                                        nsIPrincipal** aResult);
 
-  enum FrameType
-  {
-    eFrameTypeRegular,
-    eFrameTypeBrowser,
-    eFrameTypeApp
-  };
-
-  static const nsCString FrameTypeToString(FrameType aFrameType)
+  static const nsCString FrameTypeToString(uint32_t aFrameType)
   {
     switch (aFrameType) {
-      case FrameType::eFrameTypeApp:
+      case FRAME_TYPE_APP:
         return NS_LITERAL_CSTRING("app");
-      case FrameType::eFrameTypeBrowser:
+      case FRAME_TYPE_BROWSER:
         return NS_LITERAL_CSTRING("browser");
-      case FrameType::eFrameTypeRegular:
+      case FRAME_TYPE_REGULAR:
         return NS_LITERAL_CSTRING("regular");
       default:
         NS_ERROR("Unknown frame type");
         return EmptyCString();
     }
   }
 
-  FrameType GetInheritedFrameType();
+  uint32_t GetInheritedFrameType();
 
   bool HasUnloadedParent();
 
   // Dimensions of the docshell
   nsIntRect mBounds;
   nsString mName;
   nsString mTitle;
   nsString mCustomUserAgent;
@@ -994,54 +993,38 @@ protected:
   RefPtr<nsDOMNavigationTiming> mTiming;
 
   // This flag means that mTiming has been initialized but nulled out.
   // We will check the innerWin's timing before creating a new one
   // in MaybeInitTiming()
   bool mBlankTiming;
 
   // Are we a regular frame, a browser frame, or an app frame?
-  FrameType mFrameType;
+  uint32_t mFrameType;
 
   // Whether we are in an isolated mozbrowser frame.
   bool mIsInIsolatedMozBrowser;
 
-  // We only expect mOwnOrContainingAppId to be something other than
-  // UNKNOWN_APP_ID if mFrameType != eFrameTypeRegular. For vanilla iframes
-  // inside an app, we'll retrieve the containing app-id by walking up the
-  // docshell hierarchy.
-  //
-  // (This needs to be the docshell's own /or containing/ app id because the
-  // containing app frame might be in another process, in which case we won't
-  // find it by walking up the docshell hierarchy.)
-  uint32_t mOwnOrContainingAppId;
-
-  // userContextId signifying which container we are in
-  uint32_t mUserContextId;
-
   nsString mPaymentRequestId;
 
   nsString GetInheritedPaymentRequestId();
 
-  // The packageId for a signed packaged iff this docShell is created
-  // for a signed package.
-  nsString mSignedPkg;
-
   nsString mInterceptedDocumentId;
 
 private:
   nsCString mForcedCharset;
   nsCString mParentCharset;
   int32_t mParentCharsetSource;
   nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
   nsTObserverArray<nsWeakPtr> mPrivacyObservers;
   nsTObserverArray<nsWeakPtr> mReflowObservers;
   nsTObserverArray<nsWeakPtr> mScrollObservers;
   nsCString mOriginalUriString;
   nsWeakPtr mOpener;
+  mozilla::DocShellOriginAttributes mOriginAttributes;
 
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
   // Separate function to do the actual name (i.e. not _top, _self etc.)
   // searching for FindItemWithName.
   nsresult DoFindItemWithName(const char16_t* aName,
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -764,16 +764,25 @@ interface nsIDocShell : nsIDocShellTreeI
   [noscript] void notifyScrollObservers();
 
   /**
    * Returns true iff the docshell corresponds to an <iframe mozapp>.
    */
   [infallible] readonly attribute boolean isApp;
 
   /**
+   * The type of iframe that this docshell lives.
+   */
+  const unsigned long FRAME_TYPE_REGULAR = 0;
+  const unsigned long FRAME_TYPE_BROWSER = 1;
+  const unsigned long FRAME_TYPE_APP     = 2;
+
+  [infallible] attribute unsigned long frameType;
+
+  /**
    * Returns true if this docshell corresponds to an <iframe mozbrowser> or
    * <iframe mozapp>.  <xul:browser> returns false here.
    */
   [infallible] readonly attribute boolean isMozBrowserOrApp;
 
   /**
    * Returns true if this docshell corresponds to an isolated <iframe
    * mozbrowser>.
@@ -808,58 +817,24 @@ interface nsIDocShell : nsIDocShellTreeI
    * or <iframe mozapp>.  <xul:browser> returns false here.
    *
    * To compute this value, we walk up the docshell hierarchy.  If we encounter
    * a docshell with isMozBrowserOrApp before we hit the end of the hierarchy,
    * we return true.  Otherwise, we return false.
    */
   [infallible] readonly attribute boolean isInMozBrowserOrApp;
 
-   /**
-    * Indicate that this docshell corresponds to an app with the given app id.
-    *
-    * You may pass NO_APP_ID or UNKNOWN_APP_ID for containingAppId.  If you
-    * pass NO_APP_ID, then this docshell will return NO_APP_ID for appId.  If
-    * you pass UNKNOWN_APP_ID, then this docshell will search its hiearchy for
-    * an app frame and use that frame's appId.
-    *
-    * You can call this method more than once, but there's no guarantee that
-    * other components will update their view of the world if you change a
-    * docshell's app id, so tread lightly.
-    *
-    * If you call this method after calling setIsBrowserInsideApp, this
-    * docshell will forget the fact that it was a browser.
-    */
-   void setIsApp(in unsigned long ownAppId);
-
-   /**
-    * Indicate that this docshell corresponds to a browser inside an app with
-    * the given ID.  As with setIsApp, you may pass NO_APP_ID or
-    * UNKNOWN_APP_ID.
-    *
-    * As with setIsApp, you may call this more than once, but it's kind of a
-    * hack, so be careful.
-    */
-   void setIsBrowserInsideApp(in unsigned long containingAppId);
-
-   /**
-    * Indicate that this docshell corresponds to a signed package with
-    * the given packageId.
-    */
-   void setIsSignedPackage(in AString packageId);
-
   /**
    * Returns the id of the app associated with this docshell.  If this docshell
    * is an <iframe mozbrowser> inside an <iframe mozapp>, we return the app's
    * appId.
    *
    * We compute this value by walking up the docshell hierarchy until we find a
-   * docshell on which setIsApp(x) or setIsBrowserInsideApp(x) was called
-   * (ignoring those docshells where x == UNKNOWN_APP_ID).  We return the app
-   * id x.
+   * docshell on which origin attributes was set. (ignoring those docshells
+   * where x == UNKNOWN_APP_ID).  We return the app id x.
    *
    * If we don't find a docshell with an associated app id in our hierarchy, we
    * return NO_APP_ID.  We never return UNKNOWN_APP_ID.
    *
    * Notice that a docshell may have an associated app even if it returns true
    * for isBrowserElement!
    */
   [infallible] readonly attribute unsigned long appId;
@@ -1100,16 +1075,20 @@ interface nsIDocShell : nsIDocShellTreeI
    * True for top level chrome docshells. Throws if set to false with
    * top level chrome docshell.
    */
   attribute boolean windowDraggingAllowed;
 
   /**
    * Sets/gets the current scroll restoration mode.
    * @see https://html.spec.whatwg.org/#dom-history-scroll-restoration
-  */
+   */
   attribute boolean currentScrollRestorationIsManual;
 
-  /*
-   * Sets the user context ID for this docshell.
+  /**
+   * Setter and getter for the origin attributes living on this docshell.
    */
-  void setUserContextId(in unsigned long aUserContextId);
+  [implicit_jscontext]
+  jsval getOriginAttributes();
+
+  [implicit_jscontext]
+  void setOriginAttributes(in jsval aAttrs);
 };
--- a/docshell/test/unit/test_nsDefaultURIFixup_info.js
+++ b/docshell/test/unit/test_nsDefaultURIFixup_info.js
@@ -466,56 +466,48 @@ var testcases = [ {
     fixedURI: "http://mozilla5/2",
     alternateURI: "http://www.mozilla5.com/2",
     protocolChange: true,
   }, {
     input: "mozilla/foo",
     fixedURI: "http://mozilla/foo",
     alternateURI: "http://www.mozilla.com/foo",
     protocolChange: true,
+  }, {
+    input: "mozilla\\",
+    fixedURI: "http://mozilla/",
+    alternateURI: "http://www.mozilla.com/",
+    keywordLookup: true,
+    protocolChange: true,
+    affectedByDNSForSingleHosts: true,
   }];
 
 if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
   testcases.push({
     input: "C:\\some\\file.txt",
     fixedURI: "file:///C:/some/file.txt",
     protocolChange: true,
   });
   testcases.push({
     input: "//mozilla",
     fixedURI: "http://mozilla/",
     alternateURI: "http://www.mozilla.com/",
     protocolChange: true,
   });
-  testcases.push({
-    input: "mozilla\\",
-    fixedURI: "http://mozilla/",
-    alternateURI: "http://www.mozilla.com/",
-    protocolChange: true,
-  });
 } else {
   testcases.push({
     input: "/some/file.txt",
     fixedURI: "file:///some/file.txt",
     protocolChange: true,
   });
   testcases.push({
     input: "//mozilla",
     fixedURI: "file:////mozilla",
     protocolChange: true,
   });
-  // \ is an invalid character in the hostname until bug 652186 is implemented
-  testcases.push({
-    input: "mozilla\\",
-    // fixedURI: "http://mozilla\\/",
-    // alternateURI: "http://www.mozilla/",
-    keywordLookup: true,
-    protocolChange: true,
-    // affectedByDNSForSingleHosts: true,
-  });
 }
 
 function sanitize(input) {
   return input.replace(/\r|\n/g, "").trim();
 }
 
 
 var gSingleWordHostLookup = false;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8125,31 +8125,32 @@ nsContentUtils::IsPreloadType(nsContentP
     return true;
   }
   return false;
 }
 
 nsresult
 nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                               nsIDocument* aDoc,
-                                              nsIHttpChannel* aChannel)
+                                              nsIHttpChannel* aChannel,
+                                              mozilla::net::ReferrerPolicy aReferrerPolicy)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aChannel);
 
   nsCOMPtr<nsIURI> principalURI;
 
   if (IsSystemPrincipal(aPrincipal)) {
     return NS_OK;
   }
 
   aPrincipal->GetURI(getter_AddRefs(principalURI));
 
   if (!aDoc) {
-    return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default);
+    return aChannel->SetReferrerWithPolicy(principalURI, aReferrerPolicy);
   }
 
   // If it weren't for history.push/replaceState, we could just use the
   // principal's URI here.  But since we want changes to the URI effected
   // by push/replaceState to be reflected in the XHR referrer, we have to
   // be more clever.
   //
   // If the document's original URI (before any push/replaceStates) matches
@@ -8168,17 +8169,20 @@ nsContentUtils::SetFetchReferrerURIWithP
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
+  net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
+  if (referrerPolicy == net::RP_Default) {
+    referrerPolicy = aDoc->GetReferrerPolicy();
+  }
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
 bool
 nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
 {
   if (NS_IsMainThread()) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2518,17 +2518,18 @@ public:
    * or current URI as appropriate.
    *
    * aDoc may be null.
    *
    * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
    */
   static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                                 nsIDocument* aDoc,
-                                                nsIHttpChannel* aChannel);
+                                                nsIHttpChannel* aChannel,
+                                                mozilla::net::ReferrerPolicy aReferrerPolicy);
 
   static bool PushEnabled(JSContext* aCx, JSObject* aObj);
 
   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
 
   static uint32_t CookiesBehavior()
   {
     return sCookiesBehavior;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1843,36 +1843,16 @@ nsFrameLoader::MaybeCreateDocShell()
       mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
     }
   }
 
   if (!frameName.IsEmpty()) {
     mDocShell->SetName(frameName);
   }
 
-  //Grab the userContextId from owner if XUL
-  nsAutoString userContextIdStr;
-  if (namespaceID == kNameSpaceID_XUL) {
-    if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
-      mOwnerContent->GetAttr(kNameSpaceID_None,
-                             nsGkAtoms::usercontextid,
-                             userContextIdStr);
-    }
-  }
-
-  if (!userContextIdStr.IsEmpty()) {
-    nsresult rv;
-    uint32_t userContextId =
-      static_cast<uint32_t>(userContextIdStr.ToInteger(&rv));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mDocShell->SetUserContextId(userContextId);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   // Inform our docShell that it has a new child.
   // Note: This logic duplicates a lot of logic in
   // nsSubDocumentFrame::AttributeChanged.  We should fix that.
 
   int32_t parentType = docShell->ItemType();
 
   // XXXbz why is this in content code, exactly?  We should handle
   // this some other way.....  Not sure how yet.
@@ -1933,44 +1913,68 @@ nsFrameLoader::MaybeCreateDocShell()
     nsCOMPtr<nsISHistory> sessionHistory =
       do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
     webNav->SetSessionHistory(sessionHistory);
   }
 
+  DocShellOriginAttributes attrs;
+
+  if (!mOwnerContent->IsXULElement(nsGkAtoms::browser)) {
+    nsCOMPtr<nsIPrincipal> parentPrin = doc->NodePrincipal();
+    PrincipalOriginAttributes poa = BasePrincipal::Cast(parentPrin)->OriginAttributesRef();
+    attrs.InheritFromDocToChildDocShell(poa);
+  }
+
   if (OwnerIsAppFrame()) {
     // You can't be both an app and a browser frame.
     MOZ_ASSERT(!OwnerIsMozBrowserFrame());
 
     nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
     MOZ_ASSERT(ownApp);
     uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (ownApp) {
       NS_ENSURE_SUCCESS(ownApp->GetLocalId(&ownAppId), NS_ERROR_FAILURE);
     }
 
-    mDocShell->SetIsApp(ownAppId);
+    attrs.mAppId = ownAppId;
+    mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_APP);
   }
 
   if (OwnerIsMozBrowserFrame()) {
     // You can't be both a browser and an app frame.
     MOZ_ASSERT(!OwnerIsAppFrame());
 
     nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
     uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (containingApp) {
       NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
                         NS_ERROR_FAILURE);
     }
-    mDocShell->SetIsBrowserInsideApp(containingAppId);
+
+    attrs.mAppId = containingAppId;
+    attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
+    mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
     mDocShell->SetIsInIsolatedMozBrowserElement(OwnerIsIsolatedMozBrowserFrame());
   }
 
+  // Grab the userContextId from owner if XUL
+  nsAutoString userContextIdStr;
+  if ((namespaceID == kNameSpaceID_XUL) &&
+      mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, userContextIdStr) &&
+      !userContextIdStr.IsEmpty()) {
+    nsresult rv;
+    attrs.mUserContextId = userContextIdStr.ToInteger(&rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
+
   if (OwnerIsMozBrowserOrAppFrame()) {
     // For inproc frames, set the docshell properties.
     nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
     nsAutoString name;
     if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
       item->SetName(name);
     }
     mDocShell->SetFullscreenAllowed(
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHostObjectProtocolHandler.h"
 
 #include "DOMMediaStream.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/ModuleUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsClassHashtable.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPrincipal.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
@@ -738,8 +739,58 @@ NS_GetSourceForMediaSourceURI(nsIURI* aU
   nsCOMPtr<mozilla::dom::MediaSource> source = do_QueryInterface(GetDataObject(aURI));
   if (!source) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
   source.forget(aSource);
   return NS_OK;
 }
+
+#define NS_BLOBPROTOCOLHANDLER_CID \
+{ 0xb43964aa, 0xa078, 0x44b2, \
+  { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
+
+#define NS_MEDIASTREAMPROTOCOLHANDLER_CID \
+{ 0x27d1fa24, 0x2b73, 0x4db3, \
+  { 0xab, 0x48, 0xb9, 0x83, 0x83, 0x40, 0xe0, 0x81 } }
+
+#define NS_MEDIASOURCEPROTOCOLHANDLER_CID \
+{ 0x12ef31fc, 0xa8fb, 0x4661, \
+  { 0x9a, 0x63, 0xfb, 0x61, 0x04,0x5d, 0xb8, 0x61 } }
+
+#define NS_FONTTABLEPROTOCOLHANDLER_CID \
+{ 0x3fc8f04e, 0xd719, 0x43ca, \
+  { 0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSourceProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontTableProtocolHandler)
+
+NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_MEDIASTREAMPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_MEDIASOURCEPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOLHANDLER_CID);
+
+static const mozilla::Module::CIDEntry kHostObjectProtocolHandlerCIDs[] = {
+  { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, nsBlobProtocolHandlerConstructor },
+  { &kNS_MEDIASTREAMPROTOCOLHANDLER_CID, false, nullptr, nsMediaStreamProtocolHandlerConstructor },
+  { &kNS_MEDIASOURCEPROTOCOLHANDLER_CID, false, nullptr, nsMediaSourceProtocolHandlerConstructor },
+  { &kNS_FONTTABLEPROTOCOLHANDLER_CID, false, nullptr, nsFontTableProtocolHandlerConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kHostObjectProtocolHandlerContracts[] = {
+  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID },
+  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASTREAMURI_SCHEME, &kNS_MEDIASTREAMPROTOCOLHANDLER_CID },
+  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASOURCEURI_SCHEME, &kNS_MEDIASOURCEPROTOCOLHANDLER_CID },
+  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FONTTABLEURI_SCHEME, &kNS_FONTTABLEPROTOCOLHANDLER_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kHostObjectProtocolHandlerModule = {
+  mozilla::Module::kVersion,
+  kHostObjectProtocolHandlerCIDs,
+  kHostObjectProtocolHandlerContracts
+};
+
+NSMODULE_DEFN(HostObjectProtocolHandler) = &kHostObjectProtocolHandlerModule;
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -131,25 +131,9 @@ extern nsresult
 NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
 
 extern nsresult
 NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream);
 
 extern nsresult
 NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource);
 
-#define NS_BLOBPROTOCOLHANDLER_CID \
-{ 0xb43964aa, 0xa078, 0x44b2, \
-  { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
-
-#define NS_MEDIASTREAMPROTOCOLHANDLER_CID \
-{ 0x27d1fa24, 0x2b73, 0x4db3, \
-  { 0xab, 0x48, 0xb9, 0x83, 0x83, 0x40, 0xe0, 0x81 } }
-
-#define NS_MEDIASOURCEPROTOCOLHANDLER_CID \
-{ 0x12ef31fc, 0xa8fb, 0x4661, \
-  { 0x9a, 0x63, 0xfb, 0x61, 0x04,0x5d, 0xb8, 0x61 } }
-
-#define NS_FONTTABLEPROTOCOLHANDLER_CID \
-{ 0x3fc8f04e, 0xd719, 0x43ca, \
-  { 0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 } }
-
 #endif /* nsHostObjectProtocolHandler_h */
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -2559,17 +2559,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
 
   if (httpChannel) {
     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
 
     if (!IsSystemXHR()) {
       nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
       nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
       nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
-                                                    httpChannel);
+                                                    httpChannel, mozilla::net::RP_Default);
     }
 
     // Some extensions override the http protocol handler and provide their own
     // implementation. The channels returned from that implementation doesn't
     // seem to always implement the nsIUploadChannel2 interface, presumably
     // because it's a new interface.
     // Eventually we should remove this and simply require that http channels
     // implement the new interface.
--- a/dom/base/test/test_bug466409.html
+++ b/dom/base/test/test_bug466409.html
@@ -14,32 +14,26 @@ https://bugzilla.mozilla.org/show_bug.cg
   <iframe id="testframe"></iframe>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 466409 **/
 
 
-function setUniversalDetector(detector)
-{
-  var olddetector = SpecialPowers.getCharPref("intl.charset.detector");
-  SpecialPowers.setCharPref("intl.charset.detector", detector);
-  return olddetector;
-}
-
 SimpleTest.waitForExplicitFinish();
 var testframe = document.getElementById('testframe');
 
-testframe.onload = function ()
-  {
-    setUniversalDetector(olddetector);
-    ok(true, "page loaded successfully");
-    SimpleTest.finish();
+
+
+SpecialPowers.pushPrefEnv({"set": [['intl.charset.detector', 'universal_charset_detector']]}, function() {
+  testframe.onload = function () {
+      ok(true, "page loaded successfully");
+      SimpleTest.finish();
   };
+  testframe.src = "bug466409-page.html";
+});
 
-var olddetector = setUniversalDetector("universal_charset_detector");
-testframe.src = "bug466409-page.html";
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/test/test_navigator_language.html
+++ b/dom/base/test/test_navigator_language.html
@@ -17,32 +17,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 </pre>
 <script type="application/javascript;version=1.7">
   "use strict";
 
   SimpleTest.waitForExplicitFinish();
 
   /** Test for NavigatorLanguage **/
-  var prefValue = null;
   var actualLanguageChangesFromHandler = 0;
   var actualLanguageChangesFromAVL = 0;
   var expectedLanguageChanges = 0;
 
-  function setUp() {
-    try {
-      prefValue = SpecialPowers.getCharPref('intl.accept_languages');
-    } catch (e) {
-    }
-  }
-
-  function tearDown() {
-    SpecialPowers.setCharPref('intl.accept_languages', prefValue);
-  }
-
   var testValues = [
     { accept_languages: 'foo', language: 'foo', languages: ['foo'] },
     { accept_languages: '', language: '', languages: [] },
     { accept_languages: 'foo,bar', language: 'foo', languages: [ 'foo', 'bar' ] },
     { accept_languages: '  foo , bar ', language: 'foo', languages: [ 'foo', 'bar' ] },
     { accept_languages: '  foo ; bar ', language: 'foo ; bar', languages: [ 'foo ; bar' ] },
     { accept_languages: '_foo_', language: '_foo_', languages: ['_foo_'] },
     { accept_languages: 'en_', language: 'en-', languages: ['en-'] },
@@ -53,17 +41,16 @@ https://bugzilla.mozilla.org/show_bug.cg
     { accept_languages: 'en_us-cal, en_us-c', language: 'en-US-cal', languages: ['en-US-cal', 'en-US-c'] },
   ];
 
   var currentTestIdx = 0;
   var tests = [];
   function nextTest() {
     currentTestIdx++;
     if (currentTestIdx >= tests.length) {
-      tearDown();
       SimpleTest.finish();
       return;
     }
 
     tests[currentTestIdx]();
   }
 
   // Check that the API is there.
@@ -85,30 +72,30 @@ https://bugzilla.mozilla.org/show_bug.cg
     window.onlanguagechange = function() {
       isnot(navigator.languages, previous, "navigator.languages cached value was updated");
       window.onlanguagechange = null;
 
       nextTest();
     }
 
     setTimeout(function() {
-      SpecialPowers.setCharPref('intl.accept_languages', 'testArrayCached');
+      SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'testArrayCached']]});
     }, 0);
   });
 
   // Test that event handler inside the <body> works as expected and that the
   // event has the expected properties.
   tests.push(function testEventProperties() {
     document.body.setAttribute('onlanguagechange',
       "document.body.removeAttribute('onlanguagechange');" +
       "is(event.cancelable, false); is(event.bubbles, false);" +
       "nextTest();");
 
     setTimeout(function() {
-      SpecialPowers.setCharPref('intl.accept_languages', 'testEventProperties');
+      SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'testEventProperties']]}, function() {});
     }, 0);
   });
 
   // Check that the returned values such as the behavior when the underlying
   // languages change.
   tests.push(function testBasicBehaviour() {
     function checkIfDoneAndProceed() {
       if (actualLanguageChangesFromHandler == actualLanguageChangesFromAVL) {
@@ -128,17 +115,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       checkIfDoneAndProceed();
     }
     window.addEventListener('languagechange', languageChangeAVL);
 
     function* testEvents() {
       for (var i = 0; i < testValues.length; ++i) {
         var data = testValues[i];
         setTimeout(function(data) {
-          SpecialPowers.setCharPref('intl.accept_languages', data.accept_languages);
+          SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', data.accept_languages]]});
         }, 0, data);
         expectedLanguageChanges++;
         yield undefined;
 
         is(actualLanguageChangesFromAVL, expectedLanguageChanges);
         is(actualLanguageChangesFromHandler, expectedLanguageChanges);
 
         is(navigator.language, data.language);
@@ -151,32 +138,32 @@ https://bugzilla.mozilla.org/show_bug.cg
         }
       }
     }
 
     var genEvents = testEvents();
     genEvents.next();
   });
 
-  // Check that the orientationchange event isn't sent twice if the preference
+  // Check that the languagechange event isn't sent twice if the preference
   // is set to the same value.
   tests.push(function testOnlyFireIfRealChange() {
     function* changeLanguage() {
       setTimeout(function() {
-        SpecialPowers.setCharPref('intl.accept_languages', 'fr-CA');
+        SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'fr-CA']]});
       });
       yield undefined;
 
       setTimeout(function() {
         // Twice the same change, should fire only one event.
-        SpecialPowers.setCharPref('intl.accept_languages', 'fr-CA');
+        SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'fr-CA']]});
         setTimeout(function() {
           // A real change to tell the test it should now count how many changes were
           // received until now.
-          SpecialPowers.setCharPref('intl.accept_languages', 'fr-FR');
+          SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'fr-FR']]});
         });
       });
       yield undefined;
     }
 
     var genChanges = changeLanguage();
 
     var doubleEventCount = 0;
@@ -205,24 +192,21 @@ https://bugzilla.mozilla.org/show_bug.cg
     document.body.appendChild(frame);
 
     frame.contentWindow.onload = function() {
       document.body.removeChild(frame);
       frame = null;
 
       SpecialPowers.exactGC(window, function() {
         // This should not crash.
-        SpecialPowers.setCharPref('intl.accept_languages', 'en-GB');
-
-        nextTest();
+        SpecialPowers.pushPrefEnv({"set": [['intl.accept_languages', 'en-GB']]}, nextTest);
       });
     }
   });
 
   // There is one test using document.body.
   addLoadEvent(function() {
-    setUp();
     tests[0]();
   });
 
 </script>
 </body>
 </html>
--- a/dom/base/test/test_url.html
+++ b/dom/base/test/test_url.html
@@ -356,10 +356,36 @@
     URL.revokeObjectURL(url);
     ok(true, "Calling revokeObjectURL twice should be ok");
   </script>
 
   <script>
     URL.revokeObjectURL('blob:something');
     ok(true, "This should not throw.");
   </script>
+
+  <script>
+    var base = new URL("http:\\\\test.com\\path/to\\file?query\\backslash#hash\\");
+    is(base.href, "http://test.com/path/to/file?query\\backslash#hash\\");
+
+    var url = new URL("..\\", base);
+    is(url.href, "http://test.com/path/");
+
+    url = new URL("\\test", base);
+    is(url.href, "http://test.com/test");
+
+    url = new URL("\\test\\", base);
+    is(url.href, "http://test.com/test/");
+
+    url = new URL("http://example.org/test", base);
+    is(url.href, "http://example.org/test");
+
+    url = new URL("ftp://tmp/test", base);
+    is(url.href, "ftp://tmp/test");
+
+    url = new URL("ftp:\\\\tmp\\test", base);
+    is(url.href, "ftp://tmp/test");
+
+    url = new URL("scheme://tmp\\test", base);
+    is(url.href, "scheme://tmp\\test");
+  </script>
 </body>
 </html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -975,17 +975,20 @@ DOMInterfaces = {
     }
 },
 
 'Rect': {
     'nativeType': 'nsDOMCSSRect',
 },
 
 'Request': {
-    'binaryNames': { 'headers': 'headers_' },
+    'binaryNames': {
+        'headers': 'headers_',
+        'referrerPolicy': 'referrerPolicy_'
+    },
 },
 
 'Response': {
     'binaryNames': { 'headers': 'headers_' },
 },
 
 'RGBColor': {
     'nativeType': 'nsDOMCSSRGBColor',
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
@@ -15,17 +15,17 @@ using namespace mozilla::ipc;
 // Handsfree module
 //
 
 BluetoothHandsfreeNotificationHandler*
   BluetoothDaemonHandsfreeModule::sNotificationHandler;
 
 #if ANDROID_VERSION < 21
 BluetoothAddress BluetoothDaemonHandsfreeModule::sConnectedDeviceAddress(
-  BluetoothAddress::ANY);
+  BluetoothAddress::ANY());
 #endif
 
 void
 BluetoothDaemonHandsfreeModule::SetNotificationHandler(
   BluetoothHandsfreeNotificationHandler* aNotificationHandler)
 {
   sNotificationHandler = aNotificationHandler;
 }
@@ -755,17 +755,17 @@ public:
     if (NS_FAILED(rv)) {
       return rv;
     }
 
 #if ANDROID_VERSION < 21
     if (aArg1 == HFP_CONNECTION_STATE_CONNECTED) {
       sConnectedDeviceAddress = aArg2;
     } else if (aArg1 == HFP_CONNECTION_STATE_DISCONNECTED) {
-      sConnectedDeviceAddress = BluetoothAddress::ANY;
+      sConnectedDeviceAddress = BluetoothAddress::ANY();
     }
 #endif
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
 
 void
--- a/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
@@ -893,17 +893,17 @@ BluetoothGattManager::StartLeScan(const 
   if (NS_WARN_IF(index != sClients->NoIndex)) {
     DispatchReplyError(aRunnable,
                        NS_LITERAL_STRING("start LE scan failed"));
     return;
   }
 
   index = sClients->Length();
   sClients->AppendElement(new BluetoothGattClient(appUuid,
-                                                  BluetoothAddress::ANY));
+                                                  BluetoothAddress::ANY()));
   RefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mStartLeScanRunnable = aRunnable;
 
   // 'startLeScan' will be proceeded after client registered
   sBluetoothGattInterface->RegisterClient(
     appUuid, new RegisterClientResultHandler(client));
 }
 
@@ -1031,17 +1031,17 @@ BluetoothGattManager::StartAdvertising(
   if (NS_WARN_IF(index != sClients->NoIndex)) {
     DispatchReplyError(aRunnable,
                        NS_LITERAL_STRING("start advertising failed"));
     return;
   }
 
   index = sClients->Length();
   sClients->AppendElement(new BluetoothGattClient(aAppUuid,
-                                                  BluetoothAddress::ANY));
+                                                  BluetoothAddress::ANY()));
   RefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mStartAdvertisingRunnable = aRunnable;
   client->mAdvertisingData = aData;
 
   // 'startAdvertising' will be proceeded after client registered
   sBluetoothGattInterface->RegisterClient(
     aAppUuid, new RegisterClientResultHandler(client));
 }
--- a/dom/bluetooth/common/BluetoothCommon.cpp
+++ b/dom/bluetooth/common/BluetoothCommon.cpp
@@ -11,41 +11,58 @@
 bool gBluetoothDebugFlag = false;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 //
 // |BluetoothAddress|
 //
 
-const BluetoothAddress BluetoothAddress::ANY(0x00, 0x00, 0x00,
-                                             0x00, 0x00, 0x00);
+const BluetoothAddress& BluetoothAddress::ANY()
+{
+  static const BluetoothAddress sAddress(0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+  return sAddress;
+}
 
-const BluetoothAddress BluetoothAddress::ALL(0xff, 0xff, 0xff,
-                                             0xff, 0xff, 0xff);
+const BluetoothAddress& BluetoothAddress::ALL()
+{
+  static const BluetoothAddress sAddress(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+  return sAddress;
+}
 
-const BluetoothAddress BluetoothAddress::LOCAL(0x00, 0x00, 0x00,
-                                               0xff, 0xff, 0xff);
+const BluetoothAddress& BluetoothAddress::LOCAL()
+{
+  static const BluetoothAddress sAddress(0x00, 0x00, 0x00, 0xff, 0xff, 0xff);
+  return sAddress;
+}
 
 //
 // |BluetoothUuid|
 //
 
-const BluetoothUuid BluetoothUuid::ZERO(0x00, 0x00, 0x00, 0x00,
-                                        0x00, 0x00, 0x00, 0x00,
-                                        0x00, 0x00, 0x00, 0x00,
-                                        0x00, 0x00, 0x00, 0x00);
+const BluetoothUuid& BluetoothUuid::ZERO()
+{
+  static const BluetoothUuid sUuid(0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x00, 0x00);
+  return sUuid;
+}
 
 /*
  * [Bluetooth Specification Version 4.2, Volume 3, Part B, Section 2.5.1]
  *
  * To reduce the burden of storing and transferring 128-bit UUID values, a
  * range of UUID values has been pre-allocated for assignment to often-used,
  * registered purposes. The first UUID in this pre-allocated range is known as
  * the Bluetooth Base UUID and has the value 00000000-0000-1000-8000-
  * 00805F9B34FB, from the Bluetooth Assigned Numbers document.
  */
-const BluetoothUuid BluetoothUuid::BASE(0x00, 0x00, 0x00, 0x00,
-                                        0x00, 0x00, 0x10, 0x00,
-                                        0x80, 0x00, 0x00, 0x80,
-                                        0x5f, 0x9b, 0x34, 0xfb);
+const BluetoothUuid& BluetoothUuid::BASE()
+{
+  static const BluetoothUuid sUuid(0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x10, 0x00,
+                                   0x80, 0x00, 0x00, 0x80,
+                                   0x5f, 0x9b, 0x34, 0xfb);
+  return sUuid;
+}
 
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/common/BluetoothCommon.h
+++ b/dom/bluetooth/common/BluetoothCommon.h
@@ -403,30 +403,29 @@ struct BluetoothActivityEnergyInfo {
   uint64_t mTxTime;     /* in ms */
   uint64_t mRxTime;     /* in ms */
   uint64_t mIdleTime;   /* in ms */
   uint64_t mEnergyUsed; /* a product of mA, V and ms */
 };
 
 /**
  * |BluetoothAddress| stores the 6-byte MAC address of a Bluetooth
- * device. The constants ANY, ALL and LOCAL represent addresses with
- * special meaning.
+ * device. The constants returned from ANY(), ALL() and LOCAL()
+ * represent addresses with special meaning.
  */
 struct BluetoothAddress {
-
-  static const BluetoothAddress ANY;
-  static const BluetoothAddress ALL;
-  static const BluetoothAddress LOCAL;
+  static const BluetoothAddress& ANY();
+  static const BluetoothAddress& ALL();
+  static const BluetoothAddress& LOCAL();
 
   uint8_t mAddr[6];
 
   BluetoothAddress()
   {
-    Clear(); // assign ANY
+    Clear(); // assign ANY()
   }
 
   MOZ_IMPLICIT BluetoothAddress(const BluetoothAddress&) = default;
 
   BluetoothAddress(uint8_t aAddr0, uint8_t aAddr1,
                    uint8_t aAddr2, uint8_t aAddr3,
                    uint8_t aAddr4, uint8_t aAddr5)
   {
@@ -446,30 +445,30 @@ struct BluetoothAddress {
   }
 
   bool operator!=(const BluetoothAddress& aRhs) const
   {
     return !operator==(aRhs);
   }
 
   /**
-   * |Clear| assigns an invalid value (i.e., ANY) to the address.
+   * |Clear| assigns an invalid value (i.e., ANY()) to the address.
    */
   void Clear()
   {
-    operator=(ANY);
+    operator=(ANY());
   }
 
   /**
-   * |IsCleared| returns true if the address doesn not contain a
-   * specific value (i.e., it contains ANY).
+   * |IsCleared| returns true if the address does not contain a
+   * specific value (i.e., it contains ANY()).
    */
   bool IsCleared() const
   {
-    return operator==(ANY);
+    return operator==(ANY());
   }
 
   /*
    * Getter and setter methods for the address parts. The figure
    * below illustrates the mapping to bytes; from LSB to MSB.
    *
    *    |       LAP       | UAP |    NAP    |
    *    |  0  |  1  |  2  |  3  |  4  |  5  |
@@ -545,23 +544,23 @@ enum BluetoothServiceClass {
   HID              = 0x1124,
   PBAP_PCE         = 0x112e,
   PBAP_PSE         = 0x112f,
   MAP_MAS          = 0x1132,
   MAP_MNS          = 0x1133
 };
 
 struct BluetoothUuid {
-  static const BluetoothUuid ZERO;
-  static const BluetoothUuid BASE;
+  static const BluetoothUuid& ZERO();
+  static const BluetoothUuid& BASE();
 
   uint8_t mUuid[16];  // store 128-bit UUID value in big-endian order
 
   BluetoothUuid()
-    : BluetoothUuid(ZERO)
+    : BluetoothUuid(ZERO())
   { }
 
   MOZ_IMPLICIT BluetoothUuid(const BluetoothUuid&) = default;
 
   BluetoothUuid(uint8_t aUuid0, uint8_t aUuid1,
                 uint8_t aUuid2, uint8_t aUuid3,
                 uint8_t aUuid4, uint8_t aUuid5,
                 uint8_t aUuid6, uint8_t aUuid7,
@@ -605,26 +604,26 @@ struct BluetoothUuid {
 
   BluetoothUuid& operator=(const BluetoothUuid& aRhs) = default;
 
   /**
    * |Clear| assigns an invalid value (i.e., zero) to the UUID.
    */
   void Clear()
   {
-    operator=(ZERO);
+    operator=(ZERO());
   }
 
   /**
    * |IsCleared| returns true if the UUID contains a value of
    * zero.
    */
   bool IsCleared() const
   {
-    return operator==(ZERO);
+    return operator==(ZERO());
   }
 
   bool operator==(const BluetoothUuid& aRhs) const
   {
     return std::equal(aRhs.mUuid,
                       aRhs.mUuid + MOZ_ARRAY_LENGTH(aRhs.mUuid), mUuid);
   }
 
@@ -645,28 +644,28 @@ struct BluetoothUuid {
    * and 4 (indices 2 and 3) are represented by UUID16. The rest of
    * the UUID is filled with the Bluetooth Base UUID.
    *
    * Below are helpers for accessing these values.
    */
 
   void SetUuid32(uint32_t aUuid32)
   {
-    operator=(BASE);
+    operator=(BASE());
     BigEndian::writeUint32(&mUuid[0], aUuid32);
   }
 
   uint32_t GetUuid32() const
   {
     return BigEndian::readUint32(&mUuid[0]);
   }
 
   void SetUuid16(uint16_t aUuid16)
   {
-    operator=(BASE);
+    operator=(BASE());
     BigEndian::writeUint16(&mUuid[2], aUuid16);
   }
 
   uint16_t GetUuid16() const
   {
     return BigEndian::readUint16(&mUuid[2]);
   }
 };
--- a/dom/bluetooth/common/BluetoothUtils.cpp
+++ b/dom/bluetooth/common/BluetoothUtils.cpp
@@ -299,17 +299,17 @@ BytesToUuid(const nsTArray<uint8_t>& aAr
   } else {
     length = MOZ_ARRAY_LENGTH(aUuid.mUuid);
   }
 
   if (aArray.Length() < aOffset + length) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  aUuid = BluetoothUuid::BASE;
+  aUuid = BluetoothUuid::BASE();
 
   if (aEndian == ENDIAN_BIG) {
     for (size_t i = 0; i < length; ++i) {
       aUuid.mUuid[index + i] = aArray[aOffset + i];
     }
   } else {
     for (size_t i = 0; i < length; ++i) {
       aUuid.mUuid[index + i] = aArray[aOffset + length - i - 1];
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -5,16 +5,17 @@
 include protocol PCache;
 include protocol PCachePushStream;
 include protocol PCacheStreamControl;
 include InputStreamParams;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
+using ReferrerPolicy from "mozilla/dom/cache/IPCUtils.h";
 using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
 using RequestMode from "mozilla/dom/cache/IPCUtils.h";
 using RequestCache from "mozilla/dom/cache/IPCUtils.h";
 using RequestRedirect from "mozilla/dom/cache/IPCUtils.h";
 using ResponseType from "mozilla/dom/cache/IPCUtils.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct nsID from "nsID.h";
 
@@ -55,16 +56,17 @@ struct HeadersEntry
 struct CacheRequest
 {
   nsCString method;
   nsCString urlWithoutQuery;
   nsCString urlQuery;
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   nsString referrer;
+  ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   CacheReadStreamOrVoid body;
   uint32_t contentPolicyType;
   RequestCache requestCache;
   RequestRedirect requestRedirect;
 };
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -32,17 +32,17 @@ namespace dom {
 namespace cache {
 namespace db {
 
 const int32_t kFirstShippedSchemaVersion = 15;
 
 namespace {
 
 // Update this whenever the DB schema is changed.
-const int32_t kLatestSchemaVersion = 19;
+const int32_t kLatestSchemaVersion = 20;
 
 // ---------
 // The following constants define the SQL schema.  These are defined in the
 // same order the SQL should be executed in CreateOrMigrateSchema().  They are
 // broken out as constants for convenient use in validation and migration.
 // ---------
 
 // The caches table is the single source of truth about what Cache
@@ -99,19 +99,20 @@ const char* const kTableEntries =
     "response_status INTEGER NOT NULL, "
     "response_status_text TEXT NOT NULL, "
     "response_headers_guard INTEGER NOT NULL, "
     "response_body_id TEXT NULL, "
     "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
     "response_principal_info TEXT NOT NULL, "
     "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
 
+    "request_redirect INTEGER NOT NULL, "
+    "request_referrer_policy INTEGER NOT NULL"
     // New columns must be added at the end of table to migrate and
     // validate properly.
-    "request_redirect INTEGER NOT NULL"
   ")";
 
 // Create an index to support the QueryCache() matching algorithm.  This
 // needs to quickly find entries in a given Cache that match the request
 // URL.  The url query is separated in order to support the ignoreSearch
 // option.  Finally, we index hashes of the URL values instead of the
 // actual strings to avoid excessive disk bloat.  The index will duplicate
 // the contents of the columsn in the index.  The hash index will prune
@@ -184,16 +185,24 @@ static_assert(kWalAutoCheckpointSize % k
 // database schema accordingly and adjust the failing static_assert.
 static_assert(int(HeadersGuardEnum::None) == 0 &&
               int(HeadersGuardEnum::Request) == 1 &&
               int(HeadersGuardEnum::Request_no_cors) == 2 &&
               int(HeadersGuardEnum::Response) == 3 &&
               int(HeadersGuardEnum::Immutable) == 4 &&
               int(HeadersGuardEnum::EndGuard_) == 5,
               "HeadersGuardEnum values are as expected");
+static_assert(int(ReferrerPolicy::_empty) == 0 &&
+              int(ReferrerPolicy::No_referrer) == 1 &&
+              int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
+              int(ReferrerPolicy::Origin_only) == 3 &&
+              int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
+              int(ReferrerPolicy::Unsafe_url) == 5 &&
+              int(ReferrerPolicy::EndGuard_) == 6,
+              "ReferrerPolicy values are as expected");
 static_assert(int(RequestMode::Same_origin) == 0 &&
               int(RequestMode::No_cors) == 1 &&
               int(RequestMode::Cors) == 2 &&
               int(RequestMode::Navigate) == 3 &&
               int(RequestMode::EndGuard_) == 4,
               "RequestMode values are as expected");
 static_assert(int(RequestCredentials::Omit) == 0 &&
               int(RequestCredentials::Same_origin) == 1 &&
@@ -1630,16 +1639,17 @@ InsertEntry(mozIStorageConnection* aConn
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO entries ("
       "request_method, "
       "request_url_no_query, "
       "request_url_no_query_hash, "
       "request_url_query, "
       "request_url_query_hash, "
       "request_referrer, "
+      "request_referrer_policy, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
       "request_redirect, "
       "request_body_id, "
       "response_type, "
@@ -1653,16 +1663,17 @@ InsertEntry(mozIStorageConnection* aConn
       "cache_id "
     ") VALUES ("
       ":request_method, "
       ":request_url_no_query, "
       ":request_url_no_query_hash, "
       ":request_url_query, "
       ":request_url_query_hash, "
       ":request_referrer, "
+      ":request_referrer_policy, "
       ":request_headers_guard, "
       ":request_mode, "
       ":request_credentials, "
       ":request_contentpolicytype, "
       ":request_cache, "
       ":request_redirect, "
       ":request_body_id, "
       ":response_type, "
@@ -1705,16 +1716,20 @@ InsertEntry(mozIStorageConnection* aConn
   rv = state->BindUTF8StringAsBlobByName(
     NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"),
                                aRequest.referrer());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"),
+                              static_cast<int32_t>(aRequest.referrerPolicy()));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"),
     static_cast<int32_t>(aRequest.headersGuard()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_mode"),
                               static_cast<int32_t>(aRequest.mode()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1983,16 +1998,17 @@ ReadRequest(mozIStorageConnection* aConn
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "request_method, "
       "request_url_no_query, "
       "request_url_query, "
       "request_referrer, "
+      "request_referrer_policy, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
       "request_redirect, "
       "request_body_id "
     "FROM entries "
@@ -2014,58 +2030,64 @@ ReadRequest(mozIStorageConnection* aConn
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->GetUTF8String(2, aSavedRequestOut->mValue.urlQuery());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->GetString(3, aSavedRequestOut->mValue.referrer());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  int32_t referrerPolicy;
+  rv = state->GetInt32(4, &referrerPolicy);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  aSavedRequestOut->mValue.referrerPolicy() =
+    static_cast<ReferrerPolicy>(referrerPolicy);
+
   int32_t guard;
-  rv = state->GetInt32(4, &guard);
+  rv = state->GetInt32(5, &guard);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.headersGuard() =
     static_cast<HeadersGuardEnum>(guard);
 
   int32_t mode;
-  rv = state->GetInt32(5, &mode);
+  rv = state->GetInt32(6, &mode);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.mode() = static_cast<RequestMode>(mode);
 
   int32_t credentials;
-  rv = state->GetInt32(6, &credentials);
+  rv = state->GetInt32(7, &credentials);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.credentials() =
     static_cast<RequestCredentials>(credentials);
 
   int32_t requestContentPolicyType;
-  rv = state->GetInt32(7, &requestContentPolicyType);
+  rv = state->GetInt32(8, &requestContentPolicyType);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.contentPolicyType() =
     static_cast<nsContentPolicyType>(requestContentPolicyType);
 
   int32_t requestCache;
-  rv = state->GetInt32(8, &requestCache);
+  rv = state->GetInt32(9, &requestCache);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.requestCache() =
     static_cast<RequestCache>(requestCache);
 
   int32_t requestRedirect;
-  rv = state->GetInt32(9, &requestRedirect);
+  rv = state->GetInt32(10, &requestRedirect);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.requestRedirect() =
     static_cast<RequestRedirect>(requestRedirect);
 
   bool nullBody = false;
-  rv = state->GetIsNull(10, &nullBody);
+  rv = state->GetIsNull(11, &nullBody);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mHasBodyId = !nullBody;
 
   if (aSavedRequestOut->mHasBodyId) {
-    rv = ExtractId(state, 10, &aSavedRequestOut->mBodyId);
+    rv = ExtractId(state, 11, &aSavedRequestOut->mBodyId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM request_headers "
@@ -2391,80 +2413,47 @@ Validate(mozIStorageConnection* aConn)
 
   return rv;
 }
 
 // -----
 // Schema migration code
 // -----
 
-typedef nsresult (*MigrationFunc)(mozIStorageConnection*);
+typedef nsresult (*MigrationFunc)(mozIStorageConnection*, bool&);
 struct Migration
 {
   MOZ_CONSTEXPR Migration(int32_t aFromVersion, MigrationFunc aFunc)
     : mFromVersion(aFromVersion)
     , mFunc(aFunc)
   { }
   int32_t mFromVersion;
   MigrationFunc mFunc;
 };
 
 // Declare migration functions here.  Each function should upgrade
 // the version by a single increment.  Don't skip versions.
-nsresult MigrateFrom15To16(mozIStorageConnection* aConn);
-nsresult MigrateFrom16To17(mozIStorageConnection* aConn);
-nsresult MigrateFrom17To18(mozIStorageConnection* aConn);
-nsresult MigrateFrom18To19(mozIStorageConnection* aConn);
+nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema);
+nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema);
+nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema);
+nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema);
+nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema);
 
 // Configure migration functions to run for the given starting version.
 Migration sMigrationList[] = {
   Migration(15, MigrateFrom15To16),
   Migration(16, MigrateFrom16To17),
   Migration(17, MigrateFrom17To18),
   Migration(18, MigrateFrom18To19),
+  Migration(19, MigrateFrom19To20),
 };
 
 uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
 
 nsresult
-Migrate(mozIStorageConnection* aConn)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-
-  int32_t currentVersion = 0;
-  nsresult rv = aConn->GetSchemaVersion(&currentVersion);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  while (currentVersion < kLatestSchemaVersion) {
-    // Wiping old databases is handled in DBAction because it requires
-    // making a whole new mozIStorageConnection.  Make sure we don't
-    // accidentally get here for one of those old databases.
-    MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
-
-    for (uint32_t i = 0; i < sMigrationListLength; ++i) {
-      if (sMigrationList[i].mFromVersion == currentVersion) {
-        rv = sMigrationList[i].mFunc(aConn);
-        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-        break;
-      }
-    }
-
-    DebugOnly<int32_t> lastVersion = currentVersion;
-    rv = aConn->GetSchemaVersion(&currentVersion);
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    MOZ_ASSERT(currentVersion > lastVersion);
-  }
-
-  MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
-
-  return rv;
-}
-
-nsresult
 RewriteEntriesSchema(mozIStorageConnection* aConn)
 {
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA writable_schema = ON"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsCOMPtr<mozIStorageStatement> state;
@@ -2483,17 +2472,65 @@ RewriteEntriesSchema(mozIStorageConnecti
   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA writable_schema = OFF"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-nsresult MigrateFrom15To16(mozIStorageConnection* aConn)
+nsresult
+Migrate(mozIStorageConnection* aConn)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aConn);
+
+  int32_t currentVersion = 0;
+  nsresult rv = aConn->GetSchemaVersion(&currentVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  bool rewriteSchema = false;
+
+  while (currentVersion < kLatestSchemaVersion) {
+    // Wiping old databases is handled in DBAction because it requires
+    // making a whole new mozIStorageConnection.  Make sure we don't
+    // accidentally get here for one of those old databases.
+    MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
+
+    for (uint32_t i = 0; i < sMigrationListLength; ++i) {
+      if (sMigrationList[i].mFromVersion == currentVersion) {
+        bool shouldRewrite = false;
+        rv = sMigrationList[i].mFunc(aConn, shouldRewrite);
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+        if (shouldRewrite) {
+          rewriteSchema = true;
+        }
+        break;
+      }
+    }
+
+    DebugOnly<int32_t> lastVersion = currentVersion;
+    rv = aConn->GetSchemaVersion(&currentVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+    MOZ_ASSERT(currentVersion > lastVersion);
+  }
+
+  MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
+
+  if (rewriteSchema) {
+    // Now overwrite the master SQL for the entries table to remove the column
+    // default value.  This is also necessary for our Validate() method to
+    // pass on this database.
+    rv = RewriteEntriesSchema(aConn);
+  }
+
+  return rv;
+}
+
+nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   mozStorageTransaction trans(aConn, true,
                               mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
   // Add the request_redirect column with a default value of "follow".  Note,
@@ -2502,30 +2539,26 @@ nsresult MigrateFrom15To16(mozIStorageCo
   // We don't actually want to keep the default in the schema for future
   // INSERTs.
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE entries "
     "ADD COLUMN request_redirect INTEGER NOT NULL DEFAULT 0"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  // Now overwrite the master SQL for the entries table to remove the column
-  // default value.  This is also necessary for our Validate() method to
-  // pass on this database.
-  rv = RewriteEntriesSchema(aConn);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = aConn->SetSchemaVersion(16);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  aRewriteSchema = true;
+
   return rv;
 }
 
 nsresult
-MigrateFrom16To17(mozIStorageConnection* aConn)
+MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   // This migration path removes the response_redirected and
   // response_redirected_url columns from the entries table.  sqlite doesn't
   // support removing a column from a table using ALTER TABLE, so we need to
   // create a new table without those columns, fill it up with the existing
@@ -2640,30 +2673,24 @@ MigrateFrom16To17(mozIStorageConnection*
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool hasMoreData = false;
   rv = state->ExecuteStep(&hasMoreData);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
 
-  // Finally, rewrite the schema for the entries database, otherwise the
-  // returned SQL string from sqlite will wrap the name of the table in quotes,
-  // breaking the checks in Validate().
-  rv = RewriteEntriesSchema(aConn);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = aConn->SetSchemaVersion(17);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult
-MigrateFrom17To18(mozIStorageConnection* aConn)
+MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   mozStorageTransaction trans(aConn, true,
                               mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
   // This migration is needed in order to remove "only-if-cached" RequestCache
@@ -2683,17 +2710,17 @@ MigrateFrom17To18(mozIStorageConnection*
 
   rv = aConn->SetSchemaVersion(18);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult
-MigrateFrom18To19(mozIStorageConnection* aConn)
+MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   mozStorageTransaction trans(aConn, true,
                               mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
   // This migration is needed in order to update the RequestMode values for
@@ -2714,15 +2741,42 @@ MigrateFrom18To19(mozIStorageConnection*
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->SetSchemaVersion(19);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
+nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aConn);
+
+  mozStorageTransaction trans(aConn, true,
+                              mozIStorageConnection::TRANSACTION_IMMEDIATE);
+
+  // Add the request_referrer_policy column with a default value of
+  // "no-referrer-when-downgrade".  Note, we only use a default value here
+  // because its required by ALTER TABLE and we need to apply the default
+  // "no-referrer-when-downgrade" to existing records in the table. We don't
+  // actually want to keep the default in the schema for future INSERTs.
+  nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE entries "
+    "ADD COLUMN request_referrer_policy INTEGER NOT NULL DEFAULT 2"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = aConn->SetSchemaVersion(20);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  aRewriteSchema = true;
+
+  return rv;
+}
+
 
 } // anonymous namespace
 
 } // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/IPCUtils.h
+++ b/dom/cache/IPCUtils.h
@@ -19,16 +19,21 @@
 
 namespace IPC {
   template<>
   struct ParamTraits<mozilla::dom::HeadersGuardEnum> :
     public ContiguousEnumSerializer<mozilla::dom::HeadersGuardEnum,
                                     mozilla::dom::HeadersGuardEnum::None,
                                     mozilla::dom::HeadersGuardEnum::EndGuard_> {};
   template<>
+  struct ParamTraits<mozilla::dom::ReferrerPolicy> :
+    public ContiguousEnumSerializer<mozilla::dom::ReferrerPolicy,
+                                    mozilla::dom::ReferrerPolicy::_empty,
+                                    mozilla::dom::ReferrerPolicy::EndGuard_> {};
+  template<>
   struct ParamTraits<mozilla::dom::RequestMode> :
     public ContiguousEnumSerializer<mozilla::dom::RequestMode,
                                     mozilla::dom::RequestMode::Same_origin,
                                     mozilla::dom::RequestMode::EndGuard_> {};
   template<>
   struct ParamTraits<mozilla::dom::RequestCredentials> :
     public ContiguousEnumSerializer<mozilla::dom::RequestCredentials,
                                     mozilla::dom::RequestCredentials::Omit,
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -165,16 +165,17 @@ TypeUtils::ToCacheRequest(CacheRequest& 
       NS_ConvertUTF8toUTF16 urlUTF16(url);
       aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"),
                                                  urlUTF16);
       return;
     }
   }
 
   aIn->GetReferrer(aOut.referrer());
+  aOut.referrerPolicy() = aIn->ReferrerPolicy_();
 
   RefPtr<InternalHeaders> headers = aIn->Headers();
   MOZ_ASSERT(headers);
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
   aOut.mode() = aIn->Mode();
   aOut.credentials() = aIn->GetCredentialsMode();
   aOut.contentPolicyType() = aIn->ContentPolicyType();
@@ -335,16 +336,17 @@ TypeUtils::ToInternalRequest(const Cache
 
   internalRequest->SetMethod(aIn.method());
 
   nsAutoCString url(aIn.urlWithoutQuery());
   url.Append(aIn.urlQuery());
   internalRequest->SetURL(url);
 
   internalRequest->SetReferrer(aIn.referrer());
+  internalRequest->SetReferrerPolicy(aIn.referrerPolicy());
   internalRequest->SetMode(aIn.mode());
   internalRequest->SetCredentialsMode(aIn.credentials());
   internalRequest->SetContentPolicyType(aIn.contentPolicyType());
   internalRequest->SetCacheMode(aIn.requestCache());
   internalRequest->SetRedirectMode(aIn.requestRedirect());
 
   RefPtr<InternalHeaders> internalHeaders =
     ToInternalHeaders(aIn.headers(), aIn.headersGuard());
--- a/dom/cache/test/xpcshell/test_migration.js
+++ b/dom/cache/test/xpcshell/test_migration.js
@@ -17,16 +17,17 @@ function run_test() {
     return cache.keys();
   }).then(function(requestList) {
     ok(requestList.length > 0, 'should have at least one request in cache');
     requestList.forEach(function(request) {
       ok(request, 'each request in list should be non-null');
       ok(request.redirect === 'follow', 'request.redirect should default to "follow"');
       ok(request.cache === 'default', 'request.cache should have been updated to "default"' + request.cache);
       ok(request.mode === 'navigate', 'request.mode should have been updated to "navigate"');
+      ok(request.referrerPolicy === 'no-referrer-when-downgrade', 'request.referrerPolicy should have been updated to "no-referrer-when-downgrade"');
     });
     return Promise.all(requestList.map(function(request) {
       return cache.match(request);
     }));
   }).then(function(responseList) {
     ok(responseList.length > 0, 'should have at least one response in cache');
     responseList.forEach(function(response) {
       ok(response, 'each response in list should be non-null');
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -384,21 +384,23 @@ HasRasterImage(HTMLImageElement& aImageE
         imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
       return true;
     }
   }
 
   return false;
 }
 
-ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData)
+ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
+                         bool aIsPremultipliedAlpha /* = true */)
   : mParent(aGlobal)
   , mData(aData)
   , mSurface(nullptr)
   , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
+  , mIsPremultipliedAlpha(aIsPremultipliedAlpha)
 {
   MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
 }
 
 ImageBitmap::~ImageBitmap()
 {
 }
 
@@ -428,20 +430,20 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
   MOZ_ASSERT(aTarget);
 
   if (!mData) {
     return nullptr;
   }
 
   if (!mSurface) {
     mSurface = mData->GetAsSourceSurface();
-  }
 
-  if (!mSurface) {
-    return nullptr;
+    if (!mSurface) {
+      return nullptr;
+    }
   }
 
   RefPtr<DrawTarget> target = aTarget;
   IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
 
   // Check if we still need to crop our surface
   if (!mPictureRect.IsEqualEdges(surfRect)) {
 
@@ -491,16 +493,73 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
       target->CopySurface(mSurface, surfPortion, dest);
       mSurface = target->Snapshot();
     }
 
     // Make mCropRect match new surface we've cropped to
     mPictureRect.MoveTo(0, 0);
   }
 
+  // Pre-multiply alpha here.
+  // Apply pre-multiply alpha only if mIsPremultipliedAlpha is false.
+  if (!mIsPremultipliedAlpha) {
+    MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+               mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+               mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
+
+    RefPtr<DataSourceSurface> dataSourceSurface = mSurface->GetDataSurface();
+    MOZ_ASSERT(dataSourceSurface);
+
+    DataSourceSurface::ScopedMap map(dataSourceSurface, DataSourceSurface::READ_WRITE);
+    if (NS_WARN_IF(!map.IsMapped())) {
+      return nullptr;
+    }
+
+    uint8_t rIndex = 0;
+    uint8_t gIndex = 0;
+    uint8_t bIndex = 0;
+    uint8_t aIndex = 0;
+
+    if (mSurface->GetFormat() == SurfaceFormat::R8G8B8A8) {
+      rIndex = 0;
+      gIndex = 1;
+      bIndex = 2;
+      aIndex = 3;
+    } else if (mSurface->GetFormat() == SurfaceFormat::B8G8R8A8) {
+      rIndex = 2;
+      gIndex = 1;
+      bIndex = 0;
+      aIndex = 3;
+    } else if (mSurface->GetFormat() == SurfaceFormat::A8R8G8B8) {
+      rIndex = 1;
+      gIndex = 2;
+      bIndex = 3;
+      aIndex = 0;
+    }
+
+    for (int i = 0; i < dataSourceSurface->GetSize().height; ++i) {
+      uint8_t* bufferPtr = map.GetData() + map.GetStride() * i;
+      for (int i = 0; i < dataSourceSurface->GetSize().width; ++i) {
+        uint8_t r = *(bufferPtr+rIndex);
+        uint8_t g = *(bufferPtr+gIndex);
+        uint8_t b = *(bufferPtr+bIndex);
+        uint8_t a = *(bufferPtr+aIndex);
+
+        *(bufferPtr+rIndex) = gfxUtils::sPremultiplyTable[a * 256 + r];
+        *(bufferPtr+gIndex) = gfxUtils::sPremultiplyTable[a * 256 + g];
+        *(bufferPtr+bIndex) = gfxUtils::sPremultiplyTable[a * 256 + b];
+        *(bufferPtr+aIndex) = a;
+
+        bufferPtr += 4;
+      }
+    }
+
+    mSurface = dataSourceSurface;
+  }
+
   // Replace our surface with one optimized for the target we're about to draw
   // to, under the assumption it'll likely be drawn again to that target.
   // This call should be a no-op for already-optimized surfaces
   mSurface = target->OptimizeSourceSurface(mSurface);
 
   RefPtr<gfx::SourceSurface> surface(mSurface);
   return surface.forget();
 }
@@ -513,31 +572,33 @@ ImageBitmap::TransferAsImage()
   return image.forget();
 }
 
 ImageBitmapCloneData*
 ImageBitmap::ToCloneData()
 {
   ImageBitmapCloneData* result = new ImageBitmapCloneData();
   result->mPictureRect = mPictureRect;
+  result->mIsPremultipliedAlpha = mIsPremultipliedAlpha;
   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
   result->mSurface = surface->GetDataSurface();
   MOZ_ASSERT(result->mSurface);
 
   return result;
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
                                  ImageBitmapCloneData* aData)
 {
   RefPtr<layers::Image> data =
     CreateImageFromSurface(aData->mSurface);
 
-  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
+                                            aData->mIsPremultipliedAlpha);
   ErrorResult rv;
   ret->SetPictureRect(aData->mPictureRect, rv);
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
                                        OffscreenCanvas& aOffscreenCanvas,
@@ -758,17 +819,18 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   }
 
   if (NS_WARN_IF(!data)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // Create an ImageBimtap.
-  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+  // ImageData's underlying data is not alpha-premultiplied.
+  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false);
 
   // The cropping information has been handled in the CreateImageFromRawData()
   // function.
 
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
@@ -1234,19 +1296,22 @@ ImageBitmap::ReadStructuredClone(JSConte
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
   // aParent might be null.
 
   uint32_t picRectX_;
   uint32_t picRectY_;
   uint32_t picRectWidth_;
   uint32_t picRectHeight_;
+  uint32_t isPremultipliedAlpha_;
+  uint32_t dummy_;
 
   if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
-      !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_)) {
+      !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
+      !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_, &dummy_)) {
     return nullptr;
   }
 
   int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
   int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
   int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
   int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
 
@@ -1258,17 +1323,17 @@ ImageBitmap::ReadStructuredClone(JSConte
   // called because the static analysis thinks dereferencing XPCOM objects
   // can GC (because in some cases it can!), and a return statement with a
   // JSObject* type means that JSObject* is on the stack as a raw pointer
   // while destructors are running.
   JS::Rooted<JS::Value> value(aCx);
   {
     RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
     RefPtr<ImageBitmap> imageBitmap =
-      new ImageBitmap(aParent, img);
+      new ImageBitmap(aParent, img, isPremultipliedAlpha_);
 
     ErrorResult error;
     imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
                                         picRectWidth, picRectHeight), error);
     if (NS_WARN_IF(error.Failed())) {
       error.SuppressException();
       return nullptr;
     }
@@ -1288,23 +1353,25 @@ ImageBitmap::WriteStructuredClone(JSStru
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aImageBitmap);
 
   const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
   const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
   const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
   const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
+  const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
 
   // Indexing the cloned surfaces and send the index to the receiver.
   uint32_t index = aClonedSurfaces.Length();
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
-      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight))) {
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha, 0))) {
     return false;
   }
 
   RefPtr<SourceSurface> surface =
     aImageBitmap->mData->GetAsSourceSurface();
   RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
   RefPtr<DataSourceSurface> dstDataSurface;
   {
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -52,16 +52,17 @@ class PostMessageEvent; // For Structure
 class CreateImageBitmapFromBlob;
 class CreateImageBitmapFromBlobTask;
 class CreateImageBitmapFromBlobWorkerTask;
 
 struct ImageBitmapCloneData final
 {
   RefPtr<gfx::DataSourceSurface> mSurface;
   gfx::IntRect mPictureRect;
+  bool mIsPremultipliedAlpha;
 };
 
 /*
  * ImageBitmap is an opaque handler to several kinds of image-like objects from
  * HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
  * CanvasRenderingContext2D and Image Blob.
  *
  * An ImageBitmap could be painted to a canvas element.
@@ -136,17 +137,37 @@ public:
                        ImageBitmap* aImageBitmap);
 
   friend CreateImageBitmapFromBlob;
   friend CreateImageBitmapFromBlobTask;
   friend CreateImageBitmapFromBlobWorkerTask;
 
 protected:
 
-  ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData);
+  /*
+   * The default value of aIsPremultipliedAlpha is TRUE because that the
+   * data stored in HTMLImageElement, HTMLVideoElement, HTMLCanvasElement,
+   * CanvasRenderingContext2D are alpha-premultiplied in default.
+   *
+   * Actually, if one HTMLCanvasElement's rendering context is WebGLContext, it
+   * is possible to get un-premultipliedAlpha data out. But, we do not do it in
+   * the CreateInternal(from HTMLCanvasElement) method.
+   *
+   * It is also possible to decode an image which is encoded with alpha channel
+   * to be non-premultipliedAlpha. This could be applied in
+   * 1) the CreateInternal(from HTMLImageElement) method (which might trigger
+   *    re-decoding if the original decoded data is alpha-premultiplied) and
+   * 2) while decoding a blob. But we do not do it in both code path too.
+   *
+   * ImageData's underlying data is triggered as non-premultipliedAlpha, so set
+   * the aIsPremultipliedAlpha to be false in the
+   * CreateInternal(from ImageData) method.
+   */
+  ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
+              bool aIsPremultipliedAlpha = true);
 
   virtual ~ImageBitmap();
 
   void SetPictureRect(const gfx::IntRect& aRect, ErrorResult& aRv);
 
   static already_AddRefed<ImageBitmap>
   CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
                  const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
@@ -197,16 +218,18 @@ protected:
    *
    * Note that if the CreateInternal() copies and crops data from the source
    * image, then this mPictureRect is just the size of the final mData.
    *
    * The mPictureRect will be used at PrepareForDrawTarget() while user is going
    * to draw this ImageBitmap into a HTMLCanvasElement.
    */
   gfx::IntRect mPictureRect;
+
+  const bool mIsPremultipliedAlpha;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImageBitmap_h
 
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_bug1239752.js
@@ -0,0 +1,85 @@
+var RGBAValues = [[42,142,23,148],
+                  [234,165,177,91],
+                  [74,228,75,195],
+                  [140,108,73,65],
+                  [25,177,3,201],
+                  [127,104,12,199],
+                  [196,93,240,131],
+                  [250,121,231,189],
+                  [175,131,215,190],
+                  [145,122,166,70],
+                  [18,196,210,162],
+                  [225,1,90,188],
+                  [223,216,182,233],
+                  [115,48,168,56],
+                  [50,206,198,199],
+                  [152,28,70,130],
+                  [176,134,133,51],
+                  [148,46,43,144],
+                  [78,171,141,95],
+                  [24,177,102,110],
+                  [0,27,127,91],
+                  [31,221,41,170],
+                  [85,7,218,146],
+                  [65,30,198,238],
+                  [121,56,123,88],
+                  [246,39,140,146],
+                  [174,195,254,149],
+                  [29,153,92,116],
+                  [17,240,5,111],
+                  [38,162,84,143],
+                  [237,159,201,244],
+                  [93,68,14,246],
+                  [143,142,82,221],
+                  [187,215,243,154],
+                  [24,121,220,53],
+                  [80,153,151,219],
+                  [202,241,250,191]];
+
+function createOneTest(rgbaValue) {
+  return new Promise(function(resolve, reject) {
+    var tolerance = 5;
+    var r = rgbaValue[0];
+    var g = rgbaValue[1];
+    var b = rgbaValue[2];
+    var a = rgbaValue[3];
+    var imageData = new ImageData(new Uint8ClampedArray([r, g, b, a]), 1, 1);
+
+    var newImageData;
+    createImageBitmap(imageData).then(
+      function(imageBitmap) {
+        var context = document.createElement("canvas").getContext("2d");
+        context.drawImage(imageBitmap, 0, 0);
+        newImageData = context.getImageData(0, 0, 1, 1);
+        var newR = newImageData.data[0];
+        var newG = newImageData.data[1];
+        var newB = newImageData.data[2];
+        var newA = newImageData.data[3];
+        var isTheSame = Math.abs(r - newR) <= tolerance &&
+                        Math.abs(g - newG) <= tolerance &&
+                        Math.abs(b - newB) <= tolerance &&
+                        Math.abs(a - newA) <= tolerance;
+        ok(isTheSame, "newImageData(" + newR + "," + newG + "," + newB + "," + newA +
+                      ") should equal to imageData(" + r + "," + g + "," + b + "," + a + ")." +
+                      "Premultiplied Alpha is handled while creating ImageBitmap from ImageData.");
+        if (isTheSame) {
+          resolve();
+        } else {
+          reject();
+        }
+      },
+      function() {
+        reject();
+      }
+    );
+  });
+}
+
+function testBug1239752() {
+  var tests = [];
+  for (var i = 0; i < RGBAValues.length; ++i) {
+    tests.push(createOneTest(RGBAValues[i]));
+  }
+
+  return Promise.all(tests);
+}
\ No newline at end of file
--- a/dom/canvas/test/imagebitmap_structuredclone.js
+++ b/dom/canvas/test/imagebitmap_structuredclone.js
@@ -1,15 +1,16 @@
 function ok(expect, msg) {
   postMessage({"type": "status", status: !!expect, msg: msg});
 }
 
 onmessage = function(event) {
   ok(!!event.data.bitmap1, "Get the 1st ImageBitmap from the main script.");
-  ok(!!event.data.bitmap2, "Get the 2st ImageBitmap from the main script.");
+  ok(!!event.data.bitmap2, "Get the 2nd ImageBitmap from the main script.");
+  ok(!!event.data.bitmap3, "Get the 3rd ImageBitmap from the main script.");
 
   // send the first original ImageBitmap back to the main-thread
   postMessage({"type":"bitmap1",
                "bitmap":event.data.bitmap1});
 
   // create a new ImageBitmap from the 2nd original ImageBitmap
   // and then send the newly created ImageBitmap back to the main-thread
   var promise = createImageBitmap(event.data.bitmap2);
@@ -22,9 +23,13 @@ onmessage = function(event) {
 
       // finish the test
       postMessage({"type": "finish"});
     },
     function() {
       ok(false, "Cannot create a new bitmap from the original bitmap in worker.");
     }
   );
+
+  // send the third original ImageBitmap back to the main-thread
+  postMessage({"type":"bitmap3",
+               "bitmap":event.data.bitmap3});
 }
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_structuredclone_utils.js
@@ -0,0 +1,157 @@
+var gImage1;
+var gImage2;
+var gImageBitmap1;
+var gImageBitmap2;
+
+// Bug 1239752.
+var gImageData;
+var gImageBitmap3;
+
+function comparePixelColor(testImgageData, groundTruthImageData, x, y, tolerance, info) {
+  ok(testImgageData.width == groundTruthImageData.width && testImgageData.height == groundTruthImageData.height,
+     "testImgageData and groundTruthImageData should have the same dimension.");
+
+  var index = (groundTruthImageData.width * y + x) * 4;
+  var r = groundTruthImageData.data[index + 0];
+  var g = groundTruthImageData.data[index + 1];
+  var b = groundTruthImageData.data[index + 2];
+  var a = groundTruthImageData.data[index + 3];
+  var newR = testImgageData.data[index + 0];
+  var newG = testImgageData.data[index + 1];
+  var newB = testImgageData.data[index + 2];
+  var newA = testImgageData.data[index + 3];
+  var isTheSame = Math.abs(r - newR) <= tolerance &&
+                  Math.abs(g - newG) <= tolerance &&
+                  Math.abs(b - newB) <= tolerance &&
+                  Math.abs(a - newA) <= tolerance;
+  ok(isTheSame, "[" + info + "] " +
+                "newImageData(" + newR + "," + newG + "," + newB + "," + newA +
+                ") should equal to imageData(" + r + "," + g + "," + b + "," + a + ").");
+}
+
+function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  canvas1.width  = imageElement.naturalWidth;
+  canvas1.height = imageElement.naturalHeight;
+  canvas2.width  = imageElement.naturalWidth;
+  canvas2.height = imageElement.naturalHeight;
+
+  var ctx1 = canvas1.getContext('2d');
+  var ctx2 = canvas2.getContext('2d');
+
+  ctx1.drawImage(imageElement, 0, 0);
+  ctx2.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+  document.body.appendChild(canvas2);
+
+  var imageData1 = ctx1.getImageData(0, 0, canvas1.width, canvas1.height);
+  var imageData2 = ctx2.getImageData(0, 0, canvas2.width, canvas2.height);
+
+  // Create an array of pixels that is going to be tested.
+  var pixels = [];
+  var xGap = imageElement.naturalWidth / 4;
+  var yGap = imageElement.naturalHeight / 4;
+  for (var y = 0; y < imageElement.naturalHeight; y += yGap) {
+    for (var x = 0; x < imageElement.naturalWidth; x += xGap) {
+      pixels.push({"x":x, "y":y});
+    }
+  }
+
+  // Also, put the button-right pixel into pixels.
+  pixels.push({"x":imageElement.naturalWidth-1, "y":imageElement.naturalHeight-1});
+
+  // Do the test.
+  for (var pixel of pixels) {
+    comparePixelColor(imageData2, imageData1, pixel.x, pixel.y, 0);
+  }
+}
+
+function compareImageBitmapWithImageData(imageBitmap, imageData, info) {
+  var canvas1 = document.createElement('canvas');
+
+  canvas1.width  = imageBitmap.width;
+  canvas1.height = imageBitmap.height;
+
+  var ctx1 = canvas1.getContext('2d');
+
+  ctx1.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+
+  var imageData1 = ctx1.getImageData(0, 0, canvas1.width, canvas1.height);
+
+  // Create an array of pixels that is going to be tested.
+  var pixels = [];
+  var xGap = imageBitmap.width / 4;
+  var yGap = imageBitmap.height / 4;
+  for (var y = 0; y < imageBitmap.height; y += yGap) {
+    for (var x = 0; x < imageBitmap.width; x += xGap) {
+      pixels.push({"x":x, "y":y});
+    }
+  }
+
+  // Also, put the button-right pixel into pixels.
+  pixels.push({"x":imageBitmap.width-1, "y":imageBitmap.height-1});
+
+  // Do the test.
+  for (var pixel of pixels) {
+    comparePixelColor(imageData1, imageData, pixel.x, pixel.y, 5, info);
+  }
+}
+
+function prepareImageBitmaps() {
+  gImage1 = document.createElement('img');
+  gImage2 = document.createElement('img');
+  gImage1.src = "image_rgrg-256x256.png";
+  gImage2.src = "image_yellow.png";
+
+  var p1 = new Promise(function(resolve, reject) {
+    gImage1.onload = function() {
+      var promise = createImageBitmap(gImage1);
+      promise.then(function(bitmap) {
+        gImageBitmap1 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p2 = new Promise(function(resolve, reject) {
+    gImage2.onload = function() {
+      var promise = createImageBitmap(gImage2);
+      promise.then(function(bitmap) {
+        gImageBitmap2 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p3 = new Promise(function(resolve, reject) {
+    // Create an ImageData with random colors.
+    var width = 5;
+    var height = 10;
+    var data = [43,143,24,148,    235,165,179,91,   74,228,75,195,    141,109,74,65,    25,177,3,201,
+                128,105,12,199,   196,93,241,131,   250,121,232,189,  175,131,216,190,  145,123,167,70,
+                18,196,210,162,   225,1,90,188,     223,216,182,233,  118,50,168,56,    51,206,198,199,
+                153,29,70,130,    180,135,135,51,   148,46,44,144,    80,171,142,95,    25,178,102,110,
+                0,28,128,91,      31,222,42,170,    85,8,218,146,     65,30,198,238,    121,57,124,88,
+                246,40,141,146,   174,195,255,149,  30,153,92,116,    18,241,6,111,     39,162,85,143,
+                237,159,201,244,  93,68,14,246,     143,143,83,221,   187,215,243,154,  24,125,221,53,
+                80,153,151,219,   202,241,250,191,  153,129,181,57,   94,18,136,231,    41,252,168,207,
+                213,103,118,172,  53,213,184,204,   25,29,249,199,    101,55,49,167,    25,23,173,78,
+                19,234,205,155,   250,175,44,201,   215,92,25,59,     25,29,249,199,    153,129,181,57];
+
+    gImageData = new ImageData(new Uint8ClampedArray(data), width, height);
+
+    // Create an ImageBitmap from the above ImageData.
+    createImageBitmap(gImageData).then(
+      (bitmap) => { gImageBitmap3 = bitmap; resolve(true); },
+      () => { reject(); }
+    );
+
+  });
+
+  return Promise.all([p1, p2, p3]);
+}
\ No newline at end of file
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -21,19 +21,21 @@ support-files =
   image_redtransparent.png
   image_rgrg-256x256.png
   image_rrgg-256x256.png
   image_transparent.png
   image_transparent50.png
   image_yellow.png
   image_yellow75.png
   imagebitmap_bug1239300.js
+  imagebitmap_bug1239752.js
   imagebitmap_on_worker.js
   imagebitmap_structuredclone.js
   imagebitmap_structuredclone_iframe.html
+  imagebitmap_structuredclone_utils.js
   offscreencanvas.js
   offscreencanvas_mask.svg
   offscreencanvas_neuter.js
   offscreencanvas_serviceworker_inner.html
 
 [test_2d.clearRect.image.offscreen.html]
 [test_2d.clip.winding.html]
 [test_2d.composite.canvas.color-burn.html]
--- a/dom/canvas/test/test_imagebitmap.html
+++ b/dom/canvas/test/test_imagebitmap.html
@@ -7,16 +7,17 @@
 
 <img src="image_anim-gr.gif" id="image" class="resource">
 <video width="320" height="240" src="http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg&cors=anonymous" id="video" crossOrigin="anonymous" autoplay></video>
 
 <canvas id="c1" class="output" width="128" height="128"></canvas>
 <canvas id="c2" width="128" height="128"></canvas>
 
 <script src="imagebitmap_bug1239300.js"></script>
+<script src="imagebitmap_bug1239752.js"></script>
 <script type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 /**
  * [isPixel description]
  * @param  {[type]}  ctx : canvas context
  * @param  {[type]}  x   : pixel x coordinate
@@ -330,15 +331,16 @@ function runTests() {
   ctx2 = document.getElementById('c2').getContext('2d');
 
   testDraw()
     .then(testCreatePattern)
     .then(testSources)
     .then(testExceptions)
     .then(testSecurityErrors)
     .then(testBug1239300)
+    .then(testBug1239752)
     .then(SimpleTest.finish, function(ev) { failed(ev); SimpleTest.finish(); });
 }
 
 addLoadEvent(runTests);
 
 </script>
 </body>
--- a/dom/canvas/test/test_imagebitmap_structuredclone.html
+++ b/dom/canvas/test/test_imagebitmap_structuredclone.html
@@ -1,105 +1,38 @@
 <!DOCTYPE HTML>
 <title>Test ImageBitmap : Structured Clone</title>
 <meta charset="utf-8">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
+<script src="imagebitmap_structuredclone_utils.js"></script>
 <script type="text/javascript">
 
-var gImage1;
-var gImage2;
-var gImageBitmap1;
-var gImageBitmap2;
-
-function isPixel(ctx1, ctx2, x, y) {
-  var pixel1 = ctx1.getImageData(x, y, 1, 1);
-  var pixel2 = ctx2.getImageData(x, y, 1, 1);
-  ok(pixel1.data[0] == pixel2.data[0] &&
-     pixel1.data[1] == pixel2.data[1] &&
-     pixel1.data[2] == pixel2.data[2] &&
-     pixel1.data[3] == pixel2.data[3],
-    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
-}
-
-function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
-  var canvas1 = document.createElement('canvas');
-  var canvas2 = document.createElement('canvas');
-
-  canvas1.width  = imageElement.naturalWidth;
-  canvas1.height = imageElement.naturalHeight;
-  canvas2.width  = imageElement.naturalWidth;
-  canvas2.height = imageElement.naturalHeight;
-
-  var ctx1 = canvas1.getContext('2d');
-  var ctx2 = canvas2.getContext('2d');
-
-  ctx1.drawImage(imageElement, 0, 0);
-  ctx2.drawImage(imageBitmap, 0, 0);
-
-  document.body.appendChild(canvas1);
-  document.body.appendChild(canvas2);
-
-  for (var t = 0; t < 20; ++t) {
-    // check one random pixel
-    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
-    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
-    isPixel(ctx1, ctx2, randomX, randomY);
-  }
-}
-
 var worker = new Worker("imagebitmap_structuredclone.js");
 worker.onmessage = function(event) {
 
   if (event.data.type == "status") {
     ok(event.data.status, event.data.msg);
   } else if (event.data.type == "finish") {
     SimpleTest.finish();
   } else if (event.data.type == "bitmap1") {
     compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
   } else if (event.data.type == "bitmap2") {
     compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+  } else if (event.data.type == "bitmap3") {
+    compareImageBitmapWithImageData(event.data.bitmap, gImageData, "Check preserving alpha");
   }
 }
 
-function prepareTwoImageBitmap() {
-  gImage1 = document.createElement('img');
-  gImage2 = document.createElement('img');
-  gImage1.src = "image_rgrg-256x256.png";
-  gImage2.src = "image_yellow.png";
-
-  var p1 = new Promise(function(resolve, reject) {
-    gImage1.onload = function() {
-      var promise = createImageBitmap(gImage1);
-      promise.then(function(bitmap) {
-        gImageBitmap1 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  var p2 = new Promise(function(resolve, reject) {
-    gImage2.onload = function() {
-      var promise = createImageBitmap(gImage2);
-      promise.then(function(bitmap) {
-        gImageBitmap2 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  return Promise.all([p1, p2]);
-}
-
 function runTests() {
   ok(worker, "Worker created successfully.");
 
-  prepareTwoImageBitmap().then(function(){
-    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2});
+  prepareImageBitmaps().then(function(){
+    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2, "bitmap3":gImageBitmap3});
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTests);
 
 </script>
 </body>
--- a/dom/canvas/test/test_imagebitmap_structuredclone_iframe.html
+++ b/dom/canvas/test/test_imagebitmap_structuredclone_iframe.html
@@ -1,111 +1,44 @@
 <!DOCTYPE HTML>
 <title>Test ImageBitmap : StructuredClone between main window and iframe</title>
 <meta charset="utf-8">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <div id="content"></div>
+<script src="imagebitmap_structuredclone_utils.js"></script>
 <script type="text/javascript">
 
-var gImage1;
-var gImage2;
-var gImageBitmap1;
-var gImageBitmap2;
-
-function isPixel(ctx1, ctx2, x, y) {
-  var pixel1 = ctx1.getImageData(x, y, 1, 1);
-  var pixel2 = ctx2.getImageData(x, y, 1, 1);
-  ok(pixel1.data[0] == pixel2.data[0] &&
-     pixel1.data[1] == pixel2.data[1] &&
-     pixel1.data[2] == pixel2.data[2] &&
-     pixel1.data[3] == pixel2.data[3],
-    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
-}
-
-function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
-  var canvas1 = document.createElement('canvas');
-  var canvas2 = document.createElement('canvas');
-
-  canvas1.width  = imageElement.naturalWidth;
-  canvas1.height = imageElement.naturalHeight;
-  canvas2.width  = imageElement.naturalWidth;
-  canvas2.height = imageElement.naturalHeight;
-
-  var ctx1 = canvas1.getContext('2d');
-  var ctx2 = canvas2.getContext('2d');
-
-  ctx1.drawImage(imageElement, 0, 0);
-  ctx2.drawImage(imageBitmap, 0, 0);
-
-  document.body.appendChild(canvas1);
-  document.body.appendChild(canvas2);
-
-  for (var t = 0; t < 20; ++t) {
-    // check one random pixel
-    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
-    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
-    isPixel(ctx1, ctx2, randomX, randomY);
-  }
-}
-
-function prepareTwoImageBitmap() {
-  gImage1 = document.createElement('img');
-  gImage2 = document.createElement('img');
-  gImage1.src = "image_rgrg-256x256.png";
-  gImage2.src = "image_yellow.png";
-
-  var p1 = new Promise(function(resolve, reject) {
-    gImage1.onload = function() {
-      var promise = createImageBitmap(gImage1);
-      promise.then(function(bitmap) {
-        gImageBitmap1 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  var p2 = new Promise(function(resolve, reject) {
-    gImage2.onload = function() {
-      var promise = createImageBitmap(gImage2);
-      promise.then(function(bitmap) {
-        gImageBitmap2 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  return Promise.all([p1, p2]);
-}
-
 function runTests() {
   window.onmessage = function(event) {
     if (event.data.type == "status") {
       ok(event.data.status, event.data.msg);
     } else if (event.data.type == "finish") {
       SimpleTest.finish();
     } else if (event.data.type == "bitmap1") {
       compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
     } else if (event.data.type == "bitmap2") {
       compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+    } else if (event.data.type == "bitmap3") {
+      compareImageBitmapWithImageData(event.data.bitmap, gImageData, "Check preserving alpha");
     }
   }
 
   var div = document.getElementById("content");
   ok(div, "Parent exists");
 
   var ifr = document.createElement("iframe");
   ifr.addEventListener("load", iframeLoaded, false);
   ifr.setAttribute('src', "imagebitmap_structuredclone_iframe.html");
   div.appendChild(ifr);
 
   function iframeLoaded() {
-    prepareTwoImageBitmap().then(function(){
-      ifr.contentWindow.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2}, "*");
+    prepareImageBitmaps().then(function(){
+      ifr.contentWindow.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2, "bitmap3":gImageBitmap3}, "*");
     });
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTests);
 
 </script>
--- a/dom/canvas/test/test_imagebitmap_structuredclone_window.html
+++ b/dom/canvas/test/test_imagebitmap_structuredclone_window.html
@@ -1,95 +1,27 @@
 <!DOCTYPE HTML>
 <title>Test ImageBitmap : StructuredClone main window to main window</title>
 <meta charset="utf-8">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
+<script src="imagebitmap_structuredclone_utils.js"></script>
 <script type="text/javascript">
 
-var gImage1;
-var gImage2;
-var gImageBitmap1;
-var gImageBitmap2;
-
-function isPixel(ctx1, ctx2, x, y) {
-  var pixel1 = ctx1.getImageData(x, y, 1, 1);
-  var pixel2 = ctx2.getImageData(x, y, 1, 1);
-  ok(pixel1.data[0] == pixel2.data[0] &&
-     pixel1.data[1] == pixel2.data[1] &&
-     pixel1.data[2] == pixel2.data[2] &&
-     pixel1.data[3] == pixel2.data[3],
-    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
-}
-
-function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
-  var canvas1 = document.createElement('canvas');
-  var canvas2 = document.createElement('canvas');
-
-  canvas1.width  = imageElement.naturalWidth;
-  canvas1.height = imageElement.naturalHeight;
-  canvas2.width  = imageElement.naturalWidth;
-  canvas2.height = imageElement.naturalHeight;
-
-  var ctx1 = canvas1.getContext('2d');
-  var ctx2 = canvas2.getContext('2d');
-
-  ctx1.drawImage(imageElement, 0, 0);
-  ctx2.drawImage(imageBitmap, 0, 0);
-
-  document.body.appendChild(canvas1);
-  document.body.appendChild(canvas2);
-
-  for (var t = 0; t < 20; ++t) {
-    // check one random pixel
-    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
-    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
-    isPixel(ctx1, ctx2, randomX, randomY);
-  }
-}
-
 window.onmessage = function(event) {
   compareImageBitmapWithImageElement(event.data.bitmap1, gImage1);
   compareImageBitmapWithImageElement(event.data.bitmap2, gImage2);
+  compareImageBitmapWithImageData(event.data.bitmap3, gImageData, "Check preserving alpha");
   SimpleTest.finish();
 }
 
-function prepareTwoImageBitmap() {
-  gImage1 = document.createElement('img');
-  gImage2 = document.createElement('img');
-  gImage1.src = "image_rgrg-256x256.png";
-  gImage2.src = "image_yellow.png";
-
-  var p1 = new Promise(function(resolve, reject) {
-    gImage1.onload = function() {
-      var promise = createImageBitmap(gImage1);
-      promise.then(function(bitmap) {
-        gImageBitmap1 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  var p2 = new Promise(function(resolve, reject) {
-    gImage2.onload = function() {
-      var promise = createImageBitmap(gImage2);
-      promise.then(function(bitmap) {
-        gImageBitmap2 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  return Promise.all([p1, p2]);
-}
-
 function runTests() {
-  prepareTwoImageBitmap().then(function(){
-    window.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2}, "*");
+  prepareImageBitmaps().then(function(){
+    window.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2, "bitmap3":gImageBitmap3}, "*");
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTests);
 
 </script>
 </body>
--- a/dom/canvas/test/test_imagebitmap_transfer.html
+++ b/dom/canvas/test/test_imagebitmap_transfer.html
@@ -1,111 +1,46 @@
 <!DOCTYPE HTML>
 <title>Test ImageBitmap : Transfer</title>
 <meta charset="utf-8">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
+<script src="imagebitmap_structuredclone_utils.js"></script>
 <script type="text/javascript">
 
-var gImage1;
-var gImage2;
-var gImageBitmap1;
-var gImageBitmap2;
-
-function isPixel(ctx1, ctx2, x, y) {
-  var pixel1 = ctx1.getImageData(x, y, 1, 1);
-  var pixel2 = ctx2.getImageData(x, y, 1, 1);
-  ok(pixel1.data[0] == pixel2.data[0] &&
-     pixel1.data[1] == pixel2.data[1] &&
-     pixel1.data[2] == pixel2.data[2] &&
-     pixel1.data[3] == pixel2.data[3],
-    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
-}
-
-function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
-  var canvas1 = document.createElement('canvas');
-  var canvas2 = document.createElement('canvas');
-
-  canvas1.width  = imageElement.naturalWidth;
-  canvas1.height = imageElement.naturalHeight;
-  canvas2.width  = imageElement.naturalWidth;
-  canvas2.height = imageElement.naturalHeight;
-
-  var ctx1 = canvas1.getContext('2d');
-  var ctx2 = canvas2.getContext('2d');
-
-  ctx1.drawImage(imageElement, 0, 0);
-  ctx2.drawImage(imageBitmap, 0, 0);
-
-  document.body.appendChild(canvas1);
-  document.body.appendChild(canvas2);
-
-  for (var t = 0; t < 20; ++t) {
-    // check one random pixel
-    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
-    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
-    isPixel(ctx1, ctx2, randomX, randomY);
-  }
-}
-
 var worker = new Worker("imagebitmap_structuredclone.js");
 worker.onmessage = function(event) {
 
   if (event.data.type == "status") {
     ok(event.data.status, event.data.msg);
   } else if (event.data.type == "finish") {
     SimpleTest.finish();
   } else if (event.data.type == "bitmap1") {
     compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
   } else if (event.data.type == "bitmap2") {
     compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+  } else if (event.data.type == "bitmap3") {
+    compareImageBitmapWithImageData(event.data.bitmap, gImageData, "Check preserving alpha");
   }
 }
 
-function prepareTwoImageBitmap() {
-  gImage1 = document.createElement('img');
-  gImage2 = document.createElement('img');
-  gImage1.src = "image_rgrg-256x256.png";
-  gImage2.src = "image_yellow.png";
-
-  var p1 = new Promise(function(resolve, reject) {
-    gImage1.onload = function() {
-      var promise = createImageBitmap(gImage1);
-      promise.then(function(bitmap) {
-        gImageBitmap1 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  var p2 = new Promise(function(resolve, reject) {
-    gImage2.onload = function() {
-      var promise = createImageBitmap(gImage2);
-      promise.then(function(bitmap) {
-        gImageBitmap2 = bitmap;
-        resolve(true);
-      });
-    }
-  });
-
-  return Promise.all([p1, p2]);
-}
-
 function runTests() {
   ok(worker, "Worker created successfully.");
 
-  prepareTwoImageBitmap().then(function(){
-    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2},
-                       [gImageBitmap1, gImageBitmap2]);
+  prepareImageBitmaps().then(function(){
+    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2, "bitmap3":gImageBitmap3},
+                       [gImageBitmap1, gImageBitmap2, gImageBitmap3]);
 
     ok(gImageBitmap1.width == 0 && gImageBitmap1.height == 0,
        "After transfer, ImageBitmap become neutered");
     ok(gImageBitmap2.width == 0 && gImageBitmap2.height == 0,
        "After transfer, ImageBitmap become neutered");
+    ok(gImageBitmap3.width == 0 && gImageBitmap3.height == 0,
+       "After transfer, ImageBitmap become neutered");
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTests);
 
 </script>
 </body>
--- a/dom/contacts/tests/test_migration.html
+++ b/dom/contacts/tests/test_migration.html
@@ -49,42 +49,25 @@ function createDB(version) {
   backend.sendAsyncMessage("createDB", version);
 }
 
 function deleteDB() {
   info("Will delete the DB.");
   backend.sendAsyncMessage("deleteDB");
 }
 
-function setSubstringMatching(value) {
-  info("Setting substring matching to " + value);
-
-  if (value) {
-    SpecialPowers.setIntPref("dom.phonenumber.substringmatching.BR", value);
-
-    // this is the Mcc for Brazil, so that we trigger the previous pref
-    SpecialPowers.setCharPref("ril.lastKnownSimMcc", "724");
-  } else {
-    SpecialPowers.clearUserPref("dom.phonenumber.substringmatching.BR");
-    SpecialPowers.clearUserPref("ril.lastKnownSimMcc");
-  }
-
-  next();
-}
-
 var steps = [
   function setupChromeScript() {
     loadChromeScript();
     addBackendEvents();
     next();
   },
 
   deleteDB, // let's be sure the DB does not exist yet
   createDB.bind(null, 12),
-  setSubstringMatching.bind(null, 7),
 
   function testAccessMozContacts() {
     info("Checking we have the right number of contacts: " + contactsCount);
     var req = mozContacts.getCount();
     req.onsuccess = function onsuccess() {
       ok(true, "Could access the mozContacts API");
       is(this.result, contactsCount, "Contacts count is correct");
       next();
@@ -189,22 +172,23 @@ var steps = [
 
     req.onerror = function onerror() {
       ok(false, "Error while finding contact for substring matching check!");
       next();
     };
   },
 
   deleteDB,
-  setSubstringMatching.bind(null, null),
 
   function finish() {
     backend.destroy();
     info("all done!\n");
     SimpleTest.finish();
   }
 ];
 
-start_tests();
+// this is the Mcc for Brazil, so that we trigger the previous pref
+SpecialPowers.pushPrefEnv({"set": [["dom.phonenumber.substringmatching.BR", 7],
+                                   ["ril.lastKnownSimMcc", "724"]]}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1118,16 +1118,25 @@ static nsresult GetFrameForTextRect(nsIN
 nsresult
 ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  nsINode* const startNode = mFirstSelectedRange->GetStartParent();
+  nsINode* const endNode = mFirstSelectedRange->GetEndParent();
+
+  // Make sure the selection is within the root content range.
+  if (!nsContentUtils::ContentIsDescendantOf(startNode, mRootContent) ||
+      !nsContentUtils::ContentIsDescendantOf(endNode, mRootContent)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
                "The reply string must be empty");
 
   LineBreakType lineBreakType = GetLineBreakType(aEvent);
   rv = GetFlatTextLengthBefore(mFirstSelectedRange,
                                &aEvent->mReply.mOffset, lineBreakType);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -44,16 +44,17 @@
 #include "nsISupports.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsNameSpaceManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsSandboxFlags.h"
 #include "xpcpublic.h"
 #include "nsIFrame.h"
+#include "nsDisplayList.h"
 
 namespace mozilla {
 
 using namespace dom;
 using namespace hal;
 
 #define EVENT_TYPE_EQUALS(ls, message, userType, typeString, allEvents) \
   ((ls->mEventMessage == message &&                                     \
@@ -452,17 +453,17 @@ EventListenerManager::ProcessApzAwareEve
   if (!doc) {
     if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
       if (nsPIDOMWindowInner* window = helper->GetOwner()) {
         doc = window->GetExtantDoc();
       }
     }
   }
 
-  if (doc) {
+  if (doc && nsDisplayListBuilder::LayerEventRegionsEnabled()) {
     nsIPresShell* ps = doc->GetShell();
     if (ps) {
       nsIFrame* f = ps->GetRootFrame();
       if (f) {
         f->SchedulePaint();
       }
     }
   }
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -140,16 +140,17 @@ skip-if = toolkit == "gonk" || e10s
 [test_bug998809.html]
 [test_bug1017086_disable.html]
 support-files = bug1017086_inner.html
 [test_bug1017086_enable.html]
 support-files = bug1017086_inner.html
 [test_bug1079236.html]
 [test_bug1145910.html]
 [test_bug1150308.html]
+[test_bug1248459.html]
 [test_clickevent_on_input.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_continuous_wheel_events.html]
 skip-if = buildapp == 'b2g' || e10s # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop)
 [test_dblclick_explicit_original_target.html]
 [test_dom_keyboard_event.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dom_mouse_event.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1248459.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1248459
+-->
+<head>
+  <title>Test for Bug 1248459</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<input id="input" value="foo">
+<div id="div">bar</div>
+<script type="application/javascript">
+
+/** Test for Bug 1248459 **/
+/**
+ * The bug occurs when a piece of text outside of the editor's root element is
+ * somehow selected when the editor is focused. In the bug's case, it's the
+ * placeholder anonymous div that's selected. In this test's case, it's a
+ * document div that's selected.
+ */
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+  var div = document.getElementById("div");
+  var input = document.getElementById("input");
+
+  input.appendChild(div);
+  input.focus();
+
+  var editor = SpecialPowers.wrap(input).editor;
+  var sel = editor.selection;
+
+  sel.selectAllChildren(editor.rootElement);
+  var result = synthesizeQuerySelectedText();
+
+  ok(result.succeeded, "Query selected text should succeed");
+  is(result.offset, 0, "Selected text should be at offset 0");
+  is(result.text, "foo", "Selected text should match");
+
+  var range = document.createRange();
+  range.selectNode(div);
+
+  sel.removeAllRanges();
+  sel.addRange(range);
+
+  result = synthesizeQuerySelectedText();
+
+  ok(!result.succeeded, "Query out-of-bounds selection should fail");
+
+  SimpleTest.finish();
+});
+
+</script>
+</body>
+</html>
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -260,41 +260,65 @@ FetchDriver::HttpFetch()
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the same headers.
     SetRequestHeaders(httpChan);
 
     // Step 2. Set the referrer.
     nsAutoString referrer;
     mRequest->GetReferrer(referrer);
+    ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
+    net::ReferrerPolicy net_referrerPolicy = net::RP_Unset;
+    switch (referrerPolicy) {
+    case ReferrerPolicy::_empty:
+      net_referrerPolicy = net::RP_Default;
+      break;
+    case ReferrerPolicy::No_referrer:
+      net_referrerPolicy = net::RP_No_Referrer;
+      break;
+    case ReferrerPolicy::No_referrer_when_downgrade:
+      net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
+      break;
+    case ReferrerPolicy::Origin_only:
+      net_referrerPolicy = net::RP_Origin;
+      break;
+    case ReferrerPolicy::Origin_when_cross_origin:
+      net_referrerPolicy = net::RP_Origin_When_Crossorigin;
+      break;
+    case ReferrerPolicy::Unsafe_url:
+      net_referrerPolicy = net::RP_Unsafe_URL;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
+      break;
+    }
     if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
       rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
                                                          mDocument,
-                                                         httpChan);
+                                                         httpChan,
+                                                         net_referrerPolicy);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (referrer.IsEmpty()) {
       rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // From "Determine request's Referrer" step 3
       // "If request's referrer is a URL, let referrerSource be request's
       // referrer."
-      //
-      // XXXnsm - We never actually hit this from a fetch() call since both
-      // fetch and Request() create a new internal request whose referrer is
-      // always set to about:client. Should we just crash here instead until
-      // someone tries to use FetchDriver for non-fetch() APIs?
       nsCOMPtr<nsIURI> referrerURI;
       rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      uint32_t documentReferrerPolicy = mDocument ? mDocument->GetReferrerPolicy() :
+                                                    net::RP_Default;
       rv =
         httpChan->SetReferrerWithPolicy(referrerURI,
-                                        mDocument ? mDocument->GetReferrerPolicy() :
-                                                    net::RP_Default);
+                                        referrerPolicy == ReferrerPolicy::_empty ?
+                                          documentReferrerPolicy :
+                                          net_referrerPolicy);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Bug 1120722 - Authorization will be handled later.
     // Auth may require prompting, we don't support it yet.
     // The next patch in this same bug prevents this from aborting the request.
     // Credentials checks for CORS are handled by nsCORSListenerProxy,
 
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -32,16 +32,17 @@ InternalRequest::GetRequestConstructorCo
   copy->mBodyStream = mBodyStream;
   copy->mForceOriginHeader = true;
   // The "client" is not stored in our implementation. Fetch API users should
   // use the appropriate window/document/principal and other Gecko security
   // mechanisms as appropriate.
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
   // The default referrer is already about:client.
+  copy->mReferrerPolicy = mReferrerPolicy;
 
   copy->mContentPolicyType = nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
   copy->mCacheMode = mCacheMode;
   copy->mRedirectMode = mRedirectMode;
   copy->mCreatedByFetchEvent = mCreatedByFetchEvent;
   return copy.forget();
@@ -72,16 +73,17 @@ InternalRequest::Clone()
 }
 
 InternalRequest::InternalRequest(const InternalRequest& aOther)
   : mMethod(aOther.mMethod)
   , mURL(aOther.mURL)
   , mHeaders(new InternalHeaders(*aOther.mHeaders))
   , mContentPolicyType(aOther.mContentPolicyType)
   , mReferrer(aOther.mReferrer)
+  , mReferrerPolicy(aOther.mReferrerPolicy)
   , mMode(aOther.mMode)
   , mCredentialsMode(aOther.mCredentialsMode)
   , mResponseTainting(aOther.mResponseTainting)
   , mCacheMode(aOther.mCacheMode)
   , mRedirectMode(aOther.mRedirectMode)
   , mAuthenticationFlag(aOther.mAuthenticationFlag)
   , mForceOriginHeader(aOther.mForceOriginHeader)
   , mPreserveContentCodings(aOther.mPreserveContentCodings)
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -88,16 +88,17 @@ class InternalRequest final
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest)
 
   InternalRequest()
     : mMethod("GET")
     , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
     , mContentPolicyType(nsIContentPolicy::TYPE_FETCH)
     , mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
+    , mReferrerPolicy(ReferrerPolicy::_empty)
     , mMode(RequestMode::No_cors)
     , mCredentialsMode(RequestCredentials::Omit)
     , mResponseTainting(LoadTainting::Basic)
     , mCacheMode(RequestCache::Default)
     , mRedirectMode(RequestRedirect::Follow)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mPreserveContentCodings(false)
@@ -115,22 +116,24 @@ public:
 
   InternalRequest(const nsACString& aURL,
                   const nsACString& aMethod,
                   already_AddRefed<InternalHeaders> aHeaders,
                   RequestMode aMode,
                   RequestRedirect aRequestRedirect,
                   RequestCredentials aRequestCredentials,
                   const nsAString& aReferrer,
+                  ReferrerPolicy aReferrerPolicy,
                   nsContentPolicyType aContentPolicyType)
     : mMethod(aMethod)
     , mURL(aURL)
     , mHeaders(aHeaders)
     , mContentPolicyType(aContentPolicyType)
     , mReferrer(aReferrer)
+    , mReferrerPolicy(aReferrerPolicy)
     , mMode(aMode)
     , mCredentialsMode(aRequestCredentials)
     , mResponseTainting(LoadTainting::Basic)
     , mCacheMode(RequestCache::Default)
     , mRedirectMode(aRequestRedirect)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mPreserveContentCodings(false)
@@ -225,16 +228,28 @@ public:
     }
 
     MOZ_ASSERT(validReferrer);
 #endif
 
     mReferrer.Assign(aReferrer);
   }
 
+  ReferrerPolicy
+  ReferrerPolicy_() const
+  {
+    return mReferrerPolicy;
+  }
+
+  void
+  SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
+  {
+    mReferrerPolicy = aReferrerPolicy;
+  }
+
   bool
   SkipServiceWorker() const
   {
     return mSkipServiceWorker;
   }
 
   void
   SetSkipServiceWorker()
@@ -437,16 +452,17 @@ private:
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   nsContentPolicyType mContentPolicyType;
 
   // Empty string: no-referrer
   // "about:client": client (default)
   // URL: an URL
   nsString mReferrer;
+  ReferrerPolicy mReferrerPolicy;
 
   RequestMode mMode;
   RequestCredentials mCredentialsMode;
   LoadTainting mResponseTainting;
   RequestCache mCacheMode;
   RequestRedirect mRedirectMode;
 
   bool mAuthenticationFlag;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -348,16 +348,17 @@ Request::Constructor(const GlobalObject&
   if (mode == RequestMode::Navigate ||
       (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate)) {
     aRv.ThrowTypeError<MSG_INVALID_REQUEST_MODE>(NS_LITERAL_STRING("navigate"));
     return nullptr;
   }
 
   if (aInit.IsAnyMemberPresent()) {
     request->SetReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR));
+    request->SetReferrerPolicy(ReferrerPolicy::_empty);
   }
   if (aInit.mReferrer.WasPassed()) {
     const nsString& referrer = aInit.mReferrer.Value();
     if (referrer.IsEmpty()) {
       request->SetReferrer(NS_LITERAL_STRING(""));
     } else {
       nsAutoString referrerURL;
       if (NS_IsMainThread()) {
@@ -414,16 +415,20 @@ Request::Constructor(const GlobalObject&
             return nullptr;
           }
         }
       }
       request->SetReferrer(referrerURL);
     }
   }
 
+  if (aInit.mReferrerPolicy.WasPassed()) {
+    request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
+  }
+
   if (mode != RequestMode::EndGuard_) {
     request->ClearCreatedByFetchEvent();
     request->SetMode(mode);
   }
 
   if (credentials != RequestCredentials::EndGuard_) {
     request->ClearCreatedByFetchEvent();
     request->SetCredentialsMode(credentials);
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -94,16 +94,22 @@ public:
   }
 
   void
   GetReferrer(nsAString& aReferrer) const
   {
     mRequest->GetReferrer(aReferrer);
   }
 
+  ReferrerPolicy
+  ReferrerPolicy_() const
+  {
+    return mRequest->ReferrerPolicy_();
+  }
+
   InternalHeaders*
   GetInternalHeaders() const
   {
     return mRequest->Headers();
   }
 
   Headers* Headers_();
 
--- a/dom/html/HTMLOptionElement.cpp
+++ b/dom/html/HTMLOptionElement.cpp
@@ -349,29 +349,31 @@ HTMLOptionElement::IntrinsicState() cons
 
   return state;
 }
 
 // Get the select content element that contains this option
 HTMLSelectElement*
 HTMLOptionElement::GetSelect()
 {
-  nsIContent* parent = this;
-  while ((parent = parent->GetParent()) &&
-         parent->IsHTMLElement()) {
-    HTMLSelectElement* select = HTMLSelectElement::FromContent(parent);
-    if (select) {
-      return select;
-    }
-    if (!parent->IsHTMLElement(nsGkAtoms::optgroup)) {
-      break;
-    }
+  nsIContent* parent = GetParent();
+  if (!parent) {
+    return nullptr;
   }
 
-  return nullptr;
+  HTMLSelectElement* select = HTMLSelectElement::FromContent(parent);
+  if (select) {
+    return select;
+  }
+
+  if (!parent->IsHTMLElement(nsGkAtoms::optgroup)) {
+    return nullptr;
+  }
+
+  return HTMLSelectElement::FromContentOrNull(parent->GetParent());
 }
 
 already_AddRefed<HTMLOptionElement>
 HTMLOptionElement::Option(const GlobalObject& aGlobal,
                           const Optional<nsAString>& aText,
                           const Optional<nsAString>& aValue,
                           const Optional<bool>& aDefaultSelected,
                           const Optional<bool>& aSelected, ErrorResult& aError)
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -600,8 +600,9 @@ skip-if = buildapp == 'b2g' || (e10s && 
 [test_extapp.html]
 [test_image_clone_load.html]
 [test_bug1203668.html]
 [test_bug1166138.html]
 [test_bug1230665.html]
 [test_filepicker_default_directory.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1233598.html]
+[test_bug1250401.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1250401.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1250401
+-->
+<head>
+  <title>Test for Bug 1250401</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1250401">Bug 1250401</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1250401 **/
+function test_add() {
+  var select = document.createElement("select");
+
+  var g1 = document.createElement("optgroup");
+  var o1 = document.createElement("option");
+  g1.appendChild(o1);
+  select.appendChild(g1);
+
+  var g2 = document.createElement("optgroup");
+  var o2 = document.createElement("option");
+  g2.appendChild(o2);
+  select.add(g2, 0);
+
+  is(select.children.length, 1, "Select has 1 item");
+  is(select.firstChild, g1, "First item is g1");
+  is(select.firstChild.children.length, 2, "g2 has 2 children");
+  is(select.firstChild.children[0], g2, "g1 has 2 children: g2");
+  is(select.firstChild.children[1], o1, "g1 has 2 children: o1");
+  is(o1.index, 0, "o1.index should be 0");
+  is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_append() {
+  var select = document.createElement("select");
+
+  var g1 = document.createElement("optgroup");
+  var o1 = document.createElement("option");
+  g1.appendChild(o1);
+  select.appendChild(g1);
+
+  var g2 = document.createElement("optgroup");
+  var o2 = document.createElement("option");
+  g2.appendChild(o2);
+  g1.appendChild(g2);
+
+  is(select.children.length, 1, "Select has 1 item");
+  is(select.firstChild, g1, "First item is g1");
+  is(select.firstChild.children.length, 2, "g2 has 2 children");
+  is(select.firstChild.children[0], o1, "g1 has 2 children: o1");
+  is(select.firstChild.children[1], g2, "g1 has 2 children: g1");
+  is(o1.index, 0, "o1.index should be 0");
+  is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_no_select() {
+  var g1 = document.createElement("optgroup");
+  var o1 = document.createElement("option");
+  g1.appendChild(o1);
+
+  var g2 = document.createElement("optgroup");
+  var o2 = document.createElement("option");
+  g2.appendChild(o2);
+  g1.appendChild(g2);
+
+  is(g1.children.length, 2, "g2 has 2 children");
+  is(g1.children[0], o1, "g1 has 2 children: o1");
+  is(g1.children[1], g2, "g1 has 2 children: g1");
+  is(o1.index, 0, "o1.index should be 0");
+  is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_no_parent() {
+  var o1 = document.createElement("option");
+  var o2 = document.createElement("option");
+
+  is(o1.index, 0, "o1.index should be 0");
+  is(o2.index, 0, "o2.index should be 0");
+}
+
+test_add();
+test_append();
+test_no_select();
+test_no_parent();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -854,33 +854,32 @@ TabChild::Init()
   mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
 
   return NS_OK;
 }
 
 void
 TabChild::NotifyTabContextUpdated()
 {
-    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
-    MOZ_ASSERT(docShell);
-
-    if (docShell) {
-        // nsDocShell will do the right thing if we pass NO_APP_ID or
-        // UNKNOWN_APP_ID for aOwnOrContainingAppId.
-        if (IsMozBrowserElement()) {
-          docShell->SetIsBrowserInsideApp(BrowserOwnerAppId());
-          docShell->SetIsInIsolatedMozBrowserElement(IsIsolatedMozBrowserElement());
-        } else {
-          docShell->SetIsApp(OwnAppId());
-        }
-
-        OriginAttributes attrs = OriginAttributesRef();
-        docShell->SetIsSignedPackage(attrs.mSignedPkg);
-        docShell->SetUserContextId(attrs.mUserContextId);
-    }
+  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+  MOZ_ASSERT(docShell);
+
+  if (!docShell) {
+    return;
+  }
+
+  if (IsMozBrowserElement()) {
+    docShell->SetIsInIsolatedMozBrowserElement(IsIsolatedMozBrowserElement());
+  }
+  docShell->SetFrameType(IsMozBrowserElement() ?
+                           nsIDocShell::FRAME_TYPE_BROWSER :
+                           HasOwnApp() ?
+                             nsIDocShell::FRAME_TYPE_APP :
+                             nsIDocShell::FRAME_TYPE_REGULAR);
+  nsDocShell::Cast(docShell)->SetOriginAttributes(OriginAttributesRef());
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
   NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -226,17 +226,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mVideoCompleted(false, "MediaDecoderStateMachine::mVideoCompleted"),
   mNotifyMetadataBeforeFirstFrame(false),
   mDispatchedEventToDecode(false),
   mQuickBuffering(false),
   mMinimizePreroll(false),
   mDecodeThreadWaiting(false),
   mDropAudioUntilNextDiscontinuity(false),
   mDropVideoUntilNextDiscontinuity(false),
-  mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mCorruptFrames(60),
   mDecodingFirstFrame(true),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
   mSentPlaybackEndedEvent(false),
   mOutputStreamManager(new OutputStreamManager()),
   mResource(aDecoder->GetResource()),
@@ -481,23 +480,21 @@ bool MediaDecoderStateMachine::HaveEnoug
 
   return true;
 }
 
 bool
 MediaDecoderStateMachine::NeedToDecodeVideo()
 {
   MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("NeedToDecodeVideo() isDec=%d decToTar=%d minPrl=%d seek=%d enufVid=%d",
-             IsVideoDecoding(), mDecodeToSeekTarget, mMinimizePreroll,
-             mState == DECODER_STATE_SEEKING,
-             HaveEnoughDecodedVideo());
+  SAMPLE_LOG("NeedToDecodeVideo() isDec=%d minPrl=%d enufVid=%d",
+             IsVideoDecoding(), mMinimizePreroll, HaveEnoughDecodedVideo());
   return IsVideoDecoding() &&
-         ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
-          (IsDecodingFirstFrame() && VideoQueue().GetSize() == 0) ||
+         mState != DECODER_STATE_SEEKING &&
+         ((IsDecodingFirstFrame() && VideoQueue().GetSize() == 0) ||
           (!mMinimizePreroll && !HaveEnoughDecodedVideo()));
 }
 
 bool
 MediaDecoderStateMachine::NeedToSkipToNextKeyframe()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (IsDecodingFirstFrame()) {
@@ -551,27 +548,25 @@ MediaDecoderStateMachine::NeedToSkipToNe
 
   return false;
 }
 
 bool
 MediaDecoderStateMachine::NeedToDecodeAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("NeedToDecodeAudio() isDec=%d decToTar=%d minPrl=%d seek=%d enufAud=%d",
-             IsAudioDecoding(), mDecodeToSeekTarget, mMinimizePreroll,
-             mState == DECODER_STATE_SEEKING,
+  SAMPLE_LOG("NeedToDecodeAudio() isDec=%d minPrl=%d enufAud=%d",
+             IsAudioDecoding(), mMinimizePreroll,
              HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate));
 
   return IsAudioDecoding() &&
-         ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
-          (IsDecodingFirstFrame() && AudioQueue().GetSize() == 0) ||
+         mState != DECODER_STATE_SEEKING &&
+         ((IsDecodingFirstFrame() && AudioQueue().GetSize() == 0) ||
           (!mMinimizePreroll &&
-           !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate) &&
-           (mState != DECODER_STATE_SEEKING || mDecodeToSeekTarget)));
+           !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate)));
 }
 
 bool
 MediaDecoderStateMachine::IsAudioSeekComplete()
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
     mCurrentSeek.Exists(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
@@ -1021,17 +1016,18 @@ MediaDecoderStateMachine::CheckIfSeekCom
       DecodeError();
     }
   }
 
   SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
              audioSeekComplete, videoSeekComplete);
 
   if (audioSeekComplete && videoSeekComplete) {
-    mDecodeToSeekTarget = false;
+    NS_ASSERTION(AudioQueue().GetSize() <= 1, "Should decode at most one sample");
+    NS_ASSERTION(VideoQueue().GetSize() <= 1, "Should decode at most one sample");
     SeekCompleted();
   }
 }
 
 bool
 MediaDecoderStateMachine::IsAudioDecoding()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -1643,18 +1639,18 @@ MediaDecoderStateMachine::InitiateSeek()
   mSeekRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
                                  &MediaDecoderReader::Seek, seekTarget,
                                  Duration().ToMicroseconds())
     ->Then(OwnerThread(), __func__,
            [self] (media::TimeUnit) -> void {
              self->mSeekRequest.Complete();
              // We must decode the first samples of active streams, so we can determine
              // the new stream time. So dispatch tasks to do that.
-             self->mDecodeToSeekTarget = true;
-             self->DispatchDecodeTasksIfNeeded();
+             self->EnsureAudioDecodeTaskQueued();
+             self->EnsureVideoDecodeTaskQueued();
            }, [self] (nsresult aResult) -> void {
              self->mSeekRequest.Complete();
              MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
              self->DecodeError();
            }));
 }
 
 nsresult
@@ -2401,17 +2397,16 @@ MediaDecoderStateMachine::Reset()
   mDecodedAudioEndTime = 0;
   mAudioCompleted = false;
   mVideoCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
   mFirstVideoFrameAfterSeek = nullptr;
   mDropAudioUntilNextDiscontinuity = true;
   mDropVideoUntilNextDiscontinuity = true;
-  mDecodeToSeekTarget = false;
 
   mMetadataRequest.DisconnectIfExists();
   mAudioDataRequest.DisconnectIfExists();
   mAudioWaitRequest.DisconnectIfExists();
   mVideoDataRequest.DisconnectIfExists();
   mVideoWaitRequest.DisconnectIfExists();
   mSeekRequest.DisconnectIfExists();
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -1116,20 +1116,16 @@ private:
 
   // These two flags are true when we need to drop decoded samples that
   // we receive up to the next discontinuity. We do this when we seek;
   // the first sample in each stream after the seek is marked as being
   // a "discontinuity".
   bool mDropAudioUntilNextDiscontinuity;
   bool mDropVideoUntilNextDiscontinuity;
 
-  // True if we need to decode forwards to the seek target inside
-  // mCurrentSeekTarget.
-  bool mDecodeToSeekTarget;
-
   // Track the current seek promise made by the reader.
   MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
 
   // We record the playback position before we seek in order to
   // determine where the seek terminated relative to the playback position
   // we were at before the seek.
   int64_t mCurrentTimeBeforeSeek;
 
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -164,17 +164,17 @@ VP8TrackEncoder::GetMetadata()
   meta->mHeight = mFrameHeight;
   meta->mDisplayWidth = mDisplayWidth;
   meta->mDisplayHeight = mDisplayHeight;
   meta->mEncodedFrameRate = mEncodedFrameRate;
 
   return meta.forget();
 }
 
-nsresult
+bool
 VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
 {
   vpx_codec_iter_t iter = nullptr;
   EncodedFrame::FrameType frameType = EncodedFrame::VP8_P_FRAME;
   nsTArray<uint8_t> frameData;
   const vpx_codec_cx_pkt_t *pkt = nullptr;
   while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) {
     switch (pkt->kind) {
@@ -192,40 +192,37 @@ VP8TrackEncoder::GetEncodedPartitions(En
     if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
       if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
         frameType = EncodedFrame::VP8_I_FRAME;
       }
       break;
     }
   }
 
-  if (!frameData.IsEmpty() &&
-      (pkt->data.frame.pts == mEncodedTimestamp)) {
+  if (!frameData.IsEmpty()) {
     // Copy the encoded data to aData.
     EncodedFrame* videoData = new EncodedFrame();
     videoData->SetFrameType(frameType);
     // Convert the timestamp and duration to Usecs.
-    CheckedInt64 timestamp = FramesToUsecs(mEncodedTimestamp, mTrackRate);
+    CheckedInt64 timestamp = FramesToUsecs(pkt->data.frame.pts, mTrackRate);
     if (timestamp.isValid()) {
-      videoData->SetTimeStamp(
-        (uint64_t)FramesToUsecs(mEncodedTimestamp, mTrackRate).value());
+      videoData->SetTimeStamp((uint64_t)timestamp.value());
     }
     CheckedInt64 duration = FramesToUsecs(pkt->data.frame.duration, mTrackRate);
     if (duration.isValid()) {
-      videoData->SetDuration(
-        (uint64_t)FramesToUsecs(pkt->data.frame.duration, mTrackRate).value());
+      videoData->SetDuration((uint64_t)duration.value());
     }
     videoData->SwapInFrameData(frameData);
     VP8LOG("GetEncodedPartitions TimeStamp %lld Duration %lld\n",
            videoData->GetTimeStamp(), videoData->GetDuration());
     VP8LOG("frameType %d\n", videoData->GetFrameType());
     aData.AppendEncodedFrame(videoData);
   }
 
-  return NS_OK;
+  return !!pkt;
 }
 
 static bool isYUV420(const PlanarYCbCrImage::Data *aData)
 {
   if (aData->mYSize == aData->mCbCrSize * 2) {
     return true;
   }
   return false;
@@ -360,17 +357,17 @@ nsresult VP8TrackEncoder::PrepareRawFram
       return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     if (rv != 0) {
       VP8LOG("Converting an %s frame to I420 failed\n", yuvFormat.c_str());
       return NS_ERROR_FAILURE;
     }
 
-    VP8LOG("Converted an %s frame to I420\n");
+    VP8LOG("Converted an %s frame to I420\n", yuvFormat.c_str());
   } else {
     // Not YCbCr at all. Try to get access to the raw data and convert.
 
     RefPtr<SourceSurface> surf = img->GetAsSourceSurface();
     if (!surf) {
       VP8LOG("Getting surface from %s image failed\n", Stringify(format).c_str());
       return NS_ERROR_FAILURE;
     }
@@ -626,19 +623,23 @@ VP8TrackEncoder::GetEncodedTrack(Encoded
   // Remove the chunks we have processed.
   mSourceSegment.RemoveLeading(totalProcessedDuration);
   VP8LOG("RemoveLeading %lld\n",totalProcessedDuration);
 
   // End of stream, pull the rest frames in encoder.
   if (EOS) {
     VP8LOG("mEndOfStream is true\n");
     mEncodingComplete = true;
-    if (vpx_codec_encode(mVPXContext, nullptr, mEncodedTimestamp,
-                         mEncodedFrameDuration, 0, VPX_DL_REALTIME)) {
-      return NS_ERROR_FAILURE;
-    }
-    GetEncodedPartitions(aData);
+    // Bug 1243611, keep calling vpx_codec_encode and vpx_codec_get_cx_data
+    // until vpx_codec_get_cx_data return null.
+
+    do {
+      if (vpx_codec_encode(mVPXContext, nullptr, mEncodedTimestamp,
+                           mEncodedFrameDuration, 0, VPX_DL_REALTIME)) {
+        return NS_ERROR_FAILURE;
+      }
+    } while(GetEncodedPartitions(aData));
   }
 
   return NS_OK ;
 }
 
 } // namespace mozilla
--- a/dom/media/encoder/VP8TrackEncoder.h
+++ b/dom/media/encoder/VP8TrackEncoder.h
@@ -49,17 +49,19 @@ private:
   StreamTime CalculateRemainingTicks(StreamTime aDurationCopied,
                                      StreamTime aEncodedDuration);
 
   // Get the EncodeOperation for next target frame.
   EncodeOperation GetNextEncodeOperation(TimeDuration aTimeElapsed,
                                          StreamTime aProcessedDuration);
 
   // Get the encoded data from encoder to aData.
-  nsresult GetEncodedPartitions(EncodedFrameContainer& aData);
+  // Return value: false if the vpx_codec_get_cx_data returns null
+  //               for EOS detection.
+  bool GetEncodedPartitions(EncodedFrameContainer& aData);
 
   // Prepare the input data to the mVPXImageWrapper for encoding.
   nsresult PrepareRawFrame(VideoChunk &aChunk);
 
   // Output frame rate.
   uint32_t mEncodedFrameRate;
   // Duration for the output frame, reciprocal to mEncodedFrameRate.
   StreamTime mEncodedFrameDuration;
--- a/dom/media/test/external/mach_commands.py
+++ b/dom/media/test/external/mach_commands.py
@@ -61,10 +61,10 @@ def run_external_media_test(tests, testt
 @CommandProvider
 class MachCommands(MachCommandBase):
     @Command('external-media-tests', category='testing',
              description='Run Firefox external media tests.',
              conditions=[conditions.is_firefox],
              parser=setup_argument_parser,
              )
     def run_external_media_test(self, tests, **kwargs):
-        kwargs['binary'] = self.get_binary_path('app')
+        kwargs['binary'] = kwargs['binary'] or self.get_binary_path('app')
         return run_external_media_test(tests, topsrcdir=self.topsrcdir, **kwargs)
deleted file mode 100644
--- a/dom/plugins/base/android/ANPOpenGL.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <dlfcn.h>
-#include <android/log.h>
-#include "AndroidBridge.h"
-#include "ANPBase.h"
-#include "GLContextProvider.h"
-#include "nsNPAPIPluginInstance.h"
-#include "nsPluginInstanceOwner.h"
-#include "GLContextProvider.h"
-#include "GLContextEGL.h"
-
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
-#define ASSIGN(obj, name)   (obj)->name = anp_opengl_##name
-
-using namespace mozilla;
-using namespace mozilla::gl;
-
-typedef nsNPAPIPluginInstance::TextureInfo TextureInfo;
-
-static ANPEGLContext anp_opengl_acquireContext(NPP instance) {
-    nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-    GLContext* context = pinst->GLContext();
-    if (!context)
-        return nullptr;
-
-    context->MakeCurrent();
-    return GLContextEGL::Cast(context)->mContext;
-}
-
-static ANPTextureInfo anp_opengl_lockTexture(NPP instance) {
-    nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-    TextureInfo pluginInfo = pinst->LockContentTexture();
-
-    ANPTextureInfo info;
-    info.textureId = pluginInfo.mTexture;
-    info.width = pluginInfo.mWidth;
-    info.height = pluginInfo.mHeight;
-
-    // It looks like we should be passing whatever
-    // internal format Flash told us it used previously
-    // (e.g., the value of pluginInfo.mInternalFormat),
-    // but if we do that it doesn't upload to the texture
-    // for some reason.
-    info.internalFormat = 0;
-
-    return info;
-}
-
-static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) {
-    nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-    TextureInfo pluginInfo(info->textureId, info->width, info->height, info->internalFormat);
-    pinst->ReleaseContentTexture(pluginInfo);
-    pinst->RedrawPlugin();
-}
-
-static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) {
-    // OpenGL is BottomLeft if uninverted.
-    gl::OriginPos newOriginPos = gl::OriginPos::BottomLeft;
-    if (isContentInverted)
-        newOriginPos = gl::OriginPos::TopLeft;
-
-    nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-    pinst->SetOriginPos(newOriginPos);
-    pinst->RedrawPlugin();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) {
-    ASSIGN(i, acquireContext);
-    ASSIGN(i, lockTexture);
-    ASSIGN(i, releaseTexture);
-    ASSIGN(i, invertPluginContent);
-}
--- a/dom/plugins/base/android/moz.build
+++ b/dom/plugins/base/android/moz.build
@@ -11,17 +11,16 @@ EXPORTS += [
 
 SOURCES += [
     'ANPAudio.cpp',
     'ANPBitmap.cpp',
     'ANPEvent.cpp',
     'ANPLog.cpp',
     'ANPMatrix.cpp',
     'ANPNativeWindow.cpp',
-    'ANPOpenGL.cpp',
     'ANPSurface.cpp',
     'ANPSystem.cpp',
     'ANPVideo.cpp',
     'ANPWindow.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2144,19 +2144,17 @@ NPError
       LOG("get native window interface v0");
       ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result;
       InitNativeWindowInterface(i);
       return NPERR_NO_ERROR;
     }
 
     case kOpenGLInterfaceV0_ANPGetValue: {
       LOG("get openGL interface");
-      ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result;
-      InitOpenGLInterface(i);
-      return NPERR_NO_ERROR;
+      return NPERR_GENERIC_ERROR;
     }
 
     case kWindowInterfaceV1_ANPGetValue: {
       LOG("get Window interface V1");
       ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result;
       InitWindowInterfaceV1(i);
       return NPERR_NO_ERROR;
     }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -93,77 +93,16 @@ static bool EnsureGLContext()
 {
   if (!sPluginContext) {
     sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE);
   }
 
   return sPluginContext != nullptr;
 }
 
-class SharedPluginTexture final {
-public:
-  NS_INLINE_DECL_REFCOUNTING(SharedPluginTexture)
-
-  SharedPluginTexture() : mLock("SharedPluginTexture.mLock")
-  {
-  }
-
-  nsNPAPIPluginInstance::TextureInfo Lock()
-  {
-    if (!EnsureGLContext()) {
-      mTextureInfo.mTexture = 0;
-      return mTextureInfo;
-    }
-
-    if (!mTextureInfo.mTexture && sPluginContext->MakeCurrent()) {
-      sPluginContext->fGenTextures(1, &mTextureInfo.mTexture);
-    }
-
-    mLock.Lock();
-    return mTextureInfo;
-  }
-
-  void Release(nsNPAPIPluginInstance::TextureInfo& aTextureInfo)
-  {
-    mTextureInfo = aTextureInfo;
-    mLock.Unlock();
-  }
-
-  EGLImage CreateEGLImage()
-  {
-    MutexAutoLock lock(mLock);
-
-    if (!EnsureGLContext())
-      return 0;
-
-    if (mTextureInfo.mWidth == 0 || mTextureInfo.mHeight == 0)
-      return 0;
-
-    GLuint& tex = mTextureInfo.mTexture;
-    EGLImage image = gl::CreateEGLImage(sPluginContext, tex);
-
-    // We want forget about this now, so delete the texture. Assigning it to zero
-    // ensures that we create a new one in Lock()
-    sPluginContext->fDeleteTextures(1, &tex);
-    tex = 0;
-
-    return image;
-  }
-
-private:
-  // Private destructor, to discourage deletion outside of Release():
-  ~SharedPluginTexture()
-  {
-  }
-
-  nsNPAPIPluginInstance::TextureInfo mTextureInfo;
-
-  Mutex mLock;
-};
-
 static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap;
 
 #endif
 
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 using namespace mozilla::layers;
 
@@ -254,17 +193,16 @@ nsNPAPIPluginInstance::Destroy()
   Stop();
   mPlugin = nullptr;
   mAudioChannelAgent = nullptr;
 
 #if MOZ_WIDGET_ANDROID
   if (mContentSurface)
     mContentSurface->SetFrameAvailableCallback(nullptr);
 
-  mContentTexture = nullptr;
   mContentSurface = nullptr;
 
   std::map<void*, VideoInfo*>::iterator it;
   for (it = mVideos.begin(); it != mVideos.end(); it++) {
     it->second->mSurfaceTexture->SetFrameAvailableCallback(nullptr);
     delete it->second;
   }
   mVideos.clear();
@@ -925,42 +863,24 @@ void nsNPAPIPluginInstance::SetWakeLock(
     return;
 
   mWakeLocked = aLocked;
   hal::ModifyWakeLock(NS_LITERAL_STRING("screen"),
                       mWakeLocked ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
                       hal::WAKE_LOCK_NO_CHANGE);
 }
 
-void nsNPAPIPluginInstance::EnsureSharedTexture()
-{
-  if (!mContentTexture)
-    mContentTexture = new SharedPluginTexture();
-}
-
 GLContext* nsNPAPIPluginInstance::GLContext()
 {
   if (!EnsureGLContext())
     return nullptr;
 
   return sPluginContext;
 }
 
-nsNPAPIPluginInstance::TextureInfo nsNPAPIPluginInstance::LockContentTexture()
-{
-  EnsureSharedTexture();
-  return mContentTexture->Lock();
-}
-
-void nsNPAPIPluginInstance::ReleaseContentTexture(nsNPAPIPluginInstance::TextureInfo& aTextureInfo)
-{
-  EnsureSharedTexture();
-  mContentTexture->Release(aTextureInfo);
-}
-
 already_AddRefed<AndroidSurfaceTexture> nsNPAPIPluginInstance::CreateSurfaceTexture()
 {
   if (!EnsureGLContext())
     return nullptr;
 
   GLuint texture = TexturePoolOGL::AcquireTexture();
   if (!texture)
     return nullptr;
@@ -989,25 +909,16 @@ void* nsNPAPIPluginInstance::AcquireCont
 
     if (!mContentSurface)
       return nullptr;
   }
 
   return mContentSurface->NativeWindow()->Handle();
 }
 
-EGLImage
-nsNPAPIPluginInstance::AsEGLImage()
-{
-  if (!mContentTexture)
-    return 0;
-
-  return mContentTexture->CreateEGLImage();
-}
-
 AndroidSurfaceTexture*
 nsNPAPIPluginInstance::AsSurfaceTexture()
 {
   if (!mContentSurface)
     return nullptr;
 
   return mContentSurface;
 }
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -21,17 +21,16 @@
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsAutoPtr.h"
 #include "nsIRunnable.h"
 #include "GLContextTypes.h"
 #include "AndroidSurfaceTexture.h"
 #include "AndroidBridge.h"
 #include <map>
 class PluginEventRunnable;
-class SharedPluginTexture;
 #endif
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/PluginLibrary.h"
 #include "mozilla/WeakPtr.h"
 
 class nsPluginStreamListenerPeer; // browser-initiated stream class
 class nsNPAPIPluginStreamListener; // plugin-initiated stream class
@@ -204,23 +203,19 @@ public:
     }
 
     GLuint mTexture;
     int32_t mWidth;
     int32_t mHeight;
     GLuint mInternalFormat;
   };
 
-  TextureInfo LockContentTexture();
-  void ReleaseContentTexture(TextureInfo& aTextureInfo);
-
   // For ANPNativeWindow
   void* AcquireContentWindow();
 
-  EGLImage AsEGLImage();
   mozilla::gl::AndroidSurfaceTexture* AsSurfaceTexture();
 
   // For ANPVideo
   class VideoInfo {
   public:
     VideoInfo(mozilla::gl::AndroidSurfaceTexture* aSurfaceTexture) :
       mSurfaceTexture(aSurfaceTexture)
     {
@@ -355,17 +350,16 @@ protected:
   void PopPostedEvent(PluginEventRunnable* r);
   void OnSurfaceTextureFrameAvailable();
 
   uint32_t mFullScreenOrientation;
   bool mWakeLocked;
   bool mFullScreen;
   mozilla::gl::OriginPos mOriginPos;
 
-  RefPtr<SharedPluginTexture> mContentTexture;
   RefPtr<mozilla::gl::AndroidSurfaceTexture> mContentSurface;
 #endif
 
   enum {
     NOT_STARTED,
     RUNNING,
     DESTROYING,
     DESTROYED
@@ -406,17 +400,16 @@ private:
   void* mCurrentPluginEvent;
 #endif
 
   // Timestamp for the last time this plugin was stopped.
   // This is only valid when the plugin is actually stopped!
   mozilla::TimeStamp mStopTime;
 
 #ifdef MOZ_WIDGET_ANDROID
-  void EnsureSharedTexture();
   already_AddRefed<mozilla::gl::AndroidSurfaceTexture> CreateSurfaceTexture();
 
   std::map<void*, VideoInfo*> mVideos;
   bool mOnScreen;
 
   nsIntSize mCurrentSize;
 #endif
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -165,37 +165,16 @@ nsPluginInstanceOwner::NotifyPaintWaiter
     // Run this event as soon as it's safe to do so, since listeners need to
     // receive it immediately
     mWaitingForPaint = nsContentUtils::AddScriptRunner(event);
   }
 }
 
 #if MOZ_WIDGET_ANDROID
 static void
-AttachToContainerAsEGLImage(ImageContainer* container,
-                            nsNPAPIPluginInstance* instance,
-                            const LayoutDeviceRect& rect,
-                            RefPtr<Image>* out_image)
-{
-  MOZ_ASSERT(out_image);
-  MOZ_ASSERT(!*out_image);
-
-  EGLImage image = instance->AsEGLImage();
-  if (!image) {
-    return;
-  }
-
-  RefPtr<EGLImageImage> img = new EGLImageImage(
-    image, nullptr,
-    gfx::IntSize(rect.width, rect.height), instance->OriginPos(),
-    false /* owns */);
-  *out_image = img;
-}
-
-static void
 AttachToContainerAsSurfaceTexture(ImageContainer* container,
                                   nsNPAPIPluginInstance* instance,
                                   const LayoutDeviceRect& rect,
                                   RefPtr<Image>* out_image)
 {
   MOZ_ASSERT(out_image);
   MOZ_ASSERT(!*out_image);
 
@@ -248,20 +227,17 @@ nsPluginInstanceOwner::GetImageContainer
   float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
   ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
   mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
 
   container = LayerManager::CreateImageContainer();
 
   // Try to get it as an EGLImage first.
   RefPtr<Image> img;
-  AttachToContainerAsEGLImage(container, mInstance, r, &img);
-  if (!img) {
-    AttachToContainerAsSurfaceTexture(container, mInstance, r, &img);
-  }
+  AttachToContainerAsSurfaceTexture(container, mInstance, r, &img);
 
   if (img) {
     container->SetCurrentImageInTransaction(img);
   }
 #else
   if (NeedsScrollImageLayer()) {
     // windowed plugin under e10s
 #if defined(XP_WIN)
@@ -1010,19 +986,19 @@ nsPluginInstanceOwner::RequestCommitOrCa
   if (!widget) {
     widget = GetRootWidgetForPluginFrame(mPluginFrame);
     if (NS_WARN_IF(!widget)) {
       return false;
     }
   }
 
   if (aCommitted) {
-    widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION); 
+    widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION);
   } else {
-    widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION); 
+    widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION);
   }
   return true;
 }
 #endif // #ifdef XP_WIN
 
 NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
 {
 #ifdef XP_MACOSX
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1266,17 +1266,17 @@ PluginInstanceParent::UpdateScrollCaptur
         CAPTURE_LOG("no windowdc");
         aRequestNewCapture = true;
         return false;
     }
 
     RECT bounds = {0};
     ::GetWindowRect(mChildPluginHWND, &bounds);
     if ((bounds.left == bounds.right && bounds.top == bounds.bottom) ||
-        (!mWindowSize.width && !mWindowSize.height)) {
+        mWindowSize.IsEmpty()) {
         CAPTURE_LOG("empty bounds");
         // Lots of null window plugins in content, don't capture.
         return false;
     }
 
     // If we need to init mScrollCapture do so, also reset it if the size of the
     // plugin window changes.
     if (!mScrollCapture || mScrollCapture->GetSize() != mWindowSize) {
@@ -1292,17 +1292,17 @@ PluginInstanceParent::UpdateScrollCaptur
     int rgnType = ::GetWindowRgnBox(mPluginHWND, &clip);
     bool clipCorrect = !clip.left && !clip.top &&
                        clip.right == mWindowSize.width &&
                        clip.bottom == mWindowSize.height;
 
     bool isVisible = ::IsWindowVisible(mChildPluginHWND);
 
     CAPTURE_LOG("validcap=%d visible=%d region=%d clip=%d:%dx%dx%dx%d",
-                mValidFirstCapture, isVisible, rgnType, clipCorrect
+                mValidFirstCapture, isVisible, rgnType, clipCorrect,
                 clip.left, clip.top, clip.right, clip.bottom);
 
     // We have a good capture and can't update so keep using the existing
     // capture image. Otherwise fall through so we paint the fill color to
     // the layer.
     if (mValidFirstCapture && (!isVisible || !clipCorrect)) {
         return true;
     }
@@ -1321,16 +1321,21 @@ PluginInstanceParent::UpdateScrollCaptur
             CAPTURE_LOG("blt failure??");
             return false;
         }
         ::GdiFlush();
         mValidFirstCapture = true;
     }
 
     IntSize targetSize = mScrollCapture->GetSize();
+
+    if (targetSize.IsEmpty()) {
+        return false;
+    }
+
     RefPtr<gfx::DrawTarget> dt =
         gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mScrollCapture,
                                                                targetSize);
 
     if (nativeScrollCapture) {
         // Copy the native capture image over to a remotable gfx surface.
         RefPtr<gfx::SourceSurface> sourceSurface =
             gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
--- a/dom/presentation/provider/TCPPresentationServer.js
+++ b/dom/presentation/provider/TCPPresentationServer.js
@@ -575,25 +575,25 @@ TCPControlChannel.prototype = {
       DEBUG && log("TCPControlChannel - notify pending opened");
       this._listener.notifyOpened();
     }
 
     if (this._pendingOffer) {
       let offer = this._pendingOffer;
       DEBUG && log("TCPControlChannel - notify pending offer: "
                    + JSON.stringify(offer));
-      this._listener._onOffer(new ChannelDescription(offer));
+      this._listener.onOffer(new ChannelDescription(offer));
       this._pendingOffer = null;
     }
 
     if (this._pendingAnswer) {
       let answer = this._pendingAnswer;
       DEBUG && log("TCPControlChannel - notify pending answer: "
                    + JSON.stringify(answer));
-      this._listener._onAnswer(new ChannelDescription(answer));
+      this._listener.onAnswer(new ChannelDescription(answer));
       this._pendingAnswer = null;
     }
 
     if (this._pendingClose) {
       DEBUG && log("TCPControlChannel - notify pending closed");
       this._notifyClosed(this._pendingCloseReason);
       this._pendingClose = null;
     }
--- a/dom/requestsync/RequestSyncService.jsm
+++ b/dom/requestsync/RequestSyncService.jsm
@@ -82,16 +82,20 @@ this.RequestSyncService = {
   // This array contains functions to be executed after the scheduling of the
   // current task or immediately if there are not scheduling in progress.
   _afterSchedulingTasks: [],
 
   // Initialization of the RequestSyncService.
   init: function() {
     debug("init");
 
+    if (!Services.prefs.getBoolPref("dom.requestSync.enabled")) {
+      return;
+    }
+
     this._messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.addObserver(this, 'xpcom-shutdown', false);
     Services.obs.addObserver(this, 'clear-origin-data', false);
     Services.obs.addObserver(this, 'wifi-state-changed', false);
 
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -378,18 +378,19 @@ WifiGeoPositionProvider.prototype = {
     // we got some wifi data, rearm the timer.
     this.resetTimer();
 
     function isPublic(ap) {
       let mask = "_nomap"
       let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length);
       if (result != -1) {
         LOG("Filtering out " + ap.ssid + " " + result);
+        return false;
       }
-      return result;
+      return true;
     };
 
     function sort(a, b) {
       return b.signal - a.signal;
     };
 
     function encode(ap) {
       return { 'macAddress': ap.mac, 'signalStrength': ap.signal };
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -67,41 +67,35 @@ skip-if = buildapp == 'mulet' || (builda
 [test_bug38959.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug393974.html]
 [test_bug394769.html]
 [test_bug396843.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_bug400204.html]
 [test_bug404748.html]
-[test_bug406375.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_bug411103.html]
 [test_bug414291.html]
 [test_bug427744.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug42976.html]
 [test_bug430276.html]
-[test_bug437361.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-debug(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-desktop(dom.disable_open_during_load not implemented in b2g, showmodaldialog)
 [test_bug440572.html]
 [test_bug456151.html]
 [test_bug458091.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug459848.html]
 [test_bug465263.html]
 [test_bug479143.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_bug484775.html]
 [test_bug492925.html]
 [test_bug49312.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug495219.html]
-[test_bug504862.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #RANDOM # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_bug529328.html]
 [test_bug531176.html]
 [test_bug531542.html]
 [test_bug534149.html]
 [test_bug541530.html]
 [test_bug545314.html]
 [test_bug548828.html]
 [test_bug558973.html]
deleted file mode 100644
--- a/dom/tests/mochitest/bugs/test_bug406375.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=406375
--->
-<head>
-  <title>Test for Bug 406375</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body onload="runTest()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=406375">Mozilla Bug 406375</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-  
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test for Bug 406375 **/
-
-
-SimpleTest.waitForExplicitFinish();
-
-function runTest() {
-  window.showModalDialog("file_bug406375.html");
-  ok(true, "This test should not hang");
-
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/tests/mochitest/bugs/test_bug437361.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=437361
--->
-<head>
-  <title>Test for Bug 437361</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  
-  <script class="testbody" type="text/javascript">
-
-  /** Test for Bug 437361 **/
-
-  function testModalDialogBlockedCleanly() {
-    is(true, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
-    var rv = window.showModalDialog( // should be blocked without exception
-      "data:text/html,<html><body onload='close(); returnValue = 1;' /></html>");
-    is(rv, null, "Modal dialog opened unexpectedly.");
-  }
-  
-  function testModalDialogAllowed() {
-    is(false, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
-    var rv = window.showModalDialog( // should not be blocked this time
-      "data:text/html,<html><body onload='close(); returnValue = 1;' /></html>");
-    is(rv, 1, "Problem with modal dialog returnValue.");
-  }
-
-  function testOtherExceptionsNotTrapped() {
-    is(false, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
-    window.showModalDialog('about:config'); // forbidden by SecurityCheckURL
-  }
-
-  function test(disableOpen, exceptionExpected, testFn, errorMsg) {
-    var oldPrefVal = SpecialPowers.getBoolPref("dom.disable_open_during_load");
-    try {
-      SpecialPowers.setBoolPref("dom.disable_open_during_load", disableOpen);
-      testFn();
-      ok(!exceptionExpected, errorMsg);
-    } catch (_) {
-      ok(exceptionExpected, errorMsg);
-    }
-    finally {
-      SpecialPowers.setBoolPref("dom.disable_open_during_load", oldPrefVal);
-    }
-  }
-
-  test(true, false, testModalDialogBlockedCleanly,
-       "Blocked showModalDialog caused an exception.");
-       
-  test(false, false, testModalDialogAllowed,
-       "showModalDialog was blocked even though dom.disable_open_during_load was false.");
-
-  test(false, true, testOtherExceptionsNotTrapped,
-       "Incorrectly suppressed insecure showModalDialog exception.");
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437361">Mozilla Bug 437361</a>
-<p id="display"></p>
-<div id="content" style="display: none"> 
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/tests/mochitest/bugs/test_bug504862.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=504862
--->
-<head>
-  <title>Test for Bug 504862</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTest()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=504862">Mozilla Bug 504862</a>
-<script class="testbody" type="text/javascript">
-
-/** Test for Bug 504862 **/
-SimpleTest.waitForExplicitFinish();
-function onMsgRcv(event)
-{
-  is(event.data, "args: undefined", "Unexpected cross origin dialog arguments.");
-}
-
-function runTest() {
-  window.addEventListener("message", onMsgRcv, false);
-
-  var result = window.showModalDialog("file_bug504862.html", "my args");
-  // NB: We used to clear returnValue on each navigation, but now we do a
-  // security check on access, so we can safely make returnValue live on
-  // the browsing context, per spec.
-  is(result, 3, "window sees previous dialog documents return value.");
-
-  result = window.showModalDialog("http://test1.example.com/tests/dom/tests/mochitest/bugs/file_bug504862.html", "my args");
-
-  is(result, undefined, "Able to see return value from cross origin dialog.");
-
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -15,16 +15,17 @@ typedef unsigned long nsContentPolicyTyp
 interface Request {
   readonly attribute ByteString method;
   readonly attribute USVString url;
   [SameObject] readonly attribute Headers headers;
 
   [Func="mozilla::dom::Request::RequestContextEnabled"]
   readonly attribute RequestContext context;
   readonly attribute USVString referrer;
+  readonly attribute ReferrerPolicy referrerPolicy;
   readonly attribute RequestMode mode;
   readonly attribute RequestCredentials credentials;
   [Func="mozilla::dom::Request::RequestCacheEnabled"]
   readonly attribute RequestCache cache;
   readonly attribute RequestRedirect redirect;
 
   [Throws,
    NewObject] Request clone();
@@ -35,16 +36,17 @@ interface Request {
 };
 Request implements Body;
 
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
   BodyInit? body;
   USVString referrer;
+  ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   RequestCache cache;
   RequestRedirect redirect;
 };
 
 // Gecko currently does not ship RequestContext, so please don't use it in IDL
 // that is exposed to script.
@@ -55,8 +57,9 @@ enum RequestContext {
   "sharedworker", "subresource", "style", "track", "video", "worker", "xmlhttprequest",
   "xslt"
 };
 
 enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
 enum RequestCredentials { "omit", "same-origin", "include" };
 enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache" };
 enum RequestRedirect { "follow", "error", "manual" };
+enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin-only", "origin-when-cross-origin", "unsafe-url" };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -296,16 +296,17 @@ LoadRuntimeOptions(const char* aPrefName
       prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
     return;
   }
 #endif
 
   // Runtime options.
   JS::RuntimeOptions runtimeOptions;
   runtimeOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
+                .setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
                 .setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
                       NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
                 .setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
                 .setIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion")))
                 .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
                 .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
                 .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
                 .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -188,17 +188,17 @@ ChannelFromScriptURL(nsIPrincipal* princ
                        aLoadFlags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
-                                                       httpChannel);
+                                                       httpChannel, mozilla::net::RP_Default);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   channel.forget(aChannel);
   return rv;
 }
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -22,17 +22,17 @@ class ServiceWorkerInfo;
 class ServiceWorkerManager;
 class SharedWorker;
 
 bool
 ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
 class ServiceWorker final : public DOMEventTargetHelper
 {
-  friend class ServiceWorkerManager;
+  friend class ServiceWorkerInfo;
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   IMPL_EVENT_HANDLER(statechange)
   IMPL_EVENT_HANDLER(error)
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -63,17 +63,17 @@ public:
 #endif
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
               ErrorResult& aRv);
 
 private:
-  // This class can only be created from the ServiceWorkerManager.
+  // This class can only be created from ServiceWorkerInfo::GetOrCreateInstance().
   ServiceWorker(nsPIDOMWindowInner* aWindow, ServiceWorkerInfo* aInfo);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();
 
   ServiceWorkerState mState;
   const RefPtr<ServiceWorkerInfo> mInfo;
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3561,17 +3561,17 @@ ServiceWorkerManager::GetServiceWorkerFo
   } else {
     MOZ_CRASH("Invalid worker type");
   }
 
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
-  RefPtr<ServiceWorker> serviceWorker = new ServiceWorker(aWindow, info);
+  RefPtr<ServiceWorker> serviceWorker = info->GetOrCreateInstance(aWindow);
 
   serviceWorker->SetState(info->State());
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
 namespace {
 
@@ -3788,17 +3788,17 @@ ServiceWorkerManager::GetDocumentControl
   RefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(registration->mActiveWorker);
   RefPtr<ServiceWorker> serviceWorker =
-    new ServiceWorker(aWindow, registration->mActiveWorker);
+    registration->mActiveWorker->GetOrCreateInstance(aWindow);
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow,
                                     const nsAString& aScope,
@@ -5245,9 +5245,32 @@ ServiceWorkerInfo::~ServiceWorkerInfo()
 static uint64_t gServiceWorkerInfoCurrentID = 0;
 
 uint64_t
 ServiceWorkerInfo::GetNextID() const
 {
   return ++gServiceWorkerInfoCurrentID;
 }
 
+already_AddRefed<ServiceWorker>
+ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aWindow);
+
+  RefPtr<ServiceWorker> ref;
+
+  for (uint32_t i = 0; i < mInstances.Length(); ++i) {
+    MOZ_ASSERT(mInstances[i]);
+    if (mInstances[i]->GetOwner() == aWindow) {
+      ref = mInstances[i];
+      break;
+    }
+  }
+
+  if (!ref) {
+    ref = new ServiceWorker(aWindow, this);
+  }
+
+  return ref.forget();
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -300,16 +300,19 @@ public:
     mState = aState;
   }
 
   void
   AppendWorker(ServiceWorker* aWorker);
 
   void
   RemoveWorker(ServiceWorker* aWorker);
+
+  already_AddRefed<ServiceWorker>
+  GetOrCreateInstance(nsPIDOMWindowInner* aWindow);
 };
 
 #define NS_SERVICEWORKERMANAGER_IMPL_IID                 \
 { /* f4f8755a-69ca-46e8-a65d-775745535990 */             \
   0xf4f8755a,                                            \
   0x69ca,                                                \
   0x46e8,                                                \
   { 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 }     \
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -985,16 +985,17 @@ class FetchEventRunnable : public Extend
   nsString mClientId;
   bool mIsReload;
   RequestMode mRequestMode;
   RequestRedirect mRequestRedirect;
   RequestCredentials mRequestCredentials;
   nsContentPolicyType mContentPolicyType;
   nsCOMPtr<nsIInputStream> mUploadStream;
   nsCString mReferrer;
+  ReferrerPolicy mReferrerPolicy;
 public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      KeepAliveToken* aKeepAliveToken,
                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      // CSP checks might require the worker script spec
                      // later on.
                      const nsACString& aScriptSpec,
                      nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
@@ -1008,16 +1009,17 @@ public:
     , mIsReload(aIsReload)
     , mRequestMode(RequestMode::No_cors)
     , mRequestRedirect(RequestRedirect::Follow)
     // By default we set it to same-origin since normal HTTP fetches always
     // send credentials to same-origin websites unless explicitly forbidden.
     , mRequestCredentials(RequestCredentials::Same_origin)
     , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
     , mReferrer(kFETCH_CLIENT_REFERRER_STR)
+    , mReferrerPolicy(ReferrerPolicy::_empty)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD
   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
@@ -1055,26 +1057,49 @@ public:
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsILoadInfo> loadInfo;
     rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_SUCCESS(rv, rv);
 
     mContentPolicyType = loadInfo->InternalContentPolicyType();
 
-    nsCOMPtr<nsIURI> referrerURI;
-    rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (referrerURI) {
-      rv = referrerURI->GetSpec(mReferrer);
-      NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+    MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
+
+    nsAutoCString referrer;
+    // Ignore the return value since the Referer header may not exist.
+    httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
+    if (!referrer.IsEmpty()) {
+      mReferrer = referrer;
     }
 
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-    MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
+    uint32_t referrerPolicy = 0;
+    rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+    NS_ENSURE_SUCCESS(rv, rv);
+    switch (referrerPolicy) {
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
+      mReferrerPolicy = ReferrerPolicy::No_referrer;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Origin_only;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
+      mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
+      mReferrerPolicy = ReferrerPolicy::Unsafe_url;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
+      break;
+    }
 
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
     NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
 
     mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
@@ -1174,16 +1199,17 @@ private:
 
     RefPtr<InternalRequest> internalReq = new InternalRequest(mSpec,
                                                               mMethod,
                                                               internalHeaders.forget(),
                                                               mRequestMode,
                                                               mRequestRedirect,
                                                               mRequestCredentials,
                                                               NS_ConvertUTF8toUTF16(mReferrer),
+                                                              mReferrerPolicy,
                                                               mContentPolicyType);
     internalReq->SetBody(mUploadStream);
     // For Telemetry, note that this Request object was created by a Fetch event.
     internalReq->SetCreatedByFetchEvent();
 
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(globalObj.GetAsSupports());
     if (NS_WARN_IF(!global)) {
       return false;
--- a/dom/workers/test/serviceworkers/test_claim_oninstall.html
+++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html
@@ -32,29 +32,29 @@
   function testClaim() {
     ok(registration.installing, "Worker should be in installing state");
 
     navigator.serviceWorker.oncontrollerchange = function() {
       ok(false, "Claim should not succeed when the worker is not active.");
     }
 
     var p = new Promise(function(res, rej) {
-      registration.installing.onstatechange = function(e) {
-        ok(registration.waiting, "Worker should be in waitinging state");
-
-        // The worker will become active only if claim will reject inside the
-        // install handler.
-        registration.waiting.onstatechange = function(e) {
-          ok(registration.active, "Claim should reject if the worker is not active");
+      var worker = registration.installing;
+      worker.onstatechange = function(e) {
+        if (worker.state === 'installed') {
+          is(worker, registration.waiting, "Worker should be in waiting state");
+        } else if (worker.state === 'activated') {
+          // The worker will become active only if claim will reject inside the
+          // install handler.
+          is(worker, registration.active,
+             "Claim should reject if the worker is not active");
           ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
           e.target.onstatechange = null;
           res();
         }
-
-        e.target.onstatechange = null;
       }
     });
 
     return p;
   }
 
   function runTest() {
     register()
deleted file mode 100644
--- a/gfx/cairo/cairo/src/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-INSTALL_TARGETS += cairo_features
-cairo_features_FILES := cairo-features.h
-cairo_features_DEST = $(DIST)/include/cairo
-cairo_features_TARGET := export
-
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 CONFIGURE_SUBST_FILES += ['cairo-features.h']
 
 EXPORTS.cairo += [
+    '!cairo-features.h',
     'cairo-deprecated.h',
     'cairo-platform.h',
     'cairo-rename.h',
     'cairo-tee.h',
     'cairo-version.h',
     'cairo.h',
     'pixman-rename.h',
 ]
--- a/gfx/gl/AndroidSurfaceTexture.cpp
+++ b/gfx/gl/AndroidSurfaceTexture.cpp
@@ -124,23 +124,25 @@ AndroidSurfaceTexture::Attach(GLContext*
     // Wait until it's detached (or we time out)
     if (NS_FAILED(lock.Wait(aTimeout))) {
       return NS_ERROR_NOT_AVAILABLE;
     }
   }
 
   MOZ_ASSERT(aContext->IsOwningThreadCurrent(), "Trying to attach GLContext from different thread");
 
-  mAttachedContext = aContext;
-  mAttachedContext->MakeCurrent();
   aContext->fGenTextures(1, &mTexture);
 
-  UpdateCanDetach();
+  if (NS_FAILED(mSurfaceTexture->AttachToGLContext(mTexture))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  mAttachedContext = aContext;
+  mAttachedContext->MakeCurrent();
 
-  return mSurfaceTexture->AttachToGLContext(mTexture);
+  return NS_OK;
 }
 
 nsresult
 AndroidSurfaceTexture::Detach()
 {
   MonitorAutoLock lock(mMonitor);
 
   if (!CanDetach() ||
@@ -155,34 +157,31 @@ AndroidSurfaceTexture::Detach()
   mSurfaceTexture->DetachFromGLContext();
 
   mTexture = 0;
   mAttachedContext = nullptr;
   lock.NotifyAll();
   return NS_OK;
 }
 
-void
-AndroidSurfaceTexture::UpdateCanDetach()
+bool
+AndroidSurfaceTexture::CanDetach() const
 {
   // The API for attach/detach only exists on 16+, and PowerVR has some sort of
-  // fencing issue. Additionally, attach/detach seems to be busted on at least some
-  // Mali adapters (400MP2 for sure, bug 1131793)
-  bool canDetach = gfxPrefs::SurfaceTextureDetachEnabled();
-
-  mCanDetach = AndroidBridge::Bridge()->GetAPIVersion() >= 16 &&
+  // fencing issue. Additionally, attach/detach seems to be busted on at least
+  // some Mali adapters (400MP2 for sure, bug 1131793)
+  return AndroidBridge::Bridge()->GetAPIVersion() >= 16 &&
     (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::Imagination) &&
     (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::ARM /* Mali */) &&
-    canDetach;
+    gfxPrefs::SurfaceTextureDetachEnabled();
 }
 
 bool
 AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture)
 {
-  UpdateCanDetach();
 
   if (!aTexture && !CanDetach()) {
     // We have no texture and cannot initialize detached, bail out
     return false;
   }
 
   if (NS_WARN_IF(NS_FAILED(
       SurfaceTexture::New(aTexture, ReturnTo(&mSurfaceTexture))))) {
@@ -209,17 +208,16 @@ AndroidSurfaceTexture::Init(GLContext* a
   return true;
 }
 
 AndroidSurfaceTexture::AndroidSurfaceTexture()
   : mTexture(0)
   , mSurfaceTexture()
   , mSurface()
   , mAttachedContext(nullptr)
-  , mCanDetach(false)
   , mMonitor("AndroidSurfaceTexture::mContextMonitor")
 {
 }
 
 AndroidSurfaceTexture::~AndroidSurfaceTexture()
 {
   sInstances.Remove(mID);
 
--- a/gfx/gl/AndroidSurfaceTexture.h
+++ b/gfx/gl/AndroidSurfaceTexture.h
@@ -51,19 +51,19 @@ public:
   // attach while you are consuming in order to allow this.
   //
   // Only one GLContext may be attached at any given time. If another is already
   // attached, we try to wait for it to become detached.
   nsresult Attach(GLContext* aContext, PRIntervalTime aTiemout = PR_INTERVAL_NO_TIMEOUT);
 
   nsresult Detach();
 
-  // Ability to detach is based on API version (16+), and we also block PowerVR since it has some type
-  // of fencing problem. Bug 1100126.
-  bool CanDetach() const { return mCanDetach; }
+  // Ability to detach is based on API version (16+), and we also block PowerVR
+  // since it has some type of fencing problem. Bug 1100126.
+  bool CanDetach() const;
 
   GLContext* AttachedContext() const { return mAttachedContext; }
 
   AndroidNativeWindow* NativeWindow() const {
     return mNativeWindow;
   }
 
   // This attaches the updated data to the TEXTURE_EXTERNAL target
@@ -85,24 +85,22 @@ public:
   GLuint Texture() const { return mTexture; }
   const widget::sdk::Surface::Ref& JavaSurface() const { return mSurface; }
 
 private:
   AndroidSurfaceTexture();
   ~AndroidSurfaceTexture();
 
   bool Init(GLContext* aContext, GLuint aTexture);
-  void UpdateCanDetach();
 
   GLuint mTexture;
   widget::sdk::SurfaceTexture::GlobalRef mSurfaceTexture;
   widget::sdk::Surface::GlobalRef mSurface;
 
   GLContext* mAttachedContext;
-  bool mCanDetach;
 
   RefPtr<AndroidNativeWindow> mNativeWindow;
   int mID;
   nsCOMPtr<nsIRunnable> mFrameAvailableCallback;
 
   mutable Monitor mMonitor;
 };
 
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GLCONTEXTGLX_H_
 #define GLCONTEXTGLX_H_
 
 #include "GLContext.h"
 #include "GLXLibrary.h"
+#include "mozilla/X11Util.h"
 
 namespace mozilla {
 namespace gl {
 
 class GLContextGLX : public GLContext
 {
 public:
     MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextGLX, override)
@@ -23,16 +24,22 @@ public:
                     bool isOffscreen,
                     Display* display,
                     GLXDrawable drawable,
                     GLXFBConfig cfg,
                     bool deleteDrawable,
                     gfxXlibSurface* pixmap = nullptr,
                     ContextProfile profile = ContextProfile::OpenGLCompatibility);
 
+    // Finds a GLXFBConfig compatible with the provided window.
+    static bool
+    FindFBConfigForWindow(Display* display, int screen, Window window,
+                          ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
+                          GLXFBConfig* const out_config, int* const out_visid);
+
     ~GLContextGLX();
 
     virtual GLContextType GetContextType() const override { return GLContextType::GLX; }
 
     static GLContextGLX* Cast(GLContext* gl) {
         MOZ_ASSERT(gl->GetContextType() == GLContextType::GLX);
         return static_cast<GLContextGLX*>(gl);
     }
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -11,17 +11,16 @@
 #define GET_NATIVE_WINDOW(aWidget) (Window)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW))
 #endif
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/X11Util.h"
 
 #include "prenv.h"
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "GLXLibrary.h"
 #include "gfxXlibSurface.h"
@@ -861,16 +860,17 @@ GLContextGLX::~GLContextGLX()
 
     mGLX->xDestroyContext(mDisplay, mContext);
 
     if (mDeleteDrawable) {
         mGLX->xDestroyPixmap(mDisplay, mDrawable);
     }
 }
 
+
 bool
 GLContextGLX::Init()
 {
     SetupLookupFunction();
     if (!InitWithPrefix("gl", true)) {
         return false;
     }
 
@@ -1066,93 +1066,35 @@ GLContextProviderGLX::CreateForWindow(ns
     if (!display) {
         NS_ERROR("X Display required for GLX Context provider");
         return nullptr;
     }
 
     int xscreen = DefaultScreen(display);
     Window window = GET_NATIVE_WINDOW(aWidget);
 
-    int numConfigs;
     ScopedXFree<GLXFBConfig> cfgs;
-    if (sGLXLibrary.IsATI() ||
-        !sGLXLibrary.GLXVersionCheck(1, 3)) {
-        const int attribs[] = {
-            LOCAL_GLX_DOUBLEBUFFER, False,
-            0
-        };
-        cfgs = sGLXLibrary.xChooseFBConfig(display,
-                                           xscreen,
-                                           attribs,
-                                           &numConfigs);
-    } else {
-        cfgs = sGLXLibrary.xGetFBConfigs(display,
-                                         xscreen,
-                                         &numConfigs);
-    }
-
-    if (!cfgs) {
-        NS_WARNING("[GLX] glXGetFBConfigs() failed");
-        return nullptr;
-    }
-    NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
-
-    // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
-    // we could probably do this first and replace the glXGetFBConfigs
-    // with glXChooseConfigs.  Docs are sparklingly clear as always.
-    XWindowAttributes widgetAttrs;
-    if (!XGetWindowAttributes(display, window, &widgetAttrs)) {
-        NS_WARNING("[GLX] XGetWindowAttributes() failed");
-        return nullptr;
-    }
-    const VisualID widgetVisualID = XVisualIDFromVisual(widgetAttrs.visual);
-#ifdef DEBUG
-    printf("[GLX] widget has VisualID 0x%lx\n", widgetVisualID);
-#endif
-
-    int matchIndex = -1;
-
-    for (int i = 0; i < numConfigs; i++) {
-        int visid = None;
-        sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
-        if (!visid) {
-            continue;
-        }
-        if (sGLXLibrary.IsATI()) {
-            int depth;
-            Visual *visual;
-            FindVisualAndDepth(display, visid, &visual, &depth);
-            if (depth == widgetAttrs.depth &&
-                AreCompatibleVisuals(widgetAttrs.visual, visual)) {
-                matchIndex = i;
-                break;
-            }
-        } else {
-            if (widgetVisualID == static_cast<VisualID>(visid)) {
-                matchIndex = i;
-                break;
-            }
-        }
-    }
-
-    if (matchIndex == -1) {
-        NS_WARNING("[GLX] Couldn't find a FBConfig matching widget visual");
+    GLXFBConfig config;
+    int visid;
+    if (!GLContextGLX::FindFBConfigForWindow(display, xscreen, window, &cfgs,
+                                             &config, &visid))
+    {
         return nullptr;
     }
 
     GLContextGLX *shareContext = GetGlobalContextGLX();
 
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextGLX> glContext = GLContextGLX::CreateGLContext(caps,
-                                                                     shareContext,
-                                                                     false,
-                                                                     display,
-                                                                     window,
-                                                                     cfgs[matchIndex],
-                                                                     false);
+                                                                   shareContext,
+                                                                   false,
+                                                                   display,
+                                                                   window,
+                                                                   config,
+                                                                   false);
 
     return glContext.forget();
 }
 
 static bool
 ChooseConfig(GLXLibrary* glx, Display* display, int screen, const SurfaceCaps& minCaps,
              ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
              GLXFBConfig* const out_config, int* const out_visid)
@@ -1204,16 +1146,87 @@ ChooseConfig(GLXLibrary* glx, Display* d
         *out_config = curConfig;
         *out_visid = visid;
         return true;
     }
 
     return false;
 }
 
+bool
+GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window,
+                                    ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
+                                    GLXFBConfig* const out_config, int* const out_visid)
+{
+    ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
+    int numConfigs;
+    if (sGLXLibrary.IsATI() ||
+        !sGLXLibrary.GLXVersionCheck(1, 3)) {
+        const int attribs[] = {
+            LOCAL_GLX_DOUBLEBUFFER, False,
+            0
+        };
+        cfgs = sGLXLibrary.xChooseFBConfig(display,
+                                           screen,
+                                           attribs,
+                                           &numConfigs);
+    } else {
+        cfgs = sGLXLibrary.xGetFBConfigs(display,
+                                         screen,
+                                         &numConfigs);
+    }
+
+    if (!cfgs) {
+        NS_WARNING("[GLX] glXGetFBConfigs() failed");
+        return false;
+    }
+    NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
+
+    // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
+    // we could probably do this first and replace the glXGetFBConfigs
+    // with glXChooseConfigs.  Docs are sparklingly clear as always.
+    XWindowAttributes windowAttrs;
+    if (!XGetWindowAttributes(display, window, &windowAttrs)) {
+        NS_WARNING("[GLX] XGetWindowAttributes() failed");
+        return false;
+    }
+    const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
+#ifdef DEBUG
+    printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
+#endif
+
+    for (int i = 0; i < numConfigs; i++) {
+        int visid = None;
+        sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+        if (!visid) {
+            continue;
+        }
+        if (sGLXLibrary.IsATI()) {
+            int depth;
+            Visual *visual;
+            FindVisualAndDepth(display, visid, &visual, &depth);
+            if (depth == windowAttrs.depth &&
+                AreCompatibleVisuals(windowAttrs.visual, visual)) {
+                *out_config = cfgs[i];
+                *out_visid = visid;
+                return true;
+            }
+        } else {
+            if (windowVisualID == static_cast<VisualID>(visid)) {
+                *out_config = cfgs[i];
+                *out_visid = visid;
+                return true;
+            }
+        }
+    }
+
+    NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
+    return false;
+}
+
 static already_AddRefed<GLContextGLX>
 CreateOffscreenPixmapContext(const IntSize& size, const SurfaceCaps& minCaps, ContextProfile profile = ContextProfile::OpenGLCompatibility)
 {
     GLXLibrary* glx = &sGLXLibrary;
     if (!glx->EnsureInitialized())
         return nullptr;
 
     Display* display = DefaultXDisplay();
deleted file mode 100644
--- a/gfx/thebes/Makefile.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/rules.mk
-
-DeprecatedPremultiplyTables.h: $(srcdir)/genTables.py
-	$(PYTHON) $(srcdir)/genTables.py
--- a/gfx/thebes/genTables.py
+++ b/gfx/thebes/genTables.py
@@ -1,12 +1,22 @@
 #!/usr/bin/python
 
+from __future__ import print_function
+import sys
+
 def table_generator(f):
     return ",\n".join([", ".join(["0x%2.2x" % h for h in [f(i) for i in range(r,r+16)]]) for r in range(0, 65536, 16)])
 
-with open("DeprecatedPremultiplyTables.h", "w") as f:
-  f.write("const uint8_t gfxUtils::sPremultiplyTable[256*256] = {\n");
-  f.write(table_generator(lambda i: ((i / 256) * (i % 256) + 254) / 255) + "\n")
-  f.write("};\n");
-  f.write("const uint8_t gfxUtils::sUnpremultiplyTable[256*256] = {\n");
-  f.write(table_generator(lambda i: (i % 256) * 255 / ((i / 256) if (i / 256) > 0 else 255) % 256) + "\n")
-  f.write("};\n");
+def generate(output):
+    output.write("const uint8_t gfxUtils::sPremultiplyTable[256*256] = {\n");
+    output.write(table_generator(lambda i: ((i / 256) * (i % 256) + 254) / 255) + "\n")
+    output.write("};\n");
+    output.write("const uint8_t gfxUtils::sUnpremultiplyTable[256*256] = {\n");
+    output.write(table_generator(lambda i: (i % 256) * 255 / ((i / 256) if (i / 256) > 0 else 255) % 256) + "\n")
+    output.write("};\n");
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print("Usage: genTables.py <header.h>", file=sys.stderr)
+        sys.exit(1)
+    with open(sys.argv[1], 'w') as f:
+        generate(f)
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -266,16 +266,17 @@ if CONFIG['GNU_CXX']:
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 GENERATED_FILES = [
     'DeprecatedPremultiplyTables.h',
 ]
+GENERATED_FILES['DeprecatedPremultiplyTables.h'].script = 'genTables.py:generate'
 
 LOCAL_INCLUDES += [
     '/dom/workers',
     '/dom/xml',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
   LOCAL_INCLUDES += ['/widget/gonk']
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -76,36 +76,22 @@ FrameAnimator::GetCurrentImgFrameEndTime
 
 FrameAnimator::RefreshResult
 FrameAnimator::AdvanceFrame(TimeStamp aTime)
 {
   NS_ASSERTION(aTime <= TimeStamp::Now(),
                "Given time appears to be in the future");
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
 
+  RefreshResult ret;
+
+  // Determine what the next frame is, taking into account looping.
   uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
   uint32_t nextFrameIndex = currentFrameIndex + 1;
 
-  RefreshResult ret;
-  RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
-
-  // If we're done decoding, we know we've got everything we're going to get.
-  // If we aren't, we only display fully-downloaded frames; everything else
-  // gets delayed.
-  bool canDisplay = mDoneDecoding ||
-                    (nextFrame && nextFrame->IsImageComplete());
-
-  if (!canDisplay) {
-    // Uh oh, the frame we want to show is currently being decoded (partial)
-    // Wait until the next refresh driver tick and try again
-    return ret;
-  }
-
-  // If we're done decoding the next frame, go ahead and display it now and
-  // reinit with the next frame's delay time.
   if (mImage->GetNumFrames() == nextFrameIndex) {
     // We can only accurately determine if we are at the end of the loop if we are
     // done decoding, otherwise we don't know how many frames there will be.
     if (!mDoneDecoding) {
       // We've already advanced to the last decoded frame, nothing more we can do.
       // We're blocked by network/decoding from displaying the animation at the
       // rate specified, so that means the frame we are displaying (the latest
       // available) is the frame we want to be displaying at this time. So we
@@ -140,16 +126,36 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
     }
 
     // If we're done, exit early.
     if (ret.animationFinished) {
       return ret;
     }
   }
 
+  // There can be frames in the surface cache with index >= mImage->GetNumFrames()
+  // that GetRawFrame can access because the decoding thread has decoded them, but
+  // RasterImage hasn't acknowledged those frames yet. We don't want to go past
+  // what RasterImage knows about so that we stay in sync with RasterImage. The code
+  // above should obey this, the MOZ_ASSERT records this invariant.
+  MOZ_ASSERT(nextFrameIndex < mImage->GetNumFrames());
+  RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
+
+  // If we're done decoding, we know we've got everything we're going to get.
+  // If we aren't, we only display fully-downloaded frames; everything else
+  // gets delayed.
+  bool canDisplay = mDoneDecoding ||
+                    (nextFrame && nextFrame->IsImageComplete());
+
+  if (!canDisplay) {
+    // Uh oh, the frame we want to show is currently being decoded (partial)
+    // Wait until the next refresh driver tick and try again
+    return ret;
+  }
+
   // Bad data
   if (GetTimeoutForFrame(nextFrameIndex) < 0) {
     ret.animationFinished = true;
     ret.error = true;
   }
 
   if (nextFrameIndex == 0) {
     ret.dirtyRect = mFirstFrameRefreshArea;
--- a/image/test/mochitest/test_bug496292.html
+++ b/image/test/mochitest/test_bug496292.html
@@ -92,32 +92,31 @@ function loadFirst()
 }
 
 function checkFirst()
 {
   first = canvas.toDataURL();
   is(first, ref, "The default Accept header used by image loader is correct");
 
   SpecialPowers.setCharPref("image.http.accept", "image/png");
-
-  var remoteCanvas = new RemoteCanvas("bug496292-iframe-2.html");
-  remoteCanvas.load(checkSecond);
+  SpecialPowers.pushPrefEnv({"set": [["image.http.accept", "image/png"]]}, function() {
+    var remoteCanvas = new RemoteCanvas("bug496292-iframe-2.html");
+    remoteCanvas.load(checkSecond);
+  });
 }
 
 function checkSecond()
 {
   second = canvas.toDataURL();
   is(second, ref, "The modified Accept header used by image loader is correct");
 
-  try {
-    SpecialPowers.clearUserPref("image.http.accept");
-  } catch (ex) { }
-
-  var remoteCanvas = new RemoteCanvas("bug496292-iframe-1.html");
-  remoteCanvas.load(checkThird);
+  SpecialPowers.pushPrefEnv({"clear": [["image.http.accept"]]}, function() {
+    var remoteCanvas = new RemoteCanvas("bug496292-iframe-1.html");
+    remoteCanvas.load(checkThird);
+  });
 }
 
 function checkThird() {
   third = canvas.toDataURL();
   is(third, ref, "The Accept header used by image loader should be correctly reset");
 
   SimpleTest.finish();
 }
--- a/intl/unicharutil/util/moz.build
+++ b/intl/unicharutil/util/moz.build
@@ -3,17 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This file builds the unicharutil_external_s library which should be used
 # by frozen (dependent) linkage components. Internal-linkage code should use
 # unicharutil_s which is built in the internal/ subdirectory.
 
-DIRS += ['internal', 'standalone']
+DIRS += ['internal']
 
 EXPORTS += [
     'GreekCasing.h',
     'ICUUtils.h',
     'IrishCasing.h',
     'nsBidiUtils.h',
     'nsSpecialCasingData.h',
     'nsUnicharUtils.h',
deleted file mode 100644
--- a/intl/unicharutil/util/standalone/moz.build
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
-    Library('unicharutil_standalone')
-
-UNIFIED_SOURCES += [
-    '../nsUnicodeProperties.cpp',
-]
-
-if CONFIG['ENABLE_INTL_API']:
-    CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
-    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
-    USE_LIBS += ['icu']
-
-for var in ('MOZILLA_INTERNAL_API', 'MOZILLA_XPCOMRT_API', 'MOZILLA_EXTERNAL_LINKAGE',
-            'NR_SOCKET_IS_VOID_PTR', 'HAVE_STRDUP'):
-    DEFINES[var] = True
-
-if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wshadow']
--- a/ipc/chromium/src/base/atomicops.h
+++ b/ipc/chromium/src/base/atomicops.h
@@ -139,13 +139,15 @@ Atomic64 Release_Load(volatile const Ato
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
 #include "base/atomicops_internals_arm_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64)
 #include "base/atomicops_internals_arm64_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS)
 #include "base/atomicops_internals_mips_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_PPC_FAMILY)
+#include "base/atomicops_internals_ppc_gcc.h"
 #else
 #include "base/atomicops_internals_mutex.h"
 #endif
 
 #endif  // BASE_ATOMICOPS_H_
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/base/atomicops_internals_ppc_gcc.h
@@ -0,0 +1,132 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// This file is an internal atomic implementation, use atomicops.h instead.
+//
+#ifndef BASE_ATOMICOPS_INTERNALS_PPC_H_
+#define BASE_ATOMICOPS_INTERNALS_PPC_H_
+namespace base {
+namespace subtle {
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  return (__sync_val_compare_and_swap(ptr, old_value, new_value));
+}
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old_value;
+  do {
+    old_value = *ptr;
+  } while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false);
+  return old_value;
+}
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  for (;;) {
+    Atomic32 old_value = *ptr;
+    Atomic32 new_value = old_value + increment;
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
+      return new_value;
+      // The exchange took place as expected.
+    }
+    // Otherwise, *ptr changed mid-loop and we need to retry.
+  }
+}
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value, Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value, Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+inline void MemoryBarrier() {
+  __asm__ __volatile__("sync" : : : "memory"); }
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; }
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+#ifdef ARCH_CPU_PPC64
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  return (__sync_val_compare_and_swap(ptr, old_value, new_value));
+}
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  Atomic64 old_value;
+  do {
+    old_value = *ptr;
+  } while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false);
+  return old_value;
+}
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  for (;;) {
+    Atomic64 old_value = *ptr;
+    Atomic64 new_value = old_value + increment;
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
+      return new_value;
+      // The exchange took place as expected.
+    }
+    // Otherwise, *ptr changed mid-loop and we need to retry.
+  }
+}
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value, Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value, Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; }
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+#endif
+}  // namespace base::subtle
+}  // namespace base
+#endif  // BASE_ATOMICOPS_INTERNALS_PPC_GCC_H_
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -13,28 +13,31 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/Wasm.h"
 
+#include "mozilla/CheckedInt.h"
+
 #include "jsprf.h"
 
 #include "asmjs/WasmGenerator.h"
 #include "asmjs/WasmText.h"
 #include "vm/ArrayBufferObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::wasm;
 
+using mozilla::CheckedInt;
 using mozilla::IsNaN;
 
 typedef Handle<WasmModuleObject*> HandleWasmModule;
 typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule;
 
 /*****************************************************************************/
 // reporting
 
@@ -619,50 +622,16 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeReturn(f);
       default:
         break;
     }
 
     return f.fail("bad expression code");
 }
 
-static bool
-DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
-               uint32_t funcIndex)
-{
-    const uint8_t* bodyBegin = d.currentPosition();
-
-    FunctionDecoder f(cx, d, mg, fg, funcIndex);
-
-    uint32_t numExprs;
-    if (!d.readVarU32(&numExprs))
-        return Fail(cx, d, "expected number of function body expressions");
-
-    if (numExprs) {
-        for (size_t i = 0; i < numExprs - 1; i++) {
-            if (!DecodeExpr(f, ExprType::Void))
-                return false;
-        }
-
-        if (!DecodeExpr(f, f.ret()))
-            return false;
-    } else {
-        if (!CheckType(f, ExprType::Void, f.ret()))
-            return false;
-    }
-
-    const uint8_t* bodyEnd = d.currentPosition();
-    uintptr_t bodyLength = bodyEnd - bodyBegin;
-    if (!fg.bytecode().resize(bodyLength))
-        return false;
-
-    memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
-    return true;
-}
-
 /*****************************************************************************/
 // dynamic link data
 
 struct ImportName
 {
     UniqueChars module;
     UniqueChars func;
 
@@ -679,23 +648,22 @@ typedef Vector<ImportName, 0, SystemAllo
 /*****************************************************************************/
 // wasm decoding and generation
 
 typedef HashSet<const DeclaredSig*, SigHashPolicy> SigSet;
 
 static bool
 DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(SigLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(SigLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected signature section byte size");
-
     uint32_t numSigs;
     if (!d.readVarU32(&numSigs))
         return Fail(cx, d, "expected number of signatures");
 
     if (numSigs > MaxSigs)
         return Fail(cx, d, "too many signatures");
 
     if (!init->sigs.resize(numSigs))
@@ -757,23 +725,22 @@ DecodeSignatureIndex(JSContext* cx, Deco
 
     *sig = &init.sigs[sigIndex];
     return true;
 }
 
 static bool
 DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(DeclLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(DeclLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected decl section byte size");
-
     uint32_t numDecls;
     if (!d.readVarU32(&numDecls))
         return Fail(cx, d, "expected number of declarations");
 
     if (numDecls > MaxFuncs)
         return Fail(cx, d, "too many functions");
 
     if (!init->funcSigs.resize(numDecls))
@@ -788,23 +755,22 @@ DecodeDeclarationSection(JSContext* cx, 
         return Fail(cx, d, "decls section byte size mismatch");
 
     return true;
 }
 
 static bool
 DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(TableLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(TableLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected table section byte size");
-
     if (!d.readVarU32(&init->numTableElems))
         return Fail(cx, d, "expected number of table elems");
 
     if (init->numTableElems > MaxTableElems)
         return Fail(cx, d, "too many table elements");
 
     Uint32Vector elems;
     if (!elems.resize(init->numTableElems))
@@ -889,23 +855,22 @@ DecodeImport(JSContext* cx, Decoder& d, 
         return Fail(cx, d, "expected import func name");
 
     return importNames->emplaceBack(Move(moduleName), Move(funcName));
 }
 
 static bool
 DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
 {
-    if (!d.readCStringIf(ImportLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(ImportLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected import section byte size");
-
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (i >= MaxImports)
             return Fail(cx, d, "too many imports");
 
         if (!d.readCStringIf(FuncLabel))
             return Fail(cx, d, "expected 'func' import subsection");
 
         if (!DecodeImport(cx, d, init, importNames))
@@ -917,44 +882,45 @@ DecodeImportSection(JSContext* cx, Decod
 
     return true;
 }
 
 static bool
 DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg,
                     MutableHandle<ArrayBufferObject*> heap)
 {
-    if (!d.readCStringIf(MemoryLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(MemoryLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected memory section byte size");
-
-    if (!d.readCStringIf(InitialLabel))
-        return Fail(cx, d, "expected memory section initial field");
-
-    uint32_t initialHeapSize;
-    if (!d.readVarU32(&initialHeapSize))
+    uint32_t initialSizePages;
+    if (!d.readVarU32(&initialSizePages))
         return Fail(cx, d, "expected initial memory size");
 
-    if (initialHeapSize < PageSize || initialHeapSize % PageSize != 0)
-        return Fail(cx, d, "initial memory size not a multiple of 0x10000");
-
-    if (initialHeapSize > INT32_MAX)
+    CheckedInt<int32_t> initialSize = initialSizePages;
+    initialSize *= PageSize;
+    if (!initialSize.isValid())
         return Fail(cx, d, "initial memory size too big");
 
-    if (!d.readCStringIf(EndLabel))
-        return Fail(cx, d, "expected end field of memory section");
+    uint32_t maxSizePages;
+    if (!d.readVarU32(&maxSizePages))
+        return Fail(cx, d, "expected initial memory size");
+
+    CheckedInt<int32_t> maxSize = maxSizePages;
+    maxSize *= PageSize;
+    if (!maxSize.isValid())
+        return Fail(cx, d, "initial memory size too big");
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "memory section byte size mismatch");
 
     bool signalsForOOB = CompileArgs(cx).useSignalHandlersForOOB;
-    heap.set(ArrayBufferObject::createForWasm(cx, initialHeapSize, signalsForOOB));
+    heap.set(ArrayBufferObject::createForWasm(cx, initialSize.value(), signalsForOOB));
     if (!heap)
         return false;
 
     mg.initHeapUsage(HeapUsage::Unshared);
     return true;
 }
 
 typedef HashSet<const char*, CStringHasher> CStringSet;
@@ -1011,23 +977,22 @@ DecodeMemoryExport(JSContext* cx, Decode
         return false;
 
     return mg.addMemoryExport(Move(fieldName));
 }
 
 static bool
 DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
 {
-    if (!d.readCStringIf(ExportLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(ExportLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected export section byte size");
-
     CStringSet dupSet(cx);
     if (!dupSet.init())
         return false;
 
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (i >= MaxExports)
             return Fail(cx, d, "too many exports");
 
@@ -1044,28 +1009,24 @@ DecodeExportsSection(JSContext* cx, Deco
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "export section byte size mismatch");
 
     return true;
 }
 
 static bool
-DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
+DecodeFunctionBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
 {
     int64_t before = PRMJ_Now();
 
     FunctionGenerator fg;
     if (!mg.startFuncDef(d.currentOffset(), &fg))
         return false;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected func section byte size");
-
     const DeclaredSig& sig = mg.funcSig(funcIndex);
     for (ValType type : sig.args()) {
         if (!fg.addLocal(type))
             return false;
     }
 
     uint32_t numVars;
     if (!d.readVarU32(&numVars))
@@ -1074,66 +1035,90 @@ DecodeFunctionSection(JSContext* cx, Dec
     for (uint32_t i = 0; i < numVars; i++) {
         ValType type;
         if (!DecodeValType(cx, d, &type))
             return false;
         if (!fg.addLocal(type))
             return false;
     }
 
-    if (!DecodeFuncBody(cx, d, mg, fg, funcIndex))
+    const uint8_t* bodyBegin = d.currentPosition();
+
+    FunctionDecoder f(cx, d, mg, fg, funcIndex);
+
+    uint32_t numExprs;
+    if (!d.readVarU32(&numExprs))
+        return Fail(cx, d, "expected number of function body expressions");
+
+    if (numExprs) {
+        for (size_t i = 0; i < numExprs - 1; i++) {
+            if (!DecodeExpr(f, ExprType::Void))
+                return false;
+        }
+
+        if (!DecodeExpr(f, f.ret()))
+            return false;
+    } else {
+        if (!CheckType(f, ExprType::Void, f.ret()))
+            return false;
+    }
+
+    const uint8_t* bodyEnd = d.currentPosition();
+    uintptr_t bodyLength = bodyEnd - bodyBegin;
+    if (!fg.bytecode().resize(bodyLength))
         return false;
 
-    if (!d.finishSection(sectionStart))
-        return Fail(cx, d, "func section byte size mismatch");
+    memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
 
     int64_t after = PRMJ_Now();
     unsigned generateTime = (after - before) / PRMJ_USEC_PER_MSEC;
 
     return mg.finishFuncDef(funcIndex, generateTime, &fg);
 }
 
 static bool
-DecodeFunctionSections(JSContext* cx, Decoder& d, ModuleGenerator& mg)
+DecodeFunctionBodiesSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
 {
     if (!mg.startFuncDefs())
         return false;
 
-    uint32_t funcIndex = 0;
+    uint32_t sectionStart;
+    if (!d.startSection(FuncLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
 
-    for (; d.readCStringIf(FuncLabel); funcIndex++) {
-        if (funcIndex >= mg.numFuncSigs())
-            return Fail(cx, d, "more function definitions than declarations");
+    if (sectionStart == Decoder::NotStarted) {
+        if (mg.numFuncSigs() != 0)
+            return Fail(cx, d, "expected function bodies");
 
-        if (!DecodeFunctionSection(cx, d, mg, funcIndex))
+        return mg.finishFuncDefs();
+    }
+
+    for (uint32_t funcIndex = 0; funcIndex < mg.numFuncSigs(); funcIndex++) {
+        if (!DecodeFunctionBody(cx, d, mg, funcIndex))
             return false;
     }
 
-    if (funcIndex < mg.numFuncSigs())
-        return Fail(cx, d, "fewer function definitions than declarations");
+    if (!d.finishSection(sectionStart))
+        return Fail(cx, d, "function section byte size mismatch");
 
-    if (!mg.finishFuncDefs())
-        return false;
-
-    return true;
+    return mg.finishFuncDefs();
 }
 
 static bool
 DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
 {
-    if (!d.readCStringIf(DataLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(DataLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
     if (!heap)
         return Fail(cx, d, "data section requires a memory section");
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected data section byte size");
-
     uint8_t* const heapBase = heap->dataPointer();
     uint32_t const heapLength = heap->byteLength();
     uint32_t prevEnd = 0;
 
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (!d.readCStringIf(SegmentLabel))
             return Fail(cx, d, "expected segment tag");
 
@@ -1161,41 +1146,16 @@ DecodeDataSection(JSContext* cx, Decoder
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "data section byte size mismatch");
 
     return true;
 }
 
 static bool
-DecodeUnknownSection(JSContext* cx, Decoder& d)
-{
-    UniqueChars sectionName = d.readCString();
-    if (!sectionName)
-        return Fail(cx, d, "failed to read section name");
-
-    if (!strcmp(sectionName.get(), SigLabel) ||
-        !strcmp(sectionName.get(), ImportLabel) ||
-        !strcmp(sectionName.get(), DeclLabel) ||
-        !strcmp(sectionName.get(), TableLabel) ||
-        !strcmp(sectionName.get(), MemoryLabel) ||
-        !strcmp(sectionName.get(), ExportLabel) ||
-        !strcmp(sectionName.get(), FuncLabel) ||
-        !strcmp(sectionName.get(), DataLabel))
-    {
-        return Fail(cx, d, "known section out of order");
-    }
-
-    if (!d.skipSection())
-        return Fail(cx, d, "unable to skip unknown section");
-
-    return true;
-}
-
-static bool
 DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t length,
              ImportNameVector* importNames, UniqueExportMap* exportMap,
              MutableHandle<ArrayBufferObject*> heap, MutableHandle<WasmModuleObject*> moduleObj)
 {
     Decoder d(bytes, bytes + length);
 
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
@@ -1225,27 +1185,27 @@ DecodeModule(JSContext* cx, UniqueChars 
         return false;
 
     if (!DecodeMemorySection(cx, d, mg, heap))
         return false;
 
     if (!DecodeExportsSection(cx, d, mg))
         return false;
 
-    if (!DecodeFunctionSections(cx, d, mg))
+    if (!DecodeFunctionBodiesSection(cx, d, mg))
         return false;
 
     if (!DecodeDataSection(cx, d, heap))
         return false;
 
     CacheableCharsVector funcNames;
 
     while (!d.readCStringIf(EndLabel)) {
-        if (!DecodeUnknownSection(cx, d))
-            return false;
+        if (!d.skipSection())
+            return Fail(cx, d, "unable to skip unknown section");
     }
 
     if (!d.done())
         return Fail(cx, d, "failed to consume all bytes of module");
 
     UniqueModuleData module;
     UniqueStaticLinkData staticLink;
     SlowFunctionVector slowFuncs(cx);
@@ -1368,8 +1328,85 @@ wasm::Eval(JSContext* cx, Handle<ArrayBu
     if (!ImportFunctions(cx, importObj, importNames, &imports))
         return false;
 
     if (!moduleObj->module().dynamicallyLink(cx, moduleObj, heap, imports, *exportMap, exportObj))
         return false;
 
     return true;
 }
+
+static bool
+InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
+{
+    MOZ_ASSERT(cx->runtime()->options().wasm());
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args.get(0).isObject() || !args.get(0).toObject().is<ArrayBufferObject>()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    RootedObject importObj(cx);
+    if (!args.get(1).isUndefined()) {
+        if (!args.get(1).isObject()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
+            return false;
+        }
+        importObj = &args[1].toObject();
+    }
+
+    Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
+
+    RootedObject exportObj(cx);
+    if (!Eval(cx, code, importObj, &exportObj))
+        return false;
+
+    args.rval().setObject(*exportObj);
+    return true;
+}
+
+#if JS_HAS_TOSOURCE
+static bool
+wasm_toSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setString(cx->names().Wasm);
+    return true;
+}
+#endif
+
+static JSFunctionSpec wasm_static_methods[] = {
+#if JS_HAS_TOSOURCE
+    JS_FN(js_toSource_str,     wasm_toSource,     0, 0),
+#endif
+    JS_FN("instantiateModule", InstantiateModule, 1, 0),
+    JS_FS_END
+};
+
+const Class js::WasmClass = {
+    js_Wasm_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Wasm)
+};
+
+JSObject*
+js::InitWasmClass(JSContext* cx, HandleObject global)
+{
+    MOZ_ASSERT(cx->runtime()->options().wasm());
+
+    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+    if (!proto)
+        return nullptr;
+
+    RootedObject Wasm(cx, NewObjectWithGivenProto(cx, &WasmClass, proto, SingletonObject));
+    if (!Wasm)
+        return nullptr;
+
+    if (!JS_DefineProperty(cx, global, js_Wasm_str, Wasm, JSPROP_RESOLVING))
+        return nullptr;
+
+    if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
+        return nullptr;
+
+    global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
+    return Wasm;
+}
+
--- a/js/src/asmjs/Wasm.h
+++ b/js/src/asmjs/Wasm.h
@@ -14,42 +14,53 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_h
 #define wasm_h
 
+#include "NamespaceImports.h"
+
 #include "gc/Rooting.h"
+#include "js/Class.h"
 
 namespace js {
 
 class ArrayBufferObject;
 
 namespace wasm {
 
 // Return whether WebAssembly can be compiled on this platform.
 bool
 HasCompilerSupport(ExclusiveContext* cx);
 
-// The WebAssembly spec hard-codes the virtual page size to be 64KiB and limits
-// forces the linear memory to always be a multiple of 64KiB.
+// The WebAssembly spec hard-codes the virtual page size to be 64KiB and
+// requires linear memory to always be a multiple of 64KiB.
 static const unsigned PageSize = 64 * 1024;
 
 // When signal handling is used for bounds checking, MappedSize bytes are
 // reserved and the subrange [0, memory_size) is given readwrite permission.
 // See also static asserts in MIRGenerator::foldableOffsetRange.
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
 static const uint64_t Uint32Range = uint64_t(UINT32_MAX) + 1;
 static const uint64_t MappedSize = 2 * Uint32Range + PageSize;
 #endif
 
 // Compiles the given binary wasm module given the ArrayBufferObject
 // and links the module's imports with the given import object.
 bool
-Eval(JSContext* cx, JS::Handle<ArrayBufferObject*> code,
-     JS::HandleObject importObj, JS::MutableHandleObject exportObj);
+Eval(JSContext* cx, Handle<ArrayBufferObject*> code, HandleObject importObj,
+     MutableHandleObject exportObj);
 
 }  // namespace wasm
+
+// Initialization of the Wasm global object and its properties.
+
+extern const Class WasmClass;
+
+JSObject*
+InitWasmClass(JSContext* cx, HandleObject global);
+
 }  // namespace js
 
 #endif // namespace wasm_h
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -35,29 +35,30 @@ static const unsigned MaxSigs         = 
 static const unsigned MaxFuncs        = 512 * 1024;
 static const unsigned MaxImports      =   4 * 1024;
 static const unsigned MaxExports      =   4 * 1024;
 static const unsigned MaxTableElems   = 128 * 1024;
 static const unsigned MaxArgsPerFunc  =   4 * 1024;
 
 // Module header constants
 static const uint32_t MagicNumber     = 0x6d736100; // "\0asm"
-static const uint32_t EncodingVersion = -1;     // experimental
+static const uint32_t EncodingVersion = 0xa;        // will change while iterating toward release,
+                                                    // change to 1 at release, and hopefully never
+                                                    // change after that
 
 // Names:
 static const char SigLabel[]          = "sig";
 static const char ImportLabel[]       = "import";
 static const char DeclLabel[]         = "decl";
 static const char TableLabel[]        = "table";
 static const char MemoryLabel[]       = "memory";
 static const char ExportLabel[]       = "export";
 static const char FuncLabel[]         = "func";
 static const char DataLabel[]         = "data";
 static const char SegmentLabel[]      = "segment";
-static const char InitialLabel[]      = "initial";
 static const char EndLabel[]          = "";
 
 enum class Expr : uint16_t
 {
     // Control opcodes
     Nop,
     Block,
     Loop,
@@ -447,23 +448,29 @@ class Encoder
 
     MOZ_WARN_UNUSED_RESULT bool writeCString(const char* cstr) {
         return bytecode_.append(reinterpret_cast<const uint8_t*>(cstr), strlen(cstr) + 1);
     }
     MOZ_WARN_UNUSED_RESULT bool writeRawData(const uint8_t* bytes, uint32_t numBytes) {
         return bytecode_.append(bytes, numBytes);
     }
 
-    // A "section" is a contiguous region of bytes that stores its own size so
+    // A "section" is a contiguous range of bytes that stores its own size so
     // that it may be trivially skipped without examining the contents. Sections
     // require backpatching since the size of the section is only known at the
-    // end while the size's uint32 must be stored at the beginning.
+    // end while the size's uint32 must be stored at the beginning. Immediately
+    // after the section length is the string id of the section.
 
-    MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) {
-        return writePatchableVarU32(offset);
+    template <size_t IdSizeWith0>
+    MOZ_WARN_UNUSED_RESULT bool startSection(const char (&id)[IdSizeWith0], size_t* offset) {
+        static const size_t IdSize = IdSizeWith0 - 1;
+        MOZ_ASSERT(id[IdSize] == '\0');
+        return writePatchableVarU32(offset) &&
+               writeVarU32(IdSize) &&
+               writeRawData(reinterpret_cast<const uint8_t*>(id), IdSize);
     }
     void finishSection(size_t offset) {
         return patchVarU32(offset, bytecode_.length() - offset - varU32ByteLength(offset));
     }
 
     // Patching is necessary due to the combination of a preorder encoding and a
     // single-pass algorithm that only knows the precise opcode after visiting
     // children. Switching to a postorder encoding will remove these methods:
@@ -677,34 +684,56 @@ class Decoder
         if (bytesRemain() < numBytes)
             return false;
         cur_ += numBytes;
         return true;
     }
 
     // See "section" description in Encoder.
 
-    MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* startOffset) {
-        *startOffset = currentOffset();
-        uint32_t unused;
-        return readVarU32(&unused);
+    static const uint32_t NotStarted = UINT32_MAX;
+
+    template <size_t IdSizeWith0>
+    MOZ_WARN_UNUSED_RESULT bool startSection(const char (&id)[IdSizeWith0], uint32_t* startOffset) {
+        static const size_t IdSize = IdSizeWith0 - 1;
+        MOZ_ASSERT(id[IdSize] == '\0');
+        const uint8_t* before = cur_;
+        uint32_t size;
+        if (!readVarU32(&size))
+            goto backup;
+        if (bytesRemain() < size)
+            return false;
+        uint32_t idSize;
+        if (!readVarU32(&idSize))
+            goto backup;
+        if (bytesRemain() < idSize)
+            return false;
+        if (idSize != IdSize || !!memcmp(cur_, id, IdSize))
+            goto backup;
+        cur_ += IdSize;
+        *startOffset = before - beg_;
+        return  true;
+      backup:
+        cur_ = before;
+        *startOffset = NotStarted;
+        return true;
     }
     MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t startOffset) {
         uint32_t currentOffset = cur_ - beg_;
         cur_ = beg_ + startOffset;
-        uint32_t numBytes = uncheckedReadVarU32();
-        uint32_t afterNumBytes = cur_ - beg_;
+        uint32_t size = uncheckedReadVarU32();
+        uint32_t afterSize = cur_ - beg_;
         cur_ = beg_ + currentOffset;
-        return numBytes == (currentOffset - afterNumBytes);
+        return size == (currentOffset - afterSize);
     }
     MOZ_WARN_UNUSED_RESULT bool skipSection() {
-        uint32_t numBytes;
-        if (!readVarU32(&numBytes) || bytesRemain() < numBytes)
+        uint32_t size;
+        if (!readVarU32(&size) || bytesRemain() < size)
             return false;
-        cur_ += numBytes;
+        cur_ += size;
         return true;
     }
 
     // The infallible "unchecked" decoding functions can be used when we are
     // sure that the bytecode is well-formed (by construction or due to previous
     // validation).
 
     uint32_t uncheckedReadFixedU32() {
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -557,24 +557,28 @@ class WasmAstSegment : public WasmAstNod
     WasmName text() const { return text_; }
 };
 
 typedef WasmAstVector<WasmAstSegment*> WasmAstSegmentVector;
 
 class WasmAstMemory : public WasmAstNode
 {
     uint32_t initialSize_;
+    Maybe<uint32_t> maxSize_;
     WasmAstSegmentVector segments_;
 
   public:
-    explicit WasmAstMemory(uint32_t initialSize, WasmAstSegmentVector&& segments)
+    explicit WasmAstMemory(uint32_t initialSize, Maybe<uint32_t> maxSize,
+                           WasmAstSegmentVector&& segments)
       : initialSize_(initialSize),
+        maxSize_(maxSize),
         segments_(Move(segments))
     {}
     uint32_t initialSize() const { return initialSize_; }
+    const Maybe<uint32_t>& maxSize() const { return maxSize_; }
     const WasmAstSegmentVector& segments() const { return segments_; }
 };
 
 class WasmAstModule : public WasmAstNode
 {
     typedef WasmAstVector<WasmAstFunc*> FuncVector;
     typedef WasmAstVector<WasmAstImport*> ImportVector;
     typedef WasmAstVector<WasmAstExport*> ExportVector;
@@ -2892,26 +2896,31 @@ ParseSegment(WasmParseContext& c)
 
 static WasmAstMemory*
 ParseMemory(WasmParseContext& c)
 {
     WasmToken initialSize;
     if (!c.ts.match(WasmToken::Index, &initialSize, c.error))
         return nullptr;
 
+    Maybe<uint32_t> maxSize;
+    WasmToken token;
+    if (c.ts.getIf(WasmToken::Index, &token))
+        maxSize.emplace(token.index());
+
     WasmAstSegmentVector segments(c.lifo);
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmAstSegment* segment = ParseSegment(c);
         if (!segment || !segments.append(segment))
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstMemory(initialSize.index(), Move(segments));
+    return new(c.lifo) WasmAstMemory(initialSize.index(), maxSize, Move(segments));
 }
 
 static WasmAstImport*
 ParseImport(WasmParseContext& c, WasmAstModule* module)
 {
     WasmName name = c.ts.getIfName();
 
     WasmToken moduleName;
@@ -3704,21 +3713,18 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr
 // wasm AST binary serialization
 
 static bool
 EncodeSignatureSection(Encoder& e, WasmAstModule& module)
 {
     if (module.sigs().empty())
         return true;
 
-    if (!e.writeCString(SigLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(SigLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.sigs().length()))
         return false;
 
     for (WasmAstSig* sig : module.sigs()) {
         if (!e.writeVarU32(sig->args().length()))
             return false;
@@ -3737,21 +3743,18 @@ EncodeSignatureSection(Encoder& e, WasmA
 }
 
 static bool
 EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
 {
     if (module.funcs().empty())
         return true;
 
-    if (!e.writeCString(DeclLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(DeclLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.funcs().length()))
         return false;
 
     for (WasmAstFunc* func : module.funcs()) {
         if (!e.writeVarU32(func->sig().index()))
             return false;
@@ -3785,21 +3788,18 @@ EncodeImport(Encoder& e, WasmAstImport& 
 }
 
 static bool
 EncodeImportSection(Encoder& e, WasmAstModule& module)
 {
     if (module.imports().empty())
         return true;
 
-    if (!e.writeCString(ImportLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(ImportLabel, &offset))
         return false;
 
     for (WasmAstImport* imp : module.imports()) {
         if (!e.writeCString(FuncLabel))
             return false;
         if (!EncodeImport(e, *imp))
             return false;
     }
@@ -3812,32 +3812,27 @@ EncodeImportSection(Encoder& e, WasmAstM
 }
 
 static bool
 EncodeMemorySection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeMemory())
         return true;
 
-    if (!e.writeCString(MemoryLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(MemoryLabel, &offset))
         return false;
 
     WasmAstMemory& memory = *module.maybeMemory();
 
-    if (!e.writeCString(InitialLabel))
-        return false;
-
     if (!e.writeVarU32(memory.initialSize()))
         return false;
 
-    if (!e.writeCString(EndLabel))
+    uint32_t maxSize = memory.maxSize() ? *memory.maxSize() : memory.initialSize();
+    if (!e.writeVarU32(maxSize))
         return false;
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
 EncodeFunctionExport(Encoder& e, WasmAstExport& exp)
@@ -3861,21 +3856,18 @@ EncodeMemoryExport(Encoder& e, WasmAstEx
 }
 
 static bool
 EncodeExportSection(Encoder& e, WasmAstModule& module)
 {
     if (module.exports().empty())
         return true;
 
-    if (!e.writeCString(ExportLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(ExportLabel, &offset))
         return false;
 
     for (WasmAstExport* exp : module.exports()) {
         switch (exp->kind()) {
           case WasmAstExportKind::Func:
             if (!e.writeCString(FuncLabel))
                 return false;
             if (!EncodeFunctionExport(e, *exp))
@@ -3898,63 +3890,70 @@ EncodeExportSection(Encoder& e, WasmAstM
 }
 
 static bool
 EncodeTableSection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeTable())
         return true;
 
-    if (!e.writeCString(TableLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(TableLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.maybeTable()->elems().length()))
         return false;
 
     for (WasmRef& ref : module.maybeTable()->elems()) {
         if (!e.writeVarU32(ref.index()))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeFunctionSection(Encoder& e, WasmAstFunc& func)
+EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
 {
-    if (!e.writeCString(FuncLabel))
-        return false;
-
-    size_t offset;
-    if (!e.startSection(&offset))
-        return false;
-
     if (!e.writeVarU32(func.vars().length()))
         return false;
 
     for (ValType type : func.vars()) {
         if (!e.writeValType(type))
             return false;
     }
 
     if (!e.writeVarU32(func.body().length()))
         return false;
 
     for (WasmAstExpr* expr : func.body()) {
         if (!EncodeExpr(e, *expr))
             return false;
     }
 
+    return true;
+}
+
+static bool
+EncodeFunctionBodiesSection(Encoder& e, WasmAstModule& module)
+{
+    if (module.funcs().empty())
+        return true;
+
+    size_t offset;
+    if (!e.startSection(FuncLabel, &offset))
+        return false;
+
+    for (WasmAstFunc* func : module.funcs()) {
+        if (!EncodeFunctionBody(e, *func))
+            return false;
+    }
+
     e.finishSection(offset);
-
     return true;
 }
 
 static bool
 EncodeDataSegment(Encoder& e, WasmAstSegment& segment)
 {
     if (!e.writeVarU32(segment.offset()))
         return false;
@@ -3985,21 +3984,18 @@ EncodeDataSegment(Encoder& e, WasmAstSeg
 static bool
 EncodeDataSection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeMemory() || module.maybeMemory()->segments().empty())
         return true;
 
     const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
 
-    if (!e.writeCString(DataLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(DataLabel, &offset))
         return false;
 
     for (WasmAstSegment* segment : segments) {
         if (!e.writeCString(SegmentLabel))
             return false;
         if (!EncodeDataSegment(e, *segment))
             return false;
     }
@@ -4039,20 +4035,18 @@ EncodeModule(WasmAstModule& module)
         return nullptr;
 
     if (!EncodeMemorySection(e, module))
         return nullptr;
 
     if (!EncodeExportSection(e, module))
         return nullptr;
 
-    for (WasmAstFunc* func : module.funcs()) {
-        if (!EncodeFunctionSection(e, *func))
-            return nullptr;
-    }
+    if (!EncodeFunctionBodiesSection(e, module))
+        return nullptr;
 
     if (!EncodeDataSection(e, module))
         return nullptr;
 
     if (!e.writeCString(EndLabel))
         return nullptr;
 
     return Move(bytecode);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -496,52 +496,17 @@ IsProxy(JSContext* cx, unsigned argc, Va
     args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
     return true;
 }
 
 static bool
 WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setBoolean(wasm::HasCompilerSupport(cx));
-    return true;
-}
-
-static bool
-WasmEval(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject callee(cx, &args.callee());
-
-    if (args.length() < 1 || args.length() > 2) {
-        ReportUsageError(cx, callee, "Wrong number of arguments");
-        return false;
-    }
-
-    if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
-        ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
-        return false;
-    }
-
-    RootedObject importObj(cx);
-    if (!args.get(1).isUndefined()) {
-        if (!args.get(1).isObject()) {
-            ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
-            return false;
-        }
-        importObj = &args[1].toObject();
-    }
-
-    Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
-
-    RootedObject exportObj(cx);
-    if (!wasm::Eval(cx, code, importObj, &exportObj))
-        return false;
-
-    args.rval().setObject(*exportObj);
+    args.rval().setBoolean(wasm::HasCompilerSupport(cx) && cx->runtime()->options().wasm());
     return true;
 }
 
 static bool
 WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
@@ -2771,18 +2736,21 @@ ShortestPaths(JSContext* cx, unsigned ar
             path->ensureDenseInitializedLength(cx, 0, pathLength);
 
             for (size_t k = 0; k < pathLength; k++) {
                 RootedPlainObject part(cx, NewBuiltinClassInstance<PlainObject>(cx));
                 if (!part)
                     return false;
 
                 RootedValue predecessor(cx, values[i][j][k]);
-                if (!JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE))
+                if (!cx->compartment()->wrap(cx, &predecessor) ||
+                    !JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE))
+                {
                     return false;
+                }
 
                 if (names[i][j][k]) {
                     RootedString edge(cx, NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
                     if (!edge || !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE))
                         return false;
                 }
 
                 path->setDenseElement(k, ObjectValue(*part));
@@ -3703,21 +3671,16 @@ gc::ZealModeHelpText),
 "isAsmJSFunction(fn)",
 "  Returns whether the given value is a nested function in an asm.js module that has been\n"
 "  both compile- and link-time validated."),
 
     JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
 "wasmIsSupported()",
 "  Returns a boolean indicating whether WebAssembly is supported on the current device."),
 
-    JS_FN_HELP("wasmEval", WasmEval, 2, 0,
-"wasmEval(buffer, imports)",
-"  Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n"
-"  and links the module's imports with the given 'imports' object."),
-
     JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
 "wasmTextToBinary(str)",
 "  Translates the given text wasm module into its binary encoding."),
 
     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
 "isLazyFunction(fun)",
 "  True if fun is a lazy JSFunction."),
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1584,16 +1584,18 @@ OutlineTypedObject::createDerived(JSCont
     return obj;
 }
 
 /*static*/ TypedObject*
 TypedObject::createZeroed(JSContext* cx, HandleTypeDescr descr, int32_t length, gc::InitialHeap heap)
 {
     // If possible, create an object with inline data.
     if ((size_t) descr->size() <= InlineTypedObject::MaximumSize) {
+        AutoSetNewObjectMetadata metadata(cx);
+
         InlineTypedObject* obj = InlineTypedObject::create(cx, descr, heap);
         if (!obj)
             return nullptr;
         descr->initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
         return obj;
     }
 
     // Create unattached wrapper object.
@@ -2129,16 +2131,18 @@ InlineTypedObject::create(JSContext* cx,
     NewObjectKind newKind = (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
     return NewObjectWithGroup<InlineTypedObject>(cx, group, allocKind, newKind);
 }
 
 /* static */ InlineTypedObject*
 InlineTypedObject::createCopy(JSContext* cx, Handle<InlineTypedObject*> templateObject,
                               gc::InitialHeap heap)
 {
+    AutoSetNewObjectMetadata metadata(cx);
+
     Rooted<TypeDescr*> descr(cx, &templateObject->typeDescr());
     InlineTypedObject* res = create(cx, descr, heap);
     if (!res)
         return nullptr;
 
     memcpy(res->inlineTypedMem(), templateObject->inlineTypedMem(), templateObject->size());
     return res;
 }
@@ -2238,20 +2242,20 @@ OutlineTransparentTypedObject::getOrCrea
         return &owner().as<ArrayBufferObject>();
     return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
 }
 
 /******************************************************************************
  * Typed object classes
  */
 
-#define DEFINE_TYPEDOBJ_CLASS(Name, Trace)        \
+#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag)         \
     const Class Name::class_ = {                         \
         # Name,                                          \
-        Class::NON_NATIVE, \
+        Class::NON_NATIVE | flag,                        \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
         nullptr,        /* resolve     */                \
         nullptr,        /* mayResolve  */                \
         nullptr,        /* finalize    */                \
@@ -2271,20 +2275,22 @@ OutlineTransparentTypedObject::getOrCrea
             TypedObject::obj_deleteProperty,             \
             nullptr, nullptr, /* watch/unwatch */        \
             nullptr,   /* getElements */                 \
             TypedObject::obj_enumerate,                  \
             nullptr, /* thisValue */                     \
         }                                                \
     }
 
-DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace);
-DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,      OutlineTypedObject::obj_trace);
-DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  InlineTypedObject::obj_trace);
-DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,       InlineTypedObject::obj_trace);
+DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace, 0);
+DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,      OutlineTypedObject::obj_trace, 0);
+DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  InlineTypedObject::obj_trace,
+                      JSCLASS_DELAY_METADATA_CALLBACK);
+DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,       InlineTypedObject::obj_trace,
+                      JSCLASS_DELAY_METADATA_CALLBACK);
 
 static int32_t
 LengthForType(TypeDescr& descr)
 {
     switch (descr.kind()) {
       case type::Scalar:
       case type::Reference:
       case type::Struct:
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -8,16 +8,18 @@ baseline/bug852175.js
 basic/bug632964-regexp.js
 basic/bug656261.js
 basic/bug677957-2.js
 basic/bug753283.js
 basic/testBug614653.js
 basic/testBug686274.js
 basic/testManyVars.js
 basic/testTypedArrayInit.js
+debug/DebuggeeWouldRun-01.js
+debug/DebuggeeWouldRun-02.js
 gc/bug-1014972.js
 gc/bug-1246593.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 saved-stacks/bug-1006876-too-much-recursion.js
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -5,23 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* JS Garbage Collector. */
 
 #ifndef gc_Policy_h
 #define gc_Policy_h
 
 #include "mozilla/TypeTraits.h"
+#include "gc/Barrier.h"
+#include "gc/Marking.h"
 #include "js/GCPolicyAPI.h"
 
 // Forward declare the types we're defining policies for. This file is
 // included in all places that define GC things, so the real definitions
 // will be available when we do template expansion, allowing for use of
 // static members in the underlying types. We cannot, however, use
 // static_assert to verify relations between types.
+class JSLinearString;
 namespace js {
 class AccessorShape;
 class ArgumentsObject;
 class ArrayBufferObject;
 class ArrayBufferObjectMaybeShared;
 class ArrayBufferViewObject;
 class ArrayObject;
 class BaseShape;
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -1,13 +1,13 @@
 if (!wasmIsSupported())
     quit();
 
 load(libdir + "asserts.js");
 
 function wasmEvalText(str, imports) {
-    return wasmEval(wasmTextToBinary(str), imports);
+    return Wasm.instantiateModule(wasmTextToBinary(str), imports);
 }
 
 function mismatchError(actual, expect) {
     var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
     return RegExp(str);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/DebuggeeWouldRun-01.js
@@ -0,0 +1,7 @@
+// Bug 1250190: Shouldn't crash. |jit-test| exitstatus: 3
+
+g = newGlobal();
+var dbg = Debugger(g)
+dbg.onNewPromise = () => g.makeFakePromise();
+g.makeFakePromise();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/DebuggeeWouldRun-02.js
@@ -0,0 +1,7 @@
+// Bug 1250190: Shouldn't crash. |jit-test| exitstatus: 3
+
+var g = newGlobal();
+var dbg = Debugger(g)
+dbg.onNewGlobalObject = () => g.newGlobal();
+g.newGlobal();
+print("yo");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/DebuggeeWouldRun-03.js
@@ -0,0 +1,9 @@
+// Bug 1250190: Shouldn't crash. |jit-test| error: yadda
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onNewGlobalObject = function () {
+  dbg.onNewGlobalObject = function () { throw "yadda"; };
+  newGlobal();
+}
+newGlobal();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/DebuggeeWouldRun-04.js
@@ -0,0 +1,9 @@
+// Bug 1250190: Shouldn't crash. |jit-test| error: yadda
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onNewScript = function () {
+  dbg.onNewScript = function () { throw "yadda"; };
+  g.Function("noodles;");
+}
+g.Function("poodles;");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1252103.js
@@ -0,0 +1,19 @@
+// Bug 1252103: Inline typed array objects need delayed metadata collection.
+// Shouldn't crash.
+
+function foo() {
+    enableTrackAllocations();
+    gczeal(2, 10);
+    TO = TypedObject;
+    PointType = new TO.StructType({
+        y: TO.float64,
+        name: TO.string
+    })
+    LineType = new TO.StructType({
+        PointType
+    })
+    function testBasic() new LineType;
+    testBasic();
+}
+evaluate("foo()");
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1252154.js
@@ -0,0 +1,8 @@
+// Bug 1252154: Inline typed array objects need delayed metadata collection.
+// Shouldn't crash.
+
+gczeal(7,1);
+enableShellObjectMetadataCallback();
+var T = TypedObject;
+var AT = new T.ArrayType(T.Any,10);
+var v = new AT();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/bug-1252912.js
@@ -0,0 +1,6 @@
+try {
+  x = evalcx('')
+  toSource = (function() {
+  })
+} catch (foo) {}
+shortestPaths(this, ["$4"], 5)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1239075.js
@@ -0,0 +1,29 @@
+
+function g0() { with({}){}; }
+function f0(y, x) {
+    var a = y >>> 0;
+    a = a - 1 + 1;
+    g0(); // Capture the truncate result after the call.
+    var b = x / 2; // bailout.
+    return ~(a + b);
+}
+assertEq(f0(-1, 0), 0);
+assertEq(f0(-1, 1), 0);
+
+
+function g1() { with({}){}; }
+function f1(y, x) {
+    var a = y >>> 0;
+    a = a - 1 + 1;
+    g1(); // Capture the truncate result after the call.
+    var b = Math.pow(x / 2, x); // bailout.
+    return ~(a + b);
+}
+assertEq(f1(-1, 0), -1);
+assertEq(f1(-1, 1), 0);
+
+function f2(x) {
+    return ~(((~0 | 0) >>> 0 || 0) + Math.pow(Math.cos(x >>> 0), Math.atan2(0, x)))
+}
+assertEq(f2(0), -1);
+assertEq(f2(-9999), 0);
--- a/js/src/jit-test/tests/wasm/basic-memory.js
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -1,34 +1,34 @@
 load(libdir + "wasm.js");
 
 if (!wasmIsSupported())
     quit();
 
 function testLoad(type, ext, base, offset, align, expect) {
   assertEq(wasmEvalText(
     '(module' +
-    '  (memory 0x10000' +
+    '  (memory 1' +
     '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
     '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
     '  )' +
     '  (func (param i32) (result ' + type + ')' +
     '    (' + type + '.load' + ext +
     '     offset=' + offset +
     '     ' + (align != 0 ? 'align=' + align : '') +
     '     (get_local 0)' +
     '    )' +
     '  ) (export "" 0))'
   )(base), expect);
 }
 
 function testStore(type, ext, base, offset, align, value) {
   assertEq(wasmEvalText(
     '(module' +
-    '  (memory 0x10000' +
+    '  (memory 1' +
     '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
     '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
     '  )' +
     '  (func (param i32) (param ' + type + ') (result ' + type + ')' +
     '    (' + type + '.store' + ext +
     '     offset=' + offset +
     '     ' + (align != 0 ? 'align=' + align : '') +
     '     (get_local 0)' +
@@ -36,34 +36,34 @@ function testStore(type, ext, base, offs
     '    )' +
     '  ) (export "" 0))'
   )(base, value), value);
 }
 
 function testLoadError(type, ext, base, offset, align, errorMsg) {
   assertErrorMessage(() => wasmEvalText(
     '(module' +
-    '  (memory 0x10000' +
+    '  (memory 1' +
     '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
     '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
     '  )' +
     '  (func (param i32) (result ' + type + ')' +
     '    (' + type + '.load' + ext +
     '     offset=' + offset +
     '     ' + (align != 0 ? 'align=' + align : '') +
     '     (get_local 0)' +
     '    )' +
     '  ) (export "" 0))'
   ), Error, errorMsg);
 }
 
 function testStoreError(type, ext, base, offset, align, errorMsg) {
   assertErrorMessage(() => wasmEvalText(
     '(module' +
-    '  (memory 0x10000' +
+    '  (memory 1' +
     '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
     '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
     '  )' +
     '  (func (param i32) (param ' + type + ') (result ' + type + ')' +
     '    (' + type + '.store' + ext +
     '     offset=' + offset +
     '     ' + (align != 0 ? 'align=' + align : '') +
     '     (get_local 0)' +
@@ -95,17 +95,17 @@ testLoad('f64', '', 0, 0, 0, 7.949928895
 
 testLoad('i32', '8_s', 16, 0, 0, -0x10);
 testLoad('i32', '8_u', 16, 0, 0, 0xf0);
 testLoad('i32', '16_s', 16, 0, 0, -0xe10);
 testLoad('i32', '16_u', 16, 0, 0, 0xf1f0);
 
 // When these tests fail, uncomment the load/store tests below.
 function testLoadNYI(ext) {
-    assertErrorMessage(() => wasmEvalText(`(module (memory 0x10000) (func (i64.load${ext} (i32.const 0))))`), TypeError, /NYI/);
+    assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.load${ext} (i32.const 0))))`), TypeError, /NYI/);
 }
 testLoadNYI('');
 testLoadNYI('8_s');
 testLoadNYI('8_u');
 testLoadNYI('16_s');
 testLoadNYI('16_u');
 testLoadNYI('32_s');
 testLoadNYI('32_u');
@@ -117,17 +117,17 @@ testLoadNYI('32_u');
 //testLoad('i64', '32_u', 16, 0, 0, 0x8f9fafb); // TODO: i64 NYI
 
 testStore('i32', '', 0, 0, 0, -0x3f3e2c2c);
 //testStore('i32', '', 1, 0, 0, -0x3f3e2c2c); // TODO: unaligned NYI
 //testStore('i32', '', 0, 1, 0, 0xc0c1d3d4); // TODO: offset NYI
 //testStore('i32', '', 1, 1, 4, 0xc0c1d3d4); // TODO: offset NYI
 
 function testStoreNYI(ext) {
-    assertErrorMessage(() => wasmEvalText(`(module (memory 0x10000) (func (i64.store${ext} (i32.const 0) (i32.const 0))))`), TypeError, /NYI/);
+    assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.store${ext} (i32.const 0) (i32.const 0))))`), TypeError, /NYI/);
 }
 testStoreNYI('');
 testStoreNYI('8');
 testStoreNYI('16');
 testStoreNYI('32');
 //testStore('i64', '', 0, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
 //testStore('i64', '', 1, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
 //testStore('i64', '', 0, 1, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
@@ -146,16 +146,16 @@ testStore('f64', '', 0, 0, 0, 0.89012345
 //testStore('f64', '', 1, 1, 4, 0.89012345); // TODO: offsets NYI
 
 testStore('i32', '8', 0, 0, 0, 0x23);
 testStore('i32', '16', 0, 0, 0, 0x2345);
 
 testLoadError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/);
 testStoreError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/);
 
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64"));
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64"));
 
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "f32"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "f32"));
 
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "i32"));
-assertErrorMessage(() => wasmEvalText('(module (memory 0x10000) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "i32"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "i32"));
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "i32"));
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -91,18 +91,18 @@ var hasI64 = getBuildConfiguration().x64
 if (!hasI64) {
     assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
     assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/);
 }
 
 // ----------------------------------------------------------------------------
 // imports
 
-assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /Second argument, if present, must be an Object/);
-assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /Second argument, if present, must be an Object/);
+assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /second argument, if present, must be an object/);
+assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /second argument, if present, must be an object/);
 
 const noImportObj = /no import object given/;
 const notObject = /import object field is not an Object/;
 const notFunction = /import object field is not a Function/;
 
 var code = '(module (import "a" "b"))';
 assertErrorMessage(() => wasmEvalText(code), TypeError, noImportObj);
 assertErrorMessage(() => wasmEvalText(code, {}), TypeError, notObject);
@@ -127,79 +127,77 @@ wasmEvalText(code, {a:()=>{}, b:{c:()=>{
 wasmEvalText('(module (import "a" "" (result i32)))', {a: ()=> {}});
 wasmEvalText('(module (import "a" "" (result f32)))', {a: ()=> {}});
 wasmEvalText('(module (import "a" "" (result f64)))', {a: ()=> {}});
 wasmEvalText('(module (import $foo "a" "" (result f64)))', {a: ()=> {}});
 
 // ----------------------------------------------------------------------------
 // memory
 
-wasmEvalText('(module (memory 65536))');
-wasmEvalText('(module (memory 131072))');
-assertErrorMessage(() => wasmEvalText('(module (memory 0))'), TypeError, /not a multiple of 0x10000/);
-assertErrorMessage(() => wasmEvalText('(module (memory 1))'), TypeError, /not a multiple of 0x10000/);
-assertErrorMessage(() => wasmEvalText('(module (memory 65535))'), TypeError, /not a multiple of 0x10000/);
-assertErrorMessage(() => wasmEvalText('(module (memory 131071))'), TypeError, /not a multiple of 0x10000/);
-assertErrorMessage(() => wasmEvalText('(module (memory 2147483648))'), TypeError, /initial memory size too big/);
+wasmEvalText('(module (memory 0))');
+wasmEvalText('(module (memory 1))');
+assertErrorMessage(() => wasmEvalText('(module (memory 65536))'), TypeError, /initial memory size too big/);
+assertErrorMessage(() => wasmEvalText('(module (memory 32768))'), TypeError, /initial memory size too big/);
 
 // May OOM, but must not crash:
 try {
-    wasmEvalText('(module (memory 2147418112))');
+    wasmEvalText('(module (memory 32767))');
 } catch (e) {
+    print(e);
     assertEq(String(e).indexOf("out of memory") != -1, true);
 }
 
 assertErrorMessage(() => wasmEvalText('(module (export "" memory))'), TypeError, /no memory section/);
 
-var buf = wasmEvalText('(module (memory 65536) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1) (export "" memory))');
 assertEq(buf instanceof ArrayBuffer, true);
 assertEq(buf.byteLength, 65536);
 
-assertErrorMessage(() => wasmEvalText('(module (memory 65536) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/);
-assertErrorMessage(() => wasmEvalText('(module (memory 65536) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/);
-var {a, b} = wasmEvalText('(module (memory 65536) (export "a" memory) (export "b" memory))');
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/);
+assertErrorMessage(() => wasmEvalText('(module (memory 1) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/);
+var {a, b} = wasmEvalText('(module (memory 1) (export "a" memory) (export "b" memory))');
 assertEq(a instanceof ArrayBuffer, true);
 assertEq(a, b);
 
-var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))');
+var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))');
 assertEq(obj.a instanceof ArrayBuffer, true);
 assertEq(obj.b instanceof Function, true);
 assertEq(obj.c instanceof Function, true);
 assertEq(obj.a.byteLength, 65536);
 assertEq(obj.b(), 42);
 assertEq(obj.c(), undefined);
 
-var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))');
+var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))');
 assertEq(obj instanceof ArrayBuffer, true);
 assertEq(obj.a instanceof Function, true);
 assertEq(obj.b instanceof Function, true);
 assertEq(obj.a, obj.b);
 assertEq(obj.byteLength, 65536);
 assertEq(obj.a(), 42);
 
-var buf = wasmEvalText('(module (memory 65536 (segment 0 "")) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1 (segment 0 "")) (export "" memory))');
 assertEq(new Uint8Array(buf)[0], 0);
 
-var buf = wasmEvalText('(module (memory 65536 (segment 65536 "")) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1 (segment 65536 "")) (export "" memory))');
 assertEq(new Uint8Array(buf)[0], 0);
 
-var buf = wasmEvalText('(module (memory 65536 (segment 0 "a")) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1 (segment 0 "a")) (export "" memory))');
 assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
 
-var buf = wasmEvalText('(module (memory 65536 (segment 0 "a") (segment 2 "b")) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1 (segment 0 "a") (segment 2 "b")) (export "" memory))');
 assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
 assertEq(new Uint8Array(buf)[1], 0);
 assertEq(new Uint8Array(buf)[2], 'b'.charCodeAt(0));
 
-var buf = wasmEvalText('(module (memory 65536 (segment 65535 "c")) (export "" memory))');
+var buf = wasmEvalText('(module (memory 1 (segment 65535 "c")) (export "" memory))');
 assertEq(new Uint8Array(buf)[0], 0);
 assertEq(new Uint8Array(buf)[65535], 'c'.charCodeAt(0));
 
-assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/);
-assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/);
+assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/);
+assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/);
 
 // ----------------------------------------------------------------------------
 // locals
 
 assertEq(wasmEvalText('(module (func (param i32) (result i32) (get_local 0)) (export "" 0))')(), 0);
 assertEq(wasmEvalText('(module (func (param i32) (result i32) (get_local 0)) (export "" 0))')(42), 42);
 assertEq(wasmEvalText('(module (func (param i32) (param i32) (result i32) (get_local 0)) (export "" 0))')(42, 43), 42);
 assertEq(wasmEvalText('(module (func (param i32) (param i32) (result i32) (get_local 1)) (export "" 0))')(42, 43), 43);
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -1,35 +1,35 @@
 load(libdir + "wasm.js");
 
-// MagicNumber = 0x4d534100
-const magic0 = 0;
-const magic1 = 97;  // 'a'
-const magic2 = 115; // 's'
-const magic3 = 109; // 'm'
+// MagicNumber = 0x6d736100;
+const magic0 = 0x00;  // '\0'
+const magic1 = 0x61;  // 'a'
+const magic2 = 0x73;  // 's'
+const magic3 = 0x6d;  // 'm'
 
-// EncodingVersion = -1 (to be changed to 1 at some point in the future)
-const ver0 = 0xff;
-const ver1 = 0xff;
-const ver2 = 0xff;
-const ver3 = 0xff;
+// EncodingVersion = 10 (to be changed to 1 at some point in the future)
+const ver0 = 0x0a;
+const ver1 = 0x00;
+const ver2 = 0x00;
+const ver3 = 0x00;
 
 // Section names
 const sigLabel = "sig";
 const declLabel = "decl";
 const tableLabel = "table";
 const importLabel = "import";
 const exportLabel = "export";
 const funcLabel = "func";
 const dataLabel = "data";
 
 const magicError = /failed to match magic number/;
 const versionError = /failed to match binary version/;
 const extraError = /failed to consume all bytes of module/;
-const sectionError = /failed to read section name/;
+const sectionError = /failed to start section/;
 
 const I32Code = 0;
 const I64Code = 1;
 const F32Code = 2;
 const F64Code = 3;
 const I32x4Code = 4;
 const F32x4Code = 5;
 const B32x4Code = 6;
@@ -48,40 +48,49 @@ function varU32(u32) {
 }
 
 const U32MAX_LEB = [255, 255, 255, 255, 15];
 
 function moduleHeaderThen(...rest) {
     return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
 }
 
+const wasmEval = Wasm.instantiateModule;
+
 assertErrorMessage(() => wasmEval(toBuf([])), TypeError, magicError);
 assertErrorMessage(() => wasmEval(toBuf([42])), TypeError, magicError);
 assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2])), TypeError, magicError);
 assertErrorMessage(() => wasmEval(toBuf([1,2,3,4])), TypeError, magicError);
 assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3])), TypeError, versionError);
 assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, 1])), TypeError, versionError);
 assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, ver0])), TypeError, versionError);
 assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), TypeError, versionError);
 
 var o = wasmEval(toBuf(moduleHeaderThen(0)));
 assertEq(Object.getOwnPropertyNames(o).length, 0);
 
 assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(1))), TypeError, sectionError);
-assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 1))), TypeError, extraError);
+assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 0))), TypeError, extraError);
+assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 1))), TypeError, sectionError);
 
 function cstring(name) {
     return (name + '\0').split('').map(c => c.charCodeAt(0));
 }
 
+function string(name) {
+    return name.split('').map(c => c.charCodeAt(0));
+}
+
 function moduleWithSections(sectionArray) {
     var bytes = moduleHeaderThen();
     for (let section of sectionArray) {
-        bytes.push(...cstring(section.name));
-        bytes.push(...varU32(section.body.length));
+        var nameLength = varU32(section.name.length);
+        bytes.push(...varU32(nameLength.length + section.name.length + section.body.length));
+        bytes.push(...nameLength);
+        bytes.push(...string(section.name));
         bytes.push(...section.body);
     }
     bytes.push(0);
     return bytes;
 }
 
 function sigSection(sigs) {
     var body = [];
@@ -98,22 +107,26 @@ function sigSection(sigs) {
 function declSection(decls) {
     var body = [];
     body.push(...varU32(decls.length));
     for (let decl of decls)
         body.push(...varU32(decl));
     return { name: declLabel, body };
 }
 
-function funcSection(func) {
-    var body = [];
-    var locals = varU32(func.locals.length);
+function funcBody(func) {
+    var body = varU32(func.locals.length);
     for (let local of func.locals)
-        locals.push(...varU32(local));
-    body = body.concat(locals, func.body);
+        body.push(...varU32(local));
+    body.push(...varU32(func.body.length));
+    return body.concat(...func.body);
+}
+
+function bodySection(bodies) {
+    var body = [].concat(...bodies);
     return { name: funcLabel, body };
 }
 
 function importSection(imports) {
     var body = [];
     for (let imp of imports) {
         body.push(...cstring(funcLabel));
         body.push(...varU32(imp.sigIndex));
@@ -129,17 +142,17 @@ function tableSection(elems) {
     body.push(...varU32(elems.length));
     for (let i of elems)
         body.push(...varU32(i));
     return { name: tableLabel, body };
 }
 
 const v2vSig = {args:[], ret:VoidCode};
 const i2vSig = {args:[I32Code], ret:VoidCode};
-const v2vFunc = funcSection({locals:[], body:[0]});
+const v2vBody = funcBody({locals:[], body:[]});
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}, {args:[], ret:VoidCode}])]))), TypeError, /duplicate signature/);
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1]}]))), TypeError);
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1, 1, 0]}]))), TypeError);
 
@@ -148,32 +161,32 @@ wasmEval(toBuf(moduleWithSections([sigSe
 wasmEval(toBuf(moduleWithSections([sigSection([i2vSig])])));
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig, i2vSig])])));
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), TypeError, /bad expression type/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/);
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/);
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([1])]))), TypeError, /signature index out of range/);
-assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /fewer function definitions than declarations/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), v2vFunc])));
+assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /expected function bodies/);
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody])])));
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), {name: importLabel, body:[]}]))), TypeError);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([])])));
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}});
 
 wasmEval(toBuf(moduleWithSections([
     sigSection([v2vSig]),
     importSection([{sigIndex:0, module:"a", func:""}]),
     declSection([0]),
-    v2vFunc])), {a:()=>{}});
+    bodySection([v2vBody])])), {a:()=>{}});
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataLabel, body: [], } ]))), TypeError, /data section requires a memory section/);
 
 wasmEval(toBuf(moduleWithSections([tableSection([])])));
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([tableSection([0])]))), TypeError, /table element out of range/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), v2vFunc])));
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), v2vFunc])));
-assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), v2vFunc]))), TypeError, /table element out of range/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), v2vFunc, v2vFunc, v2vFunc])));
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), v2vFunc, v2vFunc, v2vFunc])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), bodySection([v2vBody])])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), bodySection([v2vBody])])));
+assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), bodySection([v2vBody])]))), TypeError, /table element out of range/);
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), bodySection([v2vBody, v2vBody, v2vBody])])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), bodySection([v2vBody, v2vBody, v2vBody])])));
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -572,71 +572,75 @@ BaselineScript::pcMappingReader(size_t i
     uint8_t* dataStart = pcMappingData() + entry.bufferOffset;
     uint8_t* dataEnd = (indexEntry == numPCMappingIndexEntries() - 1)
         ? pcMappingData() + pcMappingSize_
         : pcMappingData() + pcMappingIndexEntry(indexEntry + 1).bufferOffset;
 
     return CompactBufferReader(dataStart, dataEnd);
 }
 
+struct ICEntries
+{
+    BaselineScript* const baseline_;
+
+    explicit ICEntries(BaselineScript* baseline) : baseline_(baseline) {}
+
+    ICEntry& operator[](size_t index) const {
+        return baseline_->icEntry(index);
+    }
+};
+
 ICEntry&
 BaselineScript::icEntryFromReturnOffset(CodeOffset returnOffset)
 {
-    size_t bottom = 0;
-    size_t top = numICEntries();
-    size_t mid = bottom + (top - bottom) / 2;
-    while (mid < top) {
-        ICEntry& midEntry = icEntry(mid);
-        if (midEntry.returnOffset().offset() < returnOffset.offset())
-            bottom = mid + 1;
-        else
-            top = mid;
-        mid = bottom + (top - bottom) / 2;
-    }
+    size_t loc;
+#ifdef DEBUG
+    bool found =
+#endif
+        BinarySearchIf(ICEntries(this), 0, numICEntries(),
+                       [&returnOffset](ICEntry& entry) {
+                           size_t roffset = returnOffset.offset();
+                           size_t entryRoffset = entry.returnOffset().offset();
+                           if (roffset < entryRoffset)
+                               return -1;
+                           if (entryRoffset < roffset)
+                               return 1;
+                           return 0;
+                       },
+                       &loc);
 
-    MOZ_ASSERT(mid < numICEntries());
-    MOZ_ASSERT(icEntry(mid).returnOffset().offset() == returnOffset.offset());
+    MOZ_ASSERT(found);
+    MOZ_ASSERT(loc < numICEntries());
+    MOZ_ASSERT(icEntry(loc).returnOffset().offset() == returnOffset.offset());
+    return icEntry(loc);
+}
 
-    return icEntry(mid);
+static inline size_t
+ComputeBinarySearchMid(BaselineScript* baseline, uint32_t pcOffset)
+{
+    size_t loc;
+    BinarySearchIf(ICEntries(baseline), 0, baseline->numICEntries(),
+                   [pcOffset](ICEntry& entry) {
+                       uint32_t entryOffset = entry.pcOffset();
+                       if (pcOffset < entryOffset)
+                           return -1;
+                       if (entryOffset < pcOffset)
+                           return 1;
+                       return 0;
+                   },
+                   &loc);
+    return loc;
 }
 
 uint8_t*
 BaselineScript::returnAddressForIC(const ICEntry& ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
-static inline size_t
-ComputeBinarySearchMid(BaselineScript* baseline, uint32_t pcOffset)
-{
-    struct ICEntries
-    {
-        BaselineScript* const baseline_;
-
-        explicit ICEntries(BaselineScript* baseline) : baseline_(baseline) {}
-
-        ICEntry& operator[](size_t index) const {
-            return baseline_->icEntry(index);
-        }
-    };
-
-    size_t loc;
-    BinarySearchIf(ICEntries(baseline), 0, baseline->numICEntries(),
-                   [pcOffset](ICEntry& entry) {
-                       uint32_t entryOffset = entry.pcOffset();
-                       if (pcOffset < entryOffset)
-                           return -1;
-                       if (pcOffset > entryOffset)
-                           return 1;
-                       return 0;
-                   },
-                   &loc);
-    return loc;
-}
-
 ICEntry&
 BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
 {
     // Multiple IC entries can have the same PC offset, but this method only looks for
     // those which have isForOp() set.
     size_t mid = ComputeBinarySearchMid(this, pcOffset);
 
     // Found an IC entry with a matching PC offset.  Search backward, and then
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8324,16 +8324,18 @@ CodeGenerator::generateAsmJS(wasm::FuncO
 #if defined(JS_ION_PERF)
     // Note the end of the inline code and start of the OOL code.
     gen->perfSpewer().noteEndInlineCode(masm);
 #endif
 
     if (!generateOutOfLineCode())
         return false;
 
+    masm.flush();
+
     offsets->end = masm.currentOffset();
 
     MOZ_ASSERT(!masm.failureLabel()->used());
     MOZ_ASSERT(snapshots_.listSize() == 0);
     MOZ_ASSERT(snapshots_.RVATableSize() == 0);
     MOZ_ASSERT(recovers_.size() == 0);
     MOZ_ASSERT(bailouts_.empty());
     MOZ_ASSERT(graph.numConstants() == 0);
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -380,16 +380,22 @@ class MacroAssembler : public MacroAssem
 
     size_t instructionsSize() const {
         return size();
     }
 
     //{{{ check_macroassembler_style
   public:
     // ===============================================================
+    // MacroAssembler high-level usage.
+
+    // Flushes the assembly buffer, on platforms that need it.
+    void flush() PER_SHARED_ARCH;
+
+    // ===============================================================
     // Frame manipulation functions.
 
     inline uint32_t framePushed() const;
     inline void setFramePushed(uint32_t framePushed);
     inline void adjustFrame(int32_t value);
 
     // Adjust the frame, to account for implicit modification of the stack
     // pointer, such that callee can remove arguments on the behalf of the
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2865,39 +2865,34 @@ CloneForDeadBranches(TempAllocator& allo
 }
 
 // Examine all the users of |candidate| and determine the most aggressive
 // truncate kind that satisfies all of them.
 static MDefinition::TruncateKind
 ComputeRequestedTruncateKind(MDefinition* candidate, bool* shouldClone)
 {
     bool isCapturedResult = false;
-    bool isObservableResult = false;
     bool isRecoverableResult = true;
-    bool hasUseRemoved = candidate->isUseRemoved();
 
     MDefinition::TruncateKind kind = MDefinition::Truncate;
     for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) {
         if (use->consumer()->isResumePoint()) {
             // Truncation is a destructive optimization, as such, we need to pay
             // attention to removed branches and prevent optimization
             // destructive optimizations if we have no alternative. (see
             // UseRemoved flag)
             isCapturedResult = true;
-            isObservableResult = isObservableResult ||
-                use->consumer()->toResumePoint()->isObservableOperand(*use);
             isRecoverableResult = isRecoverableResult &&
                 use->consumer()->toResumePoint()->isRecoverableOperand(*use);
             continue;
         }
 
         MDefinition* consumer = use->consumer()->toDefinition();
         if (consumer->isRecoveredOnBailout()) {
             isCapturedResult = true;
-            hasUseRemoved = hasUseRemoved || consumer->isUseRemoved();
             continue;
         }
 
         MDefinition::TruncateKind consumerKind = consumer->operandTruncateKind(consumer->indexOf(*use));
         kind = Min(kind, consumerKind);
         if (kind == MDefinition::NoTruncate)
             break;
     }
@@ -2912,34 +2907,26 @@ ComputeRequestedTruncateKind(MDefinition
     bool needsConversion = !candidate->range() || !candidate->range()->isInt32();
 
     // If the candidate instruction appears as operand of a resume point or a
     // recover instruction, and we have to truncate its result, then we might
     // have to either recover the result during the bailout, or avoid the
     // truncation.
     if (isCapturedResult && needsConversion) {
 
-        // These optimizations are pointless if there are no removed uses or any
-        // resume point observing the result.  Not having any means that we know
-        // everything about where this results flows into.
-        if ((hasUseRemoved || (isObservableResult && isRecoverableResult)) &&
-            candidate->canRecoverOnBailout())
-        {
-            // The cloned instruction is expected to be used as a recover
-            // instruction.
+        // If the result can be recovered from all the resume points (not needed
+        // for iterating over the inlined frames), and this instruction can be
+        // recovered on bailout, then we can clone it and use the cloned
+        // instruction to encode the recover instruction.  Otherwise, we should
+        // keep the original result and bailout if the value is not in the int32
+        // range.
+        if (isRecoverableResult && candidate->canRecoverOnBailout())
             *shouldClone = true;
-
-        } else if (hasUseRemoved || isObservableResult) {
-            // 1. If uses are removed and we cannot recover the result, then we
-            // need to keep the expected result for dead branches.
-            //
-            // 2. If the result is observable and not recoverable, then the
-            // result might be read while the frame is on the stack.
+        else
             kind = Min(kind, MDefinition::TruncateAfterBailouts);
-        }
     }
 
     return kind;
 }
 
 static MDefinition::TruncateKind
 ComputeTruncateKind(MDefinition* candidate, bool* shouldClone)
 {
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4574,16 +4574,25 @@ MacroAssemblerARMCompat::asMasm()
 const MacroAssembler&
 MacroAssemblerARMCompat::asMasm() const
 {
     return *static_cast<const MacroAssembler*>(this);
 }
 
 //{{{ check_macroassembler_style
 // ===============================================================
+// MacroAssembler high-level usage.
+
+void
+MacroAssembler::flush()
+{
+    Assembler::flush();
+}
+
+// ===============================================================
 // Stack manipulation functions.
 
 void
 MacroAssembler::PushRegsInMask(LiveRegisterSet set)
 {
     int32_t diffF = set.fpus().getPushSizeInBytes();
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
 
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -304,16 +304,25 @@ template void
 MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
                                                     Register value, Register temp, AnyRegister output);
 template void
 MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
                                                     Register value, Register temp, AnyRegister output);
 
 //{{{ check_macroassembler_style
 // ===========================================================