Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 07 Apr 2015 16:26:21 -0400
changeset 238126 bfdc7e03426d51fe1816d8bf652a54455ccdd11b
parent 238125 0d0c13403046d0abcb2010f1729360a46517545c (current diff)
parent 237996 078128c2600a0c695f8a16bf561b171a523daedd (diff)
child 238127 76dbe0cc2ac7023de200434fbf3678e0ece95eb9
push id58133
push userryanvm@gmail.com
push dateWed, 08 Apr 2015 16:59:36 +0000
treeherdermozilla-inbound@8911c111a6ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
browser/devtools/shared/widgets/FlameGraph.jsm
browser/themes/linux/theme-switcher-icon.png
browser/themes/osx/theme-switcher-icon.png
browser/themes/osx/theme-switcher-icon@2x.png
browser/themes/windows/theme-switcher-icon-aero.png
browser/themes/windows/theme-switcher-icon.png
dom/media/fmp4/AVCCDecoderModule.cpp
dom/media/fmp4/AVCCDecoderModule.h
layout/analysis/pixel-conversion.js
layout/analysis/simple-match.js
services/datareporting/sessions.jsm
services/datareporting/tests/xpcshell/test_session_recorder.js
toolkit/themes/shared/reader/RM-Close-hover-24x24.svg
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Merge day clobber
\ No newline at end of file
+Hitting failures with Taskcluster-based jobs that seem like needs-clobber.
+
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -547,18 +547,17 @@ nsContextMenu.prototype = {
       aNode = gContextMenuContentData.event.target;
       aRangeParent = gContextMenuContentData.event.rangeParent;
       aRangeOffset = gContextMenuContentData.event.rangeOffset;
       editFlags = gContextMenuContentData.editFlags;
     }
 
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (aNode.namespaceURI == xulNS ||
-        aNode.nodeType == Node.DOCUMENT_NODE ||
-        this.isDisabledForEvents(aNode)) {
+        aNode.nodeType == Node.DOCUMENT_NODE) {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
@@ -1529,26 +1528,16 @@ nsContextMenu.prototype = {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
            "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
   },
 
-  isDisabledForEvents: function(aNode) {
-    let ownerDoc = aNode.ownerDocument;
-    return
-      ownerDoc.defaultView &&
-      ownerDoc.defaultView
-              .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-              .getInterface(Components.interfaces.nsIDOMWindowUtils)
-              .isNodeDisabledForEvents(aNode);
-  },
-
   isTargetATextBox: function(node) {
     if (node instanceof HTMLInputElement)
       return node.mozIsTextField(false);
 
     return (node instanceof HTMLTextAreaElement);
   },
 
   // Determines whether or not the separator with the specified ID should be
--- a/browser/components/preferences/in-content/content.xul
+++ b/browser/components/preferences/in-content/content.xul
@@ -82,56 +82,70 @@
       </row>
     </rows>
   </grid>
 </groupbox>
 
 <!-- Fonts and Colors -->
 <groupbox id="fontsGroup" data-category="paneContent" hidden="true">
   <caption><label>&fontsAndColors.label;</label></caption>
-  <hbox align="center">
-    <label control="defaultFont" accesskey="&defaultFont.accesskey;">&defaultFont.label;</label>
-    <menulist id="defaultFont" flex="1"/>
-    <label id="defaultFontSizeLabel" control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label>
-    <menulist id="defaultFontSize">
-      <menupopup>
-        <menuitem value="9" label="9"/>
-        <menuitem value="10" label="10"/>
-        <menuitem value="11" label="11"/>
-        <menuitem value="12" label="12"/>
-        <menuitem value="13" label="13"/>
-        <menuitem value="14" label="14"/>
-        <menuitem value="15" label="15"/>
-        <menuitem value="16" label="16"/>
-        <menuitem value="17" label="17"/>
-        <menuitem value="18" label="18"/>
-        <menuitem value="20" label="20"/>
-        <menuitem value="22" label="22"/>
-        <menuitem value="24" label="24"/>
-        <menuitem value="26" label="26"/>
-        <menuitem value="28" label="28"/>
-        <menuitem value="30" label="30"/>
-        <menuitem value="32" label="32"/>
-        <menuitem value="34" label="34"/>
-        <menuitem value="36" label="36"/>
-        <menuitem value="40" label="40"/>
-        <menuitem value="44" label="44"/>
-        <menuitem value="48" label="48"/>
-        <menuitem value="56" label="56"/>
-        <menuitem value="64" label="64"/>
-        <menuitem value="72" label="72"/>
-      </menupopup>
-    </menulist>
-    <button id="advancedFonts" icon="select-font"
-            label="&advancedFonts.label;"
-            accesskey="&advancedFonts.accesskey;"/>
-    <button id="colors" icon="select-color"
-            label="&colors.label;"
-            accesskey="&colors.accesskey;"/>
-  </hbox>
+
+  <grid id="fontsGrid">
+    <columns>
+      <column flex="1"/>
+      <column/>
+    </columns>
+    <rows id="fontsRows">
+      <row id="fontRow">
+        <hbox align="center">
+          <label control="defaultFont" accesskey="&defaultFont.accesskey;">&defaultFont.label;</label>
+          <menulist id="defaultFont" />
+          <label id="defaultFontSizeLabel" control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label>
+          <menulist id="defaultFontSize">
+            <menupopup>
+              <menuitem value="9" label="9"/>
+              <menuitem value="10" label="10"/>
+              <menuitem value="11" label="11"/>
+              <menuitem value="12" label="12"/>
+              <menuitem value="13" label="13"/>
+              <menuitem value="14" label="14"/>
+              <menuitem value="15" label="15"/>
+              <menuitem value="16" label="16"/>
+              <menuitem value="17" label="17"/>
+              <menuitem value="18" label="18"/>
+              <menuitem value="20" label="20"/>
+              <menuitem value="22" label="22"/>
+              <menuitem value="24" label="24"/>
+              <menuitem value="26" label="26"/>
+              <menuitem value="28" label="28"/>
+              <menuitem value="30" label="30"/>
+              <menuitem value="32" label="32"/>
+              <menuitem value="34" label="34"/>
+              <menuitem value="36" label="36"/>
+              <menuitem value="40" label="40"/>
+              <menuitem value="44" label="44"/>
+              <menuitem value="48" label="48"/>
+              <menuitem value="56" label="56"/>
+              <menuitem value="64" label="64"/>
+              <menuitem value="72" label="72"/>
+            </menupopup>
+          </menulist>
+        </hbox>
+        <button id="advancedFonts" icon="select-font"
+                label="&advancedFonts.label;"
+                accesskey="&advancedFonts.accesskey;"/>
+      </row>
+      <row id="colorsRow">
+        <hbox/>
+        <button id="colors" icon="select-color"
+                label="&colors.label;"
+                accesskey="&colors.accesskey;"/>
+      </row>
+    </rows>
+  </grid>
 </groupbox>
 
 <!-- Languages -->
 <groupbox id="languagesGroup" data-category="paneContent" hidden="true">
   <caption><label>&languages.label;</label></caption>
 
   <hbox id="languagesBox" align="center">
     <description flex="1" control="chooseLanguage">&chooseLanguage.label;</description>
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -128,30 +128,30 @@
         <menuitem label="&historyHeader.custom.label;" value="custom"/>
       </menupopup>
     </menulist>
     <label>&historyHeader.post.label;</label>
   </hbox>
   <deck id="historyPane">
     <vbox id="historyRememberPane">
       <hbox align="center" flex="1">
-        <vbox>
+        <vbox flex="1">
           <description>&rememberDescription.label;</description>
           <separator class="thin"/>
           <description>&rememberActions.pre.label;<html:a
           class="inline-link" id="historyRememberClear" href="#"
           >&rememberActions.clearHistory.label;</html:a>&rememberActions.middle.label;<html:a
           class="inline-link" id="historyRememberCookies" href="#"
           >&rememberActions.removeCookies.label;</html:a>&rememberActions.post.label;</description>
         </vbox>
       </hbox>
     </vbox>
     <vbox id="historyDontRememberPane">
       <hbox align="center" flex="1">
-        <vbox>
+        <vbox flex="1">
           <description>&dontrememberDescription.label;</description>
           <separator class="thin"/>
           <description>&dontrememberActions.pre.label;<html:a
           class="inline-link" id="historyDontRememberClear" href="#"
           >&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description>
         </vbox>
       </hbox>
     </vbox>
--- a/browser/components/readinglist/ServerClient.jsm
+++ b/browser/components/readinglist/ServerClient.jsm
@@ -139,18 +139,18 @@ ServerClient.prototype = {
           status: response.status,
           headers: response.headers
         };
         try {
           if (response.body) {
             result.body = JSON.parse(response.body);
           }
         } catch (e) {
-          log.info("Failed to parse JSON body |${body}|: ${e}",
-                    {body: response.body, e});
+          log.debug("Response is not JSON. First 1024 chars: |${body}|",
+                    { body: response.body.substr(0, 1024) });
           // We don't reject due to this (and don't even make a huge amount of
           // log noise - eg, a 50X error from a load balancer etc may not write
           // JSON.
         }
 
         resolve(result);
       }
       // We are assuming the body has already been decoded and thus contains
--- a/browser/components/readinglist/sidebar.js
+++ b/browser/components/readinglist/sidebar.js
@@ -464,16 +464,19 @@ let RLSidebar = {
     if (msg.topic != "UpdateActiveItem") {
       return;
     }
 
     if (!msg.url) {
       this.activeItem = null;
     } else {
       ReadingList.itemForURL(msg.url).then(item => {
-        this.activeItem = this.itemNodesById.get(item.id);
+        let node;
+        if (item && (node = this.itemNodesById.get(item.id))) {
+          this.activeItem = node;
+        }
       });
     }
   }
 };
 
 
 addEventListener("DOMContentLoaded", () => RLSidebar.init());
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -42,23 +42,23 @@ devtools.lazyRequireGetter(this, "CallVi
 devtools.lazyRequireGetter(this, "ThreadNode",
   "devtools/shared/profiler/tree-model", true);
 devtools.lazyRequireGetter(this, "FrameNode",
   "devtools/shared/profiler/tree-model", true);
 devtools.lazyRequireGetter(this, "JITOptimizations",
   "devtools/shared/profiler/jit", true);
 devtools.lazyRequireGetter(this, "OptionsView",
   "devtools/shared/options-view", true);
+devtools.lazyRequireGetter(this, "FlameGraphUtils",
+  "devtools/shared/widgets/FlameGraph", true);
+devtools.lazyRequireGetter(this, "FlameGraph",
+  "devtools/shared/widgets/FlameGraph", true);
 
 devtools.lazyImporter(this, "CanvasGraphUtils",
   "resource:///modules/devtools/Graphs.jsm");
-devtools.lazyImporter(this, "FlameGraphUtils",
-  "resource:///modules/devtools/FlameGraph.jsm");
-devtools.lazyImporter(this, "FlameGraph",
-  "resource:///modules/devtools/FlameGraph.jsm");
 devtools.lazyImporter(this, "SideMenuWidget",
   "resource:///modules/devtools/SideMenuWidget.jsm");
 devtools.lazyImporter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 const BRANCH_NAME = "devtools.performance.ui.";
 
 // Events emitted by various objects in the panel.
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -37,16 +37,17 @@ support-files =
 [browser_perf-front-profiler-02.js]
 [browser_perf-front-profiler-03.js]
 [browser_perf-front-profiler-04.js]
 #[browser_perf-front-profiler-05.js] bug 1077464
 #[browser_perf-front-profiler-06.js]
 [browser_perf-front-01.js]
 [browser_perf-front-02.js]
 [browser_perf-jit-view-01.js]
+[browser_perf-jit-view-02.js]
 [browser_perf-jit-model-01.js]
 [browser_perf-jit-model-02.js]
 [browser_perf-jump-to-debugger-01.js]
 [browser_perf-jump-to-debugger-02.js]
 [browser_perf-options-01.js]
 [browser_perf-options-02.js]
 [browser_perf-options-invert-call-tree-01.js]
 [browser_perf-options-invert-call-tree-02.js]
--- a/browser/devtools/performance/test/browser_perf-jit-view-01.js
+++ b/browser/devtools/performance/test/browser_perf-jit-view-01.js
@@ -25,17 +25,17 @@ function spawnTest () {
   yield startRecording(panel);
   yield stopRecording(panel);
 
   yield DetailsView.selectView("js-calltree");
 
   yield injectAndRenderProfilerData();
 
   yield checkFrame(1, [0, 1]);
-  yield checkFrame(2, [1]);
+  yield checkFrame(2, [2]);
   yield checkFrame(3);
 
   let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
   let reset = once(JITOptimizationsView, EVENTS.OPTIMIZATIONS_RESET);
   RecordingsView.selectedIndex = 0;
   yield Promise.all([select, reset]);
   ok(true, "JITOptimizations view correctly reset when switching recordings.");
 
@@ -75,88 +75,111 @@ function spawnTest () {
     let isEmpty = $("#jit-optimizations-view").classList.contains("empty");
     if (expectedOptsIndex.length === 0) {
       ok(isEmpty, "JIT Optimizations view has an empty message when selecting a frame without opt data.");
       return;
     } else {
       ok(!isEmpty, "JIT Optimizations view has no empty message.");
     }
 
+    // Get the frame info for the first opt site, since all opt sites
+    // share the same frame info
+    let frameInfo = gOpts[expectedOptsIndex[0]]._testFrameInfo;
+
+    let { $headerName, $headerLine, $headerFile } = JITOptimizationsView;
+    ok(!$headerName.hidden, "header function name should be shown");
+    ok(!$headerLine.hidden, "header line should be shown");
+    ok(!$headerFile.hidden, "header file should be shown");
+    is($headerName.textContent, frameInfo.name, "correct header function name.");
+    is($headerLine.textContent, frameInfo.line, "correct header line");
+    is($headerFile.textContent, frameInfo.file, "correct header file");
+
     // Need the value of the optimizations in its array, as its
     // an index used internally by the view to uniquely ID the opt
     for (let i of expectedOptsIndex) {
       let opt = gOpts[i];
       let { types: ionTypes, attempts } = opt;
 
       // Check attempts
       is($$(`.tree-widget-container li[data-id='["${i}","${i}-attempts"]'] .tree-widget-children .tree-widget-item`).length, attempts.length,
         `found ${attempts.length} attempts`);
 
       for (let j = 0; j < ionTypes.length; j++) {
         ok($(`.tree-widget-container li[data-id='["${i}","${i}-types","${i}-types-${j}"]']`),
           "found an ion type row");
       }
 
-      // The second optimization should display optimization failures.
+      // The second and third optimization should display optimization failures.
       let warningIcon = $(`.tree-widget-container li[data-id='["${i}"]'] .opt-icon[severity=warning]`);
-      if (i === 1) {
+      if (i === 1 || i === 2) {
         ok(warningIcon, "did find a warning icon for all strategies failing.");
       } else {
         ok(!warningIcon, "did not find a warning icon for no successful strategies");
       }
     }
   }
 }
 
 let gSamples = [{
   time: 5,
   frames: [
     { location: "(root)" },
     { location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
-    { location: "B (http://foo/bar/baz:34)", optsIndex: 1 },
+    { location: "B (http://foo/bar/boo:34)", optsIndex: 2 },
     { location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
   time: 5 + 1,
   frames: [
     { location: "(root)" },
     { location: "A (http://foo/bar/baz:12)" },
-    { location: "B (http://foo/bar/baz:34)" },
+    { location: "B (http://foo/bar/boo:34)" },
   ]
 }, {
   time: 5 + 1 + 2,
   frames: [
     { location: "(root)" },
     { location: "A (http://foo/bar/baz:12)", optsIndex: 1 },
-    { location: "B (http://foo/bar/baz:34)" },
+    { location: "B (http://foo/bar/boo:34)" },
   ]
 }, {
   time: 5 + 1 + 2 + 7,
   frames: [
     { location: "(root)" },
     { location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
     { location: "E (http://foo/bar/baz:90)" },
     { location: "F (http://foo/bar/baz:99)" }
   ]
 }];
 
 // Array of OptimizationSites
 let gOpts = [{
+  _testFrameInfo: { name: "A", line: "12", file: "@baz" },
   line: 12,
   column: 2,
   types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
     { keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
     { keyedBy: "primitive", location: "self-hosted" }
   ]}],
   attempts: [
     { outcome: "Failure1", strategy: "SomeGetter1" },
     { outcome: "Failure2", strategy: "SomeGetter2" },
     { outcome: "Inlined", strategy: "SomeGetter3" },
   ]
 }, {
+  _testFrameInfo: { name: "A", line: "12", file: "@baz" },
+  line: 12,
+  types: [{ mirType: "Int32", site: "Receiver" }], // use no types
+  attempts: [
+    { outcome: "Failure1", strategy: "SomeGetter1" },
+    { outcome: "Failure2", strategy: "SomeGetter2" },
+    { outcome: "Failure3", strategy: "SomeGetter3" },
+  ]
+}, {
+  _testFrameInfo: { name: "B", line: "34", file: "@boo" },
   line: 34,
   types: [{ mirType: "Int32", site: "Receiver" }], // use no types
   attempts: [
     { outcome: "Failure1", strategy: "SomeGetter1" },
     { outcome: "Failure2", strategy: "SomeGetter2" },
     { outcome: "Failure3", strategy: "SomeGetter3" },
   ]
 }];
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-jit-view-02.js
@@ -0,0 +1,109 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the JIT Optimizations view does not display information
+ * for meta nodes when viewing "content only".
+ */
+
+let { CATEGORY_MASK } = devtools.require("devtools/shared/profiler/global");
+Services.prefs.setBoolPref(INVERT_PREF, false);
+Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
+
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
+  let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
+
+  let profilerData = { threads: [{samples: gSamples, optimizations: gOpts}] };
+
+  is(Services.prefs.getBoolPref(JIT_PREF), false, "show JIT Optimizations pref off by default");
+
+  // Make two recordings, so we have one to switch to later, as the
+  // second one will have fake sample data
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  yield DetailsView.selectView("js-calltree");
+
+  yield injectAndRenderProfilerData();
+
+  Services.prefs.setBoolPref(JIT_PREF, true);
+  // Click the frame
+  let rendered = once(JITOptimizationsView, EVENTS.OPTIMIZATIONS_RENDERED);
+  mousedown(window, $$(".call-tree-item")[2]);
+  yield rendered;
+
+  ok($("#jit-optimizations-view").classList.contains("empty"),
+    "platform meta frame shows as empty");
+
+  let { $headerName, $headerLine, $headerFile } = JITOptimizationsView;
+  ok(!$headerName.hidden, "header function name should be shown");
+  ok($headerLine.hidden, "header line should be hidden");
+  ok($headerFile.hidden, "header file should be hidden");
+  is($headerName.textContent, "JIT", "correct header function name.");
+  is($headerLine.textContent, "", "correct header line (empty string).");
+  is($headerFile.textContent, "", "correct header file (empty string).");
+
+  yield teardown(panel);
+  finish();
+
+  function *injectAndRenderProfilerData() {
+    // Get current recording and inject our mock data
+    info("Injecting mock profile data");
+    let recording = PerformanceController.getCurrentRecording();
+    recording._profile = profilerData;
+
+    // Force a rerender
+    let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
+    JsCallTreeView.render();
+    yield rendered;
+
+    Services.prefs.setBoolPref(JIT_PREF, true);
+    ok($("#jit-optimizations-view").classList.contains("empty"),
+      "JIT Optimizations view has empty message when no frames selected.");
+
+     Services.prefs.setBoolPref(JIT_PREF, false);
+  }
+}
+
+let gSamples = [{
+  time: 5,
+  frames: [
+    { location: "(root)" },
+    { location: "A (http://foo/bar/baz:12)", optsIndex: 0 }
+  ]
+}, {
+  time: 5 + 1,
+  frames: [
+    { location: "(root)" },
+    { location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
+    { location: "JS", optsIndex: 1, category: CATEGORY_MASK("js") },
+  ]
+}];
+
+// Array of OptimizationSites
+let gOpts = [{
+  line: 12,
+  column: 2,
+  types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
+    { keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
+    { keyedBy: "primitive", location: "self-hosted" }
+  ]}],
+  attempts: [
+    { outcome: "Failure1", strategy: "SomeGetter1" },
+    { outcome: "Failure2", strategy: "SomeGetter2" },
+    { outcome: "Inlined", strategy: "SomeGetter3" },
+  ]
+}, {
+  line: 22,
+  types: [{ mirType: "Int32", site: "Receiver" }], // use no types
+  attempts: [
+    { outcome: "Failure1", strategy: "SomeGetter1" },
+    { outcome: "Failure2", strategy: "SomeGetter2" },
+    { outcome: "Failure3", strategy: "SomeGetter3" },
+  ]
+}];
--- a/browser/devtools/performance/views/details-js-flamegraph.js
+++ b/browser/devtools/performance/views/details-js-flamegraph.js
@@ -19,29 +19,33 @@ let JsFlameGraphView = Heritage.extend(D
   /**
    * Sets up the view with event binding.
    */
   initialize: Task.async(function* () {
     DetailsSubview.initialize.call(this);
 
     this.graph = new FlameGraph($("#js-flamegraph-view"));
     this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
+    this.graph.setTheme(PerformanceController.getTheme());
     yield this.graph.ready();
 
     this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
+    this._onThemeChanged = this._onThemeChanged.bind(this);
 
+    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
     this.graph.on("selecting", this._onRangeChangeInGraph);
   }),
 
   /**
    * Unbinds events.
    */
   destroy: Task.async(function* () {
     DetailsSubview.destroy.call(this);
 
+    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
     this.graph.off("selecting", this._onRangeChangeInGraph);
 
     yield this.graph.destroy();
   }),
 
   /**
    * Method for handling all the set up for rendering a new flamegraph.
    *
@@ -88,10 +92,18 @@ let JsFlameGraphView = Heritage.extend(D
    */
   _onRerenderPrefChanged: function() {
     let recording = PerformanceController.getCurrentRecording();
     let profile = recording.getProfile();
     let samples = profile.threads[0].samples;
     FlameGraphUtils.removeFromCache(samples);
   },
 
+  /**
+   * Called when `devtools.theme` changes.
+   */
+  _onThemeChanged: function (_, theme) {
+    this.graph.setTheme(theme);
+    this.graph.refresh({ force: true });
+  },
+
   toString: () => "[object JsFlameGraphView]"
 });
--- a/browser/devtools/performance/views/details-memory-flamegraph.js
+++ b/browser/devtools/performance/views/details-memory-flamegraph.js
@@ -18,29 +18,33 @@ let MemoryFlameGraphView = Heritage.exte
   /**
    * Sets up the view with event binding.
    */
   initialize: Task.async(function* () {
     DetailsSubview.initialize.call(this);
 
     this.graph = new FlameGraph($("#memory-flamegraph-view"));
     this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
+    this.graph.setTheme(PerformanceController.getTheme());
     yield this.graph.ready();
 
     this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
+    this._onThemeChanged = this._onThemeChanged.bind(this);
 
+    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
     this.graph.on("selecting", this._onRangeChangeInGraph);
   }),
 
   /**
    * Unbinds events.
    */
   destroy: Task.async(function* () {
     DetailsSubview.destroy.call(this);
 
+    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
     this.graph.off("selecting", this._onRangeChangeInGraph);
 
     yield this.graph.destroy();
   }),
 
   /**
    * Method for handling all the set up for rendering a new flamegraph.
    *
@@ -86,10 +90,18 @@ let MemoryFlameGraphView = Heritage.exte
    */
   _onRerenderPrefChanged: function() {
     let recording = PerformanceController.getCurrentRecording();
     let allocations = recording.getAllocations();
     let samples = RecordingUtils.getSamplesFromAllocations(allocations);
     FlameGraphUtils.removeFromCache(samples);
   },
 
+  /**
+   * Called when `devtools.theme` changes.
+   */
+  _onThemeChanged: function (_, theme) {
+    this.graph.setTheme(theme);
+    this.graph.refresh({ force: true });
+  },
+
   toString: () => "[object MemoryFlameGraphView]"
 });
--- a/browser/devtools/performance/views/jit-optimizations.js
+++ b/browser/devtools/performance/views/jit-optimizations.js
@@ -22,16 +22,19 @@ let JITOptimizationsView = {
    * Initialization function called when the tool starts up.
    */
   initialize: function () {
     this.reset = this.reset.bind(this);
     this._onFocusFrame = this._onFocusFrame.bind(this);
     this._toggleVisibility = this._toggleVisibility.bind(this);
 
     this.el = $("#jit-optimizations-view");
+    this.$headerName = $("#jit-optimizations-header .header-function-name");
+    this.$headerFile = $("#jit-optimizations-header .header-file");
+    this.$headerLine = $("#jit-optimizations-header .header-line");
 
     this.tree = new TreeWidget($("#jit-optimizations-raw-view"), {
       sorted: false,
       emptyText: JIT_EMPTY_TEXT
     });
 
     // Start the tree by resetting.
     this.reset();
@@ -43,16 +46,17 @@ let JITOptimizationsView = {
     JsCallTreeView.on("focus", this._onFocusFrame);
   },
 
   /**
    * Destruction function called when the tool cleans up.
    */
   destroy: function () {
     this.tree = null;
+    this.$headerName = this.$headerFile = this.$headerLine = this.el = null;
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this.reset);
     PerformanceController.off(EVENTS.PREF_CHANGED, this._toggleVisibility);
     JsCallTreeView.off("focus", this._onFocusFrame);
   },
 
   /**
    * Takes a FrameNode, with corresponding optimization data to be displayed
    * in the view.
@@ -120,17 +124,19 @@ let JITOptimizationsView = {
     let view = this.tree;
 
     // Set header information, even if the frame node
     // does not have any optimization data
     let frameData = frameNode.getInfo();
     this._setHeaders(frameData);
     this.clear();
 
-    if (!frameNode.hasOptimizations()) {
+    // If this frame node does not have optimizations, or if its a meta node in the
+    // case of only showing content, reset the view.
+    if (!frameNode.hasOptimizations() || frameNode.isMetaCategory) {
       this.reset();
       return;
     }
     this.el.classList.remove("empty");
 
     // An array of sorted OptimizationSites.
     let sites = frameNode.getOptimizations().getOptimizationSites();
 
@@ -330,28 +336,37 @@ let JITOptimizationsView = {
     let fileName;
 
     if (this._isLinkableURL(url)) {
       fileName = url.slice(url.lastIndexOf("/") + 1);
       node.classList.add("debugger-link");
       node.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + url);
       node.addEventListener("click", () => viewSourceInDebugger(url, line));
     }
-    node.textContent = `@${fileName || url}`;
+    fileName = fileName || url || "";
+    node.textContent = fileName ? `@${fileName}` : "";
     return node;
   },
 
   /**
    * Updates the headers with the current frame's data.
    */
 
   _setHeaders: function (frameData) {
-    $("#jit-optimizations-header .header-function-name").textContent = frameData.functionName;
-    this._createDebuggerLinkNode(frameData.url, frameData.line, $("#jit-optimizations-header .header-file"));
-    $("#jit-optimizations-header .header-line").textContent = frameData.line;
+    let isMeta = frameData.isMetaCategory;
+    let name = isMeta ? frameData.categoryData.label : frameData.functionName;
+    let url = isMeta ? "" : frameData.url;
+    let line = isMeta ? "" : frameData.line;
+
+    this.$headerName.textContent = name;
+    this.$headerLine.textContent = line;
+    this._createDebuggerLinkNode(url, line, this.$headerFile);
+
+    this.$headerLine.hidden = isMeta;
+    this.$headerFile.hidden = isMeta;
   },
 
   /**
    * Takes a string and returns a boolean indicating whether or not
    * this is a valid url for linking to the debugger.
    *
    * @param {String} url
    * @return {Boolean}
--- a/browser/devtools/shared/moz.build
+++ b/browser/devtools/shared/moz.build
@@ -16,17 +16,16 @@ EXTRA_JS_MODULES.devtools += [
     'Parser.jsm',
     'SplitView.jsm',
 ]
 
 EXTRA_JS_MODULES.devtools += [
     'widgets/AbstractTreeItem.jsm',
     'widgets/BreadcrumbsWidget.jsm',
     'widgets/Chart.jsm',
-    'widgets/FlameGraph.jsm',
     'widgets/Graphs.jsm',
     'widgets/GraphsWorker.js',
     'widgets/SideMenuWidget.jsm',
     'widgets/SimpleListWidget.jsm',
     'widgets/VariablesView.jsm',
     'widgets/VariablesViewController.jsm',
     'widgets/ViewHelpers.jsm',
 ]
@@ -60,13 +59,14 @@ EXTRA_JS_MODULES.devtools.shared += [
     'theme.js',
     'undo.js',
 ]
 
 EXTRA_JS_MODULES.devtools.shared.widgets += [
     'widgets/CubicBezierPresets.js',
     'widgets/CubicBezierWidget.js',
     'widgets/FastListWidget.js',
+    'widgets/FlameGraph.js',
     'widgets/Spectrum.js',
     'widgets/TableWidget.js',
     'widgets/Tooltip.js',
     'widgets/TreeWidget.js',
 ]
--- a/browser/devtools/shared/test/browser_flame-graph-01.js
+++ b/browser/devtools/shared/test/browser_flame-graph-01.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that flame graph widget works properly.
 
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_flame-graph-02.js
+++ b/browser/devtools/shared/test/browser_flame-graph-02.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that flame graph widgets may have a fixed width or height.
 
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_flame-graph-03a.js
+++ b/browser/devtools/shared/test/browser_flame-graph-03a.js
@@ -3,17 +3,17 @@
 
 // Tests that selections in the flame graph widget work properly.
 
 let TEST_DATA = [{ color: "#f00", blocks: [{ x: 0, y: 0, width: 50, height: 20, text: "FOO" }, { x: 50, y: 0, width: 100, height: 20, text: "BAR" }] }, { color: "#00f", blocks: [{ x: 0, y: 30, width: 30, height: 20, text: "BAZ" }] }];
 let TEST_BOUNDS = { startTime: 0, endTime: 150 };
 let TEST_WIDTH = 200;
 let TEST_HEIGHT = 100;
 
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_flame-graph-03b.js
+++ b/browser/devtools/shared/test/browser_flame-graph-03b.js
@@ -4,17 +4,17 @@
 // Tests that selections in the flame graph widget work properly on HiDPI.
 
 let TEST_DATA = [{ color: "#f00", blocks: [{ x: 0, y: 0, width: 50, height: 20, text: "FOO" }, { x: 50, y: 0, width: 100, height: 20, text: "BAR" }] }, { color: "#00f", blocks: [{ x: 0, y: 30, width: 30, height: 20, text: "BAZ" }] }];
 let TEST_BOUNDS = { startTime: 0, endTime: 150 };
 let TEST_WIDTH = 200;
 let TEST_HEIGHT = 100;
 let TEST_DPI_DENSITIY = 2;
 
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_flame-graph-03c.js
+++ b/browser/devtools/shared/test/browser_flame-graph-03c.js
@@ -4,17 +4,17 @@
 // Tests that vertical panning in the flame graph widget works properly.
 
 let TEST_DATA = [{ color: "#f00", blocks: [{ x: 0, y: 0, width: 50, height: 20, text: "FOO" }, { x: 50, y: 0, width: 100, height: 20, text: "BAR" }] }, { color: "#00f", blocks: [{ x: 0, y: 30, width: 30, height: 20, text: "BAZ" }] }];
 let TEST_BOUNDS = { startTime: 0, endTime: 150 };
 let TEST_WIDTH = 200;
 let TEST_HEIGHT = 100;
 let TEST_DPI_DENSITIY = 2;
 
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_flame-graph-04.js
+++ b/browser/devtools/shared/test/browser_flame-graph-04.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that text metrics in the flame graph widget work properly.
 
 let HTML_NS = "http://www.w3.org/1999/xhtml";
-let FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = 8; // px
+let FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = 9; // px
 let FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY = "sans-serif";
 let {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
-let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraph} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 let L10N = new ViewHelpers.L10N();
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
--- a/browser/devtools/shared/test/browser_flame-graph-utils-01.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-01.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that text metrics and data conversion from profiler samples
 // widget work properly in the flame graph.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils, FLAME_GRAPH_BLOCK_HEIGHT} = devtools.require("devtools/shared/widgets/FlameGraph");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
@@ -106,156 +106,156 @@ let EXPECTED_OUTPUT = [{
   blocks: [{
     srcData: {
       startTime: 50,
       rawLocation: "A"
     },
     x: 50,
     y: 0,
     width: 410,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "A"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 50,
       rawLocation: "B"
     },
     x: 50,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 160,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "B"
   }, {
     srcData: {
       startTime: 330,
       rawLocation: "B"
     },
     x: 330,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 130,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "B"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "M"
     },
     x: 0,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "M"
   }, {
     srcData: {
       startTime: 50,
       rawLocation: "C"
     },
     x: 50,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "C"
   }, {
     srcData: {
       startTime: 330,
       rawLocation: "C"
     },
     x: 330,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 130,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "C"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "N"
     },
     x: 0,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "N"
   }, {
     srcData: {
       startTime: 100,
       rawLocation: "D"
     },
     x: 100,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 110,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "D"
   }, {
     srcData: {
       startTime: 460,
       rawLocation: "X"
     },
     x: 460,
     y: 0,
     width: 40,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "X"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 210,
       rawLocation: "E"
     },
     x: 210,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 120,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "E"
   }, {
     srcData: {
       startTime: 460,
       rawLocation: "Y"
     },
     x: 460,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 40,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "Y"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "P"
     },
     x: 0,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "P"
   }, {
     srcData: {
       startTime: 210,
       rawLocation: "F"
     },
     x: 210,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 120,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "F"
   }, {
     srcData: {
       startTime: 460,
       rawLocation: "Z"
     },
     x: 460,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 40,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "Z"
   }]
 }, {
   blocks: []
 }, {
   blocks: []
 }];
--- a/browser/devtools/shared/test/browser_flame-graph-utils-02.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-02.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests consecutive duplicate frames are removed from the flame graph data.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils, FLAME_GRAPH_BLOCK_HEIGHT} = devtools.require("devtools/shared/widgets/FlameGraph");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
@@ -68,29 +68,29 @@ let EXPECTED_OUTPUT = [{
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "A"
     },
     x: 0,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "A"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "B"
     },
     x: 0,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "B"
   }]
 }, {
   blocks: []
 }, {
   blocks: []
 }, {
   blocks: []
--- a/browser/devtools/shared/test/browser_flame-graph-utils-03.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-03.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if platform frames are removed from the flame graph data.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils, FLAME_GRAPH_BLOCK_HEIGHT} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {FrameNode} = devtools.require("devtools/shared/profiler/tree-model");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
@@ -67,27 +67,27 @@ let EXPECTED_OUTPUT = [{
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "http://A"
     },
     x: 0,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "http://A"
   }, {
     srcData: {
       startTime: 0,
       rawLocation: "file://C"
     },
     x: 0,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "file://C"
   }]
 }, {
   blocks: []
 }, {
   blocks: []
 }, {
   blocks: []
@@ -97,16 +97,16 @@ let EXPECTED_OUTPUT = [{
   blocks: []
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "https://B"
     },
     x: 0,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "https://B"
   }]
 }, {
   blocks: []
 }];
--- a/browser/devtools/shared/test/browser_flame-graph-utils-04.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-04.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if (idle) nodes are added when necessary in the flame graph data.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils, FLAME_GRAPH_BLOCK_HEIGHT} = devtools.require("devtools/shared/widgets/FlameGraph");
 let {FrameNode} = devtools.require("devtools/shared/profiler/tree-model");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
@@ -91,49 +91,49 @@ let EXPECTED_OUTPUT = [{
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "http://A"
     },
     x: 0,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "http://A"
   }, {
     srcData: {
       startTime: 0,
       rawLocation: "file://C"
     },
     x: 0,
-    y: 22,
+    y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "file://C"
   }, {
     srcData: {
       startTime: 100,
       rawLocation: "http://A"
     },
     x: 100,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "http://A"
   }]
 }, {
   blocks: [{
     srcData: {
       startTime: 50,
       rawLocation: "\m/"
     },
     x: 50,
     y: 0,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "\m/"
   }]
 }, {
   blocks: []
 }, {
   blocks: []
 }, {
   blocks: []
@@ -141,26 +141,26 @@ let EXPECTED_OUTPUT = [{
   blocks: []
 }, {
   blocks: [{
     srcData: {
       startTime: 0,
       rawLocation: "https://B"
     },
     x: 0,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "https://B"
   }, {
     srcData: {
       startTime: 100,
       rawLocation: "https://B"
     },
     x: 100,
-    y: 11,
+    y: FLAME_GRAPH_BLOCK_HEIGHT,
     width: 50,
-    height: 11,
+    height: FLAME_GRAPH_BLOCK_HEIGHT,
     text: "https://B"
   }]
 }, {
   blocks: []
 }];
--- a/browser/devtools/shared/test/browser_flame-graph-utils-05.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-05.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that flame graph data is cached, and that the cache may be cleared.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils} = devtools.require("devtools/shared/widgets/FlameGraph");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
--- a/browser/devtools/shared/test/browser_flame-graph-utils-hash.js
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-hash.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if (idle) nodes are added when necessary in the flame graph data.
 
-let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+let {FlameGraphUtils} = devtools.require("devtools/shared/widgets/FlameGraph");
 
 let test = Task.async(function*() {
   let hash1 = FlameGraphUtils._getStringHash("abc");
   let hash2 = FlameGraphUtils._getStringHash("acb");
   let hash3 = FlameGraphUtils._getStringHash(Array.from(Array(100000)).join("a"));
   let hash4 = FlameGraphUtils._getStringHash(Array.from(Array(100000)).join("b"));
 
   isnot(hash1, hash2, "The hashes should not be equal (1).");
rename from browser/devtools/shared/widgets/FlameGraph.jsm
rename to browser/devtools/shared/widgets/FlameGraph.js
--- a/browser/devtools/shared/widgets/FlameGraph.jsm
+++ b/browser/devtools/shared/widgets/FlameGraph.js
@@ -1,25 +1,19 @@
 /* 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/. */
 "use strict";
 
-const Cu = Components.utils;
-
-Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
-Cu.import("resource:///modules/devtools/Graphs.jsm");
-const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
-const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
-const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
-
-this.EXPORTED_SYMBOLS = [
-  "FlameGraph",
-  "FlameGraphUtils"
-];
+const { ViewHelpers } = require("resource:///modules/devtools/ViewHelpers.jsm");
+const { AbstractCanvasGraph, GraphArea, GraphAreaDragger } = require("resource:///modules/devtools/Graphs.jsm");
+const { Promise } = require("resource://gre/modules/Promise.jsm");
+const { Task } = require("resource://gre/modules/Task.jsm");
+const { getColor } = require("devtools/shared/theme");
+const EventEmitter = require("devtools/toolkit/event-emitter");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const GRAPH_SRC = "chrome://browser/content/devtools/graphs-frame.xhtml";
 const L10N = new ViewHelpers.L10N();
 
 const GRAPH_RESIZE_EVENTS_DRAIN = 100; // ms
 
 const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00035;
@@ -29,27 +23,24 @@ const GRAPH_MIN_SELECTION_WIDTH = 0.001;
 const GRAPH_HORIZONTAL_PAN_THRESHOLD = 10; // px
 const GRAPH_VERTICAL_PAN_THRESHOLD = 30; // px
 
 const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
 const TIMELINE_TICKS_MULTIPLE = 5; // ms
 const TIMELINE_TICKS_SPACING_MIN = 75; // px
 
 const OVERVIEW_HEADER_HEIGHT = 16; // px
-const OVERVIEW_HEADER_BACKGROUND = "rgba(255,255,255,0.7)";
-const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
 const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
 const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
 const OVERVIEW_HEADER_TEXT_PADDING_LEFT = 6; // px
 const OVERVIEW_HEADER_TEXT_PADDING_TOP = 5; // px
-const OVERVIEW_TIMELINE_STROKES = "#ddd";
+const OVERVIEW_HEADER_TIMELINE_STROKE_COLOR = "rgba(128, 128, 128, 0.5)";
 
 const FLAME_GRAPH_BLOCK_BORDER = 1; // px
-const FLAME_GRAPH_BLOCK_TEXT_COLOR = "#000";
-const FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = 8; // px
+const FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = 9; // px
 const FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY = "sans-serif";
 const FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP = 0; // px
 const FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT = 3; // px
 const FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT = 3; // px
 
 /**
  * A flamegraph visualization. This implementation is responsable only with
  * drawing the graph, using a data source consisting of rectangles and
@@ -96,17 +87,19 @@ const FLAME_GRAPH_BLOCK_TEXT_PADDING_RIG
  *        The parent node holding the graph.
  * @param number sharpness [optional]
  *        Defaults to the current device pixel ratio.
  */
 function FlameGraph(parent, sharpness) {
   EventEmitter.decorate(this);
 
   this._parent = parent;
-  this._ready = promise.defer();
+  this._ready = Promise.defer();
+
+  this.setTheme();
 
   AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
     this._iframe = iframe;
     this._window = iframe.contentWindow;
     this._document = iframe.contentDocument;
     this._pixelRatio = sharpness || this._window.devicePixelRatio;
 
     let container = this._container = this._document.getElementById("graph-container");
@@ -211,24 +204,16 @@ FlameGraph.prototype = {
     this._textWidthsCache = null;
 
     this._data = null;
 
     this.emit("destroyed");
   }),
 
   /**
-   * Rendering options. Subclasses should override these.
-   */
-  overviewHeaderBackgroundColor: OVERVIEW_HEADER_BACKGROUND,
-  overviewHeaderTextColor: OVERVIEW_HEADER_TEXT_COLOR,
-  overviewTimelineStrokes: OVERVIEW_TIMELINE_STROKES,
-  blockTextColor: FLAME_GRAPH_BLOCK_TEXT_COLOR,
-
-  /**
    * Makes sure the canvas graph is of the specified width or height, and
    * doesn't flex to fit all the available space.
    */
   fixedWidth: null,
   fixedHeight: null,
 
   /**
    * How much preliminar drag is necessary to determine the panning direction.
@@ -325,24 +310,29 @@ FlameGraph.prototype = {
       startTime: this._selection.start / this._pixelRatio,
       endTime: this._selection.end / this._pixelRatio,
       verticalOffset: this._verticalOffset / this._pixelRatio
     };
   },
 
   /**
    * Updates this graph to reflect the new dimensions of the parent node.
+   *
+   * @param boolean options.force
+   *        Force redraw everything.
    */
-  refresh: function() {
+  refresh: function(options={}) {
     let bounds = this._parent.getBoundingClientRect();
     let newWidth = this.fixedWidth || bounds.width;
     let newHeight = this.fixedHeight || bounds.height;
 
-    // Prevent redrawing everything if the graph's width & height won't change.
-    if (this._width == newWidth * this._pixelRatio &&
+    // Prevent redrawing everything if the graph's width & height won't change,
+    // except if force=true.
+    if (!options.force &&
+        this._width == newWidth * this._pixelRatio &&
         this._height == newHeight * this._pixelRatio) {
       this.emit("refresh-cancelled");
       return;
     }
 
     bounds.width = newWidth;
     bounds.height = newHeight;
     this._iframe.setAttribute("width", bounds.width);
@@ -350,16 +340,29 @@ FlameGraph.prototype = {
     this._width = this._canvas.width = bounds.width * this._pixelRatio;
     this._height = this._canvas.height = bounds.height * this._pixelRatio;
 
     this._shouldRedraw = true;
     this.emit("refresh");
   },
 
   /**
+   * Sets the theme via `theme` to either "light" or "dark",
+   * and updates the internal styling to match. Requires a redraw
+   * to see the effects.
+   */
+  setTheme: function (theme) {
+    theme = theme || "light";
+    this.overviewHeaderBackgroundColor = getColor("body-background", theme);
+    this.overviewHeaderTextColor = getColor("body-color", theme);
+    // Hard to get a color that is readable across both themes for the text on the flames
+    this.blockTextColor = getColor(theme === "dark" ? "selection-color" : "body-color", theme);
+  },
+
+  /**
    * The contents of this graph are redrawn only when something changed,
    * like the data source, or the selection bounds etc. This flag tracks
    * if the rendering is "dirty" and needs to be refreshed.
    */
   _shouldRedraw: false,
 
   /**
    * Animation frame callback, invoked on each tick of the refresh driver.
@@ -380,18 +383,18 @@ FlameGraph.prototype = {
     let ctx = this._ctx;
     let canvasWidth = this._width;
     let canvasHeight = this._height;
     ctx.clearRect(0, 0, canvasWidth, canvasHeight);
 
     let selection = this._selection;
     let selectionWidth = selection.end - selection.start;
     let selectionScale = canvasWidth / selectionWidth;
+    this._drawTicks(selection.start, selectionScale);
     this._drawPyramid(this._data, this._verticalOffset, selection.start, selectionScale);
-    this._drawTicks(selection.start, selectionScale);
 
     this._shouldRedraw = false;
   },
 
   /**
    * Draws the overhead ticks in this graph.
    *
    * @param number dataOffset, dataScale
@@ -411,17 +414,17 @@ FlameGraph.prototype = {
     let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
     let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
     let textPaddingTop = OVERVIEW_HEADER_TEXT_PADDING_TOP * this._pixelRatio;
     let tickInterval = this._findOptimalTickInterval(dataScale);
 
     ctx.textBaseline = "top";
     ctx.font = fontSize + "px " + fontFamily;
     ctx.fillStyle = this.overviewHeaderTextColor;
-    ctx.strokeStyle = this.overviewTimelineStrokes;
+    ctx.strokeStyle = OVERVIEW_HEADER_TIMELINE_STROKE_COLOR;
     ctx.beginPath();
 
     for (let x = -scaledOffset % tickInterval; x < canvasWidth; x += tickInterval) {
       let lineLeft = x;
       let textLeft = lineLeft + textPaddingLeft;
       let time = Math.round((x / dataScale + dataOffset) / this._pixelRatio);
       let label = time + " " + this.timelineTickUnits;
       ctx.fillText(label, textLeft, textPaddingTop);
@@ -921,24 +924,24 @@ FlameGraph.prototype = {
    */
   _onResize: function() {
     if (this.hasData()) {
       setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);
     }
   }
 };
 
-const FLAME_GRAPH_BLOCK_HEIGHT = 11; // px
+const FLAME_GRAPH_BLOCK_HEIGHT = 12; // px
 
 const PALLETTE_SIZE = 10;
 const PALLETTE_HUE_OFFSET = Math.random() * 90;
 const PALLETTE_HUE_RANGE = 270;
-const PALLETTE_SATURATION = 60;
-const PALLETTE_BRIGHTNESS = 75;
-const PALLETTE_OPACITY = 0.7;
+const PALLETTE_SATURATION = 100;
+const PALLETTE_BRIGHTNESS = 65;
+const PALLETTE_OPACITY = 0.55;
 
 const COLOR_PALLETTE = Array.from(Array(PALLETTE_SIZE)).map((_, i) => "hsla" +
   "(" + ((PALLETTE_HUE_OFFSET + (i / PALLETTE_SIZE * PALLETTE_HUE_RANGE))|0 % 360) +
   "," + PALLETTE_SATURATION + "%" +
   "," + PALLETTE_BRIGHTNESS + "%" +
   "," + PALLETTE_OPACITY +
   ")"
 );
@@ -1109,8 +1112,12 @@ let FlameGraphUtils = {
       if (hash > Number.MAX_SAFE_INTEGER / STRING_HASH_PRIME2) {
         return hash;
       }
     }
 
     return hash;
   }
 };
+
+exports.FlameGraph = FlameGraph;
+exports.FlameGraphUtils = FlameGraphUtils;
+exports.FLAME_GRAPH_BLOCK_HEIGHT = FLAME_GRAPH_BLOCK_HEIGHT;
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -232,42 +232,34 @@ StyleSheetEditor.prototype = {
       this._fileModDate = info.lastModificationDate.getTime();
     }, this.markLinkedFileBroken);
 
     this.emit("linked-css-file");
   },
 
   /**
    * Start fetching the full text source for this editor's sheet.
+   *
+   * @return {Promise}
+   *         A promise that'll resolve with the source text once the source
+   *         has been loaded or reject on unexpected error.
    */
-  fetchSource: function(callback) {
-    return this.styleSheet.getText().then((longStr) => {
-      longStr.string().then((source) => {
-        let ruleCount = this.styleSheet.ruleCount;
-        if (!this.styleSheet.isOriginalSource) {
-          source = CssLogic.prettifyCSS(source, ruleCount);
-        }
-        this._state.text = source;
-        this.sourceLoaded = true;
+  fetchSource: function () {
+    return Task.spawn(function* () {
+      let longStr = yield this.styleSheet.getText();
+      let source = yield longStr.string();
+      let ruleCount = this.styleSheet.ruleCount;
+      if (!this.styleSheet.isOriginalSource) {
+        source = CssLogic.prettifyCSS(source, ruleCount);
+      }
+      this._state.text = source;
+      this.sourceLoaded = true;
 
-        if (callback) {
-          callback(source);
-        }
-        return source;
-      }, e => {
-        if (this._isDestroyed) {
-          console.warn("Could not fetch the source for " +
-                       this.styleSheet.href +
-                       ", the editor was destroyed");
-          Cu.reportError(e);
-        } else {
-          throw e;
-        }
-      });
-    }, e => {
+      return source;
+    }.bind(this)).then(null, e => {
       if (this._isDestroyed) {
         console.warn("Could not fetch the source for " +
                      this.styleSheet.href +
                      ", the editor was destroyed");
         Cu.reportError(e);
       } else {
         this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
         throw e;
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -905,16 +905,20 @@ PropertyInfo.prototype = {
 function createMenuItem(aMenu, aAttributes)
 {
   let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem");
 
   item.setAttribute("label", CssHtmlTree.l10n(aAttributes.label));
   item.setAttribute("accesskey", CssHtmlTree.l10n(aAttributes.accesskey));
   item.addEventListener("command", aAttributes.command);
 
+  if (aAttributes.type) {
+    item.setAttribute("type", aAttributes.type);
+  }
+
   aMenu.appendChild(item);
 
   return item;
 }
 
 /**
  * A container to give easy access to property data from the template engine.
  *
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -3058,16 +3058,20 @@ function createChild(aParent, aTag, aAtt
 
 function createMenuItem(aMenu, aAttributes) {
   let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem");
 
   item.setAttribute("label", _strings.GetStringFromName(aAttributes.label));
   item.setAttribute("accesskey", _strings.GetStringFromName(aAttributes.accesskey));
   item.addEventListener("command", aAttributes.command);
 
+  if (aAttributes.type) {
+    item.setAttribute("type", aAttributes.type);
+  }
+
   aMenu.appendChild(item);
 
   return item;
 }
 
 function setTimeout() {
   let window = Services.appShell.hiddenDOMWindow;
   return window.setTimeout.apply(window, arguments);
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -76,17 +76,17 @@ browser.jar:
   skin/classic/browser/search-engine-placeholder.png        (../shared/search/search-engine-placeholder.png)
   skin/classic/browser/badge-add-engine.png                 (../shared/search/badge-add-engine.png)
   skin/classic/browser/search-indicator-badge-add.png       (../shared/search/search-indicator-badge-add.png)
   skin/classic/browser/search-history-icon.svg              (../shared/search/history-icon.svg)
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
-  skin/classic/browser/theme-switcher-icon.png
+  skin/classic/browser/theme-switcher-icon.png              (../shared/theme-switcher-icon.png)
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/session-restore.svg                  (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                      (../shared/incontent-icons/tab-crashed.svg)
deleted file mode 100644
index f1e9a0271e957264ca34400bc436fd9bf3bf91ce..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -117,18 +117,18 @@ browser.jar:
   skin/classic/browser/badge-add-engine.png                    (../shared/search/badge-add-engine.png)
   skin/classic/browser/badge-add-engine@2x.png                 (../shared/search/badge-add-engine@2x.png)
   skin/classic/browser/search-indicator-badge-add.png          (../shared/search/search-indicator-badge-add.png)
   skin/classic/browser/search-indicator-badge-add@2x.png       (../shared/search/search-indicator-badge-add@2x.png)
   skin/classic/browser/search-history-icon.svg                 (../shared/search/history-icon.svg)
   skin/classic/browser/Secure-Glyph.png
   skin/classic/browser/Secure-Glyph@2x.png
   skin/classic/browser/slowStartup-16.png
-  skin/classic/browser/theme-switcher-icon.png
-  skin/classic/browser/theme-switcher-icon@2x.png
+  skin/classic/browser/theme-switcher-icon.png                 (../shared/theme-switcher-icon.png)
+  skin/classic/browser/theme-switcher-icon@2x.png              (../shared/theme-switcher-icon@2x.png)
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar@2x.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
deleted file mode 100644
index 757d1e0c64d787bb55e8adab0f321b6555d7882c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0d54510ec5f59b70f1962a1353d4575f1c5b989d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/readinglist/sidebar.inc.css
+++ b/browser/themes/shared/readinglist/sidebar.inc.css
@@ -1,14 +1,13 @@
 % 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/.
 
 :root, body {
-  height: 100%;
   overflow-x: hidden;
 }
 
 body {
   margin: 0;
   font: message-box;
   color: #333333;
   -moz-user-select: none;
@@ -16,21 +15,16 @@ body {
 }
 
 #emptyListInfo {
   cursor: default;
   padding: 3em 1em;
   text-align: center;
 }
 
-#list {
-  height: 100%;
-  overflow-x: auto;
-}
-
 .item {
   display: flex;
   flex-flow: row;
   cursor: pointer;
   padding: 6px;
   opacity: 0;
   max-height: 0;
   transition: opacity 150ms ease-in-out, max-height 150ms ease-in-out 150ms;
new file mode 100644
index 0000000000000000000000000000000000000000..891e7afb1055e484669e792432ac1df496aa14ca
GIT binary patch
literal 2084
zc$@(w2;29GP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F8000N;Nkl<ZSi{v<
zX>43q6>j6SK+~v1DWVDyB~n9MR8UBh*yDJ{vu|%^JTq@L&*Itb880)Ach|KYH(nvZ
z*(#PIvLI1tiV{-cl$vZcZb%zYB&a}$0JTHoHcnhSq>w6^%Xi+4?P+Se4(%UC`gGsB
z_nz-N@7#0Gy>r)H|1Uq0Lcc^L*UQj$1B1X{fvW=K`U{!9?#!GepPsN>zYM6$m+MEQ
zoQ-MierEENF>A1r*&;~+^lU^QjnN#;AAu8fXz%}o;yNy0!M&xjc$p<w#T?NZW{+%P
zHnh&Nn(OX(!$kr;+oN09G_07bvUr$CX?P1x817tHrn@^&rt6g&Z7B=BjzmFxt2kCS
z>aA#av!!QO+qHkazYcgJgLON;cOdGD*N^a+I}LN7lF1DARGv)RCz5IJUX*xVT6~{|
zFM)71vxPIjv)Nw(l#g%U`-89EZU-lpAAIqs1~xZnE`gk&mbl=={=CIFfk|CDM?ete
z!%4=6l0s_>r`y)v-ARZooy*qdi#!%cG!NRc?51t?_5jP1YP%K|mufhyU5XDS7{L}!
z+_VxAo_&>#zdd$hNCWZD&u3qtHvP#StT@Dn6Dh#~zzSPyIL$}gaDtmpe3_KvM=61<
zK^Q>1c57cJviv=IuWjp!Lj0ZuoL^SAwXfP=QQL}TxPq^q1jlu2sEVaI(;yAZy>G6J
zuQz#P%o3;)U=LT1l-IUMOUfGt5XP&oo_YPjg`x-NmH;b&FATqNK7Z`;`1<c3e8KLB
zCf~s}{lHaP`=%91Ng5)#w*Dgo3Ul!&WvvCE)_{GzS#A2qPY?g{$>)B2$PD6-O`zOD
z^@@x$IW!;mTvhY74o|%P)j(BaKa%(s@hhQqyWuyD?o!HdR+P?kRvfE+Jyze=SKZWh
zKZriN2yrRa_a@pC!0Jq21GeFq!4>*j^D}*$J+Zpih~MZbWf)sOV=Ut=T}34j_myM6
zC;!~`t=(G&PY-XfdZP^>4K8N;8XbN+$UmSRgysB8u0*|3W^&vxK#X%mnN)ABaF<m-
zYx2dR6K68c+C5{e;XS8Kz8Kag&JMnCSnlwb8g!Q8RVH_+rE~l4$AzzvsB#u^K+k*1
zg2|?z5Rcv&p?!bB=q+O;NPQAL&uso!TUsZE+TcAsYwQe+7}1F+Tt~R?Rhk@)D4{z!
zhkr+b%HYctt6Fm<%0Ivf@ton8s8)$}W~Q{b$4Hd8bgq!l&cG96M+RQ`3ch}d-br)T
zf#UEKg$jcgbH$1rjn#b+;bVmcJ1fu>F{#1EbdKQE%g0Zxn9G4+Joz9ct=<(D)|a>_
zrwj!28$9V&qBT8SR1zPde$1VmA>iE6DF6o1(W?5Va^yv>YxK?uBvFCL;x9j6s5L$?
zmjfq<hL?8se>X<+(sn>`+JOu&b4C)w6kX088Oij`Isgwx^~DjUbq24|yE9rnz~Nn=
z0q2S^wbje83^z7ymN(5)2bP!Bw(bJ)`(&F9^!>Z6wk1LHpfN>#h}7&>Eb^6}q3|Kh
z3<9fNp^7ff85TmSbEZMhdS{54J>kX$iqS*Kr#f4Oxn!F`WmCsC&hCqW`w5*rD2$nW
zrLWBxIzttEv>+!GD7k=)<Ij=U3>>r1I^NShp97mV%N{D!TTTjG(>AWiWbM-(Ps$AZ
z&!l>DXqt~SXABdKts44zf<$K?bVU=prr)!nh4%`R<E0OY=1U(3Y`{u|!SZ#L#r<}+
z4WfHaB-QkHZr>x9=!yoZPEZsQjcuCj(A|f1d0UGkQ*LsmLKQVO1CoW_ZwJOFHs(un
z$qwyGRJ3ty*_(wr)1y=|`9S<=_I#AxDT_DKrZU<JWZ<Sg(jv_dg)dN5w?k|5GY#%_
zAROBJ!vjVn%vFu8glRJU)T($dmC^rf%rEr({1*fF9y&UpI&y4~j_J=3Pvh1e1J?K*
z2;Fjot6pyE=&RcM+`dOqn1+a-fD?1aN7nDiz^~SD&fp~ylo}mOB-3=X_4bDl#>;A+
zoG1})HX7asCwxq*H9wz;-aQ03l#{NA#o}bkw(8cN{_>H3jy=-c(fvrSSlKQIC)pzh
zE}<Z;ne#ZUYwfHAc}i{dv3!*=r7-ZJ{m;LsGkGF^q&{*)B{)!W>y=YOUz@hwD3YuN
z{tjG1)kMK5+Vf2BcfL#~VqT%zM_g*yfg@Gd)>%nf^KGLCyz!cSD(rb--^;YP!Kgcq
z9Y3|n=ne}`r~s=!c08LTPoml-Hx)C3GkDGssytlX)UG>m`Zr7AP%93z6!8&!@tPlz
z#)7BX_?3y(wqW^7%8Zdn$uVU3spgIzPg(8LJw@KKVP`P5ZT9QMOF6r@ABCp-%p1R_
zoASdtvrD=$Pka*Ba)CpMD>`u!_o8mbS0>(D<1S4ez~Pc2V~O5MRWK!1n||$&Ro7u%
z*JnkIE*`7eyTAPnIO^RX5)W)DP^JnwUJxb@;v&vF`fmWz)r}p!TaY-Hlx8<m01}-A
zH(L$E_G26J3MPOH8B9PNyF(+)fhbb|T3g8&aS+%4^v{Ua>+J8&=Z355cVhkDm1i8m
z^$qpFg;UZYfSwguV@SAn!-@KxWc#sez6dAiviZf!;}h%Jd;7}}&iy8L_$;>NMO?Ed
z5#~uPUvkk@96n29*t#*AOE&Xn`zLT_Iq)T5?et%=tiNb$Gh@r|g!8{)(EQXRMaq@{
O0000<MNUMnLSTY`B?P7b
new file mode 100644
index 0000000000000000000000000000000000000000..286adfeaad64110999418d055e58ff097ef56c78
GIT binary patch
literal 5595
zc$@*-6(s73P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000%DNkl<Zc-rk-
z33yXg+7=vfLucH69mQqDT@e+XUxW%}>AodxlBQ{swpp4qX|^_PY14&bfkGXST{b~B
zp`a8OL>5ID5pYyQ5RgT{1yFx>7^Us~-|yUWlbg06)S}MwOrPg{&dFK6@BO~BpL2W3
zCI9`qxPK9envM|(n(hdVzzAS5uo2h_?Bm$V(-yNb39M{)gdP7gjZV;93j;C`)*<{J
z5D*svVRIy&HKe+Ah-V<Y_CLY!2SIci!V{7akqF~ddLcR8B51N4LVAu@FxZQPth_-&
zma~K*(=kXeIEq-FCfgw>QI_gNhC1p5Fb#4a{39Cv5U>DfB(hOSse(dh7IYS`V08Hf
zi?@Qo+ze++H|JA&xnOdYG8sz9ib~RQN2nXQu7~~rhO0Y+$Vh}|BI4CTY)XcpHMs<n
z#}7lw1+%AIW>|TvA#(tg^T_g8z4D+8z~m_t(oAk4HaVS~ATXT?>RRIP`xqau34`)|
zM1~C4nLXTyiU4WJ2$MJXJEx%Hn7wGoc2~);^&bE5mF}|Pt6ZhS)>9htPMYKnY9>cY
z*I7JFCv!x+dOyOj->}tH9R|j$22l&p_@;4fbeDl>8Cr=O>lIs!$lD4lzbP18TVFPO
z?8NuieH3}})af2)!?EKhpIo|XO}wwNwm#VMJoJ_c@-!24YO?H@V^W2I;4&~?T>@ux
zxX7)px&lfTzyU&>+8|`+6$iR!AOtat-=FNoL$-LzYL=sQ*TNwuZN9<#xS@{$4*~ZA
zT{-UMX_VJv)`BJalIqbjt-i|dOx&>n2YW<ko86)#vhzw97vcoe5yy*V{MGRCN>Y@f
zx3X3bkaL&wc$q}7ky?n5=_nSoMyKE^t}?;6%YI1@%3KcINsg!(F?Nheu1C_rWlM3R
z+b^UTtQZc=3E-%{x?PQj&?`wW&B8Ph88OL;B>c??`I%#A=GWVNg4XP2<JDa{^eq_l
z$oc9+*{gxuHgDbWw9QxfwP<83`kH~b5l7Jpsw>;ecpCHbXu_d&nvn<)5=oK4Eaa_W
zbW6YeLQa0g!A~}S5e|c{yFk4iM^BvgbbfiwlAydYF1sv52ZKxGqytCdiu&`3`>%IE
z9Gmc%;Gi;@V(GUlG;R`|(lmbBTnCK*TN@nTB`#T)Fmmivdv0OXCb|3&!`-4TIVYJD
zN`95`1L9O+1LIN}Xbc<OrBEWl-wJ~Hj^e6K6Q<A2eH$}6jDHlk^n!KAt7b$ibziv#
zRh7T@&ka#9`b<EFTgJm(+DyO5hd2{=AgqlopVArFmz-e{vLQGCXV6fy$A2_iQtae;
z&eEpW-dvt6Ti&j1gYJ&e3hmAWb*4bTZm?0(De*9JE+L$pvNS-Mks}O%4M68Mw0_D+
z+H7RIN+7f()B%9oKXhq9#fXJ0k3r1Ga<>Ua-%o}Eo!Zv(Qo38*r7xP@{?Ah~ayDTb
zydz7}13pGqNr>@sqeJ5*Y{LY3wHHd@5saTR`rJaUfDSFCf<(gPE<HB+<vR1SRcoVM
zCDkhf<bbN$@xx*GuU?C3)sTA8;iJc%f}Z7&-(>NY9mvTq-($_M*eP+#mT{9e2&L(B
zeKZ%GA)C%ydH>jy`V_qlLU3ZkMuS0$Bdn^OFl_z5K7APE?ifF9POePx&+o4JuxC3M
ze<jcbc#z}ach|0urn!I|vUU5eu#Z0dJlyLa_NI8E6uFUp425GXp2s;MBwGf=CD)&)
z@yXqz6=_X$L=}x6#1#Z#uonx7n#{L%?%sQOLD|qEC^|)D^NNS82Em&y#zqWs@8&(C
z8##pU9DDdGM$D5qqGx{p;>=gfXt*<SzO-W{o6xY`&%*)AsTyM*t0Y$DaEM_R(vwn+
zm|;}{oK;C<ef0+$Bhf3WC(U@pjNgB{NDjD6+DX!?LVAF^X4fszF@qh9@9D*Ft%&oM
z4Xc;9N=_h~`o|<sKezEQ%B!Lj+M~36C1mkXG(I#<lMR8f$|hG)MJ$Ls&ReHzdl?L}
zw}Q_jz&STF-yS%0SJlYz)mV}C9ys*F<B6$RbBl-6PC}3Tx5Pb*^M`FG!1>y9IY6s2
z*ifm68EGH^c0!RDBHh44%*2OMv+Qr6&Yiq<ZUnArALEt1fs_4WlxQ{OI{JN<a>ckg
znhvfFXziaXM~tnuI(>Kj>(kF4@c4(;i*^(VQiG}txg<}2u3g{43|k?)vLZu8h{hYZ
zFsrX(<Fq-8GLIcU@#MlK%Ti!;yI)9<(R8q(o2CAu6HdSx79gMtaRbmqPD<2dvzsII
z8o}ZoGzOghI(2qkhGYk8KycUF3+ceLuvMiH)G=OT_`zT&2S{MvJ4InZMa|2bwruP3
z(%e_|yt_Y|<0_e;H#zP>-ACKYc*wdcCOLhto;QTn<P@+Y6#7Lg1?1%h%PGD}(i(-7
z468uPH6cNhB}65rjsmxibfPr5O1?4V7V!oZfigcrR>xKpE4r&Pa|HtgiIqrb5#7t}
z@eds}s-V1PCOH6t@B4Dso@@F=EAGdI2)e0a3E~y)*#kz5nQX|;8+1tGLMO`vO{Op)
zHu*Gk-3y)f#N!Tt<@1Iz<cfSTgx8}?-*}aN0+z}7QW<J}iqkM4TU&F0B|Rr!AkT{)
z^)b|pnc}4uFROlDfi6iC-qldhANWya$k`H~lA(L;^`(Dq$Jw4*uiJ|==Nn;IgVef&
zcsK!izXHako4wp<ABR{*k!Gecgm<g|F(EIQCx#g~C?U_%LUCM*Zh}-WgM)rAU9p;u
zvNw;NJfjy*SQb>ikrTaw&;Iw&m<w@cwBoupb^mBZC*VD7UsvQ6SIsFNTst8HtuLZb
zNVodv4v?<ESQ;}5=`5ecee8;khK$MK8#Z&^qNnt{KC{av+C;XtP_EFK-l4+!fI=U2
zontWWFO;v#wkdHVdpJB=G1p&J^T6myGtAq+-2DWd^MMZBfIGsYle`GOK)Fd5?tr8l
zQBNSyHF#d};Mc&XN#dio6$oj#WKrlWEUxEidaD>_a8UY`ne$?ZJK2yCu&aQTBU@W2
zSL>~xF#%RC2q7ks?b7BWIW4<Smd%rkef1O=`CZ@WB%yCq(uasI2#-p76X6y>K%Djm
z6dEr05EPrNQD@o(nv19_C#GhLe5^8FK)S@!>I-lUrZF^b`*PRq#2q&5NN;AB+F<>x
zh4NIR<Lh*5zCd6C$pP-t52XAx>o(kEFRXl3iQ_T0ltLt0h{hTj+?OGdPF+Ggj<0wd
zXk1wT*5eoIfJAjhH%UK%9#_&?Jf9)REuW_q1oDI285&jT#t4#0Hb~{td0j1RZJ|8P
z<T^l;AQM6%kY)F+qP)J5iPyv^)7D{mVx;J<kIGSNCF9IZhYi{pG7_j~c9nht@858t
z4wx`y#yygZ5LjD@H&1JpJ}@r%?<6zZQTSd+8FGNhd7y>zbc^RZx=JF@-Jj@9x8#6M
zXz9~Q)FKe3V~~|)M7)8#!of9T+Hxj%XVvP?)92J>cuTA2=mL6zhD!Q$0e!*WI$4>#
zqQQ%#U1>&+XO%9%jfWObO9$w!-kr3Y3MfH5&6KxZI((#=o!f#0#C%<jC+G-EFx}`V
zTnMNAFVOJ<mq0q;^r#&-)x+dV$dQGu0F4*&d+d~1R$8vv<ZW`Um*hwsGi=^nEgfJh
z*en+GFr)#g*^Vzbu@3H%=ccEzJc*cCP9hh~^9b=OgU}~3LFgNqZ~#u2g!rZB>3|MI
z{P2-ux4TQw3oM?dfF7CdG{(;x;F%`lf}5m&(k?JCw`DuF>Oy$a{-I!V3*}k41*@4L
zvn14w#|GO;DSz3j_rp;6WE$l&xC3Q%h_gC8PoXujTUl~Kp9qG?3+xGhrk*ft)FfY~
zz3@vNm#gDC0vzS(K&IWd-&H(hp8QaTo#9EH#T|4Kkd^CO-9o9^Sv-r3(n6q?g>JTO
z1o_)JsoShx|1!A)LeV^(dXQ5{N;4jfOHRAC4Liav;(OtVCk?s2Z$kBmafxS|I4S=)
zdqI8b+I1VB0Ous&Pe2!y!7i&K+D5jvQ0OYE^wZ0VDA2`TSyFMe3?WL&k5#6{>aF>E
zLXCEXI6;d#OoqwDL$_e_mc7}Qn_=z*+GIWCD5_kj<?<wm99<$`{Lc4RPo;ar@l$5>
z0_Vd(7@*MNB1E!_0d<^(mHrmWhCV+oQp*Ku&5|OQ5D}-ENcnvt;>RVWnFO(n1hGs2
zW-0Ct{Kk0O)RuPv=?{AEOE-JA1?9_i26#3tTC$XOeph0(T9BwQOr-e)*Xt95+=BIn
z){cp4q3E0M4*n0u$q`n_=n_(SVQfivKN}I>AyK2Rp&=nRIMpbYr6X%<AQT~`C239n
zh7s4d$@qcs#y$~o$LW|W@^Xs2q>ytp<`sVggKtH7Gu;tX*N*dkcks|P*sSdqxrp`<
zAIt|w$ktYGVljiRNHsZShPc?y?%9EHy~>}fO=M}3vBqE-rbLRHf@uWWIimi+3pE~f
z9tC_jyPrtLYqVA-GgPNjsHhoTp*2{j{5uA#cM4XKJBTxJmzO74+$Aeu>s75PEgwFr
zm=(n2rQsn~0y2Qc*R9!(qK%X$31ek2#-uu_*?IhT5Ndq%G$2M{I)|aR;QejHvjgI$
zA^rvsFKd(=bMs5<m|U(`!?@+i^gD%gv+E7iUz?=On)k`(t#oJc7vfCM_(7+Mb^O)O
z`_Jn2k2h_37~SQBq(s9M)8%K%Px*r?@10#&uSW0qiP9N~Y_W_~j@6TX8-saKinO=G
z2E?`aHUMci0m}e^@T>@p>}}v?gT3HmQJ+($hdY=%249KbYAktcLFsdJchLp1ZW{p3
zA>63E$$9Gi57s|=u37hbTYlMM`c#co&JshqeSuKg-rnacAO1oxKUgM^76^lg#N8h1
zuK{iebwKRX*eD%|P0_QVh9&cqG1nV1Tz2mM>h3Ii;RdFQ$#!s^K|LvY%O+D^;gro=
zx6zZ_rO@*N$!pHT>MgB5=NW}=W2VopQ$u(YDWUGmfH;W$Y%n_>`)up>-nsc@by7MB
zR!gK9@&cinrzL8$S?j{!uYsQOt?!PVdmb*S8ab@8W{iL9_MKtPzayY`MuRPXliYC9
zhwgkDOYZ@Tr(`MW53e0NneHmDhy44As~X=A$r}ZAnx@ZLsQJYeiMEl(yrR|g0TLOY
z<{>>YLiqc8_kVNi5C3=gDU-8!rI;_4(FEe55S?MC)}^g6$v6%S1Um6AK^~L6BtI_g
z;pN-oZ98^#v->LMLQfOv!sK_deCVhOjOVI@-~agcc?;|NW8&*FtakKe#F2Vba}Hz^
zJ*1r1yWFe=i`A&qAR5nsC6Lbk&qgE+rZ+)SoMH8TK}X^MB!|(ZhCJl)WZd9ltdbMv
zg!PZ@Alv`nM6E|X@GP7jfD?LKJ*BT<8QVKy+UzXutZvg@ekJUch4pkT*BLS^iK7}9
zDso=v0S#mm*>=7=u&xeY`79MTK$R&Pk%X=c@(q0>;^}j@t{5(9*d8yE8!X-Er~+}Z
zuJ}6_>#%FbD+?FjEM23um>A)h-u+_(ugi!>V0|C0GdV_2nm&iUM^LZ9(fPJ*J9ggE
zKPKredWL7cs()-lBCg8Sa@_!InSDX8R^+KI+rI2g7YC9t5~7u90-kM7hDRnmO)t0m
zzW%nSuWaZj7}?0m7(&adS$z@Ii)JratUA{xGmyOi*bKY_<97k1k0#NzG(5rU$&!ld
zt}?Ej2E-)yBu>!^9oG*S#9oX2!$Gp;yjx#>7#TZxrU^zg281%f?0JbueXlWCXwtf4
z_ul<IpC30R7enB<yc{9L5R9WWP96H;$8N24_1^uX(eg24$k^TkM577x+Cw){xRBi+
zm8hvf+TUc?=N%GN8P5<GDks;=^)-%paRxouc4_ndyr*QyczQ2PVGs?XD=^&P62apb
z4)kVEUHXYp6Q|{1?l>VCE*ftOgs!5&lh4%>aKeYQ<kFcV9@}5K{lx@&4Dy;%v+d{+
zF}HJGm+{^bNgU`hHW{2my%JBG;01U7oZhzoJD`aLW3GoWuW)4wcY+Fv$N|dq95yNT
z?ibyF@i%zOwjFeGdknqC2+yBj<@J)|#Q6BBFK3)>>_bjbbh0i$9!J*tK~*EF%ZL4=
zZ2qfnD7hiG(ub6A)TKZL&_EoN{9ZD^5i;mLlequtw@V1PWyh{Py>Ml=fwq?PMP>?j
z0$qsWMTwk1SZlU>X<m42&AN>Pj1J#&+`jJ9nVs8FuMu;^k%NbRXtgW$>KA<-u#NT$
zD0kfIEt&t}N1uc*e|JrkY?-7_YCAzsH<Voi&|4ul-pYD_=mfkKf-CkHy@-3eeVy?I
zmLYwPXG1^{IfH|)$jJ#HbtW=FHSx`5tJp`;OIN%XZTFSWD8Pc9?eM*0a}}QTJDt~;
zF25I7>54_Kzn#bpzKcJ()AD!)<adGm7f461elhIfBssvr#+_irMXq-Pac$3U6{Pv#
zzTIE#?}?UOLF+kF&O-u?S-SqBmcfwtDK$HHK6YmrM~@wU`0H;EJiYOg&w9a#v#s-&
z^3PtRkE!pVUE<blJE+B<=@lM575E8Y*KBk=mN>AG$FuFe75HrD8RFXJasNwBpmi9N
z*?5e#22ptql;?<KFfY(jgp-)iD?EyAe=&RSRcN!OyNfGrdQ<M*=a5fMxLj|t-wh`!
zp9zm12ZMK!(UL5B^CKOi;W+{0(E}R5Y2x7{N1wVV#y4{U`&enrq?b&Xp$`%fB_kt(
z#qmEsL^d3+MbCt@p&`(EP9cmr2BS71eI?>c5GSmpG@ge#QGyiGt5b|*I5$|y9Z$N5
z199QK=kAMUd^1P<m6knhFv4v4rL!<EG>C@hu+<>|q|rwV_(F?KSajqiqa^g=kpHGK
zRF>-C1}5NGxr3Fg7uXu0dk%3RF3n%4e+wsc=1WtLS@U1TPPkwN9MH(fNzUNCfsw||
z5Pf#5NH?=FO#c-|hZX|;!$Sf-bD?K9(Q%fbp_0)-G(y)((kc6F?)Uk>AwDF!@D}JX
zx2`_L>@2Lu7(J~_4;mrHEumomIkHd=OGrA&G&_sxNe4Il39kG1|L016dn0|w*yH0(
zpZD<(8Cjl{o4*pv(_zUV1wsWkOwq#lV7iotP1li3$nX!TEyrzDAK9__?hD@A|3;3u
zg8#eA<ALvdF*8q}y+B)BSyN^6mdrHRyziwM^R}dB+IQoyasY>=0~*9Bjq<FXl9|PW
zYpPyC9X_ke?+Wg0kMEFw3rEnBb^~vz?&tbXv0p%UF5`w@^S1Cu86r3GG22!80Gdx=
p(zm}4^1Lp*?3I`N_wVBW{Xh7)-RxREK3xC+002ovPDHLkV1jB7<eLBh
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -100,18 +100,17 @@ browser.jar:
         skin/classic/browser/search-engine-placeholder.png           (../shared/search/search-engine-placeholder.png)
         skin/classic/browser/badge-add-engine.png                    (../shared/search/badge-add-engine.png)
         skin/classic/browser/search-indicator-badge-add.png          (../shared/search/search-indicator-badge-add.png)
         skin/classic/browser/search-history-icon.svg                 (../shared/search/history-icon.svg)
         skin/classic/browser/Secure24.png
         skin/classic/browser/Secure24-aero.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/slowStartup-16.png
-        skin/classic/browser/theme-switcher-icon.png
-        skin/classic/browser/theme-switcher-icon-aero.png
+        skin/classic/browser/theme-switcher-icon.png                 (../shared/theme-switcher-icon.png)
         skin/classic/browser/Toolbar.png
         skin/classic/browser/Toolbar-aero.png
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/Toolbar-lunaSilver.png
         skin/classic/browser/Toolbar-XP.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-XPVista7.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
@@ -561,18 +560,16 @@ browser.jar:
 % override chrome://browser/skin/toolbarbutton-dropdown-arrow.png     chrome://browser/skin/toolbarbutton-dropdown-arrow-XPVista7.png   os=WINNT osversion<=6.1
 % override chrome://browser/skin/tabbrowser/newtab.png                chrome://browser/skin/tabbrowser/newtab-XPVista7.png              os=WINNT osversion<=6.1
 % override chrome://browser/skin/tabbrowser/tab-arrow-left.png        chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.png      os=WINNT osversion<=6.1
 
 % override chrome://browser/skin/menuPanel.png                        chrome://browser/skin/menuPanel-aero.png                          os=WINNT osversion=6
 % override chrome://browser/skin/menuPanel.png                        chrome://browser/skin/menuPanel-aero.png                          os=WINNT osversion=6.1
 % override chrome://browser/skin/menuPanel-small.png                  chrome://browser/skin/menuPanel-small-aero.png                    os=WINNT osversion=6
 % override chrome://browser/skin/menuPanel-small.png                  chrome://browser/skin/menuPanel-small-aero.png                    os=WINNT osversion=6.1
-% override chrome://browser/skin/theme-switcher-icon.png              chrome://browser/skin/theme-switcher-icon-aero.png                os=WINNT osversion=6
-% override chrome://browser/skin/theme-switcher-icon.png              chrome://browser/skin/theme-switcher-icon-aero.png                os=WINNT osversion=6.1
 % override chrome://browser/skin/loop/menuPanel.png                   chrome://browser/skin/loop/menuPanel-aero.png                     os=WINNT osversion=6
 % override chrome://browser/skin/loop/menuPanel.png                   chrome://browser/skin/loop/menuPanel-aero.png                     os=WINNT osversion=6.1
 
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-aero.png                            os=WINNT osversion=6
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-aero.png                            os=WINNT osversion=6.1
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-XP.png                              os=WINNT osversion=5.2
 % override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-aero.png                       os=WINNT osversion=6
 % override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-aero.png                       os=WINNT osversion=6.1
deleted file mode 100644
index 57a49930e7310a23f9b7c84a88cb60d5f93ceb1b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0b356426ca50987a1c65fcd0f5f4d02d97a9f11e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/configure.in
+++ b/configure.in
@@ -45,17 +45,17 @@ dnl ====================================
 _SUBDIR_HOST_CFLAGS="$HOST_CFLAGS"
 _SUBDIR_HOST_CXXFLAGS="$HOST_CXXFLAGS"
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
-MOZPNG=10616
+MOZPNG=10617
 NSPR_VERSION=4
 NSPR_MINVER=4.10.8
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=2.22
 # 2_26 is the earliest version we can set GLIB_VERSION_MIN_REQUIRED.
--- a/dom/apps/OfflineCacheInstaller.jsm
+++ b/dom/apps/OfflineCacheInstaller.jsm
@@ -41,42 +41,53 @@ function enableOfflineCacheForApp(aPrinc
   Services.perms.addFromPrincipal(aPrincipal, 'offline-app',
                                   Ci.nsIPermissionManager.ALLOW_ACTION);
   // Prevent cache from being evicted:
   Services.perms.addFromPrincipal(aPrincipal, 'pin-app',
                                   Ci.nsIPermissionManager.ALLOW_ACTION);
 }
 
 
-function storeCache(applicationCache, url, file, itemType) {
+function storeCache(applicationCache, url, file, itemType, metadata) {
   let storage =
     Services.cache2.appCacheStorage(LoadContextInfo.default, applicationCache);
   let uri = Services.io.newURI(url, null, null);
+  let nowGMT = new Date().toGMTString();
+  metadata = metadata || {};
+  metadata.lastFetched = metadata.lastFetched || nowGMT;
+  metadata.lastModified = metadata.lastModified || nowGMT;
   storage.asyncOpenURI(uri, "", nsICacheStorage.OPEN_TRUNCATE, {
     onCacheEntryAvailable:
       function (cacheEntry, isNew, appCache, result) {
-        cacheEntry.setMetaDataElement('request-method', 'GET');
-        cacheEntry.setMetaDataElement('response-head', 'HTTP/1.1 200 OK\r\n');
+        cacheEntry.setMetaDataElement("request-method", "GET");
+        cacheEntry.setMetaDataElement("response-head",
+          "HTTP/1.1 200 OK\r\n" +
+          "Date: " + metadata.lastFetched + "\r\n" +
+          "Last-Modified: " + metadata.lastModified + "\r\n" +
+          "Cache-Control: no-cache\r\n");
 
         let outputStream = cacheEntry.openOutputStream(0);
 
-        // Input-Output stream machinery in order to push nsIFile content into cache
-        let inputStream = Cc['@mozilla.org/network/file-input-stream;1']
+        // Input-Output stream machinery in order to push nsIFile content into
+        // cache
+        let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                             .createInstance(Ci.nsIFileInputStream);
         inputStream.init(file, 1, -1, null);
-        let bufferedOutputStream = Cc['@mozilla.org/network/buffered-output-stream;1']
-                                     .createInstance(Ci.nsIBufferedOutputStream);
+        let bufferedOutputStream =
+          Cc["@mozilla.org/network/buffered-output-stream;1"]
+            .createInstance(Ci.nsIBufferedOutputStream);
         bufferedOutputStream.init(outputStream, 1024);
         bufferedOutputStream.writeFrom(inputStream, inputStream.available());
         bufferedOutputStream.flush();
         bufferedOutputStream.close();
         inputStream.close();
 
+        cacheEntry.setExpirationTime(0);
         cacheEntry.markValid();
-        debug (file.path + ' -> ' + url + ' (' + itemType + ')');
+        debug (file.path + " -> " + url + " (" + itemType + ")");
         applicationCache.markEntry(url, itemType);
         cacheEntry.close();
       }
   });
 }
 
 function readFile(aFile, aPrincipal, aCallback) {
 
@@ -201,77 +212,98 @@ function parseAppCache(app, path, conten
   };
 }
 
 function installCache(app) {
   if (!app.cachePath) {
     return;
   }
 
-  let cacheDir = makeFile(app.cachePath)
+  let cacheDir = makeFile(app.cachePath);
   cacheDir.append(app.appId);
+
+  let resourcesMetadata = cacheDir.clone();
+  resourcesMetadata.append('resources_metadata.json');
+
   cacheDir.append('cache');
   if (!cacheDir.exists())
     return;
 
   let cacheManifest = cacheDir.clone();
   cacheManifest.append('manifest.appcache');
   if (!cacheManifest.exists())
     return;
 
   let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
-                    app.origin, app.localId, false);
-
-  enableOfflineCacheForApp(principal);
-
-  // Get the url for the manifest.
-  let appcacheURL = app.appcache_path;
+      app.origin, app.localId, false);
 
-  // The group ID contains application id and 'f' for not being hosted in
-  // a browser element, but a mozbrowser iframe.
-  // See netwerk/cache/nsDiskCacheDeviceSQL.cpp: AppendJARIdentifier
-  let groupID = appcacheURL + '#' + app.localId+ '+f';
-  let applicationCache = applicationCacheService.createApplicationCache(groupID);
-  applicationCache.activate();
+  // If the build has been correctly configured, this should not happen!
+  // If we install the cache anyway, it won't be updateable. If we don't install
+  // it, the application won't be useable offline.
+  let metadataLoaded;
+  if (!resourcesMetadata.exists()) {
+    // Not debug, since this is something that should be logged always!
+    dump("OfflineCacheInstaller: App " + app.appId + " does have an app cache" +
+         " but does not have a resources_metadata.json file!");
+    metadataLoaded = Promise.resolve({});
+  } else {
+    metadataLoaded = new Promise(
+      (resolve, reject) =>
+        readFile(resourcesMetadata, principal, content => resolve(JSON.parse(content))));
+  }
 
-  readFile(cacheManifest, principal, function readAppCache(content) {
-    let entries = parseAppCache(app, cacheManifest.path, content);
+  metadataLoaded.then(function(metadata) {
+    enableOfflineCacheForApp(principal);
+
+    // Get the url for the manifest.
+    let appcacheURL = app.appcache_path;
+
+    // The group ID contains application id and 'f' for not being hosted in
+    // a browser element, but a mozbrowser iframe.
+    // See netwerk/cache/nsDiskCacheDeviceSQL.cpp: AppendJARIdentifier
+    let groupID = appcacheURL + '#' + app.localId+ '+f';
+    let applicationCache = applicationCacheService.createApplicationCache(groupID);
+    applicationCache.activate();
+
+    readFile(cacheManifest, principal, function readAppCache(content) {
+      let entries = parseAppCache(app, cacheManifest.path, content);
 
-    entries.urls.forEach(function processCachedFile(url) {
-      // Get this nsIFile from cache folder for this URL
-      // We have absolute urls, so remove the origin part to locate the
-      // files.
-      let path = url.replace(app.origin.spec, '');
-      let file = cacheDir.clone();
-      let paths = path.split('/');
-      paths.forEach(file.append);
+      entries.urls.forEach(function processCachedFile(url) {
+        // Get this nsIFile from cache folder for this URL
+        // We have absolute urls, so remove the origin part to locate the
+        // files.
+        let path = url.replace(app.origin.spec, '');
+        let file = cacheDir.clone();
+        let paths = path.split('/');
+        paths.forEach(file.append);
 
-      if (!file.exists()) {
-        let msg = 'File ' + file.path + ' exists in the manifest but does ' +
-                  'not points to a real file.';
-        throw new Error(msg);
-      }
+        if (!file.exists()) {
+          let msg = 'File ' + file.path + ' exists in the manifest but does ' +
+                    'not points to a real file.';
+          throw new Error(msg);
+        }
 
-      let itemType = nsIApplicationCache.ITEM_EXPLICIT;
-      if (entries.fallbacks.indexOf(url) > -1) {
-        debug('add fallback: ' + url + '\n');
-        itemType |= nsIApplicationCache.ITEM_FALLBACK;
-      }
-      storeCache(applicationCache, url, file, itemType);
-    });
+        let itemType = nsIApplicationCache.ITEM_EXPLICIT;
+        if (entries.fallbacks.indexOf(url) > -1) {
+          debug('add fallback: ' + url + '\n');
+          itemType |= nsIApplicationCache.ITEM_FALLBACK;
+        }
+        storeCache(applicationCache, url, file, itemType, metadata[path]);
+      });
 
-    let array = new MutableArray();
-    entries.namespaces.forEach(function processNamespace([type, spec, data]) {
-      debug('add namespace: ' + type + ' - ' + spec + ' - ' + data + '\n');
-      array.appendElement(new Namespace(type, spec, data), false);
+      let array = new MutableArray();
+      entries.namespaces.forEach(function processNamespace([type, spec, data]) {
+        debug('add namespace: ' + type + ' - ' + spec + ' - ' + data + '\n');
+        array.appendElement(new Namespace(type, spec, data), false);
+      });
+      applicationCache.addNamespaces(array);
+
+      storeCache(applicationCache, appcacheURL, cacheManifest,
+                 nsIApplicationCache.ITEM_MANIFEST);
     });
-    applicationCache.addNamespaces(array);
-
-    storeCache(applicationCache, appcacheURL, cacheManifest,
-               nsIApplicationCache.ITEM_MANIFEST);
   });
 }
 
 
 // Public API
 
 this.OfflineCacheInstaller = {
   installCache: installCache
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -120,18 +120,17 @@ public:
 
   nsresult ParseURL(const nsAString& aURL);
   nsresult InitializeConnection();
 
   // These methods when called can release the WebSocket object
   void FailConnection(uint16_t reasonCode,
                       const nsACString& aReasonString = EmptyCString());
   nsresult CloseConnection(uint16_t reasonCode,
-                           const nsACString& aReasonString = EmptyCString(),
-                           bool aCanceling = false);
+                           const nsACString& aReasonString = EmptyCString());
   nsresult Disconnect();
   void DisconnectInternal();
 
   nsresult ConsoleError();
   nsresult PrintErrorOnConsole(const char* aBundleURI,
                                const char16_t* aError,
                                const char16_t** aFormatStrings,
                                uint32_t aFormatStringsLen);
@@ -380,48 +379,16 @@ WebSocketImpl::PrintErrorOnConsole(const
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 namespace {
 
-class CloseRunnable final : public WorkerMainThreadRunnable
-{
-public:
-  CloseRunnable(WebSocketImpl* aImpl, uint16_t aReasonCode,
-                const nsACString& aReasonString)
-    : WorkerMainThreadRunnable(aImpl->mWorkerPrivate)
-    , mImpl(aImpl)
-    , mReasonCode(aReasonCode)
-    , mReasonString(aReasonString)
-    , mRv(NS_ERROR_FAILURE)
-  { }
-
-  bool MainThreadRun() override
-  {
-    mRv = mImpl->mChannel->Close(mReasonCode, mReasonString);
-    return true;
-  }
-
-  nsresult ErrorCode() const
-  {
-    return mRv;
-  }
-
-private:
-  // A raw pointer because this runnable is sync.
-  WebSocketImpl* mImpl;
-
-  uint16_t mReasonCode;
-  const nsACString& mReasonString;
-  nsresult mRv;
-};
-
 class CancelWebSocketRunnable final : public nsRunnable
 {
 public:
   CancelWebSocketRunnable(nsIWebSocketChannel* aChannel, uint16_t aReasonCode,
                           const nsACString& aReasonString)
     : mChannel(aChannel)
     , mReasonCode(aReasonCode)
     , mReasonString(aReasonString)
@@ -464,21 +431,19 @@ public:
 private:
   WebSocketImpl* mImpl;
 };
 
 } // anonymous namespace
 
 nsresult
 WebSocketImpl::CloseConnection(uint16_t aReasonCode,
-                               const nsACString& aReasonString,
-                               bool aCanceling)
+                               const nsACString& aReasonString)
 {
   AssertIsOnTargetThread();
-  MOZ_ASSERT(!NS_IsMainThread() || !aCanceling);
 
   if (mDisconnectingOrDisconnected) {
     return NS_OK;
   }
 
   // If this method is called because the worker is going away, we will not
   // receive the OnStop() method and we have to disconnect the WebSocket and
   // release the WorkerFeature.
@@ -495,26 +460,19 @@ WebSocketImpl::CloseConnection(uint16_t 
     mWebSocket->SetReadyState(WebSocket::CLOSING);
 
     // The channel has to be closed on the main-thread.
 
     if (NS_IsMainThread()) {
       return mChannel->Close(aReasonCode, aReasonString);
     }
 
-    if (aCanceling) {
-      nsRefPtr<CancelWebSocketRunnable> runnable =
-        new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
-      return NS_DispatchToMainThread(runnable);
-    }
-
-    nsRefPtr<CloseRunnable> runnable =
-      new CloseRunnable(this, aReasonCode, aReasonString);
-    runnable->Dispatch(mWorkerPrivate->GetJSContext());
-    return runnable->ErrorCode();
+    nsRefPtr<CancelWebSocketRunnable> runnable =
+      new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
+    return NS_DispatchToMainThread(runnable);
   }
 
   // No channel, but not disconnected: canceled or failed early
   //
   MOZ_ASSERT(readyState == WebSocket::CONNECTING,
              "Should only get here for early websocket cancel/error");
 
   // Server won't be sending us a close code, so use what's passed in here.
@@ -2028,17 +1986,17 @@ public:
 
     if (aStatus >= Canceling) {
       {
         MutexAutoLock lock(mWebSocketImpl->mMutex);
         mWebSocketImpl->mWorkerShuttingDown = true;
       }
 
       mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,
-                                      EmptyCString(), true);
+                                      EmptyCString());
     }
 
     return true;
   }
 
   bool Suspend(JSContext* aCx) override
   {
     {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -335,29 +335,28 @@ nsFrameLoader::ReallyStartLoadingInterna
     js::ProfileEntry::Category::OTHER);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mRemoteFrame) {
-    if (!mRemoteBrowser) {
-      TryRemoteBrowser();
-
-      if (!mRemoteBrowser) {
+    if (!mRemoteBrowser && !TryRemoteBrowser()) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
-      }
     }
 
-    if (mRemoteBrowserShown || ShowRemoteFrame(ScreenIntSize(0, 0))) {
-      // FIXME get error codes from child
-      mRemoteBrowser->LoadURL(mURIToLoad);
-    } else {
+    // Execute pending frame scripts before loading URL
+    EnsureMessageManager();
+
+    // FIXME get error codes from child
+    mRemoteBrowser->LoadURL(mURIToLoad);
+    
+    if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
       NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
     }
 
     return NS_OK;
   }
 
   NS_ASSERTION(mDocShell,
                "MaybeCreateDocShell succeeded with a null mDocShell");
@@ -827,23 +826,19 @@ nsFrameLoader::MarginsChanged(uint32_t a
 }
 
 bool
 nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
                                nsSubDocumentFrame *aFrame)
 {
   NS_ASSERTION(mRemoteFrame, "ShowRemote only makes sense on remote frames.");
 
-  if (!mRemoteBrowser) {
-    TryRemoteBrowser();
-
-    if (!mRemoteBrowser) {
-      NS_ERROR("Couldn't create child process.");
-      return false;
-    }
+  if (!mRemoteBrowser && !TryRemoteBrowser()) {
+    NS_ERROR("Couldn't create child process.");
+    return false;
   }
 
   // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   // cross-process layers; need to figure out what behavior we really
   // want here.  For now, hack.
   if (!mRemoteBrowserShown) {
     if (!mOwnerContent ||
         !mOwnerContent->GetComposedDoc()) {
@@ -2257,38 +2252,41 @@ nsFrameLoader::TryRemoteBrowser()
     rv = context.SetTabContextForBrowserFrame(containingApp);
   } else {
     rv = context.SetTabContextForNormalFrame();
   }
   NS_ENSURE_TRUE(rv, false);
 
   nsCOMPtr<Element> ownerElement = mOwnerContent;
   mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent);
-  if (mRemoteBrowser) {
-    mChildID = mRemoteBrowser->Manager()->ChildID();
-    nsCOMPtr<nsIDocShellTreeItem> rootItem;
-    parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
-    nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
-    nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
-
-    if (rootChromeWin) {
-      nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
-      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
-      mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
-    }
-
-    mContentParent = mRemoteBrowser->Manager();
-
-    if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
-                                   nsGkAtoms::mozpasspointerevents,
-                                   nsGkAtoms::_true,
-                                   eCaseMatters)) {
-      unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
-    }
+  if (!mRemoteBrowser) {
+    return false;
   }
+
+  mContentParent = mRemoteBrowser->Manager();
+  mChildID = mRemoteBrowser->Manager()->ChildID();
+
+  nsCOMPtr<nsIDocShellTreeItem> rootItem;
+  parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
+  nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
+  nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
+
+  if (rootChromeWin) {
+    nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+    rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+    mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
+  }
+
+  if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
+                                 nsGkAtoms::mozpasspointerevents,
+                                 nsGkAtoms::_true,
+                                 eCaseMatters)) {
+    unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
+  }
+
   return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser()
 {
   return mRemoteBrowser;
 }
@@ -2523,17 +2521,17 @@ nsFrameLoader::EnsureMessageManager()
         mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::forcemessagemanager,
                                    nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
   bool useRemoteProcess = ShouldUseRemoteProcess();
   if (mMessageManager) {
-    if (useRemoteProcess && mRemoteBrowserShown) {
+    if (useRemoteProcess && mRemoteBrowser) {
       mMessageManager->InitWithCallback(this);
     }
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
     do_QueryInterface(GetOwnerDoc()->GetWindow());
   nsCOMPtr<nsIMessageBroadcaster> parentManager;
@@ -2548,17 +2546,17 @@ nsFrameLoader::EnsureMessageManager()
     }
 
     if (!parentManager) {
       chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
     }
   }
 
   if (useRemoteProcess) {
-    mMessageManager = new nsFrameMessageManager(mRemoteBrowserShown ? this : nullptr,
+    mMessageManager = new nsFrameMessageManager(mRemoteBrowser ? this : nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
   } else {
     mMessageManager = new nsFrameMessageManager(nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
 
     mChildMessageManager =
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -255,16 +255,17 @@ NS_IMETHODIMP_(nsIContent *)
 nsInProcessTabChildGlobal::GetOwnerContent()
 {
   return mOwner;
 }
 
 nsresult
 nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
 {
+  aVisitor.mForceContentDispatch = true;
   aVisitor.mCanHandle = true;
 
 #ifdef DEBUG
   if (mOwner) {
     nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
     nsRefPtr<nsFrameLoader> fl = owner->GetFrameLoader();
     if (fl) {
       NS_ASSERTION(this == fl->GetTabChildGlobalAsEventTarget(),
--- a/dom/canvas/CanvasImageCache.cpp
+++ b/dom/canvas/CanvasImageCache.cpp
@@ -294,17 +294,17 @@ CanvasImageCache::Lookup(Element* aImage
 
   nsCOMPtr<imgIRequest> request;
   entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
   if (request != entry->mData->mRequest)
     return nullptr;
 
   gImageCache->MarkUsed(entry->mData);
 
-  *aSize = gfx::ToIntSize(entry->mData->mSize);
+  *aSize = entry->mData->mSize;
   return entry->mData->mSourceSurface;
 }
 
 SourceSurface*
 CanvasImageCache::SimpleLookup(Element* aImage)
 {
   if (!gImageCache)
     return nullptr;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4315,17 +4315,17 @@ CanvasRenderingContext2D::DrawImage(cons
     if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
       // Spec says to silently do nothing if the element is still loading.
       if (!res.mIsStillLoading) {
         error.Throw(NS_ERROR_NOT_AVAILABLE);
       }
       return;
     }
 
-    imgSize = gfx::ToIntSize(res.mSize);
+    imgSize = res.mSize;
 
     // Scale sw/sh based on aspect ratio
     if (image.IsHTMLVideoElement()) {
       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
       int32_t displayWidth = video->VideoWidth();
       int32_t displayHeight = video->VideoHeight();
       sw *= (double)imgSize.width / (double)displayWidth;
       sh *= (double)imgSize.height / (double)displayHeight;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -50,17 +50,16 @@
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
-#include "nsDirectoryServiceDefs.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #include "mozilla/SandboxInfo.h"
 #elif defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
 #endif
 
@@ -1075,86 +1074,16 @@ ContentChild::AllocPBackgroundChild(Tran
 
 PProcessHangMonitorChild*
 ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport,
                                             ProcessId aOtherProcess)
 {
     return CreateHangMonitorChild(aTransport, aOtherProcess);
 }
 
-#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-static void
-SetUpSandboxEnvironment()
-{
-    // Set up a low integrity temp directory. This only makes sense if the
-    // delayed integrity level for the content process is INTEGRITY_LEVEL_LOW.
-    nsresult rv;
-    nsCOMPtr<nsIProperties> directoryService =
-        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    nsCOMPtr<nsIFile> lowIntegrityTemp;
-    rv = directoryService->Get(NS_WIN_LOW_INTEGRITY_TEMP, NS_GET_IID(nsIFile),
-                               getter_AddRefs(lowIntegrityTemp));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Undefine returns a failure if the property is not already set.
-    unused << directoryService->Undefine(NS_OS_TEMP_DIR);
-    rv = directoryService->Set(NS_OS_TEMP_DIR, lowIntegrityTemp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Set TEMP and TMP environment variables.
-    nsAutoString lowIntegrityTempPath;
-    rv = lowIntegrityTemp->GetPath(lowIntegrityTempPath);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    bool setOK = SetEnvironmentVariableW(L"TEMP", lowIntegrityTempPath.get());
-    NS_WARN_IF_FALSE(setOK, "Failed to set TEMP to low integrity temp path");
-    setOK = SetEnvironmentVariableW(L"TMP", lowIntegrityTempPath.get());
-    NS_WARN_IF_FALSE(setOK, "Failed to set TMP to low integrity temp path");
-}
-
-void
-ContentChild::CleanUpSandboxEnvironment()
-{
-    // Sandbox environment is only currently a low integrity temp, which only
-    // makes sense for sandbox pref level 1 (and will eventually not be needed
-    // at all, once all file access is via chrome/broker process).
-    if (Preferences::GetInt("security.sandbox.content.level") != 1) {
-        return;
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIProperties> directoryService =
-        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    nsCOMPtr<nsIFile> lowIntegrityTemp;
-    rv = directoryService->Get(NS_WIN_LOW_INTEGRITY_TEMP, NS_GET_IID(nsIFile),
-                               getter_AddRefs(lowIntegrityTemp));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Don't check the return value as the directory will only have been created
-    // if it has been used.
-    unused << lowIntegrityTemp->Remove(/* aRecursive */ true);
-}
-#endif
-
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
 
 #include <stdlib.h>
 
 static bool
 GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath, nsCString &aAppDir)
 {
   nsAutoCString appPath;
@@ -1263,22 +1192,16 @@ ContentChild::RecvSetProcessSandbox()
     // Otherwise, sandboxing is best-effort.
     if (!SandboxInfo::Get().CanSandboxContent()) {
         return true;
     }
 #endif
     SetContentProcessSandbox();
 #elif defined(XP_WIN)
     mozilla::SandboxTarget::Instance()->StartSandbox();
-    // Sandbox environment is only currently a low integrity temp, which only
-    // makes sense for sandbox pref level 1 (and will eventually not be needed
-    // at all, once all file access is via chrome/broker process).
-    if (Preferences::GetInt("security.sandbox.content.level") == 1) {
-        SetUpSandboxEnvironment();
-    }
 #elif defined(XP_MACOSX)
     StartMacOSContentSandbox();
 #endif
 #endif
     return true;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -130,21 +130,16 @@ public:
     PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) override;
 
     PProcessHangMonitorChild*
     AllocPProcessHangMonitorChild(Transport* aTransport,
                                   ProcessId aOtherProcess) override;
 
-#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-    // Cleans up any resources used by the process when sandboxed.
-    void CleanUpSandboxEnvironment();
-#endif
-
     virtual bool RecvSetProcessSandbox() override;
 
     PBackgroundChild*
     AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                           override;
 
     virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                               const IPCTabContext& aContext,
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -3,42 +3,122 @@
  * 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 "mozilla/ipc/IOThreadChild.h"
 
 #include "ContentProcess.h"
 
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+#include "mozilla/Preferences.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#endif
+
 using mozilla::ipc::IOThreadChild;
 
 namespace mozilla {
 namespace dom {
 
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+static already_AddRefed<nsIFile>
+GetLowIntegrityTemp()
+{
+  MOZ_ASSERT(nsDirectoryService::gService,
+    "GetLowIntegrityTemp relies on nsDirectoryService being initialized");
+
+  // A low integrity temp only currently makes sense for sandbox pref level 1.
+  if (Preferences::GetInt("security.sandbox.content.level") != 1) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIFile> lowIntegrityTemp;
+  nsresult rv = nsDirectoryService::gService->Get(NS_WIN_LOW_INTEGRITY_TEMP,
+                                                  NS_GET_IID(nsIFile),
+                                                  getter_AddRefs(lowIntegrityTemp));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  return lowIntegrityTemp.forget();
+}
+
+static void
+SetUpSandboxEnvironment()
+{
+  MOZ_ASSERT(nsDirectoryService::gService,
+    "SetUpSandboxEnvironment relies on nsDirectoryService being initialized");
+
+  // Setup to use a low integrity temp if available.
+  nsCOMPtr<nsIFile> lowIntegrityTemp = GetLowIntegrityTemp();
+  if (!lowIntegrityTemp) {
+    return;
+  }
+
+  // Undefine returns a failure if the property is not already set.
+  unused << nsDirectoryService::gService->Undefine(NS_OS_TEMP_DIR);
+  nsresult rv = nsDirectoryService::gService->Set(NS_OS_TEMP_DIR, lowIntegrityTemp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  // Set TEMP and TMP environment variables.
+  nsAutoString lowIntegrityTempPath;
+  rv = lowIntegrityTemp->GetPath(lowIntegrityTempPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  bool setOK = SetEnvironmentVariableW(L"TEMP", lowIntegrityTempPath.get());
+  NS_WARN_IF_FALSE(setOK, "Failed to set TEMP to low integrity temp path");
+  setOK = SetEnvironmentVariableW(L"TMP", lowIntegrityTempPath.get());
+  NS_WARN_IF_FALSE(setOK, "Failed to set TMP to low integrity temp path");
+}
+
+static void
+CleanUpSandboxEnvironment()
+{
+  // Remove low integrity temp if it exists.
+  nsCOMPtr<nsIFile> lowIntegrityTemp = GetLowIntegrityTemp();
+  if (!lowIntegrityTemp) {
+    return;
+  }
+
+  // Don't check the return value as the directory will only have been created
+  // if it has been used.
+  unused << lowIntegrityTemp->Remove(/* aRecursive */ true);
+}
+#endif
+
 void
 ContentProcess::SetAppDir(const nsACString& aPath)
 {
   mXREEmbed.SetAppDir(aPath);
 }
 
 bool
 ContentProcess::Init()
 {
     mContent.Init(IOThreadChild::message_loop(),
                          ParentPid(),
                          IOThreadChild::channel());
     mXREEmbed.Start();
     mContent.InitXPCOM();
+
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+    SetUpSandboxEnvironment();
+#endif
     
     return true;
 }
 
 void
 ContentProcess::CleanUp()
 {
 #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-    mContent.CleanUpSandboxEnvironment();
+    CleanUpSandboxEnvironment();
 #endif
     mXREEmbed.Stop();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1688,16 +1688,20 @@ TabChild::IsRootContentDocument()
 
     return !HasAppOwnerApp();
 }
 
 bool
 TabChild::RecvLoadURL(const nsCString& aURI,
                       const BrowserConfiguration& aConfiguration)
 {
+    if (!InitTabChildGlobal()) {
+      return false;
+    }
+
     SetProcessNameToAppName();
 
     nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
                                            nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                                            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER,
                                            nullptr, nullptr, nullptr);
     if (NS_FAILED(rv)) {
         NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -261,17 +261,16 @@ TabParent::TabParent(nsIContentParent* a
   , mIMECompositionStart(0)
   , mIMESeqno(0)
   , mIMECompositionRectOffset(0)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
   , mDefaultScale(0)
-  , mShown(false)
   , mUpdatedDimensions(false)
   , mChromeOffset(0, 0)
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
@@ -749,23 +748,16 @@ TabParent::LoadURL(nsIURI* aURI)
 
     if (mCreatingWindow) {
         // Don't send the message if the child wants to load its own URL.
         MOZ_ASSERT(mDelayedURL.IsEmpty());
         mDelayedURL = spec;
         return;
     }
 
-    if (!mShown) {
-        NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before "
-                                   "Show(). Ignoring LoadURL.\n",
-                                   spec.get()).get());
-        return;
-    }
-
     uint32_t appId = OwnOrContainingAppId();
     if (mSendOfflineStatus && NS_IsAppOffline(appId)) {
       // If the app is offline in the parent process
       // pass that state to the child process as well
       unused << SendAppOfflineStatus(appId, true);
     }
     mSendOfflineStatus = false;
 
@@ -819,18 +811,16 @@ TabParent::LoadURL(nsIURI* aURI)
             }
         }
     }
 }
 
 void
 TabParent::Show(const ScreenIntSize& size, bool aParentIsActive)
 {
-    // sigh
-    mShown = true;
     mDimensions = size;
     if (mIsDestroyed) {
         return;
     }
 
     TextureFactoryIdentifier textureFactoryIdentifier;
     uint64_t layersId = 0;
     bool success = false;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -424,17 +424,16 @@ protected:
     LayoutDeviceIntRect mIMECaretRect;
     LayoutDeviceIntRect mIMEEditorRect;
 
     nsIntRect mRect;
     ScreenIntSize mDimensions;
     ScreenOrientation mOrientation;
     float mDPI;
     CSSToLayoutDeviceScale mDefaultScale;
-    bool mShown;
     bool mUpdatedDimensions;
     nsIntPoint mChromeOffset;
 
 private:
     already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
     layout::RenderFrameParent* GetRenderFrame();
     nsRefPtr<nsIContentParent> mManager;
     void TryCacheDPIAndScale();
deleted file mode 100644
--- a/dom/media/fmp4/AVCCDecoderModule.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "AVCCDecoderModule.h"
-#include "ImageContainer.h"
-#include "MediaTaskQueue.h"
-#include "mp4_demuxer/DecoderData.h"
-#include "mp4_demuxer/AnnexB.h"
-#include "mp4_demuxer/H264.h"
-
-namespace mozilla
-{
-
-class AVCCMediaDataDecoder : public MediaDataDecoder {
-public:
-
-  AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
-                       const mp4_demuxer::VideoDecoderConfig& aConfig,
-                       layers::LayersBackend aLayersBackend,
-                       layers::ImageContainer* aImageContainer,
-                       FlushableMediaTaskQueue* aVideoTaskQueue,
-                       MediaDataDecoderCallback* aCallback);
-
-  virtual ~AVCCMediaDataDecoder();
-
-  virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
-  virtual bool IsWaitingMediaResources() override;
-  virtual bool IsDormantNeeded() override;
-  virtual void AllocateMediaResources() override;
-  virtual void ReleaseMediaResources() override;
-  virtual bool IsHardwareAccelerated() const override;
-
-private:
-  // Will create the required MediaDataDecoder if we have a AVC SPS.
-  // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
-  // will set mError accordingly.
-  nsresult CreateDecoder();
-  nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
-  nsresult CheckForSPSChange(mp4_demuxer::MP4Sample* aSample);
-  void UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData);
-
-  nsRefPtr<PlatformDecoderModule> mPDM;
-  mp4_demuxer::VideoDecoderConfig mCurrentConfig;
-  layers::LayersBackend mLayersBackend;
-  nsRefPtr<layers::ImageContainer> mImageContainer;
-  nsRefPtr<FlushableMediaTaskQueue> mVideoTaskQueue;
-  MediaDataDecoderCallback* mCallback;
-  nsRefPtr<MediaDataDecoder> mDecoder;
-  nsresult mLastError;
-};
-
-AVCCMediaDataDecoder::AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
-                                           const mp4_demuxer::VideoDecoderConfig& aConfig,
-                                           layers::LayersBackend aLayersBackend,
-                                           layers::ImageContainer* aImageContainer,
-                                           FlushableMediaTaskQueue* aVideoTaskQueue,
-                                           MediaDataDecoderCallback* aCallback)
-  : mPDM(aPDM)
-  , mCurrentConfig(aConfig)
-  , mLayersBackend(aLayersBackend)
-  , mImageContainer(aImageContainer)
-  , mVideoTaskQueue(aVideoTaskQueue)
-  , mCallback(aCallback)
-  , mDecoder(nullptr)
-  , mLastError(NS_OK)
-{
-  CreateDecoder();
-}
-
-AVCCMediaDataDecoder::~AVCCMediaDataDecoder()
-{
-}
-
-nsresult
-AVCCMediaDataDecoder::Init()
-{
-  if (mDecoder) {
-    return mDecoder->Init();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
-{
-  if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
-    return NS_ERROR_FAILURE;
-  }
-  nsresult rv;
-  if (!mDecoder) {
-    // It is not possible to create an AVCC H264 decoder without SPS.
-    // As such, creation will fail if the extra_data just extracted doesn't
-    // contain a SPS.
-    rv = CreateDecoderAndInit(aSample);
-    if (rv == NS_ERROR_NOT_INITIALIZED) {
-      // We are missing the required SPS to create the decoder.
-      // Ignore for the time being, the MP4Sample will be dropped.
-      return NS_OK;
-    }
-  } else {
-    rv = CheckForSPSChange(aSample);
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aSample->extra_data = mCurrentConfig.extra_data;
-
-  return mDecoder->Input(aSample);
-}
-
-nsresult
-AVCCMediaDataDecoder::Flush()
-{
-  if (mDecoder) {
-    return mDecoder->Flush();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Drain()
-{
-  if (mDecoder) {
-    return mDecoder->Drain();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Shutdown()
-{
-  if (mDecoder) {
-    nsresult rv = mDecoder->Shutdown();
-    mDecoder = nullptr;
-    return rv;
-  }
-  return NS_OK;
-}
-
-bool
-AVCCMediaDataDecoder::IsWaitingMediaResources()
-{
-  if (mDecoder) {
-    return mDecoder->IsWaitingMediaResources();
-  }
-  return MediaDataDecoder::IsWaitingMediaResources();
-}
-
-bool
-AVCCMediaDataDecoder::IsDormantNeeded()
-{
-  return true;
-}
-
-void
-AVCCMediaDataDecoder::AllocateMediaResources()
-{
-  // Nothing to do, decoder will be allocated on the fly when required.
-}
-
-void
-AVCCMediaDataDecoder::ReleaseMediaResources()
-{
-  Shutdown();
-}
-
-nsresult
-AVCCMediaDataDecoder::CreateDecoder()
-{
-  if (!mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.extra_data)) {
-    // nothing found yet, will try again later
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  UpdateConfigFromExtraData(mCurrentConfig.extra_data);
-
-  mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
-                                      mLayersBackend,
-                                      mImageContainer,
-                                      mVideoTaskQueue,
-                                      mCallback);
-  if (!mDecoder) {
-    mLastError = NS_ERROR_FAILURE;
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-nsresult
-AVCCMediaDataDecoder::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
-{
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
-    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
-  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  UpdateConfigFromExtraData(extra_data);
-
-  nsresult rv = CreateDecoder();
-  NS_ENSURE_SUCCESS(rv, rv);
-  return Init();
-}
-
-bool
-AVCCMediaDataDecoder::IsHardwareAccelerated() const
-{
-  if (mDecoder) {
-    return mDecoder->IsHardwareAccelerated();
-  }
-  return MediaDataDecoder::IsHardwareAccelerated();
-}
-
-nsresult
-AVCCMediaDataDecoder::CheckForSPSChange(mp4_demuxer::MP4Sample* aSample)
-{
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
-    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
-  if (!mp4_demuxer::AnnexB::HasSPS(extra_data) ||
-      mp4_demuxer::AnnexB::CompareExtraData(extra_data,
-                                            mCurrentConfig.extra_data)) {
-    return NS_OK;
-  }
-  // The SPS has changed, signal to flush the current decoder and create a
-  // new one.
-  mDecoder->Flush();
-  ReleaseMediaResources();
-  return CreateDecoderAndInit(aSample);
-}
-
-void
-AVCCMediaDataDecoder::UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData)
-{
-  mp4_demuxer::SPSData spsdata;
-  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
-      spsdata.pic_width > 0 && spsdata.pic_height > 0) {
-    mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
-    mCurrentConfig.image_width = spsdata.pic_width;
-    mCurrentConfig.image_height = spsdata.pic_height;
-    mCurrentConfig.display_width = spsdata.display_width;
-    mCurrentConfig.display_height = spsdata.display_height;
-  }
-  mCurrentConfig.extra_data = aExtraData;
-}
-
-// AVCCDecoderModule
-
-AVCCDecoderModule::AVCCDecoderModule(PlatformDecoderModule* aPDM)
-: mPDM(aPDM)
-{
-  MOZ_ASSERT(aPDM);
-}
-
-AVCCDecoderModule::~AVCCDecoderModule()
-{
-}
-
-nsresult
-AVCCDecoderModule::Startup()
-{
-  return mPDM->Startup();
-}
-
-already_AddRefed<MediaDataDecoder>
-AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                                      layers::LayersBackend aLayersBackend,
-                                      layers::ImageContainer* aImageContainer,
-                                      FlushableMediaTaskQueue* aVideoTaskQueue,
-                                      MediaDataDecoderCallback* aCallback)
-{
-  nsRefPtr<MediaDataDecoder> decoder;
-
-  if ((!aConfig.mime_type.EqualsLiteral("video/avc") &&
-       !aConfig.mime_type.EqualsLiteral("video/mp4")) ||
-      !mPDM->DecoderNeedsAVCC(aConfig)) {
-    // There is no need for an AVCC wrapper for non-AVC content.
-    decoder = mPDM->CreateVideoDecoder(aConfig,
-                                       aLayersBackend,
-                                       aImageContainer,
-                                       aVideoTaskQueue,
-                                       aCallback);
-  } else {
-    decoder = new AVCCMediaDataDecoder(mPDM,
-                                       aConfig,
-                                       aLayersBackend,
-                                       aImageContainer,
-                                       aVideoTaskQueue,
-                                       aCallback);
-  }
-  return decoder.forget();
-}
-
-already_AddRefed<MediaDataDecoder>
-AVCCDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
-                                      FlushableMediaTaskQueue* aAudioTaskQueue,
-                                      MediaDataDecoderCallback* aCallback)
-{
-  return mPDM->CreateAudioDecoder(aConfig,
-                                  aAudioTaskQueue,
-                                  aCallback);
-}
-
-bool
-AVCCDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
-{
-  return mPDM->SupportsAudioMimeType(aMimeType);
-}
-
-bool
-AVCCDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
-{
-  return mPDM->SupportsVideoMimeType(aMimeType);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/fmp4/AVCCDecoderModule.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef mozilla_AVCCDecoderModule_h
-#define mozilla_AVCCDecoderModule_h
-
-#include "PlatformDecoderModule.h"
-
-namespace mozilla {
-
-class AVCCMediaDataDecoder;
-
-// AVCCDecoderModule is a PlatformDecoderModule wrapper used to ensure that
-// only AVCC format is fed to the underlying PlatformDecoderModule.
-// The AVCCDecoderModule allows playback of content where the SPS NAL may not be
-// provided in the init segment (e.g. AVC3 or Annex B)
-// AVCCDecoderModule will monitor the input data, and will delay creation of the
-// MediaDataDecoder until a SPS and PPS NALs have been extracted.
-//
-// AVCC-only decoder modules are AppleVideoDecoder and EMEH264Decoder.
-
-class AVCCDecoderModule : public PlatformDecoderModule {
-public:
-  explicit AVCCDecoderModule(PlatformDecoderModule* aPDM);
-  virtual ~AVCCDecoderModule();
-
-  virtual nsresult Startup() override;
-
-  virtual already_AddRefed<MediaDataDecoder>
-  CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                     layers::LayersBackend aLayersBackend,
-                     layers::ImageContainer* aImageContainer,
-                     FlushableMediaTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
-
-  virtual already_AddRefed<MediaDataDecoder>
-  CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
-                     FlushableMediaTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
-
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override;
-
-private:
-  nsRefPtr<PlatformDecoderModule> mPDM;
-};
-
-} // namespace mozilla
-
-#endif // mozilla_AVCCDecoderModule_h
--- a/dom/media/fmp4/BlankDecoderModule.cpp
+++ b/dom/media/fmp4/BlankDecoderModule.cpp
@@ -233,21 +233,27 @@ public:
     nsRefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
                                                        aAudioTaskQueue,
                                                        aCallback);
     return decoder.forget();
   }
 
   virtual bool
-  SupportsAudioMimeType(const nsACString& aMimeType) override
+  SupportsMimeType(const nsACString& aMimeType) override
   {
     return true;
   }
 
+  ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+  {
+    return kNeedNone;
+  }
+
 };
 
 already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
 {
   nsRefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
   return pdm.forget();
 }
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -153,16 +153,17 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mFoundSPSForTelemetry(false)
   , mIsEncrypted(false)
   , mAreDecodersSetup(false)
   , mIndexReady(false)
+  , mLastSeenEnd(-1)
   , mDemuxerMonitor("MP4 Demuxer")
 #if defined(MP4_READER_DORMANT_HEURISTIC)
   , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
 #endif
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
@@ -338,26 +339,26 @@ MP4Reader::ExtractCryptoInitData(nsTArra
   }
 }
 
 bool
 MP4Reader::IsSupportedAudioMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("audio/mpeg") ||
           aMimeType.EqualsLiteral("audio/mp4a-latm")) &&
-         mPlatform->SupportsAudioMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 bool
 MP4Reader::IsSupportedVideoMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("video/mp4") ||
           aMimeType.EqualsLiteral("video/avc") ||
           aMimeType.EqualsLiteral("video/x-vnd.on2.vp6")) &&
-         mPlatform->SupportsVideoMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 void
 MP4Reader::PreReadMetadata()
 {
   if (mPlatform) {
     RequestCodecResource();
   }
@@ -508,19 +509,20 @@ MP4Reader::EnsureDecodersSetup()
     mPlatform = PlatformDecoderModule::Create();
     NS_ENSURE_TRUE(mPlatform, false);
   }
 
   if (HasAudio()) {
     NS_ENSURE_TRUE(IsSupportedAudioMimeType(mDemuxer->AudioConfig().mime_type),
                    false);
 
-    mAudio.mDecoder = mPlatform->CreateAudioDecoder(mDemuxer->AudioConfig(),
-                                                    mAudio.mTaskQueue,
-                                                    mAudio.mCallback);
+    mAudio.mDecoder =
+      mPlatform->CreateDecoder(mDemuxer->AudioConfig(),
+                               mAudio.mTaskQueue,
+                               mAudio.mCallback);
     NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false);
     nsresult rv = mAudio.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   if (HasVideo()) {
     NS_ENSURE_TRUE(IsSupportedVideoMimeType(mDemuxer->VideoConfig().mime_type),
                    false);
@@ -529,21 +531,22 @@ MP4Reader::EnsureDecodersSetup()
       mVideo.mDecoder =
         mSharedDecoderManager->CreateVideoDecoder(mPlatform,
                                                   mDemuxer->VideoConfig(),
                                                   mLayersBackendType,
                                                   mDecoder->GetImageContainer(),
                                                   mVideo.mTaskQueue,
                                                   mVideo.mCallback);
     } else {
-      mVideo.mDecoder = mPlatform->CreateVideoDecoder(mDemuxer->VideoConfig(),
-                                                      mLayersBackendType,
-                                                      mDecoder->GetImageContainer(),
-                                                      mVideo.mTaskQueue,
-                                                      mVideo.mCallback);
+      mVideo.mDecoder =
+        mPlatform->CreateDecoder(mDemuxer->VideoConfig(),
+                                 mVideo.mTaskQueue,
+                                 mVideo.mCallback,
+                                 mLayersBackendType,
+                                 mDecoder->GetImageContainer());
     }
     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
     nsresult rv = mVideo.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   mAreDecodersSetup = true;
   return true;
@@ -595,17 +598,17 @@ MP4Reader::GetNextKeyframeTime()
 
 void
 MP4Reader::DisableHardwareAcceleration()
 {
   if (HasVideo() && mSharedDecoderManager) {
     mSharedDecoderManager->DisableHardwareAcceleration();
 
     const VideoDecoderConfig& video = mDemuxer->VideoConfig();
-    if (!mSharedDecoderManager->Recreate(video, mLayersBackendType, mDecoder->GetImageContainer())) {
+    if (!mSharedDecoderManager->Recreate(video)) {
       MonitorAutoLock mon(mVideo.mMonitor);
       mVideo.mError = true;
       if (mVideo.HasPromise()) {
         mVideo.RejectPromise(DECODE_ERROR, __func__);
       }
     } else {
       MonitorAutoLock lock(mVideo.mMonitor);
       ScheduleUpdate(kVideo);
@@ -1153,9 +1156,44 @@ MP4Reader::SetSharedDecoderManager(Share
 }
 
 bool
 MP4Reader::VideoIsHardwareAccelerated() const
 {
   return mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated();
 }
 
+void
+MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mShutdown) {
+    return;
+  }
+
+  if (mLastSeenEnd < 0) {
+    MonitorAutoLock mon(mDemuxerMonitor);
+    mLastSeenEnd = mDecoder->GetResource()->GetLength();
+    if (mLastSeenEnd < 0) {
+      // We dont have a length. Demuxer would have been blocking already.
+      return;
+    }
+  }
+  int64_t end = aOffset + aLength;
+  if (end <= mLastSeenEnd) {
+    return;
+  }
+  mLastSeenEnd = end;
+
+  if (HasVideo()) {
+    auto& decoder = GetDecoderData(kVideo);
+    MonitorAutoLock lock(decoder.mMonitor);
+    decoder.mDemuxEOS = false;
+  }
+  if (HasAudio()) {
+    auto& decoder = GetDecoderData(kAudio);
+    MonitorAutoLock lock(decoder.mMonitor);
+    decoder.mDemuxEOS = false;
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -72,16 +72,17 @@ public:
   virtual void ReadUpdatedMetadata(MediaInfo* aInfo) override;
 
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
   virtual bool IsMediaSeekable() override;
 
   virtual int64_t GetEvictionOffset(double aTime) override;
+  virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) override;
 
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) override;
 
   // For Media Resource Management
   virtual void SetIdle() override;
   virtual bool IsWaitingMediaResources() override;
   virtual bool IsDormantNeeded() override;
   virtual void ReleaseMediaResources() override;
@@ -282,16 +283,17 @@ private:
   bool mFoundSPSForTelemetry;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mAreDecodersSetup;
 
   bool mIndexReady;
+  int64_t mLastSeenEnd;
   Monitor mDemuxerMonitor;
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 
 #if defined(MP4_READER_DORMANT_HEURISTIC)
   const bool mDormantEnabled;
 #endif
 };
 
--- a/dom/media/fmp4/PlatformDecoderModule.cpp
+++ b/dom/media/fmp4/PlatformDecoderModule.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "PlatformDecoderModule.h"
-#include "AVCCDecoderModule.h"
 
 #ifdef XP_WIN
 #include "WMFDecoderModule.h"
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 #ifdef MOZ_APPLEMEDIA
@@ -27,16 +26,19 @@
 #include "mozilla/Preferences.h"
 #ifdef MOZ_EME
 #include "EMEDecoderModule.h"
 #include "mozilla/CDMProxy.h"
 #endif
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 
+#include "mp4_demuxer/DecoderData.h"
+#include "H264Converter.h"
+
 namespace mozilla {
 
 extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
 
 bool PlatformDecoderModule::sUseBlankDecoder = false;
 bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
 bool PlatformDecoderModule::sGonkDecoderEnabled = false;
 bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
@@ -102,20 +104,17 @@ PlatformDecoderModule::CreateCDMWrapper(
     // decrypted output of the CDM.
     pdm = Create();
     if (!pdm) {
       return nullptr;
     }
   }
 
   nsRefPtr<PlatformDecoderModule> emepdm(
-    new AVCCDecoderModule(new EMEDecoderModule(aProxy,
-                                               pdm,
-                                               cdmDecodesAudio,
-                                               cdmDecodesVideo)));
+    new EMEDecoderModule(aProxy, pdm, cdmDecodesAudio, cdmDecodesVideo));
   return emepdm.forget();
 }
 #endif
 
 /* static */
 already_AddRefed<PlatformDecoderModule>
 PlatformDecoderModule::Create()
 {
@@ -146,55 +145,81 @@ PlatformDecoderModule::CreatePDM()
 #ifdef XP_WIN
   nsRefPtr<PlatformDecoderModule> m(new WMFDecoderModule());
   return m.forget();
 #endif
 #ifdef MOZ_FFMPEG
   if (sFFmpegDecoderEnabled) {
     nsRefPtr<PlatformDecoderModule> m = FFmpegRuntimeLinker::CreateDecoderModule();
     if (m) {
-      nsRefPtr<PlatformDecoderModule> m2(new AVCCDecoderModule(m));
-      return m2.forget();
+      return m.forget();
     }
   }
 #endif
 #ifdef MOZ_APPLEMEDIA
-  nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new AppleDecoderModule()));
+  nsRefPtr<PlatformDecoderModule> m(new AppleDecoderModule());
   return m.forget();
 #endif
 #ifdef MOZ_GONK_MEDIACODEC
   if (sGonkDecoderEnabled) {
     nsRefPtr<PlatformDecoderModule> m(new GonkDecoderModule());
     return m.forget();
   }
 #endif
 #ifdef MOZ_WIDGET_ANDROID
   if(sAndroidMCDecoderEnabled){
     nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
     return m.forget();
   }
 #endif
   if (sGMPDecoderEnabled) {
-    nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new GMPDecoderModule()));
+    nsRefPtr<PlatformDecoderModule> m(new GMPDecoderModule());
     return m.forget();
   }
   return nullptr;
 }
 
-bool
-PlatformDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+already_AddRefed<MediaDataDecoder>
+PlatformDecoderModule::CreateDecoder(const mp4_demuxer::TrackConfig& aConfig,
+                                     FlushableMediaTaskQueue* aTaskQueue,
+                                     MediaDataDecoderCallback* aCallback,
+                                     layers::LayersBackend aLayersBackend,
+                                     layers::ImageContainer* aImageContainer)
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm");
+  nsRefPtr<MediaDataDecoder> m;
+
+  if (aConfig.IsAudioConfig()) {
+    m = CreateAudioDecoder(static_cast<const mp4_demuxer::AudioDecoderConfig&>(aConfig),
+                           aTaskQueue,
+                           aCallback);
+    return m.forget();
+  }
+
+  if (!aConfig.IsVideoConfig()) {
+    return nullptr;
+  }
+
+  if (H264Converter::IsH264(aConfig)) {
+    m = new H264Converter(this,
+                          static_cast<const mp4_demuxer::VideoDecoderConfig&>(aConfig),
+                          aLayersBackend,
+                          aImageContainer,
+                          aTaskQueue,
+                          aCallback);
+  } else {
+    m = CreateVideoDecoder(static_cast<const mp4_demuxer::VideoDecoderConfig&>(aConfig),
+                           aLayersBackend,
+                           aImageContainer,
+                           aTaskQueue,
+                           aCallback);
+  }
+  return m.forget();
 }
 
 bool
-PlatformDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
+PlatformDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
-  return aMimeType.EqualsLiteral("video/mp4") || aMimeType.EqualsLiteral("video/avc");
-}
-
-bool
-PlatformDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
-{
-  return false;
+  return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+    aMimeType.EqualsLiteral("video/mp4") ||
+    aMimeType.EqualsLiteral("video/avc");
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -9,16 +9,17 @@
 
 #include "MediaDecoderReader.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include <queue>
 
 namespace mp4_demuxer {
+class TrackConfig;
 class VideoDecoderConfig;
 class AudioDecoderConfig;
 class MP4Sample;
 }
 
 class nsIThreadPool;
 
 namespace mozilla {
@@ -81,67 +82,86 @@ public:
   // that we use on on aTaskQueue to decode the decrypted stream.
   // This is called on the decode task queue.
   static already_AddRefed<PlatformDecoderModule>
   CreateCDMWrapper(CDMProxy* aProxy,
                    bool aHasAudio,
                    bool aHasVideo);
 #endif
 
-  // Creates an H.264 decoder. The layers backend is passed in so that
+  // Creates a decoder.
+  // See CreateVideoDecoder and CreateAudioDecoder for implementation details.
+  virtual already_AddRefed<MediaDataDecoder>
+  CreateDecoder(const mp4_demuxer::TrackConfig& aConfig,
+                FlushableMediaTaskQueue* aTaskQueue,
+                MediaDataDecoderCallback* aCallback,
+                layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
+                layers::ImageContainer* aImageContainer = nullptr);
+
+  // An audio decoder module must support AAC by default.
+  // A video decoder must support H264 by default.
+  // If more codecs are to be supported, SupportsMimeType will have
+  // to be extended
+  virtual bool SupportsMimeType(const nsACString& aMimeType);
+
+  enum ConversionRequired {
+    kNeedNone,
+    kNeedAVCC,
+    kNeedAnnexB,
+  };
+
+  // Indicates that the decoder requires a specific format.
+  // The PlatformDecoderModule will convert the demuxed data accordingly before
+  // feeding it to MediaDataDecoder::Input.
+  virtual ConversionRequired DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const = 0;
+
+  virtual void DisableHardwareAcceleration() {}
+
+  virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const {
+    return true;
+  }
+
+protected:
+  PlatformDecoderModule() {}
+  virtual ~PlatformDecoderModule() {}
+
+  friend class H264Converter;
+  // Creates a Video decoder. The layers backend is passed in so that
   // decoders can determine whether hardware accelerated decoding can be used.
   // Asynchronous decoding of video should be done in runnables dispatched
   // to aVideoTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // Output and errors should be returned to the reader via aCallback.
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // Returns nullptr if the decoder can't be created.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                    layers::LayersBackend aLayersBackend,
-                    layers::ImageContainer* aImageContainer,
-                    FlushableMediaTaskQueue* aVideoTaskQueue,
-                    MediaDataDecoderCallback* aCallback) = 0;
+                     layers::LayersBackend aLayersBackend,
+                     layers::ImageContainer* aImageContainer,
+                     FlushableMediaTaskQueue* aVideoTaskQueue,
+                     MediaDataDecoderCallback* aCallback) = 0;
 
   // Creates an Audio decoder with the specified properties.
   // Asynchronous decoding of audio should be done in runnables dispatched to
   // aAudioTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // Output and errors should be returned to the reader via aCallback.
   // Returns nullptr if the decoder can't be created.
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) = 0;
 
-  // An audio decoder module must support AAC by default.
-  // If more audio codec is to be supported, SupportsAudioMimeType will have
-  // to be extended
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType);
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType);
-
-  // Indicates if the video decoder requires AVCC format.
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig);
-
-  virtual void DisableHardwareAcceleration() {}
-
-  virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const {
-    return true;
-  }
-
-protected:
-  PlatformDecoderModule() {}
-  virtual ~PlatformDecoderModule() {}
   // Caches pref media.fragmented-mp4.use-blank-decoder
   static bool sUseBlankDecoder;
   static bool sFFmpegDecoderEnabled;
   static bool sGonkDecoderEnabled;
   static bool sAndroidMCDecoderPreferred;
   static bool sAndroidMCDecoderEnabled;
   static bool sGMPDecoderEnabled;
 };
@@ -208,17 +228,16 @@ public:
   // this function returns, the decoder must be ready to accept new input
   // for decoding. This function is called when the demuxer seeks, before
   // decoding resumes after the seek.
   // While the reader calls Flush(), it ignores all output sent to it;
   // it is safe (but pointless) to send output while Flush is called.
   // The MP4Reader will not call Input() while it's calling Flush().
   virtual nsresult Flush() = 0;
 
-
   // Causes all complete samples in the pipeline that can be decoded to be
   // output. If the decoder can't produce samples from the current output,
   // it drops the input samples. The decoder may be holding onto samples
   // that are required to decode samples that it expects to get in future.
   // This is called when the demuxer reaches end of stream.
   // The MP4Reader will not call Input() while it's calling Drain().
   // This function is asynchronous. The MediaDataDecoder must call
   // MediaDataDecoderCallback::DrainComplete() once all remaining
@@ -239,13 +258,22 @@ public:
     return false;
   };
   virtual bool IsDormantNeeded() {
     return false;
   };
   virtual void AllocateMediaResources() {}
   virtual void ReleaseMediaResources() {}
   virtual bool IsHardwareAccelerated() const { return false; }
+
+  // ConfigurationChanged will be called to inform the video or audio decoder
+  // that the format of the next input sample is about to change.
+  // If video decoder, aConfig will be a VideoDecoderConfig object.
+  // If audio decoder, aConfig will be a AudioDecoderConfig object.
+  virtual nsresult ConfigurationChanged(const mp4_demuxer::TrackConfig& aConfig)
+  {
+    return NS_OK;
+  }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -78,25 +78,28 @@ SharedDecoderManager::CreateVideoDecoder
   PlatformDecoderModule* aPDM,
   const mp4_demuxer::VideoDecoderConfig& aConfig,
   layers::LayersBackend aLayersBackend,
   layers::ImageContainer* aImageContainer,
   FlushableMediaTaskQueue* aVideoTaskQueue,
   MediaDataDecoderCallback* aCallback)
 {
   if (!mDecoder) {
+    mLayersBackend = aLayersBackend;
+    mImageContainer = aImageContainer;
     // We use the manager's task queue for the decoder, rather than the one
     // passed in, so that none of the objects sharing the decoder can shutdown
     // the task queue while we're potentially still using it for a *different*
     // object also sharing the decoder.
-    mDecoder = aPDM->CreateVideoDecoder(aConfig,
-                                        aLayersBackend,
-                                        aImageContainer,
-                                        mTaskQueue,
-                                        mCallback);
+    mDecoder =
+      aPDM->CreateDecoder(aConfig,
+                          mTaskQueue,
+                          mCallback,
+                          mLayersBackend,
+                          mImageContainer);
     if (!mDecoder) {
       mPDM = nullptr;
       return nullptr;
     }
     mPDM = aPDM;
     nsresult rv = mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
@@ -108,27 +111,25 @@ SharedDecoderManager::CreateVideoDecoder
 void
 SharedDecoderManager::DisableHardwareAcceleration()
 {
   MOZ_ASSERT(mPDM);
   mPDM->DisableHardwareAcceleration();
 }
 
 bool
-SharedDecoderManager::Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                               layers::LayersBackend aLayersBackend,
-                               layers::ImageContainer* aImageContainer)
+SharedDecoderManager::Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig)
 {
   mDecoder->Flush();
   mDecoder->Shutdown();
-  mDecoder = mPDM->CreateVideoDecoder(aConfig,
-                                      aLayersBackend,
-                                      aImageContainer,
-                                      mTaskQueue,
-                                      mCallback);
+  mDecoder = mPDM->CreateDecoder(aConfig,
+                                 mTaskQueue,
+                                 mCallback,
+                                 mLayersBackend,
+                                 mImageContainer);
   if (!mDecoder) {
     return false;
   }
   nsresult rv = mDecoder->Init();
   return rv == NS_OK;
 }
 
 void
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -37,26 +37,26 @@ public:
   void SetIdle(MediaDataDecoder* aProxy);
   void ReleaseMediaResources();
   void Shutdown();
 
   friend class SharedDecoderProxy;
   friend class SharedDecoderCallback;
 
   void DisableHardwareAcceleration();
-  bool Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                layers::LayersBackend aLayersBackend,
-                layers::ImageContainer* aImageContainer);
+  bool Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig);
 
 private:
   virtual ~SharedDecoderManager();
   void DrainComplete();
 
   nsRefPtr<PlatformDecoderModule> mPDM;
   nsRefPtr<MediaDataDecoder> mDecoder;
+  layers::LayersBackend mLayersBackend;
+  nsRefPtr<layers::ImageContainer> mImageContainer;
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   SharedDecoderProxy* mActiveProxy;
   MediaDataDecoderCallback* mActiveCallback;
   nsAutoPtr<MediaDataDecoderCallback> mCallback;
   bool mWaitForInternalDrain;
   Monitor mMonitor;
   bool mDecoderReleasedResources;
 };
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -58,20 +58,16 @@ public:
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   void Cleanup() override {
     mGLContext = nullptr;
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override {
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   bool WantCopy() {
     // Allocating a texture is incredibly slow on PowerVR
     return mGLContext->Vendor() != GLVendor::Imagination;
   }
 
@@ -243,18 +239,23 @@ public:
                                              numChannels,
                                              sampleRate);
     mCallback->Output(data);
     return NS_OK;
   }
 };
 
 
-bool AndroidDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType) {
-  return static_cast<bool>(CreateDecoder(aMimeType));
+bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType)
+{
+  if (aMimeType.EqualsLiteral("video/mp4") ||
+      aMimeType.EqualsLiteral("video/avc")) {
+    return true;
+  }
+  return static_cast<bool>(mozilla::CreateDecoder(aMimeType));
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(
                                 const mp4_demuxer::VideoDecoderConfig& aConfig,
                                 layers::LayersBackend aLayersBackend,
                                 layers::ImageContainer* aImageContainer,
                                 FlushableMediaTaskQueue* aVideoTaskQueue,
@@ -291,16 +292,26 @@ AndroidDecoderModule::CreateAudioDecoder
 
   nsRefPtr<MediaDataDecoder> decoder =
     new AudioDataDecoder(aConfig, format, aCallback);
 
   return decoder.forget();
 
 }
 
+PlatformDecoderModule::ConversionRequired
+AndroidDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+{
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAnnexB;
+  } else {
+    return kNeedNone;
+  }
+}
+
 MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
                                              const nsACString& aMimeType,
                                              MediaFormat::Param aFormat,
                                              MediaDataDecoderCallback* aCallback)
   : mType(aType)
   , mMimeType(aMimeType)
   , mFormat(aFormat)
   , mCallback(aCallback)
--- a/dom/media/fmp4/android/AndroidDecoderModule.h
+++ b/dom/media/fmp4/android/AndroidDecoderModule.h
@@ -30,17 +30,20 @@ public:
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
 
   AndroidDecoderModule() {}
   virtual ~AndroidDecoderModule() {}
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
 
   MediaCodecDataDecoder(MediaData::Type aType,
                         const nsACString& aMimeType,
                         widget::sdk::MediaFormat::Param aFormat,
--- a/dom/media/fmp4/apple/AppleDecoderModule.cpp
+++ b/dom/media/fmp4/apple/AppleDecoderModule.cpp
@@ -187,20 +187,25 @@ AppleDecoderModule::CreateAudioDecoder(c
                                        MediaDataDecoderCallback* aCallback)
 {
   nsRefPtr<MediaDataDecoder> decoder =
     new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
 bool
-AppleDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/mpeg");
+  return aMimeType.EqualsLiteral("audio/mpeg") ||
+    PlatformDecoderModule::SupportsMimeType(aMimeType);
 }
 
-bool
-AppleDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+AppleDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return true;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/apple/AppleDecoderModule.h
+++ b/dom/media/fmp4/apple/AppleDecoderModule.h
@@ -27,19 +27,20 @@ public:
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
-  virtual bool
-  DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 
   static void Init();
   static nsresult CanDecode();
 
 private:
   friend class InitTask;
   friend class LinkTask;
   friend class UnlinkTask;
--- a/dom/media/fmp4/eme/EMEDecoderModule.cpp
+++ b/dom/media/fmp4/eme/EMEDecoderModule.cpp
@@ -248,21 +248,22 @@ EMEDecoderModule::CreateVideoDecoder(con
                                                 aConfig,
                                                 aLayersBackend,
                                                 aImageContainer,
                                                 aVideoTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
-  nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateVideoDecoder(aConfig,
-                                                              aLayersBackend,
-                                                              aImageContainer,
-                                                              aVideoTaskQueue,
-                                                              aCallback));
+  nsRefPtr<MediaDataDecoder> decoder(
+    mPDM->CreateDecoder(aConfig,
+                        aVideoTaskQueue,
+                        aCallback,
+                        aLayersBackend,
+                        aImageContainer));
   if (!decoder) {
     return nullptr;
   }
 
   if (!aConfig.crypto.valid) {
     return decoder.forget();
   }
 
@@ -281,32 +282,35 @@ EMEDecoderModule::CreateAudioDecoder(con
     nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
     wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
                                                 aConfig,
                                                 aAudioTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
-  nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAudioDecoder(aConfig,
-                                                              aAudioTaskQueue,
-                                                              aCallback));
+  nsRefPtr<MediaDataDecoder> decoder(
+    mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback));
   if (!decoder) {
     return nullptr;
   }
 
   if (!aConfig.crypto.valid) {
     return decoder.forget();
   }
 
   nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy));
   return emeDecoder.forget();
 }
 
-bool
-EMEDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+EMEDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return mCDMDecodesVideo && aConfig.crypto.valid;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/eme/EMEDecoderModule.h
+++ b/dom/media/fmp4/eme/EMEDecoderModule.h
@@ -37,18 +37,18 @@ public:
                     MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool
-  DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 
 private:
   nsRefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
   nsRefPtr<PlatformDecoderModule> mPDM;
   // We run the PDM on its own task queue.
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   bool mCDMDecodesAudio;
--- a/dom/media/fmp4/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/fmp4/ffmpeg/FFmpegDecoderModule.h
@@ -46,28 +46,31 @@ public:
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
     nsRefPtr<MediaDataDecoder> decoder =
       new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
     return decoder.forget();
   }
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override
   {
-    return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
+    return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE ||
+      FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
   }
 
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override
   {
-    return FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
-  }
-
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override
-  {
-    return true;
+    if (aConfig.IsVideoConfig() &&
+        (aConfig.mime_type.EqualsLiteral("video/avc") ||
+         aConfig.mime_type.EqualsLiteral("video/mp4"))) {
+      return PlatformDecoderModule::kNeedAVCC;
+    } else {
+      return kNeedNone;
+    }
   }
 
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDecoderModule_h__
--- a/dom/media/fmp4/gmp/GMPDecoderModule.cpp
+++ b/dom/media/fmp4/gmp/GMPDecoderModule.cpp
@@ -70,16 +70,20 @@ GMPDecoderModule::CreateAudioDecoder(con
 
   nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
   wrapper->SetProxyTarget(new GMPAudioDecoder(aConfig,
                                               aAudioTaskQueue,
                                               wrapper->Callback()));
   return wrapper.forget();
 }
 
-bool
-GMPDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+GMPDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
   // GMPVideoCodecType::kGMPVideoCodecH264 specifies that encoded frames must be in AVCC format.
-  return true;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/gmp/GMPDecoderModule.h
+++ b/dom/media/fmp4/gmp/GMPDecoderModule.h
@@ -26,14 +26,15 @@ public:
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif // GMPDecoderModule_h_
--- a/dom/media/fmp4/gonk/GonkDecoderModule.cpp
+++ b/dom/media/fmp4/gonk/GonkDecoderModule.cpp
@@ -46,9 +46,19 @@ GonkDecoderModule::CreateAudioDecoder(co
                                       MediaDataDecoderCallback* aCallback)
 {
   nsRefPtr<MediaDataDecoder> decoder =
   new GonkMediaDataDecoder(new GonkAudioDecoderManager(aAudioTaskQueue, aConfig),
                            aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
+PlatformDecoderModule::ConversionRequired
+GonkDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+{
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/gonk/GonkDecoderModule.h
+++ b/dom/media/fmp4/gonk/GonkDecoderModule.h
@@ -26,13 +26,16 @@ public:
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   static void Init();
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -447,30 +447,16 @@ GonkVideoDecoderManager::SendSampleToOMX
   }
 
   return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->data),
                          aSample->size,
                          aSample->composition_timestamp,
                          0);
 }
 
-bool
-GonkVideoDecoderManager::PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample)
-{
-  if (aSample != nullptr) {
-    // We must prepare samples in AVC Annex B.
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      GVDM_LOG("Failed to convert sample to annex B!");
-      return false;
-    }
-  }
-
-  return true;
-}
-
 void
 GonkVideoDecoderManager::ClearQueueFrameTime()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   mFrameTimeInfo.Clear();
 }
 
 nsresult
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -53,18 +53,16 @@ public:
 
   virtual void AllocateMediaResources();
 
   virtual void ReleaseMediaResources();
 
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 protected:
-  virtual bool PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) override;
-
   virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) override;
 
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
     int32_t mStride = 0;
--- a/dom/media/fmp4/moz.build
+++ b/dom/media/fmp4/moz.build
@@ -1,30 +1,30 @@
 # -*- 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/.
 
 EXPORTS += [
-    'AVCCDecoderModule.h',
     'MP4Decoder.h',
     'MP4Reader.h',
     'MP4Stream.h',
     'PlatformDecoderModule.h',
     'SharedDecoderManager.h',
+    'wrappers/H264Converter.h'
 ]
 
 UNIFIED_SOURCES += [
-    'AVCCDecoderModule.cpp',
     'BlankDecoderModule.cpp',
     'MP4Decoder.cpp',
     'MP4Stream.cpp',
     'PlatformDecoderModule.cpp',
     'SharedDecoderManager.cpp',
+    'wrappers/H264Converter.cpp'
 ]
 
 SOURCES += [
     'MP4Reader.cpp',
 ]
 
 DIRS += ['gmp']
 
--- a/dom/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -116,29 +116,36 @@ bool
 WMFDecoderModule::SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const
 {
   // If DXVA is enabled, but we're not going to use it for this specific config, then
   // we can't use the shared decoder.
   return !sDXVAEnabled || ShouldUseDXVA(aConfig);
 }
 
 bool
-WMFDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
+WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
   return aMimeType.EqualsLiteral("video/mp4") ||
          aMimeType.EqualsLiteral("video/avc") ||
          aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
-         aMimeType.EqualsLiteral("video/webm; codecs=vp9");
+         aMimeType.EqualsLiteral("video/webm; codecs=vp9") ||
+         aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+         aMimeType.EqualsLiteral("audio/mpeg");
 }
 
-bool
-WMFDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+PlatformDecoderModule::ConversionRequired
+WMFDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
-         aMimeType.EqualsLiteral("audio/mpeg");
+  if (aConfig.IsVideoConfig() &&
+      (aConfig.mime_type.EqualsLiteral("video/avc") ||
+       aConfig.mime_type.EqualsLiteral("video/mp4"))) {
+    return kNeedAnnexB;
+  } else {
+    return kNeedNone;
+  }
 }
 
 static bool
 ClassesRootRegKeyExists(const nsAString& aRegKeyPath)
 {
   nsresult rv;
 
   nsCOMPtr<nsIWindowsRegKey> regKey =
--- a/dom/media/fmp4/wmf/WMFDecoderModule.h
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.h
@@ -26,26 +26,28 @@ public:
                      FlushableMediaTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  bool SupportsVideoMimeType(const nsACString& aMimeType) override;
-  bool SupportsAudioMimeType(const nsACString& aMimeType) override;
+  bool SupportsMimeType(const nsACString& aMimeType) override;
 
   virtual void DisableHardwareAcceleration() override
   {
     sDXVAEnabled = false;
   }
 
   virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const override;
 
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
+
   // Accessors that report whether we have the required MFTs available
   // on the system to play various codecs. Windows Vista doesn't have the
   // H.264/AAC decoders if the "Platform Update Supplement for Windows Vista"
   // is not installed.
   static bool HasAAC();
   static bool HasH264();
 
   // Called on main thread.
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -236,22 +236,16 @@ WMFVideoMFTManager::Init()
 
 HRESULT
 WMFVideoMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
 {
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
-  if (mStreamType != VP8 && mStreamType != VP9) {
-    // We must prepare samples in AVC Annex B.
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      return E_FAIL;
-    }
-  }
   // Forward sample data to the decoder.
   const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
   uint32_t length = aSample->size;
   return mDecoder->Input(data, length, aSample->composition_timestamp);
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/wrappers/H264Converter.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "H264Converter.h"
+#include "ImageContainer.h"
+#include "MediaTaskQueue.h"
+#include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
+
+namespace mozilla
+{
+
+  // H264 AnnexB or AVCC handler
+#include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
+
+H264Converter::H264Converter(PlatformDecoderModule* aPDM,
+                             const mp4_demuxer::VideoDecoderConfig& aConfig,
+                             layers::LayersBackend aLayersBackend,
+                             layers::ImageContainer* aImageContainer,
+                             FlushableMediaTaskQueue* aVideoTaskQueue,
+                             MediaDataDecoderCallback* aCallback)
+  : mPDM(aPDM)
+  , mCurrentConfig(aConfig)
+  , mLayersBackend(aLayersBackend)
+  , mImageContainer(aImageContainer)
+  , mVideoTaskQueue(aVideoTaskQueue)
+  , mCallback(aCallback)
+  , mDecoder(nullptr)
+  , mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
+  , mLastError(NS_OK)
+{
+  CreateDecoder();
+}
+
+H264Converter::~H264Converter()
+{
+}
+
+nsresult
+H264Converter::Init()
+{
+  if (mDecoder) {
+    return mDecoder->Init();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Input(mp4_demuxer::MP4Sample* aSample)
+{
+  if (!mNeedAVCC) {
+    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  nsresult rv;
+  if (!mDecoder) {
+    // It is not possible to create an AVCC H264 decoder without SPS.
+    // As such, creation will fail if the extra_data just extracted doesn't
+    // contain a SPS.
+    rv = CreateDecoderAndInit(aSample);
+    if (rv == NS_ERROR_NOT_INITIALIZED) {
+      // We are missing the required SPS to create the decoder.
+      // Ignore for the time being, the MP4Sample will be dropped.
+      return NS_OK;
+    }
+  } else {
+    rv = CheckForSPSChange(aSample);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aSample->extra_data = mCurrentConfig.extra_data;
+
+  return mDecoder->Input(aSample);
+}
+
+nsresult
+H264Converter::Flush()
+{
+  if (mDecoder) {
+    return mDecoder->Flush();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Drain()
+{
+  if (mDecoder) {
+    return mDecoder->Drain();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Shutdown()
+{
+  if (mDecoder) {
+    nsresult rv = mDecoder->Shutdown();
+    mDecoder = nullptr;
+    return rv;
+  }
+  return NS_OK;
+}
+
+bool
+H264Converter::IsWaitingMediaResources()
+{
+  if (mDecoder) {
+    return mDecoder->IsWaitingMediaResources();
+  }
+  return MediaDataDecoder::IsWaitingMediaResources();
+}
+
+bool
+H264Converter::IsDormantNeeded()
+{
+  if (mNeedAVCC) {
+    return true;
+  }
+  return mDecoder ?
+    mDecoder->IsDormantNeeded() : MediaDataDecoder::IsDormantNeeded();
+}
+
+void
+H264Converter::AllocateMediaResources()
+{
+  if (mNeedAVCC) {
+    // Nothing to do, decoder will be allocated on the fly when required.
+    return;
+  }
+  if (mDecoder) {
+    mDecoder->AllocateMediaResources();
+  }
+}
+
+void
+H264Converter::ReleaseMediaResources()
+{
+  if (mNeedAVCC) {
+    Shutdown();
+    return;
+  }
+  if (mDecoder) {
+    mDecoder->ReleaseMediaResources();
+  }
+}
+
+bool
+H264Converter::IsHardwareAccelerated() const
+{
+  if (mDecoder) {
+    return mDecoder->IsHardwareAccelerated();
+  }
+  return MediaDataDecoder::IsHardwareAccelerated();
+}
+
+nsresult
+H264Converter::CreateDecoder()
+{
+  if (mNeedAVCC && !mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.extra_data)) {
+    // nothing found yet, will try again later
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  UpdateConfigFromExtraData(mCurrentConfig.extra_data);
+
+  mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
+                                      mLayersBackend,
+                                      mImageContainer,
+                                      mVideoTaskQueue,
+                                      mCallback);
+  if (!mDecoder) {
+    mLastError = NS_ERROR_FAILURE;
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+nsresult
+H264Converter::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
+{
+  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
+  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  UpdateConfigFromExtraData(extra_data);
+
+  nsresult rv = CreateDecoder();
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Init();
+}
+
+nsresult
+H264Converter::CheckForSPSChange(mp4_demuxer::MP4Sample* aSample)
+{
+  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
+  if (!mp4_demuxer::AnnexB::HasSPS(extra_data) ||
+      mp4_demuxer::AnnexB::CompareExtraData(extra_data,
+                                            mCurrentConfig.extra_data)) {
+        return NS_OK;
+      }
+  if (!mNeedAVCC) {
+    UpdateConfigFromExtraData(extra_data);
+    mDecoder->ConfigurationChanged(mCurrentConfig);
+    return NS_OK;
+  }
+  // The SPS has changed, signal to flush the current decoder and create a
+  // new one.
+  mDecoder->Flush();
+  ReleaseMediaResources();
+  return CreateDecoderAndInit(aSample);
+}
+
+void
+H264Converter::UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData)
+{
+  mp4_demuxer::SPSData spsdata;
+  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
+      spsdata.pic_width > 0 && spsdata.pic_height > 0) {
+    mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
+    mCurrentConfig.image_width = spsdata.pic_width;
+    mCurrentConfig.image_height = spsdata.pic_height;
+    mCurrentConfig.display_width = spsdata.display_width;
+    mCurrentConfig.display_height = spsdata.display_height;
+  }
+  mCurrentConfig.extra_data = aExtraData;
+}
+
+/* static */
+bool
+H264Converter::IsH264(const mp4_demuxer::TrackConfig& aConfig)
+{
+  return aConfig.mime_type.EqualsLiteral("video/avc") ||
+    aConfig.mime_type.EqualsLiteral("video/mp4");
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/wrappers/H264Converter.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_H264Converter_h
+#define mozilla_H264Converter_h
+
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+// H264Converter is a MediaDataDecoder wrapper used to ensure that
+// only AVCC or AnnexB is fed to the underlying MediaDataDecoder.
+// The H264Converter allows playback of content where the SPS NAL may not be
+// provided in the init segment (e.g. AVC3 or Annex B)
+// H264Converter will monitor the input data, and will delay creation of the
+// MediaDataDecoder until a SPS and PPS NALs have been extracted.
+
+class H264Converter : public MediaDataDecoder {
+public:
+
+  H264Converter(PlatformDecoderModule* aPDM,
+                const mp4_demuxer::VideoDecoderConfig& aConfig,
+                layers::LayersBackend aLayersBackend,
+                layers::ImageContainer* aImageContainer,
+                FlushableMediaTaskQueue* aVideoTaskQueue,
+                MediaDataDecoderCallback* aCallback);
+  virtual ~H264Converter();
+
+  virtual nsresult Init() override;
+  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Flush() override;
+  virtual nsresult Drain() override;
+  virtual nsresult Shutdown() override;
+  virtual bool IsWaitingMediaResources() override;
+  virtual bool IsDormantNeeded() override;
+  virtual void AllocateMediaResources() override;
+  virtual void ReleaseMediaResources() override;
+  virtual bool IsHardwareAccelerated() const override;
+
+  // Return true if mimetype is H.264.
+  static bool IsH264(const mp4_demuxer::TrackConfig& aConfig);
+
+private:
+  // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
+  // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
+  // will set mError accordingly.
+  nsresult CreateDecoder();
+  nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
+  nsresult CheckForSPSChange(mp4_demuxer::MP4Sample* aSample);
+  void UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData);
+
+  nsRefPtr<PlatformDecoderModule> mPDM;
+  mp4_demuxer::VideoDecoderConfig mCurrentConfig;
+  layers::LayersBackend mLayersBackend;
+  nsRefPtr<layers::ImageContainer> mImageContainer;
+  nsRefPtr<FlushableMediaTaskQueue> mVideoTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+  nsRefPtr<MediaDataDecoder> mDecoder;
+  bool mNeedAVCC;
+  nsresult mLastError;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_H264Converter_h
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -560,18 +560,18 @@ class GMPStorageTest : public GMPDecrypt
   }
 
   void TestGetNodeId()
   {
     AssertIsOnGMPThread();
 
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
-    const nsString origin1 = NS_LITERAL_STRING("example1.com");
-    const nsString origin2 = NS_LITERAL_STRING("example2.org");
+    const nsString origin1 = NS_LITERAL_STRING("http://example1.com");
+    const nsString origin2 = NS_LITERAL_STRING("http://example2.org");
 
     nsCString PBnodeId1 = GetNodeId(origin1, origin2, true);
     nsCString PBnodeId2 = GetNodeId(origin1, origin2, true);
 
     // Node ids for the same origins should be the same in PB mode.
     EXPECT_TRUE(PBnodeId1.Equals(PBnodeId2));
 
     nsCString PBnodeId3 = GetNodeId(origin2, origin1, true);
@@ -597,18 +597,18 @@ class GMPStorageTest : public GMPDecrypt
       this, &GMPStorageTest::TestGetNodeId_Continuation, nodeId1), thread);
   }
 
   void TestGetNodeId_Continuation(nsCString aNodeId1) {
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Once we clear storage, the node ids generated for the same origin-pair
     // should be different.
-    const nsString origin1 = NS_LITERAL_STRING("example1.com");
-    const nsString origin2 = NS_LITERAL_STRING("example2.org");
+    const nsString origin1 = NS_LITERAL_STRING("http://example1.com");
+    const nsString origin2 = NS_LITERAL_STRING("http://example2.org");
     nsCString nodeId3 = GetNodeId(origin1, origin2, false);
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
   class CreateDecryptorDone : public GetGMPDecryptorCallback
   {
@@ -702,18 +702,18 @@ class GMPStorageTest : public GMPDecrypt
       GeckoMediaPluginService::GetGeckoMediaPluginService();
 
     // Send a message to the fake GMP for it to run its own tests internally.
     // It sends us a "test-storage complete" message when its passed, or
     // some other message if its tests fail.
     Expect(NS_LITERAL_CSTRING("test-storage complete"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate storage data for some sites.
    * 2. Forget about one of the sites.
    * 3. Check if the storage data for the forgotten site are erased correctly.
@@ -723,32 +723,32 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   void TestForgetThisSite_AnotherSite() {
     Shutdown();
 
     // Generate storage data for another site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example3.com"),
+                    NS_LITERAL_STRING("http://example4.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   struct NodeInfo {
     explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
     nsCString siteToForget;
     nsTArray<nsCString> expectedRemainingNodeIds;
@@ -766,17 +766,17 @@ class GMPStorageTest : public GMPDecrypt
       }
     }
   private:
     NodeInfo* mNodeInfo;
   };
 
   void TestForgetThisSite_CollectSiteInfo() {
     nsAutoPtr<NodeInfo> siteInfo(
-        new NodeInfo(NS_LITERAL_CSTRING("example1.com")));
+        new NodeInfo(NS_LITERAL_CSTRING("http://example1.com")));
     // Collect nodeIds that are expected to remain for later comparison.
     EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
     // Invoke "Forget this site" on the main thread.
     NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
         this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
   }
 
   void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
@@ -856,18 +856,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
 }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t| to clear recent history.
@@ -878,18 +878,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t+1| to clear recent history.
@@ -900,18 +900,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   class MaxMTimeFinder {
   public:
     MaxMTimeFinder() : mMaxTime(0) {}
     void operator()(nsIFile* aFile) {
@@ -1014,76 +1014,76 @@ class GMPStorageTest : public GMPDecrypt
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation));
 
     nsCString update("store crossOriginTestRecordId ");
     update.AppendInt((int64_t)t);
 
     // Open decryptor on one, origin, write a record, and test that that
     // record can't be read on another origin.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example3.com"),
+                    NS_LITERAL_STRING("http://example4.com"),
                     false,
                     update);
   }
 
   void TestCrossOriginStorage_RecordStoredContinuation() {
     // Close the old decryptor, and create a new one on a different origin,
     // and try to read the record.
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example5.com"),
-                    NS_LITERAL_STRING("example6.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example5.com"),
+                    NS_LITERAL_STRING("http://example6.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
   }
 
   void TestPBStorage() {
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     nsCString response("stored pbdata test-pb-data");
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestPBStorage_RecordStoredContinuation));
 
     // Open decryptor on one, origin, write a record, close decryptor,
     // open another, and test that record can be read, close decryptor,
     // then send pb-last-context-closed notification, then open decryptor
     // and check that it can't read that data; it should have been purged.
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("store pbdata test-pb-data"));
   }
 
   void TestPBStorage_RecordStoredContinuation() {
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void TestPBStorage_RecordRetrievedContinuation() {
     Shutdown();
     SimulatePBModeExit();
 
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
   {
     if (mDecryptor) {
       Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
@@ -1103,30 +1103,30 @@ class GMPStorageTest : public GMPDecrypt
         NS_NewRunnableMethod(this, aCallback)));
 
     CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
   }
 
   void TestAsyncShutdownTimeout() {
     // Create decryptors that timeout in their async shutdown.
     // If the gtest hangs on shutdown, test fails!
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
-                                  NS_LITERAL_STRING("example8.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example7.com"),
+                                  NS_LITERAL_STRING("http://example8.com"),
                                   &GMPStorageTest::TestAsyncShutdownTimeout2);
   };
 
   void TestAsyncShutdownTimeout2() {
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
-                                  NS_LITERAL_STRING("example10.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example9.com"),
+                                  NS_LITERAL_STRING("http://example10.com"),
                                   &GMPStorageTest::TestAsyncShutdownTimeout3);
   };
 
   void TestAsyncShutdownTimeout3() {
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
-                                  NS_LITERAL_STRING("example12.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example11.com"),
+                                  NS_LITERAL_STRING("http://example12.com"),
                                   &GMPStorageTest::SetFinished);
   };
 
   void TestAsyncShutdownStorage() {
     // Instruct the GMP to write a token (the current timestamp, so it's
     // unique) during async shutdown, then shutdown the plugin, re-create
     // it, and check that the token was successfully stored.
     auto t = time(0);
@@ -1139,18 +1139,18 @@ class GMPStorageTest : public GMPDecrypt
     // the token.
     nsCString response("shutdown-token received ");
     response.Append(token);
     Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
 
     // Test that a GMP can write to storage during shutdown, and retrieve
     // that written data in a subsequent session.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
+                    NS_LITERAL_STRING("http://example14.com"),
                     false,
                     update);
   }
 
   void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
     ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
   }
@@ -1158,42 +1158,42 @@ class GMPStorageTest : public GMPDecrypt
   void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
     // Create a new instance of the plugin, retrieve the token written
     // during shutdown and verify it is correct.
     nsCString response("retrieved shutdown-token ");
     response.Append(aToken);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
+                    NS_LITERAL_STRING("http://example14.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve-shutdown-token"));
   }
 
 #if defined(XP_WIN)
   void TestOutputProtection() {
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("OP tests completed"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example15.com"),
-                    NS_LITERAL_STRING("example16.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example15.com"),
+                    NS_LITERAL_STRING("http://example16.com"),
                     false,
                     NS_LITERAL_CSTRING("test-op-apis"));
   }
 #endif
 
   void TestPluginVoucher() {
     Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example17.com"),
-                    NS_LITERAL_STRING("example18.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example17.com"),
+                    NS_LITERAL_STRING("http://example18.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
   }
 
   void TestGetRecordNamesInMemoryStorage() {
     TestGetRecordNames(true);
   }
 
@@ -1232,18 +1232,18 @@ class GMPStorageTest : public GMPDecrypt
       nsIRunnable* continuation = nullptr;
       if (i + 1 == num) {
         continuation =
           NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
       }
       Expect(response, continuation);
     }
 
-    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
-                    NS_LITERAL_STRING("bar.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://foo.com"),
+                    NS_LITERAL_STRING("http://bar.com"),
                     aPrivateBrowsing,
                     Move(updates));
   }
 
   void TestGetRecordNames_QueryNames() {
     nsCString response("record-names ");
     response.Append(mRecordNames);
     Expect(response,
@@ -1285,18 +1285,18 @@ class GMPStorageTest : public GMPDecrypt
     response.AppendLiteral(" ");
     response.Append(data);
     Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
     nsCString update("store ");
     update.Append(longRecordName);
     update.AppendLiteral(" ");
     update.Append(data);
-    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
-                    NS_LITERAL_STRING("baz.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://fuz.com"),
+                    NS_LITERAL_STRING("http://baz.com"),
                     false,
                     update);
   }
 
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -163,22 +163,26 @@ MediaSourceReader::RequestAudioData()
     case SOURCE_NEW:
       GetAudioReader()->ResetDecode();
       mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
                               ->RefableThen(GetTaskQueue(), __func__, this,
                                             &MediaSourceReader::CompleteAudioSeekAndDoRequest,
                                             &MediaSourceReader::CompleteAudioSeekAndRejectPromise));
       break;
     case SOURCE_NONE:
-      if (mLastAudioTime) {
+      if (!mLastAudioTime) {
+        // This is the first call to RequestAudioData.
+        // Fallback to using decoder with earliest data.
+        mAudioSourceDecoder = FirstDecoder(MediaData::AUDIO_DATA);
+      }
+      if (mLastAudioTime || !mAudioSourceDecoder) {
         CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
         break;
       }
-      // Fallback to using first reader
-      mAudioSourceDecoder = mAudioTrack->Decoders()[0];
+      // Fallback to getting first frame from first decoder.
     default:
       DoAudioRequest();
       break;
   }
   return p;
 }
 
 void MediaSourceReader::DoAudioRequest()
@@ -333,22 +337,26 @@ MediaSourceReader::RequestVideoData(bool
     case SOURCE_NEW:
       GetVideoReader()->ResetDecode();
       mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
                              ->RefableThen(GetTaskQueue(), __func__, this,
                                            &MediaSourceReader::CompleteVideoSeekAndDoRequest,
                                            &MediaSourceReader::CompleteVideoSeekAndRejectPromise));
       break;
     case SOURCE_NONE:
-      if (mLastVideoTime) {
+      if (!mLastVideoTime) {
+        // This is the first call to RequestVideoData.
+        // Fallback to using decoder with earliest data.
+        mVideoSourceDecoder = FirstDecoder(MediaData::VIDEO_DATA);
+      }
+      if (mLastVideoTime || !mVideoSourceDecoder) {
         CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
         break;
       }
-      // Fallback to using first reader.
-      mVideoSourceDecoder = mVideoTrack->Decoders()[0];
+      // Fallback to getting first frame from first decoder.
     default:
       DoVideoRequest();
       break;
   }
 
   return p;
 }
 
@@ -1028,16 +1036,46 @@ MediaSourceReader::GetBuffered(dom::Time
 
     intersectionRanges->Intersection(sourceRanges);
   }
 
   MSE_DEBUG("ranges=%s", DumpTimeRanges(intersectionRanges).get());
   return NS_OK;
 }
 
+already_AddRefed<SourceBufferDecoder>
+MediaSourceReader::FirstDecoder(MediaData::Type aType)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  TrackBuffer* trackBuffer =
+    aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
+  MOZ_ASSERT(trackBuffer);
+  const nsTArray<nsRefPtr<SourceBufferDecoder>>& decoders = trackBuffer->Decoders();
+  if (decoders.IsEmpty()) {
+    return nullptr;
+  }
+
+  nsRefPtr<SourceBufferDecoder> firstDecoder;
+  double lowestStartTime = PositiveInfinity<double>();
+
+  for (uint32_t i = 0; i < decoders.Length(); ++i) {
+    nsRefPtr<TimeRanges> r = new TimeRanges();
+    decoders[i]->GetBuffered(r);
+    double start = r->GetStartTime();
+    if (start < 0) {
+      continue;
+    }
+    if (start < lowestStartTime) {
+      firstDecoder = decoders[i];
+      lowestStartTime = start;
+    }
+  }
+  return firstDecoder.forget();
+}
+
 nsRefPtr<MediaDecoderReader::WaitForDataPromise>
 MediaSourceReader::WaitForData(MediaData::Type aType)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
   MaybeNotifyHaveData();
   return p;
@@ -1048,23 +1086,33 @@ MediaSourceReader::MaybeNotifyHaveData()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   bool haveAudio = false, haveVideo = false;
   bool ended = IsEnded();
   // If we are in ended mode, we will resolve any pending wait promises.
   // The next Request*Data will handle END_OF_STREAM or going back into waiting
   // mode.
   if (!IsSeeking() && mAudioTrack) {
-    haveAudio = HaveData(mLastAudioTime, MediaData::AUDIO_DATA);
+    if (!mLastAudioTime) {
+      nsRefPtr<SourceBufferDecoder> d = FirstDecoder(MediaData::AUDIO_DATA);
+      haveAudio = !!d;
+    } else {
+      haveAudio = HaveData(mLastAudioTime, MediaData::AUDIO_DATA);
+    }
     if (ended || haveAudio) {
       WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
     }
   }
   if (!IsSeeking() && mVideoTrack) {
-    haveVideo = HaveData(mLastVideoTime, MediaData::VIDEO_DATA);
+    if (!mLastVideoTime) {
+      nsRefPtr<SourceBufferDecoder> d = FirstDecoder(MediaData::VIDEO_DATA);
+      haveVideo = !!d;
+    } else {
+      haveVideo = HaveData(mLastVideoTime, MediaData::VIDEO_DATA);
+    }
     if (ended || haveVideo) {
       WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
     }
   }
   MSE_DEBUG("isSeeking=%d haveAudio=%d, haveVideo=%d ended=%d",
             IsSeeking(), haveAudio, haveVideo, ended);
 }
 
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -218,16 +218,17 @@ private:
   void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
 
   // Return a decoder from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
   already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
                                                       int64_t aTolerance /* microseconds */,
                                                       const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
+  already_AddRefed<SourceBufferDecoder> FirstDecoder(MediaData::Type aType);
 
   void AttemptSeek();
   bool IsSeeking() { return mPendingSeekTime != -1; }
 
   bool IsNearEnd(MediaData::Type aType, int64_t aTime /* microseconds */);
   int64_t LastSampleTime(MediaData::Type aType);
 
   nsRefPtr<SourceBufferDecoder> mAudioSourceDecoder;
--- a/dom/media/webm/IntelWebMVideoDecoder.cpp
+++ b/dom/media/webm/IntelWebMVideoDecoder.cpp
@@ -111,17 +111,17 @@ IntelWebMVideoDecoder::Create(WebMReader
   return decoder.forget();
 }
 
 bool
 IntelWebMVideoDecoder::IsSupportedVideoMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
           aMimeType.EqualsLiteral("video/webm; codecs=vp9")) &&
-         mPlatform->SupportsVideoMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 nsresult
 IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
 {
   mPlatform = PlatformDecoderModule::Create();
   if (!mPlatform) {
     return NS_ERROR_FAILURE;
@@ -142,21 +142,22 @@ IntelWebMVideoDecoder::Init(unsigned int
   default:
     return NS_ERROR_FAILURE;
   }
 
   const VideoDecoderConfig& video = *mDecoderConfig;
   if (!IsSupportedVideoMimeType(video.mime_type)) {
     return NS_ERROR_FAILURE;
   }
-  mMediaDataDecoder = mPlatform->CreateVideoDecoder(video,
-                                                    mReader->GetLayersBackendType(),
-                                                    mReader->GetDecoder()->GetImageContainer(),
-                                                    mTaskQueue,
-                                                    this);
+  mMediaDataDecoder =
+    mPlatform->CreateDecoder(video,
+                             mTaskQueue,
+                             this,
+                             mReader->GetLayersBackendType(),
+                             mReader->GetDecoder()->GetImageContainer());
   if (!mMediaDataDecoder) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv = mMediaDataDecoder->Init();
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -465,18 +465,19 @@ MediaEngineGonkVideoSource::Notify(const
 void
 MediaEngineGonkVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
   ICameraControl::Configuration config;
   config.mMode = ICameraControl::kPictureMode;
   config.mPreviewSize.width = aCapability.width;
   config.mPreviewSize.height = aCapability.height;
+  config.mPictureSize.width = aCapability.width;
+  config.mPictureSize.height = aCapability.height;
   mCameraControl->Start(&config);
-  mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, config.mPreviewSize);
 
   hal::RegisterScreenConfigurationObserver(this);
 }
 
 void
 MediaEngineGonkVideoSource::StopImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/network/tests/test_udpsocket.html
+++ b/dom/network/tests/test_udpsocket.html
@@ -316,16 +316,19 @@ function testOpenWithoutClose() {
   info('test for open without close');
 
   let closed = [];
   for (let i = 0; i < 50; i++) {
     let socket = new UDPSocket();
     closed.push(socket.closed);
   }
 
+  SpecialPowers.gc();
+  info('all unrefereced socket should be closed right after GC');
+
   return Promise.all(closed);
 }
 
 function testBFCache() {
   info('test for bfcache behavior');
 
   let socket = new UDPSocket();
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -108,17 +108,17 @@ CreateDrawTargetForSurface(gfxASurface *
     format = SurfaceFormat::A8;
   } else if (aSurface->GetContentType() == gfxContentType::COLOR) {
     format = SurfaceFormat::B8G8R8X8;
   } else {
     format = SurfaceFormat::B8G8R8A8;
   }
   RefPtr<DrawTarget> drawTarget =
     Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(),
-                                             ToIntSize(gfxIntSize(aSurface->GetSize())),
+                                             aSurface->GetSize(),
                                              &format);
   aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
   return drawTarget;
 }
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
                                          const nsCString& aMimeType,
                                          const uint16_t& aMode,
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1436,45 +1436,23 @@ PromiseWorkerProxy::StoreISupports(nsISu
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsMainThreadPtrHandle<nsISupports> supports(
     new nsMainThreadPtrHolder<nsISupports>(aSupports));
   mSupportsArray.AppendElement(supports);
 }
 
-namespace {
-
-class PromiseWorkerProxyControlRunnable final
-  : public WorkerControlRunnable
+bool
+PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx,
+                                             WorkerPrivate* aWorkerPrivate)
 {
-  nsRefPtr<PromiseWorkerProxy> mProxy;
-
-public:
-  PromiseWorkerProxyControlRunnable(WorkerPrivate* aWorkerPrivate,
-                                    PromiseWorkerProxy* aProxy)
-    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-    , mProxy(aProxy)
-  {
-    MOZ_ASSERT(aProxy);
-  }
-
-  virtual bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    mProxy->CleanUp(aCx);
-    return true;
-  }
-
-private:
-  ~PromiseWorkerProxyControlRunnable()
-  {}
-};
-
-} // anonymous namespace
+  mProxy->CleanUp(aCx);
+  return true;
+}
 
 void
 PromiseWorkerProxy::RunCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue,
                                 RunCallbackFunc aFunc)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -6,16 +6,18 @@
 #define mozilla_dom_PromiseWorkerProxy_h
 
 // Required for Promise::PromiseTaskSync.
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 #include "nsProxyRelease.h"
 
+#include "WorkerRunnable.h"
+
 namespace mozilla {
 namespace dom {
 
 class Promise;
 
 namespace workers {
 class WorkerPrivate;
 }
@@ -48,18 +50,22 @@ class WorkerPrivate;
 //
 // PromiseWorkerProxy can also be used in situations where there is no main
 // thread Promise, or where special handling is required on the worker thread
 // for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
 // 2 above. When the main thread is ready to resolve the worker thread promise,
 // dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
 // worker. This might be null! In the WorkerRunnable's WorkerRun() use
 // GetWorkerPromise() to access the Promise and resolve/reject it. Then call
-// CleanUp() on the worker
-// thread.
+// CleanUp() on the worker thread.
+//
+// IMPORTANT: Dispatching the runnable to the worker thread may fail causing
+// the promise to leak. To successfully release the promise on the
+// worker thread in this case, use |PromiseWorkerProxyControlRunnable| to
+// dispatch a control runnable that will deref the object on the correct thread.
 
 class PromiseWorkerProxy : public PromiseNativeHandler,
                            public workers::WorkerFeature
 {
   friend class PromiseWorkerProxyRunnable;
 
   // This overrides the non-threadsafe refcounting in PromiseNativeHandler.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy, override)
@@ -73,16 +79,27 @@ public:
   workers::WorkerPrivate* GetWorkerPrivate() const;
 
   Promise* GetWorkerPromise() const;
 
   void StoreISupports(nsISupports* aSupports);
 
   void CleanUp(JSContext* aCx);
 
+  Mutex& GetCleanUpLock()
+  {
+    return mCleanUpLock;
+  }
+
+  bool IsClean() const
+  {
+    mCleanUpLock.AssertCurrentThreadOwns();
+    return mCleanedUp;
+  }
+
 protected:
   virtual void ResolvedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) override;
@@ -114,12 +131,36 @@ private:
   // Aimed to keep objects alive when doing the structured-clone read/write,
   // which can be added by calling StoreISupports() on the main thread.
   nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
 
   // Ensure the worker and the main thread won't race to access |mCleanedUp|.
   Mutex mCleanUpLock;
 };
 
+// Helper runnable used for releasing the proxied promise when the worker
+// is not accepting runnables and the promise object would leak.
+// See the instructions above.
+class PromiseWorkerProxyControlRunnable final : public workers::WorkerControlRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mProxy;
+
+public:
+  PromiseWorkerProxyControlRunnable(workers::WorkerPrivate* aWorkerPrivate,
+                                    PromiseWorkerProxy* aProxy)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override;
+
+private:
+  ~PromiseWorkerProxyControlRunnable()
+  {}
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseWorkerProxy_h
--- a/dom/webidl/Client.webidl
+++ b/dom/webidl/Client.webidl
@@ -17,16 +17,18 @@ interface Client {
   void postMessage(any message, optional sequence<Transferable> transfer);
 };
 
 [Exposed=ServiceWorker]
 interface WindowClient : Client {
   readonly attribute VisibilityState visibilityState;
   readonly attribute boolean focused;
   readonly attribute FrameType frameType;
+
+  [Throws]
   Promise<WindowClient> focus();
 };
 
 enum FrameType {
   "auxiliary",
   "top-level",
   "nested",
   "none"
--- a/dom/workers/ServiceWorkerClient.h
+++ b/dom/workers/ServiceWorkerClient.h
@@ -45,20 +45,20 @@ class ServiceWorkerClient : public nsISu
                             public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient)
 
   ServiceWorkerClient(nsISupports* aOwner,
                       const ServiceWorkerClientInfo& aClientInfo)
-    : mOwner(aOwner),
-      mId(aClientInfo.mClientId),
-      mWindowId(aClientInfo.mWindowId),
-      mUrl(aClientInfo.mUrl)
+    : mOwner(aOwner)
+    , mId(aClientInfo.mClientId)
+    , mUrl(aClientInfo.mUrl)
+    , mWindowId(aClientInfo.mWindowId)
   {
     MOZ_ASSERT(aOwner);
   }
 
   nsISupports*
   GetParentObject() const
   {
     return mOwner;
@@ -84,17 +84,19 @@ public:
 
 protected:
   virtual ~ServiceWorkerClient()
   { }
 
 private:
   nsCOMPtr<nsISupports> mOwner;
   nsString mId;
+  nsString mUrl;
+
+protected:
   uint64_t mWindowId;
-  nsString mUrl;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkerclient_h
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* 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 "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerClients.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -36,210 +37,103 @@ ServiceWorkerClients::ServiceWorkerClien
 JSObject*
 ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ClientsBinding::Wrap(aCx, this, aGivenProto);
 }
 
 namespace {
 
-// Helper class used for passing the promise between threads while
-// keeping the worker alive.
-class PromiseHolder final : public WorkerFeature
-{
-  friend class MatchAllRunnable;
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseHolder)
-
-public:
-  PromiseHolder(WorkerPrivate* aWorkerPrivate,
-                Promise* aPromise)
-    : mWorkerPrivate(aWorkerPrivate),
-      mPromise(aPromise),
-      mCleanUpLock("promiseHolderCleanUpLock"),
-      mClean(false)
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(mPromise);
-
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this))) {
-      // Worker has been canceled and will go away.
-      // The ResolvePromiseWorkerRunnable won't run, so we can set mPromise to
-      // nullptr.
-      mPromise = nullptr;
-      mClean = true;
-    }
-  }
-
-  Promise*
-  GetPromise() const
-  {
-    return mPromise;
-  }
-
-  void
-  Clean()
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    MutexAutoLock lock(mCleanUpLock);
-    if (mClean) {
-      return;
-    }
-
-    mPromise = nullptr;
-    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
-    mClean = true;
-  }
-
-  bool
-  Notify(JSContext* aCx, Status aStatus)
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (aStatus > Running) {
-      Clean();
-    }
-
-    return true;
-  }
-
-private:
-  ~PromiseHolder()
-  {
-    MOZ_ASSERT(mClean);
-  }
-
-  WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<Promise> mPromise;
-
-  // Used to prevent race conditions on |mClean| and to ensure that either a
-  // Notify() call or a dispatch back to the worker thread occurs before
-  // this object is released.
-  Mutex mCleanUpLock;
-
-  bool mClean;
-};
-
 class ResolvePromiseWorkerRunnable final : public WorkerRunnable
 {
-  nsRefPtr<PromiseHolder> mPromiseHolder;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
   nsTArray<ServiceWorkerClientInfo> mValue;
 
 public:
   ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
-                               PromiseHolder* aPromiseHolder,
+                               PromiseWorkerProxy* aPromiseProxy,
                                nsTArray<ServiceWorkerClientInfo>& aValue)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-      mPromiseHolder(aPromiseHolder)
+      mPromiseProxy(aPromiseProxy)
   {
     AssertIsOnMainThread();
     mValue.SwapElements(aValue);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
-    Promise* promise = mPromiseHolder->GetPromise();
+    Promise* promise = mPromiseProxy->GetWorkerPromise();
     MOZ_ASSERT(promise);
 
     nsTArray<nsRefPtr<ServiceWorkerClient>> ret;
     for (size_t i = 0; i < mValue.Length(); i++) {
       ret.AppendElement(nsRefPtr<ServiceWorkerClient>(
             new ServiceWorkerWindowClient(promise->GetParentObject(),
                                           mValue.ElementAt(i))));
     }
     promise->MaybeResolve(ret);
 
     // release the reference on the worker thread.
-    mPromiseHolder->Clean();
+    mPromiseProxy->CleanUp(aCx);
 
     return true;
   }
 };
 
-class ReleasePromiseRunnable final : public MainThreadWorkerControlRunnable
-{
-  nsRefPtr<PromiseHolder> mPromiseHolder;
-
-public:
-  ReleasePromiseRunnable(WorkerPrivate* aWorkerPrivate,
-                         PromiseHolder* aPromiseHolder)
-    : MainThreadWorkerControlRunnable(aWorkerPrivate),
-      mPromiseHolder(aPromiseHolder)
-  { }
-
-private:
-  ~ReleasePromiseRunnable()
-  { }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseHolder->Clean();
-
-    return true;
-  }
-
-};
-
 class MatchAllRunnable final : public nsRunnable
 {
   WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<PromiseHolder> mPromiseHolder;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
   nsCString mScope;
 public:
   MatchAllRunnable(WorkerPrivate* aWorkerPrivate,
-                   PromiseHolder* aPromiseHolder,
+                   PromiseWorkerProxy* aPromiseProxy,
                    const nsCString& aScope)
     : mWorkerPrivate(aWorkerPrivate),
-      mPromiseHolder(aPromiseHolder),
+      mPromiseProxy(aPromiseProxy),
       mScope(aScope)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
 
-    MutexAutoLock lock(mPromiseHolder->mCleanUpLock);
-    if (mPromiseHolder->mClean) {
+    MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
+    if (mPromiseProxy->IsClean()) {
       // Don't resolve the promise if it was already released.
       return NS_OK;
     }
 
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     nsTArray<ServiceWorkerClientInfo> result;
 
     swm->GetAllClients(mScope, result);
     nsRefPtr<ResolvePromiseWorkerRunnable> r =
-      new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseHolder, result);
+      new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result);
 
     AutoSafeJSContext cx;
     if (r->Dispatch(cx)) {
       return NS_OK;
     }
 
     // Dispatch to worker thread failed because the worker is shutting down.
     // Use a control runnable to release the runnable on the worker thread.
-    nsRefPtr<ReleasePromiseRunnable> releaseRunnable =
-      new ReleasePromiseRunnable(mWorkerPrivate, mPromiseHolder);
+    nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
+      new PromiseWorkerProxyControlRunnable(mWorkerPrivate, mPromiseProxy);
 
     if (!releaseRunnable->Dispatch(cx)) {
-      NS_RUNTIMEABORT("Failed to dispatch PromiseHolder control runnable.");
+      NS_RUNTIMEABORT("Failed to dispatch MatchAll promise control runnable.");
     }
 
     return NS_OK;
   }
 };
 
 } // anonymous namespace
 
@@ -259,26 +153,26 @@ ServiceWorkerClients::MatchAll(const Cli
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  nsRefPtr<PromiseHolder> promiseHolder = new PromiseHolder(workerPrivate,
-                                                            promise);
-  if (!promiseHolder->GetPromise()) {
+  nsRefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (!promiseProxy->GetWorkerPromise()) {
     // Don't dispatch if adding the worker feature failed.
     return promise.forget();
   }
 
   nsRefPtr<MatchAllRunnable> r =
     new MatchAllRunnable(workerPrivate,
-                         promiseHolder,
+                         promiseProxy,
                          NS_ConvertUTF16toUTF8(scope));
   nsresult rv = NS_DispatchToMainThread(r);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
   }
 
   return promise.forget();
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -2,33 +2,151 @@
 /* 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 "ServiceWorkerWindowClient.h"
 
 #include "mozilla/dom/ClientBinding.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 JSObject*
 ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return WindowClientBinding::Wrap(aCx, this, aGivenProto);
 }
 
+namespace {
+
+// Passing a null clientInfo will reject the promise with InvalidAccessError.
+class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+
+public:
+  ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
+                                 PromiseWorkerProxy* aPromiseProxy,
+                                 UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mPromiseProxy(aPromiseProxy)
+    , mClientInfo(Move(aClientInfo))
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    Promise* promise = mPromiseProxy->GetWorkerPromise();
+    MOZ_ASSERT(promise);
+
+    if (mClientInfo) {
+      nsRefPtr<ServiceWorkerWindowClient> client =
+        new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
+      promise->MaybeResolve(client);
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    }
+
+    // Release the reference on the worker thread.
+    mPromiseProxy->CleanUp(aCx);
+
+    return true;
+  }
+};
+
+class ClientFocusRunnable final : public nsRunnable
+{
+  uint64_t mWindowId;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
+
+public:
+  ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
+    : mWindowId(aWindowId)
+    , mPromiseProxy(aPromiseProxy)
+  {
+    MOZ_ASSERT(mPromiseProxy);
+    MOZ_ASSERT(mPromiseProxy->GetWorkerPromise());
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+    nsGlobalWindow* window = nsGlobalWindow::GetOuterWindowWithId(mWindowId);
+    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+
+    if (window) {
+      ErrorResult result;
+      //FIXME(catalinb): Bug 1144660 - check if we are allowed to focus here.
+      window->Focus(result);
+      clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument()));
+    }
+
+    DispatchResult(Move(clientInfo));
+    return NS_OK;
+  }
+
+private:
+  void
+  DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+  {
+    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    nsRefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+      new ResolveOrRejectPromiseRunnable(workerPrivate, mPromiseProxy,
+                                         Move(aClientInfo));
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    JSContext* cx = jsapi.cx();
+    if (!resolveRunnable->Dispatch(cx)) {
+      nsRefPtr<PromiseWorkerProxyControlRunnable> controlRunnable =
+        new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
+      if (!controlRunnable->Dispatch(cx)) {
+        NS_RUNTIMEABORT("Failed to dispatch Focus promise control runnable.");
+      }
+    }
+  }
+};
+
+} // anonymous namespace
+
 already_AddRefed<Promise>
-ServiceWorkerWindowClient::Focus() const
+ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
 {
-  ErrorResult result;
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+  workerPrivate->AssertIsOnWorkerThread();
+
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   MOZ_ASSERT(global);
 
-  nsRefPtr<Promise> promise = Promise::Create(global, result);
-  if (NS_WARN_IF(result.Failed())) {
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+  nsRefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (!promiseProxy->GetWorkerPromise()) {
+    // Don't dispatch if adding the worker feature failed.
+    return promise.forget();
+  }
+
+  nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
+                                                            promiseProxy);
+  aRv = NS_DispatchToMainThread(r);
+  if (NS_WARN_IF(aRv.Failed())) {
+    promise->MaybeReject(aRv.ErrorCode());
+  }
+
   return promise.forget();
 }
--- a/dom/workers/ServiceWorkerWindowClient.h
+++ b/dom/workers/ServiceWorkerWindowClient.h
@@ -42,17 +42,17 @@ public:
 
   mozilla::dom::FrameType
   FrameType() const
   {
     return mFrameType;
   }
 
   already_AddRefed<Promise>
-  Focus() const;
+  Focus(ErrorResult& aRv) const;
 
 private:
   ~ServiceWorkerWindowClient()
   { }
 
   mozilla::dom::VisibilityState mVisibilityState;
   bool mFocused;
   mozilla::dom::FrameType mFrameType;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5893,21 +5893,20 @@ WorkerPrivate::NotifyFeatures(JSContext*
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(aStatus > Running, "Bad status!");
 
   if (aStatus >= Closing) {
     CancelAllTimeouts(aCx);
   }
 
-  nsAutoTArray<WorkerFeature*, 30> features;
-  features.AppendElements(mFeatures);
-
-  for (uint32_t index = 0; index < features.Length(); index++) {
-    if (!features[index]->Notify(aCx, aStatus)) {
+  nsTObserverArray<WorkerFeature*>::ForwardIterator iter(mFeatures);
+  while (iter.HasMore()) {
+    WorkerFeature* feature = iter.GetNext();
+    if (!feature->Notify(aCx, aStatus)) {
       NS_WARNING("Failed to notify feature!");
     }
   }
 
   nsAutoTArray<ParentType*, 10> children;
   children.AppendElements(mChildWorkers);
 
   for (uint32_t index = 0; index < children.Length(); index++) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -20,16 +20,17 @@
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsTObserverArray.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
 class nsIDocument;
@@ -847,17 +848,17 @@ class WorkerPrivate : public WorkerPriva
   nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
   nsRefPtr<WorkerThread> mThread;
   PRThread* mPRThread;
 
   // Things touched on worker thread only.
   nsRefPtr<WorkerGlobalScope> mScope;
   nsRefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
   nsTArray<ParentType*> mChildWorkers;
-  nsTArray<WorkerFeature*> mFeatures;
+  nsTObserverArray<WorkerFeature*> mFeatures;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
   uint32_t mDebuggerEventLoopLevel;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
     nsRefPtr<EventTarget> mEventTarget;
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/client_focus_worker.js
@@ -0,0 +1,15 @@
+onmessage = function(e) {
+  if (!e.source) {
+    dump("ERROR: message doesn't have a source.");
+  }
+
+  // The client should be a window client
+  if (e.source instanceof WindowClient) {
+    // this will dispatch a focus event on the client
+    e.source.focus().then(function(client) {
+      client.postMessage(client.focused);
+    });
+  } else {
+    dump("ERROR: client should be a WindowClient");
+  }
+};
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -12,16 +12,17 @@ support-files =
   simpleregister/index.html
   simpleregister/ready.html
   controller/index.html
   unregister/index.html
   unregister/unregister.html
   workerUpdate/update.html
   sw_clients/simple.html
   sw_clients/service_worker_controlled.html
+  sw_clients/focus_stealing_client.html
   match_all_worker.js
   match_all_advanced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
@@ -58,16 +59,17 @@ support-files =
   serviceworker_not_sharedworker.js
   match_all_client/match_all_client_id.html
   match_all_client_id_worker.js
   source_message_posting_worker.js
   scope/scope_worker.js
   redirect_serviceworker.sjs
   importscript.sjs
   importscript_worker.js
+  client_focus_worker.js
 
 [test_unregister.html]
 [test_installation_simple.html]
 [test_fetch_event.html]
 skip-if = true # Bug 1136780
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
 [test_match_all.html]
@@ -84,8 +86,9 @@ skip-if = true # Bug 1136780
 [test_match_all_client_properties.html]
 [test_close.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_match_all_client_id.html]
 [test_sandbox_intercept.html]
 [test_request_context.html]
 [test_importscript.html]
+[test_client_focus.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
@@ -0,0 +1,33 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1130686 - Test service worker client.focus: client </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <!--
+    We load this as an iframe to blur the main test window.
+  -->
+</head>
+<body>/
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  if (!parent) {
+    info("error: sw_clients/focus_stealing_client.html shouldn't be launched directly!");
+  }
+
+  window.onload = function() {
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage("READY", "*");
+    });
+  }
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -0,0 +1,94 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1130686 - Test service worker client.focus </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<!--
+  This test checks that client.focus is able to restore focus to the main window
+  when an iframe is holding the focus.
+-->
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  var registration;
+  var worker;
+
+  function start() {
+    return navigator.serviceWorker.register("client_focus_worker.js",
+                                            { scope: "./sw_clients/focus_stealing_client.html" })
+      .then((swr) => registration = swr);
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(result) {
+      ok(result, "Unregister should return true.");
+    });
+  }
+
+  function loseFocus() {
+    var p = new Promise(function(res, rej) {
+      window.onmessage = function(e) {
+        if (e.data == "READY") {
+          ok(true, "iframe created.");
+          iframe.contentWindow.focus();
+        }
+      }
+      window.onblur = function() {
+        ok(true, "blurred");
+        res();
+      }
+    });
+
+    content = document.getElementById("content");
+    ok(content, "parent exists.");
+
+    iframe = document.createElement("iframe");
+    content.appendChild(iframe);
+
+    iframe.setAttribute('src', "sw_clients/focus_stealing_client.html");
+    return p;
+  }
+
+  function testFocus() {
+    var p = new Promise(function(res, rej) {
+      navigator.serviceWorker.onmessage = function(e) {
+        ok(e.data, "client object is marked as focused.");
+        ok(document.hasFocus(), "document has focus.");
+        res();
+      }
+    });
+
+    ok(registration.active, "active worker exists.");
+    registration.active.postMessage("go");
+    return p;
+  }
+
+  function runTest() {
+     start()
+      .then(loseFocus)
+      .then(testFocus)
+      .then(unregister)
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+      }).then(SimpleTest.finish);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -291,17 +291,17 @@ GLContextProviderCGL::CreateHeadless(boo
 }
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 static nsRefPtr<GLContext> gGlobalContext;
 
 GLContext*
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -899,17 +899,17 @@ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1229,17 +1229,17 @@ already_AddRefed<GLContext>
 GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 GLContext*
 GLContextProviderGLX::GetGlobalContext()
 {
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -635,25 +635,25 @@ GLContextProviderWGL::CreateHeadless(boo
         return nullptr;
     }
 
     nsRefPtr<GLContext> retGL = glContext.get();
     return retGL.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
+GLContextProviderWGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 static nsRefPtr<GLContextWGL> gGlobalContext;
 
 GLContext *
--- a/gfx/layers/TextureDIB.cpp
+++ b/gfx/layers/TextureDIB.cpp
@@ -121,17 +121,17 @@ DIBTextureHost::DIBTextureHost(TextureFl
   : TextureHost(aFlags)
   , mIsLocked(false)
 {
   // We added an extra ref for transport, so we shouldn't AddRef now.
   mSurface =
     dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
   MOZ_ASSERT(mSurface);
 
-  mSize = ToIntSize(mSurface->GetSize());
+  mSize = mSurface->GetSize();
   mFormat = ImageFormatToSurfaceFormat(
     gfxPlatform::GetPlatform()->OptimalFormatForContent(mSurface->GetContentType()));
 }
 
 bool
 DIBTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
 {
   if (!mTextureSource) {
--- a/gfx/layers/YCbCrImageDataSerializer.cpp
+++ b/gfx/layers/YCbCrImageDataSerializer.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include <string.h>                     // for memcpy
-#include "gfx2DGlue.h"                  // for ToIntSize
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Logging.h"        // for gfxDebug
 #include "mozilla/gfx/Types.h"
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "yuv_convert.h"                // for ConvertYCbCrToRGB32, etc
 
 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -634,17 +634,17 @@ Transform(const gfxImageSurface* aDest,
           RefPtr<DataSourceSurface> aSrc,
           const gfx3DMatrix& aTransform,
           gfxPoint aDestOffset)
 {
   if (aTransform.IsSingular()) {
     return;
   }
 
-  IntSize destSize = ToIntSize(aDest->GetSize());
+  IntSize destSize = aDest->GetSize();
   SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
                                            destSize.height,
                                            kBGRA_8888_SkColorType,
                                            kPremul_SkAlphaType);
   SkBitmap destBitmap;
   destBitmap.setInfo(destInfo, aDest->Stride());
   destBitmap.setPixels((uint32_t*)aDest->Data());
   SkCanvas destCanvas(destBitmap);
@@ -692,17 +692,17 @@ BasicLayerManager_Matrix3DToPixman(const
 }
 
 static void
 Transform(const gfxImageSurface* aDest,
           RefPtr<DataSourceSurface> aSrc,
           const gfx3DMatrix& aTransform,
           gfxPoint aDestOffset)
 {
-  IntSize destSize = ToIntSize(aDest->GetSize());
+  IntSize destSize = aDest->GetSize();
   pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == gfxImageFormat::ARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
                                                   destSize.width,
                                                   destSize.height,
                                                   (uint32_t*)aDest->Data(),
                                                   aDest->Stride());
 
   IntSize srcSize = aSrc->GetSize();
   pixman_image_t* src = pixman_image_create_bits(aSrc->GetFormat() == SurfaceFormat::B8G8R8A8 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -139,14 +139,14 @@ TextureClientX11::BorrowDrawTarget()
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mLocked);
 
   if (!mSurface) {
     return nullptr;
   }
 
   if (!mDrawTarget) {
-    IntSize size = ToIntSize(mSurface->GetSize());
+    IntSize size = mSurface->GetSize();
     mDrawTarget = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
   }
 
   return mDrawTarget;
 }
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -16,17 +16,17 @@ X11TextureSourceBasic::X11TextureSourceB
   : mCompositor(aCompositor),
     mSurface(aSurface)
 {
 }
 
 IntSize
 X11TextureSourceBasic::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 SurfaceFormat
 X11TextureSourceBasic::GetFormat() const
 {
   gfxContentType type = mSurface->GetContentType();
   return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
 }
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -253,27 +253,25 @@ ClientTiledPaintedLayer::UseProgressiveD
 
   if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
     // This layer is fixed-position and so even if it does have a scrolling
     // ancestor it will likely be entirely on-screen all the time, so we
     // should draw it all at once
     return false;
   }
 
-  // XXX We probably want to disable progressive drawing for non active APZ layers in the future
-  //     but we should wait for a proper test case before making this change.
-#if 0 //!defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
-  LayerMetricsWrapper scrollAncestor;
-  GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
-  MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty
-  const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
-  if (!IsScrollingOnCompositor(parentMetrics)) {
-    return false;
+  if (gfxPrefs::AsyncPanZoomEnabled()) {
+    LayerMetricsWrapper scrollAncestor;
+    GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+    MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty
+    const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
+    if (!IsScrollingOnCompositor(parentMetrics)) {
+      return false;
+    }
   }
-#endif
 
   return true;
 }
 
 bool
 ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
                                             const nsIntRegion& aVisibleRegion,
                                             LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -800,17 +800,17 @@ ApplyAsyncTransformToScrollbarForContent
     if (aScrollbarIsDescendant) {
       xTranslation *= metrics.GetPresShellResolution();
     }
 
     scrollbarTransform.PostScale(xScale, 1.f, 1.f);
     scrollbarTransform.PostTranslate(xTranslation, 0, 0);
   }
 
-  Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();
+  Matrix4x4 transform = aScrollbar->GetLocalTransform() * scrollbarTransform;
 
   if (aScrollbarIsDescendant) {
     // If the scrollbar layer is a child of the content it is a scrollbar for,
     // then we need to make a couple of adjustments to the scrollbar's transform.
     //
     //  - First, the content's resolution applies to the scrollbar as well.
     //    Since we don't actually want the scroll thumb's size to vary with
     //    the zoom (other than its length reflecting the fraction of the
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -339,17 +339,18 @@ RenderLayers(ContainerT* aContainer,
 
   for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
     PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
     LayerComposite* layerToRender = preparedData.mLayer;
     const RenderTargetIntRect& clipRect = preparedData.mClipRect;
     Layer* layer = layerToRender->GetLayer();
 
     gfxRGBA color;
-    if (LayerHasCheckerboardingAPZC(layer, &color)) {
+    if ((layer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+        LayerHasCheckerboardingAPZC(layer, &color)) {
       // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
       // and only fill in that area. However the layer bounds takes into account the base translation
       // for the painted layer whereas the checkerboard region does not. One does not simply
       // intersect areas in different coordinate spaces. So we do this a little more permissively
       // and only fill in the background when we know there is checkerboard, which in theory
       // should only occur transiently.
       nsIntRect layerBounds = layer->GetLayerBounds();
       EffectChain effectChain(layer);
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -2,17 +2,16 @@
  * 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 "TextureHost.h"
 
 #include "CompositableHost.h"           // for CompositableHost
 #include "LayersLogging.h"              // for AppendToString
-#include "gfx2DGlue.h"                  // for ToIntSize
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
--- a/gfx/layers/composite/X11TextureHost.cpp
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -78,13 +78,13 @@ X11TextureHost::GetFormat() const
   }
 #endif
   return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
 }
 
 IntSize
 X11TextureHost::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 }
 }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1453,28 +1453,34 @@ CompositorD3D11::PaintToTarget()
 }
 
 void
 CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
 {
   if (SUCCEEDED(hr)) {
     return;
   }
-  // XXX - It would be nice to use gfxCriticalError, but it needs to
-  // be made to work off the main thread first.
-  //MOZ_ASSERT(aSeverity != DebugAssert);
 
   if (aSeverity == Critical) {
     MOZ_CRASH("Unrecoverable D3D11 error");
   }
 
-  if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) {
+  bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
+
+  if (deviceRemoved && mDevice) {
     hr = mDevice->GetDeviceRemovedReason();
   }
 
+  // Device reset may not be an error on our side, but can mess things up so
+  // it's useful to see it in the reports.
+  gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
+    << (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
+                      : "[CompositorD3D11] error code: ")
+    << hexa(hr);
+
   // Always crash if we are making invalid calls
   if (hr == DXGI_ERROR_INVALID_CALL) {
     MOZ_CRASH("Invalid D3D11 api call");
   }
 
   if (aSeverity == Recoverable) {
     NS_WARNING("Encountered a recoverable D3D11 error");
   }
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -162,29 +162,29 @@ private:
   bool VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
   bool UpdateConstantBuffers();
   void SetSamplerForFilter(gfx::Filter aFilter);
   void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
   void PaintToTarget();
 
-  virtual gfx::IntSize GetWidgetSize() const override { return gfx::ToIntSize(mSize); }
+  virtual gfx::IntSize GetWidgetSize() const override { return mSize; }
 
   RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<ID3D11Device> mDevice;
   RefPtr<IDXGISwapChain> mSwapChain;
   RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
   RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
 
   DeviceAttachmentsD3D11* mAttachments;
 
   nsIWidget* mWidget;
 
-  nsIntSize mSize;
+  gfx::IntSize mSize;
 
   HWND mHwnd;
 
   D3D_FEATURE_LEVEL mFeatureLevel;
 
   VertexShaderConstants mVSConstants;
   PixelShaderConstants mPSConstants;
   bool mDisableSequenceForNextFrame;
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -147,17 +147,17 @@ private:
   void CheckResetCount();
 
   void FailedToResetDevice();
 
   void ReportFailure(const nsACString &aMsg, HRESULT aCode);
 
   virtual gfx::IntSize GetWidgetSize() const override
   {
-    return gfx::ToIntSize(mSize);
+    return mSize;
   }
 
   /* Device manager instance for this compositor */
   nsRefPtr<DeviceManagerD3D9> mDeviceManager;
 
   /* Swap chain associated with this compositor */
   nsRefPtr<SwapChainD3D9> mSwapChain;
 
--- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
@@ -235,26 +235,26 @@ DeviceManagerD3D9::Init()
     if (!d3d9Create) {
       gfxCriticalError() << "[D3D9] Failed to load symbols";
       return false;
     }
 
     mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION));
 
     if (!mD3D9) {
-      gfxCriticalError() << "[D3D9] Failed to create the device";
+      gfxCriticalError() << "[D3D9] Failed to create the IDirect3D9 object";
       return false;
     }
   }
 
   D3DADAPTER_IDENTIFIER9 ident;
   hr = mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &ident);
 
   if (FAILED(hr)) {
-    gfxCriticalError() << "[D3D9] Failed to create the environment";
+    gfxCriticalError() << "[D3D9] Failed to create the environment code: " << gfx::hexa(hr);
     return false;
   }
 
   D3DPRESENT_PARAMETERS pp;
   memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS));
 
   pp.BackBufferWidth = 1;
   pp.BackBufferHeight = 1;
@@ -326,153 +326,153 @@ DeviceManagerD3D9::Init()
   if (mNv3DVUtils) { 
     IUnknown* devUnknown = nullptr; 
     if (mDevice) { 
       mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown); 
     } 
     mNv3DVUtils->SetDeviceInfo(devUnknown); 
   } 
 
-  auto failCreateShaderMsg = "[D3D9] failed to create a critical resource (shader)";
+  auto failCreateShaderMsg = "[D3D9] failed to create a critical resource (shader) code: ";
 
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS,
                                    getter_AddRefs(mLayerVS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS,
                                   getter_AddRefs(mRGBPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPS,
                                   getter_AddRefs(mRGBAPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPS,
                                   getter_AddRefs(mComponentPass1PS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPS,
                                   getter_AddRefs(mComponentPass2PS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS,
                                   getter_AddRefs(mYCbCrPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS,
                                   getter_AddRefs(mSolidColorPS));
 
   if (FAILED(hr)) {
     gfxCriticalError() << failCreateShaderMsg;
     return false;
   }
 
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVSMask,
                                    getter_AddRefs(mLayerVSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVSMask3D,
                                    getter_AddRefs(mLayerVSMask3D));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPSMask,
                                   getter_AddRefs(mRGBPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPSMask,
                                   getter_AddRefs(mRGBAPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPSMask3D,
                                   getter_AddRefs(mRGBAPSMask3D));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPSMask,
                                   getter_AddRefs(mComponentPass1PSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPSMask,
                                   getter_AddRefs(mComponentPass2PSMask));
 
   if (FAILED(hr)) {
     gfxCriticalError() << failCreateShaderMsg;
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPSMask,
                                   getter_AddRefs(mYCbCrPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPSMask,
                                   getter_AddRefs(mSolidColorPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   if (!CreateVertexBuffer()) {
     gfxCriticalError() << "[D3D9] Failed to create a critical resource (vbo)";
     return false;
   }
 
   hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex));
   if (FAILED(hr)) {
-    gfxCriticalError() << "[D3D9] Failed to set the stream source";
+    gfxCriticalError() << "[D3D9] Failed to set the stream source code: " << gfx::hexa(hr);
     return false;
   }
 
   D3DVERTEXELEMENT9 elements[] = {
     { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
       D3DDECLUSAGE_POSITION, 0 },
     D3DDECL_END()
   };
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -442,17 +442,17 @@ DataTextureSourceD3D9::Update(gfx::DataS
 bool
 DataTextureSourceD3D9::Update(gfxWindowsSurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
   if (!mCompositor || !mCompositor->device()) {
     NS_WARNING("No D3D device to update the texture.");
     return false;
   }
-  mSize = ToIntSize(aSurface->GetSize());
+  mSize = aSurface->GetSize();
 
   uint32_t bpp = 0;
 
   _D3DFORMAT format = D3DFMT_A8R8G8B8;
   mFormat = ImageFormatToSurfaceFormat(
     gfxPlatform::GetPlatform()->OptimalFormatForContent(aSurface->GetContentType()));
   switch (mFormat) {
   case SurfaceFormat::B8G8R8X8:
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -44,18 +44,17 @@ CreateSharedRGBImage(ImageContainer *aIm
   nsRefPtr<Image> image = aImageContainer->CreateImage(ImageFormat::SHARED_RGB);
 
   if (!image) {
     NS_WARNING("Failed to create SharedRGBImage");
     return nullptr;
   }
 
   nsRefPtr<SharedRGBImage> rgbImage = static_cast<SharedRGBImage*>(image.get());
-  if (!rgbImage->Allocate(gfx::ToIntSize(aSize),
-                          gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
+  if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
     NS_WARNING("Failed to allocate a shared image");
     return nullptr;
   }
   return image.forget();
 }
 
 SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
 : Image(nullptr, ImageFormat::SHARED_RGB)
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -302,31 +302,31 @@ public:
   GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit);
 
   const gfx::Matrix4x4& GetProjMatrix() const {
     return mProjMatrix;
   }
 private:
   virtual gfx::IntSize GetWidgetSize() const override
   {
-    return gfx::ToIntSize(mWidgetSize);
+    return mWidgetSize;
   }
 
   bool InitializeVR();
   void DestroyVR(GLContext *gl);
 
   void DrawVRDistortion(const gfx::Rect& aRect,
                         const gfx::Rect& aClipRect,
                         const EffectChain& aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4& aTransform);
 
   /** Widget associated with this compositor */
   nsIWidget *mWidget;
-  nsIntSize mWidgetSize;
+  gfx::IntSize mWidgetSize;
   nsRefPtr<GLContext> mGLContext;
   UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
   gfx::Matrix4x4 mProjMatrix;
 
   /** The size of the surface we are rendering to */
   nsIntSize mSurfaceSize;
 
   ScreenPoint mRenderOffset;
--- a/gfx/layers/opengl/X11TextureSourceOGL.cpp
+++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp
@@ -55,17 +55,17 @@ X11TextureSourceOGL::BindTexture(GLenum 
   }
 
   ApplyFilterToBoundTexture(gl(), aFilter, LOCAL_GL_TEXTURE_2D);
 }
 
 IntSize
 X11TextureSourceOGL::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 SurfaceFormat
 X11TextureSourceOGL::GetFormat() const {
   gfxContentType type = mSurface->GetContentType();
   return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
 }
 
--- a/gfx/tests/gtest/TestSkipChars.cpp
+++ b/gfx/tests/gtest/TestSkipChars.cpp
@@ -64,16 +64,19 @@ TestIterator()
 
   gfxSkipCharsIterator iter1(skipChars1);
 
   EXPECT_TRUE(iter1.GetOriginalOffset() == 0) <<
     "[2] Check initial original offset";
   EXPECT_TRUE(iter1.GetSkippedOffset() == 0) <<
     "[3] Check initial skipped offset";
 
+  EXPECT_TRUE(iter1.IsOriginalCharSkipped() == false) <<
+    "[3a] Check IsOriginalCharSkipped for initial position";
+
   uint32_t expectSkipped1[] =
   {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
      9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
     18, 19, 20, 21, 22, 23, 24, 25, 26, 27 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) {
     EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i]) <<
       "[4] Check mapping of original to skipped for " << i;
@@ -84,16 +87,28 @@ TestIterator()
     10, 11, 12, 13, 14, 15, 16, 17, 18,
     20, 21, 22, 23, 24, 25, 26, 27, 28 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) {
     EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i]) <<
       "[5] Check mapping of skipped to original for " << i;
   }
 
+  bool expectIsOriginalSkipped1[] =
+  {  false, false, false, false, false, false, false, false, false, true,
+     false, false, false, false, false, false, false, false, false, true,
+     false, false, false, false, false, false, false, false, false
+  };
+
+  for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped1); i++) {
+    iter1.SetOriginalOffset(i);
+    EXPECT_TRUE(iter1.IsOriginalCharSkipped() == expectIsOriginalSkipped1[i]) <<
+      "[5.a] Check IsOriginalCharSkipped for " << i;
+  }
+
   // Test a gfxSkipChars that starts with skipped chars
   gfxSkipChars skipChars2;
 
   skipChars2.SkipChars(9);
   skipChars2.KeepChar();
   skipChars2.SkipChars(9);
   skipChars2.KeepChar();
   skipChars2.SkipChars(9);
@@ -103,16 +118,19 @@ TestIterator()
 
   gfxSkipCharsIterator iter2(skipChars2);
 
   EXPECT_TRUE(iter2.GetOriginalOffset() == 0) <<
     "[7] Check initial original offset";
   EXPECT_TRUE(iter2.GetSkippedOffset() == 0) <<
     "[8] Check initial skipped offset";
 
+  EXPECT_TRUE(iter2.IsOriginalCharSkipped() == true) <<
+    "[8a] Check IsOriginalCharSkipped for initial position";
+
   uint32_t expectSkipped2[] =
   {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) {
     EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i]) <<
       "[9] Check mapping of original to skipped for " << i;
@@ -120,16 +138,28 @@ TestIterator()
 
   uint32_t expectOriginal2[] = { 9, 19, 29 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
     EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) <<
       "[10] Check mapping of skipped to original for " << i;
   }
 
+  bool expectIsOriginalSkipped2[] =
+  {  true, true, true, true, true, true, true, true, true, false,
+     true, true, true, true, true, true, true, true, true, false,
+     true, true, true, true, true, true, true, true, true
+  };
+
+  for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped2); i++) {
+    iter2.SetOriginalOffset(i);
+    EXPECT_TRUE(iter2.IsOriginalCharSkipped() == expectIsOriginalSkipped2[i]) <<
+      "[10.a] Check IsOriginalCharSkipped for " << i;
+  }
+
   return true;
 }
 
 TEST(Gfx, gfxSkipChars) {
   TestConstructor();
   TestLength();
   TestIterator();
 }
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -143,17 +143,17 @@ void AssertYCbCrSurfacesEqual(PlanarYCbC
   }
 }
 
 // Run the test for a texture client and a surface
 void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) {
 
   // client allocation
   ASSERT_TRUE(texture->CanExposeDrawTarget());
-  texture->AllocateForSurface(ToIntSize(surface->GetSize()));
+  texture->AllocateForSurface(surface->GetSize());
   ASSERT_TRUE(texture->IsAllocated());
 
   ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
   // client painting
   RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
   RefPtr<SourceSurface> source =
     gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
   dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -76,21 +76,16 @@ inline IntPoint ToIntPoint(const nsIntPo
   return IntPoint(aPoint.x, aPoint.y);
 }
 
 inline Size ToSize(const gfxSize &aSize)
 {
   return Size(Float(aSize.width), Float(aSize.height));
 }
 
-inline IntSize ToIntSize(const gfxIntSize &aSize)
-{
-  return IntSize(aSize.width, aSize.height);
-}
-
 inline Filter ToFilter(GraphicsFilter aFilter)
 {
   switch (aFilter) {
   case GraphicsFilter::FILTER_NEAREST:
     return Filter::POINT;
   case GraphicsFilter::FILTER_GOOD:
     return Filter::GOOD;
   default:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -832,17 +832,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // DrawTarget and can't pick a better surface type. Doing this also avoids
     // readback of aSurface's surface into memory if, for example, aSurface
     // wraps an xlib cairo surface (which can be important to avoid a major
     // slowdown).
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::CAIRO_SURFACE;
     surf.mSurface = aSurface->CairoSurface();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     // We return here regardless of whether CreateSourceSurfaceFromNativeSurface
     // succeeds or not since we don't expect to be able to do any better below
     // if it fails.
     //
     // Note that the returned SourceSurfaceCairo holds a strong reference to
     // the cairo_surface_t* that it wraps, which essencially means it holds a
     // strong reference to aSurface since aSurface shares its
     // cairo_surface_t*'s reference count variable. As a result we can't cache
@@ -857,17 +857,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
 
 #ifdef XP_WIN
   if (aSurface->GetType() == gfxSurfaceType::D2D &&
       format != SurfaceFormat::A8) {
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::D3D10_TEXTURE;
     surf.mSurface = static_cast<gfxD2DSurface*>(aSurface)->GetTexture();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     mozilla::gfx::DrawTarget *dt = static_cast<mozilla::gfx::DrawTarget*>(aSurface->GetData(&kDrawTarget));
     if (dt) {
       dt->Flush();
     }
     srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
   }
 #endif
   // Currently no other DrawTarget types implement CreateSourceSurfaceFromNativeSurface
@@ -905,17 +905,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // likely create a DataSourceSurface (possibly involving copying and/or
     // readback), and the OptimizeSourceSurface may well copy again and upload
     // to the GPU. So, while this code path is rarely hit, hitting it may be
     // very slow.
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::CAIRO_SURFACE;
     surf.mSurface = aSurface->CairoSurface();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     RefPtr<DrawTarget> drawTarget =
       Factory::CreateDrawTarget(BackendType::CAIRO, IntSize(1, 1), format);
     if (!drawTarget) {
       gfxWarning() << "gfxPlatform::GetSourceSurfaceForSurface failed in CreateDrawTarget";
       return nullptr;
     }
     srcBuffer = drawTarget->CreateSourceSurfaceFromNativeSurface(surf);
     if (srcBuffer) {
@@ -951,17 +951,17 @@ gfxPlatform::GetWrappedDataSourceSurface
 {
   nsRefPtr<gfxImageSurface> image = aSurface->GetAsImageSurface();
   if (!image) {
     return nullptr;
   }
   RefPtr<DataSourceSurface> result =
     Factory::CreateWrappingDataSourceSurface(image->Data(),
                                              image->Stride(),
-                                             ToIntSize(image->GetSize()),
+                                             image->GetSize(),
                                              ImageFormatToSurfaceFormat(image->Format()));
 
   if (!result) {
     return nullptr;
   }
 
   // If we wrapped the underlying data of aSurface, then we need to add user data
   // to make sure aSurface stays alive until we are done with the data.
--- a/gfx/thebes/gfxSkipChars.h
+++ b/gfx/thebes/gfxSkipChars.h
@@ -172,27 +172,30 @@ public:
                          int32_t aOriginalStringToSkipCharsOffset,
                          int32_t aOriginalStringOffset)
         : mSkipChars(&aSkipChars),
           mOriginalStringOffset(0),
           mSkippedStringOffset(0),
           mCurrentRangeIndex(-1),
           mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
     {
-          SetOriginalOffset(aOriginalStringOffset);
+        SetOriginalOffset(aOriginalStringOffset);
     }
 
     explicit gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
                                   int32_t aOriginalStringToSkipCharsOffset = 0)
         : mSkipChars(&aSkipChars),
           mOriginalStringOffset(0),
           mSkippedStringOffset(0),
-          mCurrentRangeIndex(-1),
           mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
-    { }
+    {
+        mCurrentRangeIndex =
+            mSkipChars->mRanges.IsEmpty() ||
+            mSkipChars->mRanges[0].Start() > 0 ? -1 : 0;
+    }
 
     gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
         : mSkipChars(aIterator.mSkipChars),
           mOriginalStringOffset(aIterator.mOriginalStringOffset),
           mSkippedStringOffset(aIterator.mSkippedStringOffset),
           mCurrentRangeIndex(aIterator.mCurrentRangeIndex),
           mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset)
     { }
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -436,18 +436,17 @@ CreateSamplingRestrictedDrawable(gfxDraw
     // matter what we do here, but we should avoid trying to
     // create a zero-size surface.
     if (needed.IsEmpty())
         return nullptr;
 
     gfxIntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
 
     RefPtr<DrawTarget> target =
-      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
-                                                                   aFormat);
+      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
     if (!target) {
       return nullptr;
     }
 
     nsRefPtr<gfxContext> tmpCtx = new gfxContext(target);
     tmpCtx->SetOperator(OptimalFillOperator());
     aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true,
                     GraphicsFilter::FILTER_FAST, 1.0, gfxMatrix::Translation(needed.TopLeft()));
@@ -864,17 +863,17 @@ gfxUtils::GetYCbCrToRGBDestFormatAndSize
     TypeFromSize(aData.mYSize.width,
                       aData.mYSize.height,
                       aData.mCbCrSize.width,
                       aData.mCbCrSize.height);
 
   // 'prescale' is true if the scaling is to be done as part of the
   // YCbCr to RGB conversion rather than on the RGB data when rendered.
   bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 &&
-                    ToIntSize(aSuggestedSize) != aData.mPicSize;
+                    aSuggestedSize != aData.mPicSize;
 
   if (aSuggestedFormat == gfxImageFormat::RGB16_565) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (prescale &&
         !IsScaleYCbCrToRGB565Fast(aData.mPicX,
                                        aData.mPicY,
                                        aData.mPicSize.width,
                                        aData.mPicSize.height,
@@ -900,17 +899,17 @@ gfxUtils::GetYCbCrToRGBDestFormatAndSize
   }
   if (aSuggestedFormat == gfxImageFormat::RGB24) {
     /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data.
        See bugs 639415 and 640073. */
     if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == YV24)
       prescale = false;
   }
   if (!prescale) {
-    ToIntSize(aSuggestedSize) = aData.mPicSize;
+    aSuggestedSize = aData.mPicSize;
   }
 }
 
 void
 gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData& aData,
                             const gfxImageFormat& aDestFormat,
                             const gfxIntSize& aDestSize,
                             unsigned char* aDestBuffer,
@@ -924,17 +923,17 @@ gfxUtils::ConvertYCbCrToRGB(const Planar
               aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1));
   YUVType yuvtype =
     TypeFromSize(aData.mYSize.width,
                       aData.mYSize.height,
                       aData.mCbCrSize.width,
                       aData.mCbCrSize.height);
 
   // Convert from YCbCr to RGB now, scaling the image if needed.
-  if (ToIntSize(aDestSize) != aData.mPicSize) {
+  if (aDestSize != aData.mPicSize) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (aDestFormat == gfxImageFormat::RGB16_565) {
       ScaleYCbCrToRGB565(aData.mYChannel,
                               aData.mCbChannel,
                               aData.mCrChannel,
                               aDestBuffer,
                               aData.mPicX,
                               aData.mPicY,
--- a/gfx/thebes/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp
@@ -267,17 +267,17 @@ gfxWindowsNativeDrawing::PaintToContext(
         nsRefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface();
         if (!gfxAlphaRecovery::RecoverAlpha(black, white)) {
             NS_ERROR("Alpha recovery failure");
             return;
         }
         RefPtr<DataSourceSurface> source =
             Factory::CreateWrappingDataSourceSurface(black->Data(),
                                                      black->Stride(),
-                                                     ToIntSize(black->GetSize()),
+                                                     black->GetSize(),
                                                      SurfaceFormat::B8G8R8A8);
 
         mContext->Save();
         mContext->SetMatrix(
           mContext->CurrentMatrix().Translate(mNativeRect.TopLeft()));
         mContext->NewPath();
         mContext->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect.Size()));
 
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -568,17 +568,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
         cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ?
             SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
     if (method != eAlphaExtraction) {
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
             native.mType = NativeSurfaceType::CAIRO_SURFACE;
             native.mSurface = tempXlibSurface;
-            native.mSize = ToIntSize(size);
+            native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
             }
         } else {
@@ -610,17 +610,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
         }
 
         gfxASurface* paintSurface = blackImage;
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
             native.mType = NativeSurfaceType::CAIRO_SURFACE;
             native.mSurface = paintSurface->CairoSurface();
-            native.mSize = ToIntSize(size);
+            native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
             }
         } else {
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -276,17 +276,17 @@ nsPNGDecoder::InitInternal()
   if (mCMSMode == eCMSMode_Off) {
     png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
   }
 
   png_set_keep_unknown_chunks(mPNG, 1, unused_chunks,
                               (int)sizeof(unused_chunks)/5);
 #endif
 
-#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
   if (mCMSMode != eCMSMode_Off) {
     png_set_chunk_malloc_max(mPNG, 4000000L);
   }
 #endif
 
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #ifndef PR_LOGGING
   // Disallow palette-index checking, for speed; we would ignore the warning
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -93,17 +93,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   }
 
   // Create a surface to draw into.
   RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
-      CreateOffscreenContentDrawTarget(ToIntSize(size), surfaceFormat);
+      CreateOffscreenContentDrawTarget(size, surfaceFormat);
   if (!target) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
 
   // Create our drawable.
   RefPtr<SourceSurface> innerSurface =
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -2617,16 +2617,19 @@ void imgCacheValidator::AddProxy(imgRequ
   mProxies.AppendObject(aProxy);
 }
 
 /** nsIRequestObserver methods **/
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 {
+  // We may be holding on to a document, so ensure that it's released.
+  nsCOMPtr<nsISupports> context = mContext.forget();
+
   // If for some reason we don't still have an existing request (probably
   // because OnStartRequest got delivered more than once), just bail.
   if (!mRequest) {
     MOZ_ASSERT_UNREACHABLE("OnStartRequest delivered more than once?");
     aRequest->Cancel(NS_BINDING_ABORTED);
     return NS_ERROR_FAILURE;
   }
 
@@ -2665,17 +2668,17 @@ NS_IMETHODIMP imgCacheValidator::OnStart
         // Notify synchronously, because we're already in OnStartRequest, an
         // asynchronously-called function.
         proxy->SyncNotifyListener();
       }
 
       // We don't need to load this any more.
       aRequest->Cancel(NS_BINDING_ABORTED);
 
-      mRequest->SetLoadId(mContext);
+      mRequest->SetLoadId(context);
       mRequest->SetValidator(nullptr);
 
       mRequest = nullptr;
 
       mNewRequest = nullptr;
       mNewEntry = nullptr;
 
       return NS_OK;
@@ -2706,17 +2709,17 @@ NS_IMETHODIMP imgCacheValidator::OnStart
 
   mRequest->SetValidator(nullptr);
   mRequest = nullptr;
 
   // We use originalURI here to fulfil the imgIRequest contract on GetURI.
   nsCOMPtr<nsIURI> originalURI;
   channel->GetOriginalURI(getter_AddRefs(originalURI));
   mNewRequest->Init(originalURI, uri, mHadInsecureRedirect, aRequest, channel,
-                    mNewEntry, mContext, loadingPrincipal, corsmode, refpol);
+                    mNewEntry, context, loadingPrincipal, corsmode, refpol);
 
   mDestListener = new ProxyListener(mNewRequest);
 
   // Try to add the new request into the cache. Note that the entry must be in
   // the cache before the proxies' ownership changes, because adding a proxy
   // changes the caching behaviour for imgRequests.
   mImgLoader->PutIntoCache(originalURI, mNewEntry);
 
@@ -2735,16 +2738,19 @@ NS_IMETHODIMP imgCacheValidator::OnStart
   mNewEntry = nullptr;
 
   return mDestListener->OnStartRequest(aRequest, ctxt);
 }
 
 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
 NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
 {
+  // Be sure we've released the document that we may have been holding on to.
+  mContext = nullptr;
+
   if (!mDestListener)
     return NS_OK;
 
   return mDestListener->OnStopRequest(aRequest, ctxt, status);
 }
 
 /** nsIStreamListener methods **/
 
--- a/intl/unicharutil/util/moz.build
+++ b/intl/unicharutil/util/moz.build
@@ -1,15 +1,15 @@
 # -*- 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 += ['internal']
+DIRS += ['internal', 'standalone']
 
 EXPORTS += [
     'GreekCasing.h',
     'ICUUtils.h',
     'IrishCasing.h',
     'nsBidiUtils.h',
     'nsSpecialCasingData.h',
     'nsUnicharUtils.h',
new file mode 100644
--- /dev/null
+++ b/intl/unicharutil/util/standalone/moz.build
@@ -0,0 +1,23 @@
+# -*- 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')
+
+intl_unicharutil_util_lcppsrcs = [
+    'nsUnicodeProperties.cpp',
+]
+
+intl_unicharutil_util_cppsrcs = [
+    '%s/intl/unicharutil/util/%s' % (TOPSRCDIR, s) \
+        for s in intl_unicharutil_util_lcppsrcs
+]
+
+UNIFIED_SOURCES += intl_unicharutil_util_cppsrcs
+
+for var in ('MOZILLA_INTERNAL_API', 'MOZILLA_XPCOMRT_API', 'MOZILLA_EXTERNAL_LINKAGE',
+            'NR_SOCKET_IS_VOID_PTR', 'HAVE_STRDUP'):
+    DEFINES[var] = True
--- a/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -677,16 +677,22 @@ BuiltinToName(AsmJSExit::BuiltinKind bui
     // Note: this label is regexp-matched by
     // browser/devtools/profiler/cleopatra/js/parserWorker.js.
 
     switch (builtin) {
       case AsmJSExit::Builtin_ToInt32:   return "ToInt32 (in asm.js)";
 #if defined(JS_CODEGEN_ARM)
       case AsmJSExit::Builtin_IDivMod:   return "software idivmod (in asm.js)";
       case AsmJSExit::Builtin_UDivMod:   return "software uidivmod (in asm.js)";
+      case AsmJSExit::Builtin_AtomicCmpXchg:  return "Atomics.compareExchange (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchAdd: return "Atomics.add (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchSub: return "Atomics.sub (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchAnd: return "Atomics.and (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchOr:  return "Atomics.or (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchXor: return "Atomics.xor (in asm.js)";
 #endif
       case AsmJSExit::Builtin_ModD:      return "fmod (in asm.js)";
       case AsmJSExit::Builtin_SinD:      return "Math.sin (in asm.js)";
       case AsmJSExit::Builtin_CosD:      return "Math.cos (in asm.js)";
       case AsmJSExit::Builtin_TanD:      return "Math.tan (in asm.js)";
       case AsmJSExit::Builtin_ASinD:     return "Math.asin (in asm.js)";
       case AsmJSExit::Builtin_ACosD:     return "Math.acos (in asm.js)";
       case AsmJSExit::Builtin_ATanD:     return "Math.atan (in asm.js)";
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -75,16 +75,22 @@ namespace AsmJSExit
 
     // For Reason_Builtin, the list of builtins, so they can be displayed in the
     // profile call stack.
     enum BuiltinKind {
         Builtin_ToInt32,
 #if defined(JS_CODEGEN_ARM)
         Builtin_IDivMod,
         Builtin_UDivMod,
+        Builtin_AtomicCmpXchg,
+        Builtin_AtomicFetchAdd,
+        Builtin_AtomicFetchSub,
+        Builtin_AtomicFetchAnd,
+        Builtin_AtomicFetchOr,
+        Builtin_AtomicFetchXor,
 #endif
         Builtin_ModD,
         Builtin_SinD,
         Builtin_CosD,
         Builtin_TanD,
         Builtin_ASinD,
         Builtin_ACosD,
         Builtin_ATanD,
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -812,16 +812,26 @@ NewExportedFunction(JSContext* cx, const
 }
 
 static bool
 HandleDynamicLinkFailure(JSContext* cx, CallArgs args, AsmJSModule& module, HandlePropertyName name)
 {
     if (cx->isExceptionPending())
         return false;
 
+    // Source discarding is allowed to affect JS semantics because it is never
+    // enabled for normal JS content.
+    bool haveSource = module.scriptSource()->hasSourceData();
+    if (!haveSource && !JSScript::loadSource(cx, module.scriptSource(), &haveSource))
+        return false;
+    if (!haveSource) {
+        JS_ReportError(cx, "asm.js link failure with source discarding enabled");
+        return false;
+    }
+
     uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
     uint32_t end = module.srcEndBeforeCurly();
     Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end));
     if (!src)
         return false;
 
     RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED,
                                                name, JSFunction::FinalizeKind,
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -30,16 +30,17 @@
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsprf.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "prmjtime.h"
 
+#include "builtin/AtomicsObject.h"
 #include "frontend/Parser.h"
 #include "jit/IonCode.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 
 #include "jsobjinlines.h"
 
@@ -685,16 +686,28 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
         return RedirectCall(FuncCast(CoerceInPlace_ToNumber), Args_General1);
       case AsmJSImm_ToInt32:
         return RedirectCall(FuncCast<int32_t (double)>(JS::ToInt32), Args_Int_Double);
 #if defined(JS_CODEGEN_ARM)
       case AsmJSImm_aeabi_idivmod:
         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
       case AsmJSImm_aeabi_uidivmod:
         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
+      case AsmJSImm_AtomicCmpXchg:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t, int32_t)>(js::atomics_cmpxchg_asm_callout), Args_General4);
+      case AsmJSImm_AtomicFetchAdd:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_add_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchSub:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_sub_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchAnd:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_and_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchOr:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_or_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchXor:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_xor_asm_callout), Args_General3);
 #endif
       case AsmJSImm_ModD:
         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
       case AsmJSImm_SinD:
 #ifdef _WIN64
         // Workaround a VS 2013 sin issue, see math_sin_uncached.
         return RedirectCall(FuncCast<double (double)>(js::math_sin_uncached), Args_Double_Double);
 #else
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -8916,29 +8916,44 @@ GenerateFFIExits(ModuleCompiler& m, cons
 //     (coming out) and preserve non-volatile registers.
 static bool
 GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin)
 {
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(masm.framePushed() == 0);
 
     MIRTypeVector argTypes(m.cx());
-    if (!argTypes.reserve(2))
+    if (!argTypes.reserve(5))
         return false;
 
     switch (builtin) {
       case AsmJSExit::Builtin_ToInt32:
         argTypes.infallibleAppend(MIRType_Int32);
         break;
 #if defined(JS_CODEGEN_ARM)
       case AsmJSExit::Builtin_IDivMod:
       case AsmJSExit::Builtin_UDivMod:
         argTypes.infallibleAppend(MIRType_Int32);
         argTypes.infallibleAppend(MIRType_Int32);
         break;
+      case AsmJSExit::Builtin_AtomicCmpXchg:
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        break;
+      case AsmJSExit::Builtin_AtomicFetchAdd:
+      case AsmJSExit::Builtin_AtomicFetchSub:
+      case AsmJSExit::Builtin_AtomicFetchAnd:
+      case AsmJSExit::Builtin_AtomicFetchOr:
+      case AsmJSExit::Builtin_AtomicFetchXor:
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        break;
 #endif
       case AsmJSExit::Builtin_SinD:
       case AsmJSExit::Builtin_CosD:
       case AsmJSExit::Builtin_TanD:
       case AsmJSExit::Builtin_ASinD:
       case AsmJSExit::Builtin_ACosD:
       case AsmJSExit::Builtin_ATanD:
       case AsmJSExit::Builtin_CeilD:
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -210,16 +210,88 @@ atomics_fence_impl(JSContext* cx, Mutabl
 
 bool
 js::atomics_fence(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return atomics_fence_impl(cx, args.rval());
 }
 
+static int32_t
+do_cmpxchg(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate, void* viewData,
+           uint32_t offset, bool* badArrayType)
+{
+    // CAS always sets oldval to the old value of the cell.
+    // addr must be a T*, and oldval and newval should be variables of type T
+
+#if defined(CXX11_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \
+    } while(0)
+#elif defined(GNU_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \
+    } while(0)
+#else
+# define CAS(a, b, c, newval)  (void)newval
+#endif
+
+    switch (viewType) {
+      case Scalar::Int8: {
+          int8_t oldval = (int8_t)oldCandidate;
+          int8_t newval = (int8_t)newCandidate;
+          CAS(int8_t, (int8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint8: {
+          uint8_t oldval = (uint8_t)oldCandidate;
+          uint8_t newval = (uint8_t)newCandidate;
+          CAS(uint8_t, (uint8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint8Clamped: {
+          uint8_t oldval = ClampIntForUint8Array(oldCandidate);
+          uint8_t newval = ClampIntForUint8Array(newCandidate);
+          CAS(uint8_t, (uint8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Int16: {
+          int16_t oldval = (int16_t)oldCandidate;
+          int16_t newval = (int16_t)newCandidate;
+          CAS(int16_t, (int16_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint16: {
+          uint16_t oldval = (uint16_t)oldCandidate;
+          uint16_t newval = (uint16_t)newCandidate;
+          CAS(uint16_t, (uint16_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Int32: {
+          int32_t oldval = oldCandidate;
+          int32_t newval = newCandidate;
+          CAS(int32_t, (int32_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint32: {
+          uint32_t oldval = (uint32_t)oldCandidate;
+          uint32_t newval = (uint32_t)newCandidate;
+          CAS(uint32_t, (uint32_t*)viewData + offset, oldval, newval);
+          return (int32_t)oldval;
+      }
+      default:
+        *badArrayType = true;
+        return 0;
+    }
+
+    // Do not undef CAS, it is used later
+}
+
 bool
 js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
     HandleValue oldv = args.get(2);
     HandleValue newv = args.get(3);
@@ -237,88 +309,27 @@ js::atomics_compareExchange(JSContext* c
         return false;
     int32_t newCandidate;
     if (!ToInt32(cx, newv, &newCandidate))
         return false;
 
     if (!inRange)
         return atomics_fence_impl(cx, r);
 
-    // CAS always sets oldval to the old value of the cell.
-    // addr must be a T*, and oldval and newval should be variables of type T
+    bool badType = false;
+    int32_t result = do_cmpxchg(view->type(), oldCandidate, newCandidate, view->viewData(), offset, &badType);
 
-#if defined(CXX11_ATOMICS)
-# define CAS(T, addr, oldval, newval)                                    \
-    do {                                                                \
-        std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \
-    } while(0)
-#elif defined(GNU_ATOMICS)
-# define CAS(T, addr, oldval, newval)                                    \
-    do {                                                                \
-        oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \
-    } while(0)
-#else
-# define CAS(a, b, c, newval)  (void)newval
-#endif
+    if (badType)
+        return ReportBadArrayType(cx);
 
-    switch (view->type()) {
-      case Scalar::Int8: {
-          int8_t oldval = (int8_t)oldCandidate;
-          int8_t newval = (int8_t)newCandidate;
-          CAS(int8_t, (int8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint8: {
-          uint8_t oldval = (uint8_t)oldCandidate;
-          uint8_t newval = (uint8_t)newCandidate;
-          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint8Clamped: {
-          uint8_t oldval = ClampIntForUint8Array(oldCandidate);
-          uint8_t newval = ClampIntForUint8Array(newCandidate);
-          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Int16: {
-          int16_t oldval = (int16_t)oldCandidate;
-          int16_t newval = (int16_t)newCandidate;
-          CAS(int16_t, (int16_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint16: {
-          uint16_t oldval = (uint16_t)oldCandidate;
-          uint16_t newval = (uint16_t)newCandidate;
-          CAS(uint16_t, (uint16_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Int32: {
-          int32_t oldval = oldCandidate;
-          int32_t newval = newCandidate;
-          CAS(int32_t, (int32_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint32: {
-          uint32_t oldval = (uint32_t)oldCandidate;
-          uint32_t newval = (uint32_t)newCandidate;
-          CAS(uint32_t, (uint32_t*)view->viewData() + offset, oldval, newval);
-          r.setNumber((double)oldval);
-          return true;
-      }
-      default:
-        return ReportBadArrayType(cx);
-    }
-
-    // Do not undef CAS, it is used later
+    if (view->type() == Scalar::Uint32)
+        r.setNumber((double)(uint32_t)result);
+    else
+        r.setInt32(result);
+    return true;
 }
 
 bool
 js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
@@ -683,16 +694,162 @@ js::atomics_xor(JSContext* cx, unsigned 
     return atomics_binop_impl<do_xor>(cx, args.get(0), args.get(1), args.get(2), args.rval());
 }
 
 #undef INTEGRAL_TYPES_FOR_EACH
 #undef CAST_ATOMIC
 #undef DO_NOTHING
 #undef ZERO
 
+// asm.js callouts for platforms that do not have non-word-sized
+// atomics where we don't want to inline the logic for the atomics.
+//
+// size is currently -1 (signed byte), 1 (unsigned byte), -2 (signed halfword),
+// or 2 (halfword).
+// ptr is the byte offset within the heap array.  This will have low bit zero
+// for halfword accesses.
+// value (for binops) and oldval/newval (for cmpxchg) are the values
+// to be operated upon.
+
+static void
+GetCurrentAsmJSHeap(void** heap, size_t* length)
+{
+    JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
+    AsmJSModule& mod = rt->asmJSActivationStack()->module();
+    *heap = mod.heapDatum();
+    *length = mod.heapLength();
+}
+
+int32_t
+js::atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_add::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_add::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_add::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_add::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_sub::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_sub::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_sub::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_sub::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_and::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_and::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_and::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_and::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_or::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_or::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_or::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_or::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_xor::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_xor::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_xor::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_xor::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    bool badType = false;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_cmpxchg(Scalar::Int8, oldval, newval, heap, offset, &badType);
+      case Scalar::Uint8:
+        return do_cmpxchg(Scalar::Uint8, oldval, newval, heap, offset, &badType);
+      case Scalar::Int16:
+        return do_cmpxchg(Scalar::Int16, oldval, newval, heap, offset>>1, &badType);
+      case Scalar::Uint16:
+        return do_cmpxchg(Scalar::Uint16, oldval, newval, heap, offset>>1, &badType);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
 namespace js {
 
 // Represents one waiting worker.
 //
 // The type is declared opaque in SharedArrayObject.h.  Instances of
 // js::FutexWaiter are stack-allocated and linked onto a list across a
 // call to FutexRuntime::wait().
 //
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -39,16 +39,24 @@ bool atomics_add(JSContext* cx, unsigned
 bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp);
 
+/* asm.js callouts */
+int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval);
+
 class FutexRuntime
 {
 public:
     static bool initialize();
     static void destroy();
 
     static void lock();
     static void unlock();
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2489,27 +2489,34 @@ DumpStringRepresentation(JSContext* cx, 
     str->dumpRepresentation(stderr, 0);
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 static bool
-SetLazyParsingEnabled(JSContext* cx, unsigned argc, Value* vp)
+SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (argc < 1) {
-        JS_ReportError(cx, "setLazyParsingEnabled: need an argument");
-        return false;
-    }
-
-    bool arg = ToBoolean(args.get(0));
-    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(!arg);
+    bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
+    JS::CompartmentOptionsRef(cx->compartment()).setDisableLazyParsing(disable);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+SetDiscardSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
+    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(discard);
 
     args.rval().setUndefined();
     return true;
 }
 
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
@@ -2897,19 +2904,25 @@ gc::ZealModeHelpText),
 "  because the object is a revoked proxy)."),
 
 #ifdef DEBUG
     JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
 "dumpStringRepresentation(str)",
 "  Print a human-readable description of how the string |str| is represented.\n"),
 #endif
 
-    JS_FN_HELP("setLazyParsingEnabled", SetLazyParsingEnabled, 1, 0,
-"setLazyParsingEnabled(bool)",
-"  Enable or disable lazy parsing in the current compartment.  The default is enabled."),
+    JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0,
+"setLazyParsingDisabled(bool)",
+"  Explicitly disable lazy parsing in the current compartment.  The default is that lazy "
+"  parsing is not explicitly disabled."),
+
+    JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0,
+"setDiscardSource(bool)",
+"  Explicitly enable source discarding in the current compartment.  The default is that "
+"  source discarding is not explicitly enabled."),
 
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1480,17 +1480,17 @@ OutlineTypedObject::createUnattachedWith
                clasp == &OutlineOpaqueTypedObject::class_);
 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
                                                              TaggedProto(&descr->typedProto()),
                                                              descr));
     if (!group)
         return nullptr;
 
-    NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
+    NewObjectKind newKind = (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
     OutlineTypedObject* obj = NewObjectWithGroup<OutlineTypedObject>(cx, group,
                                                                      gc::AllocKind::OBJECT0,
                                                                      newKind);
     if (!obj)
         return nullptr;
 
     obj->setOwnerAndData(nullptr, nullptr);
     return obj;
@@ -2114,17 +2114,17 @@ InlineTypedObject::create(JSContext* cx,
                          : &InlineTransparentTypedObject::class_;
 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
                                                              TaggedProto(&descr->typedProto()),
                                                              descr));
     if (!group)
         return nullptr;
 
-    NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
+    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)
 {
     Rooted<TypeDescr*> descr(cx, &templateObject->typeDescr());
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -142,16 +142,17 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
     return true;
 }
 
 static inline bool
 CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
 {
     return options.canLazilyParse &&
            !options.hasPollutedGlobalScope &&
+           !cx->compartment()->options().disableLazyParsing() &&
            !cx->compartment()->options().discardSource() &&
            !options.sourceIsLazy;
 }
 
 static void
 MarkFunctionsWithinEvalScript(JSScript* script)
 {
     // Mark top level functions in an eval script as being within an eval.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1227,17 +1227,17 @@ Parser<ParseHandler>::newFunction(Handle
         flags = JSFunction::INTERPRETED;
         break;
     }
     
     gc::AllocKind allocKind = JSFunction::FinalizeKind;
     if (kind == Arrow)
         allocKind = JSFunction::ExtendedFinalizeKind;
     fun = NewFunctionWithProto(context, nullptr, 0, flags, NullPtr(), atom, proto,
-                               allocKind, MaybeSingletonObject);
+                               allocKind, TenuredObject);
     if (!fun)
         return nullptr;
     if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
     return fun;
 }
 
 static bool
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -848,23 +848,16 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(markRelocatableCells);
     sb.markRelocatableCells(&trc);
     TIME_END(markRelocatableCells);
 
     TIME_START(markGenericEntries);
     sb.markGenericEntries(&trc);
     TIME_END(markGenericEntries);
 
-    TIME_START(checkHashTables);
-#ifdef JS_GC_ZEAL
-    if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
-        CheckHashTablesAfterMovingGC(rt);
-#endif
-    TIME_END(checkHashTables);
-
     TIME_START(markRuntime);
     rt->gc.markRuntime(&trc);
     TIME_END(markRuntime);
 
     TIME_START(markDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
@@ -910,16 +903,24 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(sweep);
     sweep();
     TIME_END(sweep);
 
     TIME_START(clearStoreBuffer);
     rt->gc.storeBuffer.clear();
     TIME_END(clearStoreBuffer);
 
+    // Make sure hashtables have been updated after the collection.
+    TIME_START(checkHashTables);
+#ifdef JS_GC_ZEAL
+    if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
+        CheckHashTablesAfterMovingGC(rt);
+#endif
+    TIME_END(checkHashTables);
+
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
         shrinkAllocableSpace();
     TIME_END(resize);
--- a/js/src/jit-test/tests/SIMD/swizzle.js
+++ b/js/src/jit-test/tests/SIMD/swizzle.js
@@ -55,17 +55,32 @@ function testBailouts(uglyDuckling) {
             caught = true;
             assertEq(i, 149);
             assertEq(e instanceof TypeError, true);
         }
         assertEq(i < 149 || caught, true);
     }
 }
 
+function testInt32x4SwizzleBailout() {
+    // Test out-of-bounds non-constant indices. This is expected to throw.
+    var i4 = SIMD.int32x4(1, 2, 3, 4);
+    for (var i = 0; i < 150; i++) {
+        assertEqX4(SIMD.int32x4.swizzle(i4, i, 3, 2, 0), [i + 1, 4, 3, 1]);
+    }
+}
+
 f();
 testBailouts(-1);
 testBailouts(4);
 testBailouts(2.5);
 testBailouts(undefined);
 testBailouts(null);
 testBailouts({});
 testBailouts('one');
 testBailouts(true);
+
+try {
+    testInt32x4SwizzleBailout();
+    throw 'not caught';
+} catch(e) {
+    assertEq(e instanceof TypeError, true);
+}
--- a/js/src/jit-test/tests/asm.js/testBug1147144.js
+++ b/js/src/jit-test/tests/asm.js/testBug1147144.js
@@ -1,14 +1,20 @@
 load(libdir + 'asm.js');
-setLazyParsingEnabled(false)
+load(libdir + 'asserts.js');
+
+if (!isAsmJSCompilationAvailable())
+    quit();
+
+setDiscardSource(true)
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toString(), "function asmModule() {\n    [sourceless code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toSource(), "(function asmModule() {\n    [sourceless code]\n})");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule1() { "use asm"; function asmFun() {} return asmFun }`), asmModule1).toString(), "function asmModule1() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule2() { "use asm"; function asmFun() {} return asmFun }`), asmModule2).toSource(), "function asmModule2() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule3() { "use asm"; function asmFun() {} return asmFun }`), asmModule3)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule4() { "use asm"; function asmFun() {} return asmFun }`), asmModule4)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`).toString(), "function anonymous() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`).toSource(), "(function anonymous() {\n    [sourceless code]\n})");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
+assertThrowsInstanceOf(()=>asmCompile('stdlib',USE_ASM + "var sin=stdlib.Math.sin;return {}")({Math:{}}), Error);
--- a/js/src/jit-test/tests/basic/eval-scopes.js
+++ b/js/src/jit-test/tests/basic/eval-scopes.js
@@ -17,17 +17,17 @@ function hasGname(f, v, hasIt = true) {
     } catch (e) {
 	print(e.stack);
 	throw e;
     }
 }
 
 var x = "outer";
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 {
     let x = "inner";
     eval("function g() { assertEq(x, 'inner');} g()");
     eval("function g2() { (function nest() { assertEq(x, 'inner'); })(); } g2()");
 }
 eval(`
      function g3() {
 	 assertEq(x, 'outer');
@@ -38,17 +38,17 @@ eval(`
 eval(`
      function g4() {
 	 function nest() { assertEq(x, 'outer'); }
 	 nest();
 	 return nest;
      }
      hasGname(g4(), 'x');
      `);
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 {
     let x = "inner";
     eval("function h() { assertEq(x, 'inner');} h()");
     eval("function h2() { (function nest() { assertEq(x, 'inner'); })(); } h2()");
 }
 // It sure would be nice if we could run the h3/h4 tests below, but it turns out
 // that lazy functions and eval don't play together all that well.  See bug
@@ -65,37 +65,37 @@ eval(`
      function h4() {
 	 function nest() { assertEq(x, 'outer'); }
 	 nest();
 	 return nest;
      }
      hasGname(h4(), 'x', false);
      `);
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 with ({}) {
     let x = "inner";
     eval("function i() { assertEq(x, 'inner');} i()");
     eval("function i2() { (function nest() { assertEq(x, 'inner'); })(); } i2()");
 }
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 with ({}) {
     let x = "inner";
     eval("function j() { assertEq(x, 'inner');} j()");
     eval("function j2() { (function nest() { assertEq(x, 'inner'); })(); } j2()");
 }
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 (function () {
     var x = "inner";
     eval("function k() { assertEq(x, 'inner');} k()");
     eval("function k2() { (function nest() { assertEq(x, 'inner'); })(); } k2()");
 })();
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 (function () {
     let x = "inner";
     eval("function l() { assertEq(x, 'inner');} l()");
     eval("function l2() { (function nest() { assertEq(x, 'inner'); })(); } l2()");
 })();
 
 var y1 = 5;
@@ -109,17 +109,17 @@ assertEq(y1, 5);
 
 eval(`
      'use strict';
      var y2 = 6;
      assertEq(y2, 6);
      (function() { assertEq(y2, 6); })()
      `);
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 
 var y3 = 5;
 eval(`
      'use strict';
      var y3 = 6;
      assertEq(y3, 6);
      (function() { assertEq(y3, 6); })()
      `);
@@ -127,9 +127,9 @@ assertEq(y3, 5);
 
 eval(`
      'use strict';
      var y4 = 6;
      assertEq(y4, 6);
      (function() { assertEq(y4, 6); })()
      `);
 
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
--- a/js/src/jit-test/tests/basic/function-gname.js
+++ b/js/src/jit-test/tests/basic/function-gname.js
@@ -21,29 +21,29 @@ function hasGname(f, v) {
 }
 
 var x = "outer";
 
 var f1 = new Function("assertEq(x, 'outer')");
 f1();
 hasGname(f1, 'x');
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 var f2 = new Function("assertEq(x, 'outer')");
 f2();
 hasGname(f2, 'x');
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 {
     let x = "inner";
     var f3 = new Function("assertEq(x, 'outer')");
     f3();
     hasGname(f3, 'x');
 }
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 {
     let x = "inner";
     var f4 = new Function("assertEq(x, 'outer')");
     f4();
     hasGname(f4, 'x');
 }
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1148383.js
@@ -0,0 +1,19 @@
+// This testcase tests setting object metadata for objects created from JIT
+// code.
+if (!("getJitCompilerOptions" in this))
+  quit();
+opts = getJitCompilerOptions();
+if (!opts['ion.enable'] || !opts['baseline.enable'])
+  quit();
+
+function TestCase() {}
+function reportCompare () {
+  var output = "";
+  var testcase = new TestCase();
+  testcase.reason = output;
+}
+reportCompare();
+gczeal(4, 1000);
+setObjectMetadataCallback(true);
+for (var i = 0; i < 10000; ++i)
+  reportCompare();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/bug-1149495.js
@@ -0,0 +1,6 @@
+try {
+  offThreadCompileScript('Error()', { lineNumber: (4294967295)});
+  runOffThreadScript().stack;
+} catch (e) {
+  // Ignore "Error: Can't use offThreadCompileScript with --no-threads"
+}
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1372,17 +1372,17 @@ BaselineCompiler::emit_JSOP_OBJECT()
 {
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
         if (!obj)
             return false;
 
         prepareVMCall();
 
-        pushArg(ImmWord(js::MaybeSingletonObject));
+        pushArg(ImmWord(TenuredObject));
         pushArg(ImmGCPtr(obj));
 
         if (!callVM(DeepCloneObjectLiteralInfo))
             return false;
 
         // Box and push return value.
         masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
         frame.push(R0);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -169,16 +169,58 @@ void
 ReceiverGuard::trace(JSTracer* trc)
 {
     if (shape_)
         TraceEdge(trc, &shape_, "receiver_guard_shape");
     else
         TraceEdge(trc, &group_, "receiver_guard_group");
 }
 
+ReceiverGuard::StackGuard::StackGuard(JSObject* obj)
+  : group(nullptr), shape(nullptr)
+{
+    if (obj) {
+        if (obj->is<UnboxedPlainObject>()) {
+            group = obj->group();
+            if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+                shape = expando->lastProperty();
+        } else if (obj->is<TypedObject>()) {
+            group = obj->group();
+        } else {
+            shape = obj->maybeShape();
+        }
+    }
+}
+
+ReceiverGuard::StackGuard::StackGuard(ObjectGroup* group, Shape* shape)
+  : group(group), shape(shape)
+{
+    if (group) {
+        if (IsTypedObjectClass(group->clasp()))
+            this->shape = nullptr;
+        else if (group->clasp() != &UnboxedPlainObject::class_)
+            this->group = nullptr;
+    }
+}
+
+/* static */ int32_t
+ReceiverGuard::keyBits(JSObject* obj)
+{
+    if (obj->is<UnboxedPlainObject>()) {
+        // Both the group and shape need to be guarded for unboxed plain objects.
+        return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
+    }
+    if (obj->is<TypedObject>()) {
+        // Only the group needs to be guarded for typed objects.
+        return 2;
+    }
+    // Other objects only need the shape to be guarded.
+    return 3;
+}
+
 /* static */ void
 ICStub::trace(JSTracer* trc)
 {
     markCode(trc, "baseline-stub-jitcode");
 
     // If the stub is a monitored fallback stub, then mark the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
@@ -3161,24 +3203,23 @@ ICUnaryArith_Double::Compiler::generateS
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
 // GetElem_Fallback
 //
 
-static void GetFixedOrDynamicSlotOffset(NativeObject* obj, uint32_t slot,
-                                        bool* isFixed, uint32_t* offset)
+static void GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset)
 {
     MOZ_ASSERT(isFixed);
     MOZ_ASSERT(offset);
-    *isFixed = obj->isFixedSlot(slot);
-    *offset = *isFixed ? NativeObject::getFixedSlotOffset(slot)
-                       : obj->dynamicSlotIndex(slot) * sizeof(Value);
+    *isFixed = shape->slot() < shape->numFixedSlots();
+    *offset = *isFixed ? NativeObject::getFixedSlotOffset(shape->slot())
+                       : (shape->slot() - shape->numFixedSlots()) * sizeof(Value);
 }
 
 static JSObject*
 GetDOMProxyProto(JSObject* obj)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     return obj->getTaggedProto().toObjectOrNull();
 }
@@ -3347,17 +3388,19 @@ CheckHasNoSuchProperty(JSContext* cx, Ha
 }
 
 static bool
 IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false)
 {
     MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj));
 
     if (!isDOMProxy && !obj->isNative()) {
-        if (obj == holder || !obj->is<UnboxedPlainObject>() || !obj->is<TypedObject>())
+        if (obj == holder)
+            return false;
+        if (!obj->is<UnboxedPlainObject>() && !obj->is<TypedObject>())
             return false;
     }
 
     // Don't handle objects which require a prototype guard. This should
     // be uncommon so handling it is likely not worth the complexity.
     if (obj->hasUncacheableProto())
         return false;
 
@@ -3423,66 +3466,75 @@ IsCacheableGetPropCall(JSContext* cx, JS
         *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     *isScripted = true;
     return true;
 }
 
-static bool
-IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, JSObject* holder, Shape* shape)
-{
-    if (!shape)
-        return false;
-
+static Shape*
+LastPropertyForSetProp(JSObject* obj)
+{
+    if (obj->isNative())
+        return obj->as<NativeObject>().lastProperty();
+
+    if (obj->is<UnboxedPlainObject>()) {
+        UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+        return expando ? expando->lastProperty() : nullptr;
+    }
+
+    return nullptr;
+}
+
+static bool
+IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, Shape* propertyShape)
+{
     // Object shape must not have changed during the property set.
-    if (!obj->isNative() || obj->as<NativeObject>().lastProperty() != oldShape)
-        return false;
-
-    // Currently we only optimize direct writes.
-    if (obj != holder)
-        return false;
-
-    if (!shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable())
-        return false;
+    if (LastPropertyForSetProp(obj) != oldShape)
+        return false;
+
+    if (!propertyShape->hasSlot() ||
+        !propertyShape->hasDefaultSetter() ||
+        !propertyShape->writable())
+    {
+        return false;
+    }
 
     return true;
 }
 
 static bool
 IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape,
-                          jsid id, JSObject* holder, Shape* shape,
-                          size_t* protoChainDepth)
-{
-    if (!shape)
-        return false;
-
-    // Property must be set directly on object, and be last added property of object.
-    if (!obj->isNative() || obj != holder || shape != obj->as<NativeObject>().lastProperty())
-        return false;
-
-    // Object must be extensible, oldShape must be immediate parent of curShape.
-    if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
+                          jsid id, Shape* propertyShape, size_t* protoChainDepth)
+{
+    // The property must be the last added property of the object.
+    if (LastPropertyForSetProp(obj) != propertyShape)
+        return false;
+
+    // Object must be extensible, oldShape must be immediate parent of current shape.
+    if (!obj->nonProxyIsExtensible() || propertyShape->previous() != oldShape)
         return false;
 
     // Basic shape checks.
-    if (shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter() ||
-        !shape->writable())
+    if (propertyShape->inDictionary() ||
+        !propertyShape->hasSlot() ||
+        !propertyShape->hasDefaultSetter() ||
+        !propertyShape->writable())
     {
         return false;
     }
 
     // If object has a resolve hook, don't inline
     if (obj->getClass()->resolve)
         return false;
 
     size_t chainDepth = 0;
-    // walk up the object prototype chain and ensure that all prototypes
-    // are native, and that all prototypes have setter defined on the property
+    // Walk up the object prototype chain and ensure that all prototypes are
+    // native, and that all prototypes have no setter defined on the property.
     for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) {
         chainDepth++;
         // if prototype is non-native, don't optimize
         if (!proto->isNative())
             return false;
 
         // if prototype defines this property in a non-plain way, don't optimize
         Shape* protoShape = proto->as<NativeObject>().lookup(cx, id);
@@ -3493,17 +3545,17 @@ IsCacheableSetPropAddSlot(JSContext* cx,
         // to be invoked and thus prevent inlining of property addition.
         if (proto->getClass()->resolve)
              return false;
     }
 
     // Only add a IC entry if the dynamic slots didn't change when the shapes
     // changed.  Need to ensure that a shape change for a subsequent object
     // won't involve reallocating the slot array.
-    if (NativeObject::dynamicSlotsCount(shape) != NativeObject::dynamicSlotsCount(oldShape))
+    if (NativeObject::dynamicSlotsCount(propertyShape) != NativeObject::dynamicSlotsCount(oldShape))
         return false;
 
     *protoChainDepth = chainDepth;
     return true;
 }
 
 static bool
 IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
@@ -3753,18 +3805,17 @@ TryAttachNativeGetValueElemStub(JSContex
         if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
         RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(),
-                                    shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
         ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot
                                             : ICStub::GetElem_NativePrototypeSlot;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
                                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                     (obj == holder) ? "direct" : "prototype",
@@ -5935,17 +5986,17 @@ TryAttachGlobalNameValueStub(JSContext* 
             uint32_t slot = shape->slot() - current->numFixedSlots();
 
             JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName) stub");
             ICGetName_Global::Compiler compiler(cx, monitorStub, current->lastProperty(), slot);
             newStub = compiler.getStub(compiler.getStubSpace(script));
         } else {
             bool isFixedSlot;
             uint32_t offset;
-            GetFixedOrDynamicSlotOffset(current, shape->slot(), &isFixedSlot, &offset);
+            GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
             if (!IsCacheableGetPropReadSlot(global, current, shape))
                 return true;
 
             JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName prototype) stub");
             ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_NativePrototype, false, monitorStub,
                                              global, current, name, isFixedSlot, offset,
                                              /* inputDefinitelyObject = */ true);
             newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -6058,18 +6109,17 @@ TryAttachScopeNameStub(JSContext* cx, Ha
     // IonBuilder::getPropTryCommonGetter (which requires a Baseline stub to
     // work) handles non-outerized this objects correctly.
 
     if (!IsCacheableGetPropReadSlot(scopeChain, scopeChain, shape))
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(&scopeChain->as<NativeObject>(),
-                                shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     ICStub* newStub;
 
     switch (shapes.length()) {
       case 1: {
         ICGetName_Scope<0>::Compiler compiler(cx, monitorStub, &shapes, isFixedSlot, offset);
         newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -6516,26 +6566,31 @@ UpdateExistingGenerationalDOMProxyStub(I
                 updateStub->setGeneration(generation);
                 return true;
             }
         }
     }
     return false;
 }
 
-static bool
-HasUnanalyzedNewScript(JSObject* obj)
+// Return whether obj is in some PreliminaryObjectArray and has a structure
+// that might change in the future.
+static bool
+IsPreliminaryObject(JSObject* obj)
 {
     if (obj->isSingleton())
         return false;
 
     TypeNewScript* newScript = obj->group()->newScript();
     if (newScript && !newScript->analyzed())
         return true;
 
+    if (obj->group()->maybePreliminaryObjects())
+        return true;
+
     return false;
 }
 
 static void
 StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub)
 {
     // Before the new script properties analysis has been performed on a type,
     // all instances of that type have the maximum number of fixed slots.
@@ -6578,34 +6633,34 @@ TryAttachNativeGetValuePropStub(JSContex
         return false;
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         // Instantiate this property for singleton holders, for use during Ion compilation.
         if (IsIonEnabled(cx))
             EnsureTrackPropertyTypes(cx, holder, NameToId(name));
 
         ICStub::Kind kind =
             (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetProp(Native %s) stub",
                     (obj == holder) ? "direct" : "prototype");
         ICGetPropNativeCompiler compiler(cx, kind, isCallProp, monitorStub, obj, holder,
                                          name, isFixedSlot, offset);
         ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
-        if (HasUnanalyzedNewScript(obj))
+        if (IsPreliminaryObject(obj))
             newStub->notePreliminaryObject();
         else
             StripPreliminaryObjectStubs(cx, stub);
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -6833,17 +6888,17 @@ TryAttachUnboxedExpandoGetPropStub(JSCon
         return true;
 
     Shape* shape = expando->lookup(cx, name);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
     ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, isCallProp, monitorStub, obj, obj,
                                      name, isFixedSlot, offset);
     ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
@@ -6930,17 +6985,17 @@ TryAttachPrimitiveGetPropStub(JSContext*
 
     // For now, only look for properties directly set on the prototype.
     RootedShape shape(cx, proto->lookup(cx, id));
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(proto, shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
     JitSpew(JitSpew_BaselineIC, "  Generating GetProp_Primitive stub");
     ICGetProp_Primitive::Compiler compiler(cx, monitorStub, primitiveType, proto,
                                            isFixedSlot, offset);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
@@ -8166,83 +8221,100 @@ BaselineScript::noteAccessedGetter(uint3
 // value property.
 static bool
 TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICSetProp_Fallback* stub,
                           HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
                           HandlePropertyName name, HandleId id, HandleValue rhs, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
-    if (!obj->isNative() || obj->watched())
+    if (obj->watched())
         return true;
 
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
         return false;
+    if (obj != holder)
+        return true;
+
+    if (!obj->isNative()) {
+        if (obj->is<UnboxedPlainObject>()) {
+            UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+            if (expando) {
+                shape = expando->lookup(cx, name);
+                if (!shape)
+                    return true;
+            } else {
+                return true;
+            }
+        } else {
+            return true;
+        }
+    }
 
     size_t chainDepth;
-    if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, holder, shape, &chainDepth)) {
+    if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, shape, &chainDepth)) {
         // Don't attach if proto chain depth is too high.
         if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
             return true;
 
         // Don't attach if we are adding a property to an object which the new
         // script properties analysis hasn't been performed for yet, as there
         // may be a shape change required here afterwards. Pretend we attached
         // a stub, though, so the access is not marked as unoptimizable.
         if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.ADD) stub");
         ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup,
                                             chainDepth, isFixedSlot, offset);
         ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
-    if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) {
+    if (IsCacheableSetPropWriteSlot(obj, oldShape, shape)) {
         // For some property writes, such as the initial overwrite of global
         // properties, TI will not mark the property as having been
         // overwritten. Don't attach a stub in this case, so that we don't
         // execute another write to the property without TI seeing that write.
         EnsureTrackPropertyTypes(cx, obj, id);
         if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
-        MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
+        MOZ_ASSERT(LastPropertyForSetProp(obj) == oldShape,
                    "Should this really be a SetPropWriteSlot?");
         ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
         ICSetProp_Native* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
-        if (HasUnanalyzedNewScript(obj))
+        if (IsPreliminaryObject(obj))
             newStub->notePreliminaryObject();
         else
             StripPreliminaryObjectStubs(cx, stub);
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -8439,16 +8511,21 @@ DoSetPropFallback(JSContext* cx, Baselin
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->maybeShape());
     RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
     if (!oldGroup)
         return false;
     ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj));
 
+    if (obj->is<UnboxedPlainObject>()) {
+        if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+            oldShape = expando->lastProperty();
+    }
+
     bool attached = false;
     // There are some reasons we can fail to attach a stub that are temporary.
     // We want to avoid calling noteUnoptimizableAccess() if the reason we
     // failed to attach a stub is one of those temporary reasons, since we might
     // end up attaching a stub for the exact same access later.
     bool isTemporarilyUnoptimizable = false;
     if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
         lhs.isObject() &&
@@ -8576,36 +8653,60 @@ bool
 ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code)
 {
     CodeOffsetLabel offset(returnOffset_);
     offset.fixup(&masm);
     cx->compartment()->jitCompartment()->initBaselineSetPropReturnAddr(code->raw() + offset.offset());
     return true;
 }
 
+static void
+GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj,
+                                      Register object, Register scratch,
+                                      size_t offsetOfGroup, size_t offsetOfShape, Label* failure)
+{
+    // Guard against object group.
+    masm.loadPtr(Address(BaselineStubReg, offsetOfGroup), scratch);
+    masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
+                   failure);
+
+    // Guard against shape or expando shape.
+    masm.loadPtr(Address(BaselineStubReg, offsetOfShape), scratch);
+    if (obj->is<UnboxedPlainObject>()) {
+        Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
+        masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
+        Label done;
+        masm.push(object);
+        masm.loadPtr(expandoAddress, object);
+        masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
+        masm.pop(object);
+        masm.jump(failure);
+        masm.bind(&done);
+        masm.pop(object);
+    } else {
+        masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
+    }
+}
+
 bool
 ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    Register objReg = masm.extractObject(R0, ExtractTemp0);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
 
-    // Unbox and shape guard.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-
-    // Guard that the object group matches.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
-                   &failure);
+    GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+                                          ICSetProp_Native::offsetOfGroup(),
+                                          ICSetProp_Native::offsetOfShape(),
+                                          &failure);
 
     // Stow both R0 and R1 (object and value).
     EmitStowICValues(masm, 2);
 
     // Type update stub expects the value to check in R0.
     masm.moveValue(R1, R0);
 
     // Call the type-update stub.
@@ -8614,17 +8715,23 @@ ICSetProp_Native::Compiler::generateStub
 
     // Unstow R0 and R1 (object and key)
     EmitUnstowICValues(masm, 2);
 
     regs.add(R0);
     regs.takeUnchecked(objReg);
 
     Register holderReg;
-    if (isFixedSlot_) {
+    if (obj_->is<UnboxedPlainObject>()) {
+        // We are loading off the expando object, so use that for the holder.
+        holderReg = regs.takeAny();
+        masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+        if (!isFixedSlot_)
+            masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
+    } else if (isFixedSlot_) {
         holderReg = objReg;
     } else {
         holderReg = regs.takeAny();
         masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
     }
 
     // Perform the store.
     masm.load32(Address(BaselineStubReg, ICSetProp_Native::offsetOfOffset()), scratch);
@@ -8679,29 +8786,25 @@ ICSetPropNativeAddCompiler::getStub(ICSt
 bool
 ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
     Label failureUnstow;
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    Register objReg = masm.extractObject(R0, ExtractTemp0);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
 
-    // Unbox and guard against old shape.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(0)), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-
-    // Guard that the object group matches.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
-                   &failure);
+    GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+                                          ICSetProp_NativeAdd::offsetOfGroup(),
+                                          ICSetProp_NativeAddImpl<0>::offsetOfShape(0),
+                                          &failure);
 
     // Stow both R0 and R1 (object and value).
     EmitStowICValues(masm, 2);
 
     regs = availableGeneralRegs(1);
     scratch = regs.takeAny();
     Register protoReg = regs.takeAny();
     // Check the proto chain.
@@ -8723,54 +8826,71 @@ ICSetPropNativeAddCompiler::generateStub
     if (!callTypeUpdateIC(masm, sizeof(Value)))
         return false;
 
     // Unstow R0 and R1 (object and key)
     EmitUnstowICValues(masm, 2);
     regs = availableGeneralRegs(2);
     scratch = regs.takeAny();
 
-    // Changing object shape.  Write the object's new shape.
-    Address shapeAddr(objReg, JSObject::offsetOfShape());
-    EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
-    masm.storePtr(scratch, shapeAddr);
-
-    // Try to change the object's group.
-    Label noGroupChange;
-
-    // Check if the cache has a new group to change to.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
-    masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
-
-    // Check if the old group still has a newScript.
-    masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::Equal,
-                   Address(scratch, ObjectGroup::offsetOfAddendum()),
-                   ImmWord(0),
-                   &noGroupChange);
-
-    // Reload the new group from the cache.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
-
-    // Change the object's group.
-    Address groupAddr(objReg, JSObject::offsetOfGroup());
-    EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
-    masm.storePtr(scratch, groupAddr);
-
-    masm.bind(&noGroupChange);
+    if (obj_->is<PlainObject>()) {
+        // Try to change the object's group.
+        Label noGroupChange;
+
+        // Check if the cache has a new group to change to.
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+        masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
+
+        // Check if the old group still has a newScript.
+        masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
+        masm.branchPtr(Assembler::Equal,
+                       Address(scratch, ObjectGroup::offsetOfAddendum()),
+                       ImmWord(0),
+                       &noGroupChange);
+
+        // Reload the new group from the cache.
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+
+        // Change the object's group.
+        Address groupAddr(objReg, JSObject::offsetOfGroup());
+        EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
+        masm.storePtr(scratch, groupAddr);
+
+        masm.bind(&noGroupChange);
+    }
 
     Register holderReg;
     regs.add(R0);
     regs.takeUnchecked(objReg);
-    if (isFixedSlot_) {
-        holderReg = objReg;
+
+    if (obj_->is<UnboxedPlainObject>()) {
+        holderReg = regs.takeAny();
+        masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+
+        // Write the expando object's new shape.
+        Address shapeAddr(holderReg, JSObject::offsetOfShape());
+        EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+        masm.storePtr(scratch, shapeAddr);
+
+        if (!isFixedSlot_)
+            masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
     } else {
-        holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+        // Write the object's new shape.
+        Address shapeAddr(objReg, JSObject::offsetOfShape());
+        EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+        masm.storePtr(scratch, shapeAddr);
+
+        if (isFixedSlot_) {
+            holderReg = objReg;
+        } else {
+            holderReg = regs.takeAny();
+            masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+        }
     }
 
     // Perform the store.  No write barrier required since this is a new
     // initialization.
     masm.load32(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfOffset()), scratch);
     masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
 
     if (holderReg != objReg)
@@ -9536,17 +9656,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
 
                     // This is temporary until the analysis is perfomed, so
                     // don't treat this as unoptimizable.
                     *handled = true;
                     return true;
                 }
             }
 
-            JSObject* thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
+            JSObject* thisObject = CreateThisForFunction(cx, fun, TenuredObject);
             if (!thisObject)
                 return false;
 
             if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
                 templateObject = thisObject;
         }
 
         JitSpew(JitSpew_BaselineIC,
@@ -12077,17 +12197,17 @@ ICSetProp_Native::ICSetProp_Native(JitCo
 
 ICSetProp_Native*
 ICSetProp_Native::Compiler::getStub(ICStubSpace* space)
 {
     RootedObjectGroup group(cx, obj_->getGroup(cx));
     if (!group)
         return nullptr;
 
-    RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
+    RootedShape shape(cx, LastPropertyForSetProp(obj_));
     ICSetProp_Native* stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
     if (!stub || !stub->initUpdatingChain(cx, space))
         return nullptr;
     return stub;
 }
 
 ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode* stubCode, ObjectGroup* group,
                                          size_t protoChainDepth,
@@ -12263,17 +12383,17 @@ ICCall_ScriptedFunCall::Clone(ICStubSpac
 ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitCode* stubCode,
                                                                  ICStub* firstMonitorStub,
                                                                  Shape* shape,
                                                                  Shape* expandoShape,
                                                                  JSObject* holder,
                                                                  Shape* holderShape,
                                                                  JSFunction* getter,
                                                                  uint32_t pcOffset)
-  : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(shape),
+  : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(nullptr, shape),
                         holder, holderShape, getter, pcOffset),
     expandoShape_(expandoShape)
 { }
 
 ICGetPropCallDOMProxyNativeCompiler::ICGetPropCallDOMProxyNativeCompiler(JSContext* cx,
                                                                          ICStub::Kind kind,
                                                                          ICStub* firstMonitorStub,
                                                                          Handle<ProxyObject*> proxy,
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3868,49 +3868,37 @@ class ReceiverGuard
         {}
     };
 
     struct StackGuard
     {
         ObjectGroup* group;
         Shape* shape;
 
+        StackGuard()
+          : group(nullptr), shape(nullptr)
+        {}
+
         MOZ_IMPLICIT StackGuard(const ReceiverGuard& guard)
           : group(guard.group_), shape(guard.shape_)
         {}
 
         MOZ_IMPLICIT StackGuard(const RootedStackGuard& guard)
           : group(guard.group), shape(guard.shape)
         {}
 
-        explicit StackGuard(JSObject* obj)
-          : group(nullptr), shape(nullptr)
-        {
-            if (obj) {
-                if (obj->is<UnboxedPlainObject>()) {
-                    group = obj->group();
-                    if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
-                        shape = expando->lastProperty();
-                } else if (obj->is<TypedObject>()) {
-                    group = obj->group();
-                } else {
-                    shape = obj->maybeShape();
-                }
-            }
+        explicit StackGuard(JSObject* obj);
+        StackGuard(ObjectGroup* group, Shape* shape);
+
+        bool operator ==(const StackGuard& other) const {
+            return group == other.group && shape == other.shape;
         }
 
-        explicit StackGuard(Shape* shape)
-          : group(nullptr), shape(shape)
-        {}
-
-        Shape* ownShape() const {
-            // Get a shape belonging to the object itself, rather than an unboxed expando.
-            if (!group || !group->maybeUnboxedLayout())
-                return shape;
-            return nullptr;
+        bool operator !=(const StackGuard& other) const {
+            return !(*this == other);
         }
     };
 
     explicit ReceiverGuard(const StackGuard& guard)
       : group_(guard.group), shape_(guard.shape)
     {}
 
     bool matches(const StackGuard& guard) {
@@ -3926,42 +3914,26 @@ class ReceiverGuard
 
     Shape* shape() const {
         return shape_;
     }
     ObjectGroup* group() const {
         return group_;
     }
 
-    Shape* ownShape() const {
-        return StackGuard(*this).ownShape();
-    }
-
     static size_t offsetOfShape() {
         return offsetof(ReceiverGuard, shape_);
     }
     static size_t offsetOfGroup() {
         return offsetof(ReceiverGuard, group_);
     }
 
     // Bits to munge into IC compiler keys when that IC has a ReceiverGuard.
-    // This uses at two bits for data.
-    static int32_t keyBits(JSObject* obj) {
-        if (obj->is<UnboxedPlainObject>()) {
-            // Both the group and shape need to be guarded for unboxed objects.
-            return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
-        }
-        if (obj->is<TypedObject>()) {
-            // Only the group needs to be guarded for typed objects.
-            return 2;
-        }
-        // Other objects only need the shape to be guarded, except for ICs
-        // which always guard the group.
-        return 3;
-    }
+    // This uses at most two bits for data.
+    static int32_t keyBits(JSObject* obj);
 };
 
 // Base class for native GetProp stubs.
 class ICGetPropNativeStub : public ICMonitoredStub
 {
     // Object shape/group.
     ReceiverGuard receiverGuard_;
 
@@ -4802,17 +4774,19 @@ class ICSetProp_Native : public ICUpdate
 
     class Compiler : public ICStubCompiler {
         RootedObject obj_;
         bool isFixedSlot_;
         uint32_t offset_;
 
       protected:
         virtual int32_t getKey() const {
-            return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16);
+            return static_cast<int32_t>(kind) |
+                   (static_cast<int32_t>(isFixedSlot_) << 16) |
+                   (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17);
         }
 
         bool generateStubCode(MacroAssembler& masm);
 
       public:
         Compiler(JSContext* cx, HandleObject obj, bool isFixedSlot, uint32_t offset)
           : ICStubCompiler(cx, ICStub::SetProp_Native),
             obj_(cx, obj),
@@ -4904,18 +4878,20 @@ class ICSetPropNativeAddCompiler : publi
     RootedShape oldShape_;
     RootedObjectGroup oldGroup_;
     size_t protoChainDepth_;
     bool isFixedSlot_;
     uint32_t offset_;
 
   protected:
     virtual int32_t getKey() const {
-        return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16) |
-               (static_cast<int32_t>(protoChainDepth_) << 20);
+        return static_cast<int32_t>(kind) |
+               (static_cast<int32_t>(isFixedSlot_) << 16) |
+               (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17) |
+               (static_cast<int32_t>(protoChainDepth_) << 18);
     }
 
     bool generateStubCode(MacroAssembler& masm);
 
   public:
     ICSetPropNativeAddCompiler(JSContext* cx, HandleObject obj,
                                HandleShape oldShape, HandleObjectGroup oldGroup,
                                size_t protoChainDepth, bool isFixedSlot, uint32_t offset);
@@ -4928,17 +4904,21 @@ class ICSetPropNativeAddCompiler : publi
             return nullptr;
 
         // Only specify newGroup when the object's group changes due to the
         // object becoming fully initialized per the acquired properties
         // analysis.
         if (newGroup == oldGroup_)
             newGroup = nullptr;
 
-        RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
+        RootedShape newShape(cx);
+        if (obj_->isNative())
+            newShape = obj_->as<NativeObject>().lastProperty();
+        else
+            newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
 
         return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
                     space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
     }
 
     ICUpdatedStub* getStub(ICStubSpace* space);
 };
 
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -85,92 +85,80 @@ VectorAppendNoDuplicate(S& list, T value
 {
     for (size_t i = 0; i < list.length(); i++) {
         if (list[i] == value)
             return true;
     }
     return list.append(value);
 }
 
+static bool
+AddReceiver(const ReceiverGuard::StackGuard& receiver,
+            BaselineInspector::ReceiverVector& receivers,
+            BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
+{
+    if (receiver.group && receiver.group->maybeUnboxedLayout()) {
+        if (receiver.group->unboxedLayout().nativeGroup())
+            return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
+    }
+    return VectorAppendNoDuplicate(receivers, receiver);
+}
+
 bool
-BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc,
-                                          ShapeVector& nativeShapes,
-                                          ObjectGroupVector& unboxedGroups,
+BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                           ObjectGroupVector& convertUnboxedGroups)
 {
-    // Return lists of native shapes and unboxed objects seen by the baseline
-    // IC for the current op. Empty lists indicate no shapes/types are known,
-    // or there was an uncacheable access. convertUnboxedGroups is used for
-    // unboxed object groups which have been seen, but have had instances
-    // converted to native objects and should be eagerly converted by Ion.
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    // Return a list of the receivers seen by the baseline IC for the current
+    // op. Empty lists indicate no receivers are known, or there was an
+    // uncacheable access. convertUnboxedGroups is used for unboxed object
+    // groups which have been seen, but have had instances converted to native
+    // objects and should be eagerly converted by Ion.
+    MOZ_ASSERT(receivers.empty());
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     if (!hasBaselineScript())
         return true;
 
     MOZ_ASSERT(isValidPC(pc));
     const ICEntry& entry = icEntryFromPC(pc);
 
     ICStub* stub = entry.firstStub();
     while (stub->next()) {
-        Shape* shape = nullptr;
-        ObjectGroup* group = nullptr;
+        ReceiverGuard::StackGuard receiver;
         if (stub->isGetProp_Native()) {
-            shape = stub->toGetProp_Native()->receiverGuard().ownShape();
+            receiver = stub->toGetProp_Native()->receiverGuard();
         } else if (stub->isSetProp_Native()) {
-            shape = stub->toSetProp_Native()->shape();
+            receiver = ReceiverGuard::StackGuard(stub->toSetProp_Native()->group(),
+                                                 stub->toSetProp_Native()->shape());
         } else if (stub->isGetProp_Unboxed()) {
-            group = stub->toGetProp_Unboxed()->group();
+            receiver = ReceiverGuard::StackGuard(stub->toGetProp_Unboxed()->group(), nullptr);
         } else if (stub->isSetProp_Unboxed()) {
-            group = stub->toSetProp_Unboxed()->group();
-        }
-
-        if (!shape && !group) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
+            receiver = ReceiverGuard::StackGuard(stub->toSetProp_Unboxed()->group(), nullptr);
+        } else {
+            receivers.clear();
             return true;
         }
 
-        if (group && group->unboxedLayout().nativeGroup()) {
-            if (!VectorAppendNoDuplicate(convertUnboxedGroups, group))
-                return false;
-            shape = group->unboxedLayout().nativeShape();
-            group = nullptr;
-        }
-
-        if (shape) {
-            if (!VectorAppendNoDuplicate(nativeShapes, shape))
-                return false;
-        } else {
-            if (!VectorAppendNoDuplicate(unboxedGroups, group))
-                return false;
-        }
+        if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
+            return false;
 
         stub = stub->next();
     }
 
     if (stub->isGetProp_Fallback()) {
-        if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
-        }
+        if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
+            receivers.clear();
     } else {
-        if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
-        }
+        if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
+            receivers.clear();
     }
 
-    // Don't inline if there are more than 5 shapes/groups.
-    if (nativeShapes.length() + unboxedGroups.length() > 5) {
-        nativeShapes.clear();
-        unboxedGroups.clear();
-    }
+    // Don't inline if there are more than 5 receivers.
+    if (receivers.length() > 5)
+        receivers.clear();
 
     return true;
 }
 
 ICStub*
 BaselineInspector::monomorphicStub(jsbytecode* pc)
 {
     if (!hasBaselineScript())
@@ -584,66 +572,39 @@ GlobalShapeForGetPropFunction(ICStub* st
         if (Shape* shape = guard.shape()) {
             if (shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
                 return shape;
         }
     }
     return nullptr;
 }
 
-static bool
-AddReceiver(BaselineInspector::ShapeVector& nativeShapes,
-            BaselineInspector::ObjectGroupVector& unboxedGroups,
-            ReceiverGuard::StackGuard receiver)
-{
-    if (Shape* shape = receiver.ownShape())
-        return VectorAppendNoDuplicate(nativeShapes, shape);
-
-    // Only unboxed objects with no expandos are handled by the common
-    // getprop/setprop optimizations.
-    if (!receiver.shape)
-        return VectorAppendNoDuplicate(unboxedGroups, receiver.group);
-
-    return false;
-}
-
-static bool
-AddReceiverForGetPropFunction(BaselineInspector::ShapeVector& nativeShapes,
-                              BaselineInspector::ObjectGroupVector& unboxedGroups,
-                              ICGetPropCallGetter* stub)
-{
-    if (stub->isOwnGetter())
-        return true;
-
-    return AddReceiver(nativeShapes, unboxedGroups, stub->receiverGuard());
-}
-
 bool
 BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonGetter, Shape** globalShape,
                                          bool* isOwnProperty,
-                                         ShapeVector& nativeShapes,
-                                         ObjectGroupVector& unboxedGroups)
+                                         ReceiverVector& receivers,
+                                         ObjectGroupVector& convertUnboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    MOZ_ASSERT(receivers.empty());
+    MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isGetProp_CallScripted() ||
             stub->isGetProp_CallNative())
         {
             ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
             bool isOwn = nstub->isOwnGetter();
-            if (!AddReceiverForGetPropFunction(nativeShapes, unboxedGroups, nstub))
+            if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonGetter = nstub->getter();
                 *globalShape = GlobalShapeForGetPropFunction(nstub);
                 *isOwnProperty = isOwn;
@@ -665,39 +626,39 @@ BaselineInspector::commonGetPropFunction
         } else {
             return false;
         }
     }
 
     if (!*holder)
         return false;
 
-    MOZ_ASSERT(*isOwnProperty == (nativeShapes.empty() && unboxedGroups.empty()));
+    MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
     return true;
 }
 
 bool
 BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonSetter, bool* isOwnProperty,
-                                         ShapeVector& nativeShapes,
-                                         ObjectGroupVector& unboxedGroups)
+                                         ReceiverVector& receivers,
+                                         ObjectGroupVector& convertUnboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    MOZ_ASSERT(receivers.empty());
+    MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
             ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
-            if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard()))
+            if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonSetter = nstub->setter();
                 *isOwnProperty = false;
             } else if (nstub->holderShape() != *holderShape) {
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -87,21 +87,19 @@ class BaselineInspector
         }
         return ICInspectorType(this, pc, ent);
     }
 
     ICStub* monomorphicStub(jsbytecode* pc);
     bool dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond);
 
   public:
-    typedef Vector<Shape*, 4, JitAllocPolicy> ShapeVector;
+    typedef Vector<ReceiverGuard::StackGuard, 4, JitAllocPolicy> ReceiverVector;
     typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector;
-    bool maybeInfoForPropertyOp(jsbytecode* pc,
-                                ShapeVector& nativeShapes,
-                                ObjectGroupVector& unboxedGroups,
+    bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                 ObjectGroupVector& convertUnboxedGroups);
 
     SetElemICInspector setElemICInspector(jsbytecode* pc) {
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
 
     MIRType expectedResultType(jsbytecode* pc);
     MCompare::CompareType expectedCompareType(jsbytecode* pc);
@@ -121,20 +119,20 @@ class BaselineInspector
 
     JSFunction* getSingleCallee(jsbytecode* pc);
 
     DeclEnvObject* templateDeclEnvObject();
     CallObject* templateCallObject();
 
     bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
-                               ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups);
+                               ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups);
     bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                JSFunction** commonSetter, bool* isOwnProperty,
-                               ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups);
+                               ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups);
 
     bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineInspector_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1945,17 +1945,17 @@ CodeGenerator::visitTableSwitchV(LTableS
 
 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 void
 CodeGenerator::visitCloneLiteral(LCloneLiteral* lir)
 {
-    pushArg(ImmWord(js::MaybeSingletonObject));
+    pushArg(ImmWord(TenuredObject));
     pushArg(ToRegister(lir->getObjectLiteral()));
     callVM(DeepCloneObjectLiteralInfo, lir);
 }
 
 void
 CodeGenerator::visitParameter(LParameter* lir)
 {
 }
@@ -2256,79 +2256,80 @@ CodeGenerator::visitStoreSlotV(LStoreSlo
     const ValueOperand value = ToValue(lir, LStoreSlotV::Value);
 
     if (lir->mir()->needsBarrier())
        emitPreBarrier(Address(base, offset));
 
     masm.storeValue(value, Address(base, offset));
 }
 
+static void
+GuardReceiver(MacroAssembler& masm, const ReceiverGuard::StackGuard& guard,
+              Register obj, Register scratch, Label* miss, bool checkNullExpando)
+{
+    if (guard.group) {
+        masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss);
+
+        Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando());
+        if (guard.shape) {
+            masm.loadPtr(expandoAddress, scratch);
+            masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss);
+            masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss);
+        } else if (checkNullExpando) {
+            masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss);
+        }
+    } else {
+        masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss);
+    }
+}
+
 void
 CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch,
                                           const TypedOrValueRegister& output)
 {
     MGetPropertyPolymorphic* mir = ins->mirRaw()->toGetPropertyPolymorphic();
 
-    size_t total = mir->numUnboxedGroups() + mir->numShapes();
-    MOZ_ASSERT(total > 1);
-
-    bool groupInScratch = mir->numUnboxedGroups() > 1;
-    bool shapeInScratch = mir->numShapes() > 1;
-
     Label done;
 
-    for (size_t i = 0; i < total; i++) {
-        bool unboxedGroup = i < mir->numUnboxedGroups();
-
-        ImmGCPtr comparePtr = unboxedGroup
-                              ? ImmGCPtr(mir->unboxedGroup(i))
-                              : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups()));
-        Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape());
-
-        if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch))
-            masm.loadPtr(addr, scratch);
-
-        bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch;
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        ReceiverGuard::StackGuard receiver = mir->receiver(i);
 
         Label next;
-        if (i == total - 1) {
-            if (inScratch)
-                bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
-            else
-                bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
-        } else {
-            if (inScratch)
-                masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
-            else
-                masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
-        }
-
-        if (unboxedGroup) {
-            const UnboxedLayout::Property* property =
-                mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name());
-            Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
-
-            masm.loadUnboxedProperty(propertyAddr, property->type, output);
-        } else {
-            Shape* shape = mir->shape(i - mir->numUnboxedGroups());
+        GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+
+        if (receiver.shape) {
+            // If this is an unboxed expando access, GuardReceiver loaded the
+            // expando object into scratch.
+            Register target = receiver.group ? scratch : obj;
+
+            Shape* shape = mir->shape(i);
             if (shape->slot() < shape->numFixedSlots()) {
                 // Fixed slot.
-                masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())),
+                masm.loadTypedOrValue(Address(target, NativeObject::getFixedSlotOffset(shape->slot())),
                                       output);
             } else {
                 // Dynamic slot.
                 uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value);
-                masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
+                masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch);
                 masm.loadTypedOrValue(Address(scratch, offset), output);
             }
+        } else {
+            const UnboxedLayout::Property* property =
+                receiver.group->unboxedLayout().lookup(mir->name());
+            Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
+
+            masm.loadUnboxedProperty(propertyAddr, property->type, output);
         }
 
-        if (i != total - 1)
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, ins->snapshot());
+        } else {
             masm.jump(&done);
-        masm.bind(&next);
+            masm.bind(&next);
+        }
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV* ins)
 {
@@ -2349,83 +2350,64 @@ CodeGenerator::visitGetPropertyPolymorph
 }
 
 void
 CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch,
                                           const ConstantOrRegister& value)
 {
     MSetPropertyPolymorphic* mir = ins->mirRaw()->toSetPropertyPolymorphic();
 
-    size_t total = mir->numUnboxedGroups() + mir->numShapes();
-    MOZ_ASSERT(total > 1);
-
-    bool groupInScratch = mir->numUnboxedGroups() > 1;
-    bool shapeInScratch = mir->numShapes() > 1;
-
     Label done;
-    for (size_t i = 0; i < total; i++) {
-        bool unboxedGroup = i < mir->numUnboxedGroups();
-
-        ImmGCPtr comparePtr = unboxedGroup
-                              ? ImmGCPtr(mir->unboxedGroup(i))
-                              : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups()));
-        Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape());
-
-        if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch))
-            masm.loadPtr(addr, scratch);
-
-        bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch;
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        ReceiverGuard::StackGuard receiver = mir->receiver(i);
 
         Label next;
-        if (i == total - 1) {
-            if (inScratch)
-                bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
-            else
-                bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
+        GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+
+        if (receiver.shape) {
+            // If this is an unboxed expando access, GuardReceiver loaded the
+            // expando object into scratch.
+            Register target = receiver.group ? scratch : obj;
+
+            Shape* shape = mir->shape(i);
+            if (shape->slot() < shape->numFixedSlots()) {
+                // Fixed slot.
+                Address addr(target, NativeObject::getFixedSlotOffset(shape->slot()));
+                if (mir->needsBarrier())
+                    emitPreBarrier(addr);
+                masm.storeConstantOrRegister(value, addr);
+            } else {
+                // Dynamic slot.
+                masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch);
+                Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
+                if (mir->needsBarrier())
+                    emitPreBarrier(addr);
+                masm.storeConstantOrRegister(value, addr);
+            }
         } else {
-            if (inScratch)
-                masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
-            else
-                masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
-        }
-
-        if (unboxedGroup) {
             const UnboxedLayout::Property* property =
-                mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name());
+                receiver.group->unboxedLayout().lookup(mir->name());
             Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
 
             if (property->type == JSVAL_TYPE_OBJECT)
                 masm.patchableCallPreBarrier(propertyAddr, MIRType_Object);
             else if (property->type == JSVAL_TYPE_STRING)
                 masm.patchableCallPreBarrier(propertyAddr, MIRType_String);
             else
                 MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type));
 
             masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr);
+        }
+
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, ins->snapshot());
         } else {
-            Shape* shape = mir->shape(i - mir->numUnboxedGroups());
-            if (shape->slot() < shape->numFixedSlots()) {
-                // Fixed slot.
-                Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot()));
-                if (mir->needsBarrier())
-                    emitPreBarrier(addr);
-                masm.storeConstantOrRegister(value, addr);
-            } else {
-                // Dynamic slot.
-                masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
-                Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
-                if (mir->needsBarrier())
-                    emitPreBarrier(addr);
-                masm.storeConstantOrRegister(value, addr);
-            }
+            masm.jump(&done);
+            masm.bind(&next);
         }
-
-        if (i != total - 1)
-            masm.jump(&done);
-        masm.bind(&next);
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins)
 {
@@ -2543,52 +2525,57 @@ CodeGenerator::visitGuardObjectIdentity(
 
 void
 CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir)
 {
     const MGuardReceiverPolymorphic* mir = lir->mir();
     Register obj = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
 
-    MOZ_ASSERT(mir->numShapes() + mir->numUnboxedGroups() > 1);
-
     Label done;
 
-    if (mir->numShapes()) {
-        masm.loadObjShape(obj, temp);
-
-        for (size_t i = 0; i < mir->numShapes(); i++) {
-            Shape* shape = mir->getShape(i);
-            if (i == mir->numShapes() - 1 && !mir->numUnboxedGroups())
-                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), lir->snapshot());
-            else
-                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
-        }
-    }
-
-    if (mir->numUnboxedGroups()) {
-        // The guard requires that unboxed objects not have expandos.
-        bailoutCmpPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfShape()),
-                      ImmWord(0), lir->snapshot());
-
-        masm.loadObjGroup(obj, temp);
-
-        for (size_t i = 0; i < mir->numUnboxedGroups(); i++) {
-            ObjectGroup* group = mir->getUnboxedGroup(i);
-            if (i == mir->numUnboxedGroups() - 1)
-                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(group), lir->snapshot());
-            else
-                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(group), &done);
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        const ReceiverGuard::StackGuard& receiver = mir->receiver(i);
+
+        Label next;
+        GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true);
+
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, lir->snapshot());
+        } else {
+            masm.jump(&done);
+            masm.bind(&next);
         }
     }
 
     masm.bind(&done);
 }
 
 void
+CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir)
+{
+    Label miss;
+
+    Register obj = ToRegister(lir->object());
+    masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual,
+                   Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss);
+
+    bailoutFrom(&miss, lir->snapshot());
+}
+
+void
+CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register result = ToRegister(lir->getDef(0));
+
+    masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result);
+}
+
+void
 CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
     Register scratch = ToTempRegisterOrInvalid(lir->temp());
 
     Label miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(), scratch, &miss);
     bailoutFrom(&miss, lir->snapshot());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -118,16 +118,18 @@ class CodeGenerator : public CodeGenerat
     void visitStoreSlotT(LStoreSlotT* lir);
     void visitStoreSlotV(LStoreSlotV* lir);
     void visitElements(LElements* lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir);
     void visitMaybeToDoubleElement(LMaybeToDoubleElement* lir);
     void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir);
     void visitGuardObjectIdentity(LGuardObjectIdentity* guard);
     void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
+    void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
+    void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
     void visitTypeBarrierV(LTypeBarrierV* lir);
     void visitTypeBarrierO(LTypeBarrierO* lir);
     void visitMonitorTypes(LMonitorTypes* lir);
     void visitPostWriteBarrierO(LPostWriteBarrierO* lir);
     void visitPostWriteBarrierV(LPostWriteBarrierV* lir);
     void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
     void visitCallNative(LCallNative* call);
     void emitCallInvokeFunction(LInstruction* call, Register callereg,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10588,66 +10588,68 @@ IonBuilder::getPropTryUnboxed(bool* emit
 
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
 
 MDefinition*
 IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
-                const BaselineInspector::ShapeVector& receiverShapes,
-                const BaselineInspector::ObjectGroupVector& receiverUnboxedGroups,
+                const BaselineInspector::ReceiverVector& receivers,
+                const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
                 bool isOwnProperty)
 {
     MOZ_ASSERT(holder);
     MOZ_ASSERT(holderShape);
 
+    obj = convertUnboxedObjects(obj, convertUnboxedGroups);
+
     if (isOwnProperty) {
-        MOZ_ASSERT(receiverShapes.empty());
+        MOZ_ASSERT(receivers.empty());
         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
     }
 
     MDefinition* holderDef = constantMaybeNursery(holder);
     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
 
-    return addGuardReceiverPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
+    return addGuardReceiverPolymorphic(obj, receivers);
 }
 
 bool
 IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                    TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     Shape* lastProperty = nullptr;
     JSFunction* commonGetter = nullptr;
     Shape* globalShape = nullptr;
     JSObject* foundProto = nullptr;
     bool isOwnProperty = false;
-    BaselineInspector::ShapeVector receiverShapes(alloc());
-    BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc());
+    BaselineInspector::ReceiverVector receivers(alloc());
+    BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
     if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter,
                                           &globalShape, &isOwnProperty,
-                                          receiverShapes, receiverUnboxedGroups))
+