Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 26 Nov 2015 16:56:39 +0100
changeset 308475 b6daad4d568f47988524d22c17ca723a8c727d27
parent 308474 2b1a10c958156968bc4842471ffa9c919ee80e92 (current diff)
parent 308472 74c7941a9e22d50057800771ebae07f69deecc9f (diff)
child 308476 edbb1fc67a7e598fc799f531c2dfb57466bd1d61
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone45.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 mozilla-central to b2g-inbound
browser/base/content/test/general/bug_1182546.xml
browser/base/content/test/general/test_bug1182546.html
dom/contacts/tests/contacts_chromescript.js
js/src/asmjs/AsmJSGlobals.h
js/xpconnect/tests/components/js/Makefile.in
js/xpconnect/tests/components/js/moz.build
js/xpconnect/tests/components/native/Makefile.in
--- a/addon-sdk/source/lib/sdk/base64.js
+++ b/addon-sdk/source/lib/sdk/base64.js
@@ -33,11 +33,11 @@ exports.decode = function (data, charset
 
   return atob(data);
 }
 
 exports.encode = function (data, charset) {
   if (isUTF8(charset))
     return btoa(unescape(encodeURIComponent(data)))
 
-  data = String.fromCharCode(...[(c.charCodeAt(0) & 0xff) for (c of data)]);
+  data = String.fromCharCode(...Array.from(data, c => (c.charCodeAt(0) & 0xff)));
   return btoa(data);
 }
--- a/addon-sdk/source/lib/sdk/content/context-menu.js
+++ b/addon-sdk/source/lib/sdk/content/context-menu.js
@@ -302,17 +302,17 @@ function getItemWorkerForWindow(item, wi
 }
 
 // A very simple remote proxy for every item. It's job is to provide data for
 // the main process to use to determine visibility state and to call into
 // content scripts when clicked.
 var RemoteItem = Class({
   initialize: function(options, manager) {
     this.id = options.id;
-    this.contexts = [instantiateContext(c) for (c of options.contexts)];
+    this.contexts = options.contexts.map(instantiateContext);
     this.contentScript = options.contentScript;
     this.contentScriptFile = options.contentScriptFile;
 
     this.manager = manager;
 
     this.workerMap = new Map();
     keepAlive.set(this.id, this);
   },
--- a/addon-sdk/source/lib/sdk/content/utils.js
+++ b/addon-sdk/source/lib/sdk/content/utils.js
@@ -85,17 +85,17 @@ function WorkerHost (workerFor) {
   }
 }
 exports.WorkerHost = WorkerHost;
 
 function makeChildOptions(options) {
   function makeStringArray(arrayOrValue) {
     if (!arrayOrValue)
       return [];
-    return [String(v) for (v of [].concat(arrayOrValue))];
+    return [].concat(arrayOrValue).map(String);
   }
 
   return {
     id: String(uuid()),
     contentScript: makeStringArray(options.contentScript),
     contentScriptFile: makeStringArray(options.contentScriptFile),
     contentScriptOptions: options.contentScriptOptions ?
                           JSON.stringify(options.contentScriptOptions) :
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -350,17 +350,17 @@ function itemActivated(item, clickedNode
   let process = processes.getById(lastContextProcessId);
   if (process)
     process.port.emit('sdk/contextmenu/activateitems', items, data);
 }
 
 function serializeItem(item) {
   return {
     id: internal(item).id,
-    contexts: [c.serialize() for (c of item.context)],
+    contexts: item.context.map(c => c.serialize()),
     contentScript: item.contentScript,
     contentScriptFile: item.contentScriptFile,
   };
 }
 
 // All things that appear in the context menu extend this
 var BaseItem = Class({
   initialize: function initialize() {
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -375,17 +375,17 @@ TestRunner.prototype = {
         this.pass("There is a clean UI.");
       });
     }).
     then(() => {
       this.testRunSummary.push({
         name: this.test.name,
         passed: this.test.passed,
         failed: this.test.failed,
-        errors: [error for (error in this.test.errors)].join(", ")
+        errors: Object.keys(this.test.errors).join(", ")
       });
 
       if (this.onDone !== null) {
         let onDone = this.onDone;
         this.onDone = null;
         timer.setTimeout(_ => onDone(this));
       }
     }).
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -188,17 +188,17 @@ function showResults() {
   if (gWeakrefInfo) {
     gWeakrefInfo.forEach(
       function(info) {
         var ref = info.weakref.get();
         if (ref !== null) {
           var data = ref.__url__ ? ref.__url__ : ref;
           var warning = data == "[object Object]"
             ? "[object " + data.constructor.name + "(" +
-              [p for (p in data)].join(", ") + ")]"
+              Object.keys(data).join(", ") + ")]"
             : data;
           console.warn("LEAK", warning, info.bin);
         }
       }
     );
   }
 
   onDone(results);
@@ -456,18 +456,17 @@ var consoleListener = {
     if (!(object instanceof Ci.nsIScriptError))
       return;
     this.errorsLogged++;
     var message = object.QueryInterface(Ci.nsIConsoleMessage).message;
     if (IMPORTANT_ERRORS.find(msg => message.indexOf(msg) >= 0)) {
       testConsole.error(message);
       return;
     }
-    var pointless = [err for (err of POINTLESS_ERRORS)
-                         if (message.indexOf(err) >= 0)];
+    var pointless = POINTLESS_ERRORS.filter(err => message.indexOf(err) >= 0);
     if (pointless.length == 0 && message)
       testConsole.log(message);
   }
 };
 
 function TestRunnerConsole(base, options) {
   let proto = extend(base, {
     errorsLogged: 0,
--- a/addon-sdk/source/lib/sdk/timers.js
+++ b/addon-sdk/source/lib/sdk/timers.js
@@ -57,17 +57,17 @@ var immediates = new Map();
 
 var dispatcher = _ => {
   // Allow scheduling of a new dispatch loop.
   dispatcher.scheduled = false;
   // Take a snapshot of timer `id`'s that have being present before
   // starting a dispatch loop, in order to ignore timers registered
   // in side effect to dispatch while also skipping immediates that
   // were removed in side effect.
-  let ids = [id for ([id] of immediates)];
+  let ids = [...immediates.keys()];
   for (let id of ids) {
     let immediate = immediates.get(id);
     if (immediate) {
       immediates.delete(id);
       try { immediate(); }
       catch (error) { console.exception(error); }
     }
   }
--- a/addon-sdk/source/test/test-file.js
+++ b/addon-sdk/source/test/test-file.js
@@ -60,22 +60,19 @@ exports.testBasename = function(assert) 
 
   path = file.join(path, "bar");
   assert.equal(file.basename(path), "bar",
                    "basename should work on paths with multiple components");
 };
 
 exports.testList = function(assert) {
   let list = file.list(profilePath);
-  let found = [ true for (name of list)
-                    if (name === fileNameInProfile) ];
+  let found = list.filter(name => name === fileNameInProfile);
 
-  if (found.length > 1)
-    assert.fail("a dir can't contain two files of the same name!");
-  assert.equal(found[0], true, "file.list() should work");
+  assert.equal(found.length, 1, "file.list() should work");
 
   assert.throws(function() {
     file.list(filePathInProfile);
   }, ERRORS.NOT_A_DIRECTORY, "file.list() on non-dir should raise error");
 
   assert.throws(function() {
     file.list(file.join(dirPathInProfile, "does-not-exist"));
   }, ERRORS.FILE_NOT_FOUND, "file.list() on nonexistent should raise error");
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -169,16 +169,17 @@
 @RESPATH@/components/docshell.xpt
 @RESPATH@/components/dom.xpt
 @RESPATH@/components/dom_activities.xpt
 @RESPATH@/components/dom_apps.xpt
 @RESPATH@/components/dom_newapps.xpt
 @RESPATH@/components/dom_audiochannel.xpt
 @RESPATH@/components/dom_base.xpt
 @RESPATH@/components/dom_system.xpt
+@RESPATH@/components/dom_workers.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 #ifdef MOZ_B2G_RIL
 @RESPATH@/components/dom_wappush.xpt
 @RESPATH@/components/dom_mobileconnection.xpt
 #endif
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1473,24 +1473,24 @@ pref("network.disable.ipc.security", tru
 
 // CustomizableUI debug logging.
 pref("browser.uiCustomization.debug", false);
 
 // CustomizableUI state of the browser's user interface
 pref("browser.uiCustomization.state", "");
 
 // The remote content URL shown for FxA signup. Must use HTTPS.
-pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v1");
+pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v2");
 
 // The URL where remote content that forces re-authentication for Firefox Accounts
 // should be fetched.  Must use HTTPS.
-pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
+pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v2");
 
 // The remote content URL shown for signin in. Must use HTTPS.
-pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v1");
+pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v2");
 
 // The remote content URL where FxAccountsWebChannel messages originate.
 pref("identity.fxaccounts.remote.webchannel.uri", "https://accounts.firefox.com/");
 
 // The URL we take the user to when they opt to "manage" their Firefox Account.
 // Note that this will always need to be in the same TLD as the
 // "identity.fxaccounts.remote.signup.uri" pref.
 pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings");
--- a/browser/base/content/browser-loop.js
+++ b/browser/base/content/browser-loop.js
@@ -467,42 +467,47 @@ var LoopUI;
     _maybeShowBrowserSharingInfoBar: function() {
       this._hideBrowserSharingInfoBar();
 
       // Don't show the infobar if it's been permanently disabled from the menu.
       if (!this.MozLoopService.getLoopPref(kPrefBrowserSharingInfoBar)) {
         return;
       }
 
-      // Create the menu that is shown when the menu-button' dropmarker is clicked
-      // inside the notification bar.
-      let menuPopup = document.createElementNS(kNSXUL, "menupopup");
-      let menuItem = menuPopup.appendChild(document.createElementNS(kNSXUL, "menuitem"));
-      menuItem.setAttribute("label", this._getString("infobar_menuitem_dontshowagain_label"));
-      menuItem.setAttribute("accesskey", this._getString("infobar_menuitem_dontshowagain_accesskey"));
-      menuItem.addEventListener("command", () => {
-        // We're being told to hide the bar permanently.
-        this._hideBrowserSharingInfoBar(true);
-      });
-
       let box = gBrowser.getNotificationBox();
+      let paused = false;
       let bar = box.appendNotification(
         this._getString("infobar_screenshare_browser_message"),
         kBrowserSharingNotificationId,
         // Icon is defined in browser theme CSS.
         null,
         box.PRIORITY_WARNING_LOW,
         [{
-          label: this._getString("infobar_button_gotit_label"),
-          accessKey: this._getString("infobar_button_gotit_accesskey"),
-          type: "menu-button",
-          popup: menuPopup,
-          anchor: "dropmarker",
+          label: this._getString("infobar_button_pause_label"),
+          accessKey: this._getString("infobar_button_pause_accesskey"),
+          isDefault: false,
+          callback: (event, buttonInfo, buttonNode) => {
+            paused = !paused;
+            bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
+              this._getString("infobar_screenshare_browser_message");
+            bar.classList.toggle("paused", paused);
+            buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
+              this._getString("infobar_button_pause_label");
+            buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
+              this._getString("infobar_button_pause_accesskey");
+            return true;
+          }
+        },
+        {
+          label: this._getString("infobar_button_stop_label"),
+          accessKey: this._getString("infobar_button_stop_accesskey"),
+          isDefault: true,
           callback: () => {
             this._hideBrowserSharingInfoBar();
+            LoopUI.MozLoopService.hangupAllChatWindows();
           }
         }]
       );
 
       // Keep showing the notification bar until the user explicitly closes it.
       bar.persistence = -1;
     },
 
deleted file mode 100644
--- a/browser/base/content/test/general/bug_1182546.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html [
-  <!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
-  %passwordManagerDTD;
-]>
-<window>&savedLogins.title;</window>
--- a/browser/base/content/test/general/mochitest.ini
+++ b/browser/base/content/test/general/mochitest.ini
@@ -20,23 +20,21 @@ support-files =
   offlineChild2.cacheManifest
   offlineChild2.cacheManifest^headers^
   offlineChild2.html
   offlineEvent.cacheManifest
   offlineEvent.cacheManifest^headers^
   offlineEvent.html
   subtst_contextmenu.html
   video.ogg
-  bug_1182546.xml
 
 [test_bug364677.html]
 [test_bug395533.html]
 [test_contextmenu.html]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
 [test_contextmenu_input.html]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" || e10s # disabled on Linux due to bug 513558
 [test_feed_discovery.html]
 skip-if = e10s
 [test_offlineNotification.html]
 skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
 [test_offline_gzip.html]
 skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
-[test_bug1182546.html]
deleted file mode 100644
--- a/browser/base/content/test/general/test_bug1182546.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1182546
--->
-<head>
-  <title>Bug 1182546 - Test block loading DTD from random page</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<iframe id="testframe" src="bug_1182546.xml"></iframe>
-
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-
-addLoadEvent(function() {
-  // make sure the DTD loader (nsExpatDriver) prevents accessing chrome: from random pages
-  var childNodes = testframe.contentDocument.documentElement.childNodes;
-
-  // make sure '&savedLogins.title;' from bug_1182546.xml does not translate into 'Saved Logins'
-  // the URL 'chrome://passwordmgr/locale/passwordManager.dtd' should not be accessible from content
-  var nodeValue = childNodes[0].nodeValue;
-  isnot(nodeValue, "Saved Logins",
-        "expatDriver should prevent accessing &savedLogins.title;");
-  ok(nodeValue.startsWith("XML Parsing Error: undefined entity"),
-  	 "expatDriver should not allow accessing chrome:");
-});
-
-addLoadEvent(SimpleTest.finish);
-
-</script>
-</body>
-</html>
--- a/browser/components/distribution.js
+++ b/browser/components/distribution.js
@@ -197,16 +197,41 @@ DistributionCustomizer.prototype = {
         if (item.description) {
           let bmId = yield PlacesUtils.promiseItemId(bm.guid);
           PlacesUtils.annotations.setItemAnnotation(bmId,
                                                     "bookmarkProperties/description",
                                                     item.description, 0,
                                                     PlacesUtils.annotations.EXPIRE_NEVER);
         }
 
+        if (item.icon && item.iconData) {
+          try {
+            let faviconURI = this._makeURI(item.icon);
+            PlacesUtils.favicons.replaceFaviconDataFromDataURL(
+              faviconURI, item.iconData, 0,
+              Services.scriptSecurityManager.getSystemPrincipal());
+
+            PlacesUtils.favicons.setAndFetchFaviconForPage(
+              this._makeURI(item.link), faviconURI, false,
+              PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null,
+              Services.scriptSecurityManager.getSystemPrincipal());
+          } catch(e) {
+            Cu.reportError(e);
+          }
+        }
+
+        if (item.keyword) {
+          try {
+            yield PlacesUtils.keywords.insert({ keyword: item.keyword,
+                                                url: item.link });
+          } catch(e) {
+            Cu.reportError(e);
+          }
+        }
+
         break;
       }
     }
   }),
 
   _customizationsApplied: false,
   applyCustomizations: function DIST_applyCustomizations() {
     this._customizationsApplied = true;
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -1,105 +1,112 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+
+const INTEGER = /^[1-9]\d*$/;
+
 var {
   EventManager,
 } = ExtensionUtils;
 
 // This file provides some useful code for the |tabs| and |windows|
 // modules. All of the code is installed on |global|, which is a scope
 // shared among the different ext-*.js scripts.
 
 
 // Manages icon details for toolbar buttons in the |pageAction| and
 // |browserAction| APIs.
 global.IconDetails = {
-  // Accepted icon sizes.
-  SIZES: ["19", "38"],
-
   // Normalizes the various acceptable input formats into an object
-  // with two properties, "19" and "38", containing icon URLs.
+  // with icon size as key and icon URL as value.
+  //
+  // If a context is specified (function is called from an extension):
+  // Throws an error if an invalid icon size was provided or the
+  // extension is not allowed to load the specified resources.
+  //
+  // If no context is specified, instead of throwing an error, this
+  // function simply logs a warning message.
   normalize(details, extension, context=null, localize=false) {
     let result = {};
 
-    if (details.imageData) {
-      let imageData = details.imageData;
+    try {
+      if (details.imageData) {
+        let imageData = details.imageData;
 
-      if (imageData instanceof Cu.getGlobalForObject(imageData).ImageData) {
-        imageData = {"19": imageData};
-      }
+        if (imageData instanceof Cu.getGlobalForObject(imageData).ImageData) {
+          imageData = {"19": imageData};
+        }
 
-      for (let size of this.SIZES) {
-        if (size in imageData) {
+        for (let size of Object.keys(imageData)) {
+          if (!INTEGER.test(size)) {
+            throw new Error(`Invalid icon size ${size}, must be an integer`);
+          }
+
           result[size] = this.convertImageDataToPNG(imageData[size], context);
         }
       }
-    }
+
+      if (details.path) {
+        let path = details.path;
+        if (typeof path != "object") {
+          path = {"19": path};
+        }
 
-    if (details.path) {
-      let path = details.path;
-      if (typeof path != "object") {
-        path = {"19": path};
-      }
+        let baseURI = context ? context.uri : extension.baseURI;
 
-      let baseURI = context ? context.uri : extension.baseURI;
+        for (let size of Object.keys(path)) {
+          if (!INTEGER.test(size)) {
+            throw new Error(`Invalid icon size ${size}, must be an integer`);
+          }
 
-      for (let size of this.SIZES) {
-        if (size in path) {
           let url = path[size];
           if (localize) {
             url = extension.localize(url);
           }
 
           url = baseURI.resolve(path[size]);
 
           // The Chrome documentation specifies these parameters as
           // relative paths. We currently accept absolute URLs as well,
           // which means we need to check that the extension is allowed
-          // to load them.
-          try {
-            Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
-              extension.principal, url,
-              Services.scriptSecurityManager.DISALLOW_SCRIPT);
-          } catch (e) {
-            if (context) {
-              throw e;
-            }
-            // If there's no context, it's because we're handling this
-            // as a manifest directive. Log a warning rather than
-            // raising an error, but don't accept the URL in any case.
-            extension.manifestError(`Access to URL '${url}' denied`);
-            continue;
-          }
+          // to load them. This will throw an error if it's not allowed.
+          Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
+            extension.principal, url,
+            Services.scriptSecurityManager.DISALLOW_SCRIPT);
 
           result[size] = url;
         }
       }
+    } catch (e) {
+      // Function is called from extension code, delegate error.
+      if (context) {
+        throw e;
+      }
+      // If there's no context, it's because we're handling this
+      // as a manifest directive. Log a warning rather than
+      // raising an error.
+      extension.manifestError(`Invalid icon data: ${e}`);
     }
 
     return result;
   },
 
   // Returns the appropriate icon URL for the given icons object and the
   // screen resolution of the given window.
   getURL(icons, window, extension) {
     const DEFAULT = "chrome://browser/content/extension.svg";
 
-    // Use the higher resolution image if we're doing any up-scaling
-    // for high resolution monitors.
-    let res = window.devicePixelRatio;
-    let size = res > 1 ? "38" : "19";
-
-    return icons[size] || icons["19"] || icons["38"] || DEFAULT;
+    return AddonManager.getPreferredIconURL({icons: icons}, 18, window) || DEFAULT;
   },
 
   convertImageDataToPNG(imageData, context) {
     let document = context.contentWindow.document;
     let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = imageData.width;
     canvas.height = imageData.height;
     canvas.getContext("2d").putImageData(imageData, 0, 0);
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -96,16 +96,39 @@ add_task(function* testDetailsObjects() 
           "1": browser.runtime.getURL("data/a.png"),
           "2": imageData.red.url, } },
       { details: {
           "path": { "38": "a.png" },
           "imageData": imageData.red.imageData, },
         resolutions: {
           "1": imageData.red.url,
           "2": browser.runtime.getURL("data/a.png"), } },
+
+      // Various resolutions
+      { details: { "path": { "18": "a.png", "32": "a-x2.png" } },
+        resolutions: {
+          "1": browser.runtime.getURL("data/a.png"),
+          "2": browser.runtime.getURL("data/a-x2.png"), } },
+      { details: { "path": { "16": "16.png", "100": "100.png" } },
+        resolutions: {
+          "1": browser.runtime.getURL("data/100.png"),
+          "2": browser.runtime.getURL("data/100.png"), } },
+      { details: { "path": { "2": "2.png"} },
+        resolutions: {
+          "1": browser.runtime.getURL("data/2.png"),
+          "2": browser.runtime.getURL("data/2.png"), } },
+      { details: { "path": {
+        "6": "6.png",
+        "18": "18.png",
+        "32": "32.png",
+        "48": "48.png",
+        "128": "128.png" } },
+        resolutions: {
+          "1": browser.runtime.getURL("data/18.png"),
+          "2": browser.runtime.getURL("data/48.png"), } },
     ];
 
     // Allow serializing ImageData objects for logging.
     ImageData.prototype.toJSON = () => "<ImageData>";
 
     var tabId;
 
     browser.test.onMessage.addListener((msg, test) => {
@@ -192,16 +215,72 @@ add_task(function* testDetailsObjects() 
 
     let pageActionImage = document.getElementById(pageActionId);
     is(pageActionImage.src, imageURL, "page action has the correct image");
   }
 
   yield extension.unload();
 });
 
+// Test that an error is thrown when providing invalid icon sizes
+add_task(function *testInvalidIconSizes() {
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "browser_action": {},
+      "page_action": {},
+    },
+
+    background: function () {
+      browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        var tabId = tabs[0].id;
+
+        for (var api of ["pageAction", "browserAction"]) {
+          // helper function to run setIcon and check if it fails
+          let assertSetIconThrows = function(detail, error, message) {
+            try {
+              detail.tabId = tabId;
+              browser[api].setIcon(detail);
+
+              browser.test.fail("Expected an error on invalid icon size.");
+              browser.test.notifyFail("setIcon with invalid icon size");
+              return;
+            } catch (e) {
+              browser.test.succeed("setIcon with invalid icon size");
+            }
+          }
+
+          // test invalid icon size inputs
+          for (var type of ["path", "imageData"]) {
+            assertSetIconThrows({ [type]: { "abcdef": "test.png" } });
+            assertSetIconThrows({ [type]: { "48px": "test.png" } });
+            assertSetIconThrows({ [type]: { "20.5": "test.png" } });
+            assertSetIconThrows({ [type]: { "5.0": "test.png" } });
+            assertSetIconThrows({ [type]: { "-300": "test.png" } });
+            assertSetIconThrows({ [type]: {
+              "abc": "test.png",
+              "5": "test.png"
+            }});
+          }
+
+          assertSetIconThrows({ imageData: { "abcdef": "test.png" }, path: {"5": "test.png"} });
+          assertSetIconThrows({ path: { "abcdef": "test.png" }, imageData: {"5": "test.png"} });
+        }
+
+        browser.test.notifyPass("setIcon with invalid icon size");
+      });
+    }
+  });
+
+  yield Promise.all([extension.startup(), extension.awaitFinish("setIcon with invalid icon size")]);
+
+  yield extension.unload();
+});
+
+
 // Test that default icon details in the manifest.json file are handled
 // correctly.
 add_task(function *testDefaultDetails() {
   // TODO: Test localized variants.
   let icons = [
     "foo/bar.png",
     "/foo/bar.png",
     { "19": "foo/bar.png" },
@@ -289,32 +368,32 @@ add_task(function* testSecureURLsDenied(
 
         browser.test.notifyPass("setIcon security tests");
       });
     },
   });
 
   yield extension.startup();
 
-  yield extension.awaitFinish();
+  yield extension.awaitFinish("setIcon security tests");
   yield extension.unload();
 
 
   // Test URLs included in the manifest.
 
   let urls = ["chrome://browser/content/browser.xul",
               "javascript:true"];
 
   let matchURLForbidden = url => ({
-    message: new RegExp(`Loading extension.*Access to.*'${url}' denied`),
+    message: new RegExp(`Loading extension.*Invalid icon data: NS_ERROR_DOM_BAD_URI`),
   });
 
+  // Because the underlying method throws an error on invalid data,
+  // only the first invalid URL of each component will be logged.
   let messages = [matchURLForbidden(urls[0]),
-                  matchURLForbidden(urls[1]),
-                  matchURLForbidden(urls[0]),
                   matchURLForbidden(urls[1])];
 
   let waitForConsole = new Promise(resolve => {
     // Not necessary in browser-chrome tests, but monitorConsole gripes
     // if we don't call it.
     SimpleTest.waitForExplicitFinish();
 
     SimpleTest.monitorConsole(resolve, messages);
@@ -325,18 +404,18 @@ add_task(function* testSecureURLsDenied(
       "browser_action": {
         "default_icon": {
           "19": urls[0],
           "38": urls[1],
         },
       },
       "page_action": {
         "default_icon": {
-          "19": urls[0],
-          "38": urls[1],
+          "19": urls[1],
+          "38": urls[0],
         },
       },
     },
   });
 
   yield extension.startup();
   yield extension.unload();
 
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -435,17 +435,26 @@ loop.panel = (function(_, mozL10n) {
     },
 
     handleClickEntry: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
         roomToken: this.props.room.roomToken
       }));
-      this.closeWindow();
+
+      // Open url if needed.
+      loop.request("getSelectedTabMetadata").then(function(metadata) {
+        var contextURL = this.props.room.decryptedContext.urls &&
+          this.props.room.decryptedContext.urls[0].location;
+        if (contextURL && metadata.url !== contextURL) {
+          loop.request("OpenURL", contextURL);
+        }
+        this.closeWindow();
+      }.bind(this));
     },
 
     handleClick: function(e) {
       e.preventDefault();
       e.stopPropagation();
 
       this.setState({
         eventPosY: e.pageY
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -435,17 +435,26 @@ loop.panel = (function(_, mozL10n) {
     },
 
     handleClickEntry: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
         roomToken: this.props.room.roomToken
       }));
-      this.closeWindow();
+
+      // Open url if needed.
+      loop.request("getSelectedTabMetadata").then(function(metadata) {
+        var contextURL = this.props.room.decryptedContext.urls &&
+          this.props.room.decryptedContext.urls[0].location;
+        if (contextURL && metadata.url !== contextURL) {
+          loop.request("OpenURL", contextURL);
+        }
+        this.closeWindow();
+      }.bind(this));
     },
 
     handleClick: function(e) {
       e.preventDefault();
       e.stopPropagation();
 
       this.setState({
         eventPosY: e.pageY
--- a/browser/components/loop/content/shared/img/icons-16x16.svg
+++ b/browser/components/loop/content/shared/img/icons-16x16.svg
@@ -74,16 +74,19 @@
       <path fill="#ccc" d="M14.191,4.579c0-1.149-0.832-2.08-1.857-2.08h-0.902V1.997C11.432,0.894,10.633,0,9.648,0H6.352 C5.367,0,4.568,0.894,4.568,1.997v0.502H3.666c-1.026,0-1.857,0.932-1.857,2.08v1.389h1.064l0.785,8.416 C3.742,15.29,4.499,16,5.383,16h5.231c0.883,0,1.64-0.709,1.724-1.614l0.788-8.417h1.064V4.579z M5.535,2.218V2.065 c0-0.58,0.42-1.05,0.938-1.05h3.055c0.518,0,0.937,0.47,0.937,1.05v0.153c0,0.098-0.016,0.191-0.038,0.281H5.573 C5.55,2.409,5.535,2.316,5.535,2.218z M11.184,14.277c-0.029,0.305-0.29,0.562-0.57,0.562H5.383c-0.281,0-0.541-0.257-0.57-0.562 L4.038,5.969h7.924L11.184,14.277z"/>
       <rect x="7.612" y="7.291" fill="#ccc" width="0.774" height="6.191"/>
       <polyline fill="#ccc" points="9.934,7.291 9.16,7.291 9.16,13.482 9.934,13.482  "/>
       <rect x="6.065" y="7.291" fill="#ccc" width="0.774" height="6.191"/>
     </g>
     <g id="globe-shape" transform="translate(-40.000000, -40.000000)" fill-rule="evenodd">
       <path d="M48,56 C52.418278,56 56,52.418278 56,48 C56,43.581722 52.418278,40 48,40 C43.581722,40 40,43.581722 40,48 C40,52.418278 43.581722,56 48,56 Z M42.630492,47.0453156 C42.5321586,46.9747601 42.3116031,46.9575379 42.3388253,46.7925379 C42.3560475,46.6919823 42.4593808,46.613649 42.5438253,46.5642045 C42.6538253,46.5003156 42.7766031,46.4980934 42.9010475,46.5092045 C42.9310475,46.5119823 42.9866031,46.5069823 43.0021586,46.5297601 C43.010492,46.5425379 43.0277142,46.5492045 43.0416031,46.553649 C43.0743808,46.563649 43.1088253,46.5642045 43.1427142,46.5703156 C43.1932697,46.5803156 43.2288253,46.6247601 43.280492,46.588649 C43.3388253,46.5469823 43.3482697,46.5392045 43.4188253,46.5475379 C43.4827142,46.5553156 43.520492,46.5064268 43.5743808,46.5108712 C43.5910475,46.5125379 43.605492,46.5158712 43.6182697,46.5208712 C43.6238253,46.5030934 43.6316031,46.4869823 43.6427142,46.483649 C43.6693808,46.4753156 43.7282697,46.5403156 43.755492,46.5458712 C43.825492,46.5597601 43.8210475,46.5114268 43.8266031,46.4608712 C43.8616031,46.4542045 43.8782697,46.5030934 43.910492,46.4725379 C43.9093808,46.4825379 43.915492,46.4964268 43.9149364,46.5069823 C43.9221586,46.5114268 43.9288253,46.5114268 43.9360475,46.5064268 C43.9388253,46.5003156 43.9393808,46.4947601 43.9377142,46.488649 C43.9543808,46.4942045 43.9632697,46.4819823 43.965492,46.4614268 C43.9777142,46.4630934 43.9982697,46.4553156 44.0110475,46.4575379 C44.0199364,46.4208712 44.0416031,46.3730934 44.0149364,46.3397601 C44.0221586,46.3380934 44.0288253,46.3369823 44.0360475,46.3347601 C44.0366031,46.2997601 44.060492,46.2830934 44.0616031,46.253649 C44.0232697,46.248649 43.9816031,46.2508712 43.9421586,46.2519823 C43.965492,46.2303156 44.0216031,46.1830934 44.0271586,46.1542045 C44.0371586,46.103649 43.9699364,46.0719823 43.975492,46.0114268 C43.9816031,46.043649 44.0182697,46.1169823 44.0521586,46.1280934 C44.1282697,46.153649 44.1066031,46.0775379 44.1121586,46.0380934 C44.130492,45.9130934 44.2432697,46.0164268 44.2449364,46.0819823 C44.2782697,46.0064268 44.3599364,46.0897601 44.3232697,46.1542045 C44.3049364,46.1858712 44.2799364,46.173649 44.3021586,46.2147601 C44.3182697,46.243649 44.3416031,46.2447601 44.3749364,46.2369823 C44.3843808,46.2197601 44.3921586,46.2003156 44.3916031,46.1803156 C44.4499364,46.1619823 44.4877142,46.2314268 44.4538253,46.273649 C44.4971586,46.2497601 44.5421586,46.2269823 44.5888253,46.2153156 C44.560492,46.1197601 44.5327142,46.0253156 44.5510475,45.9219823 C44.5549364,45.8997601 44.5582697,45.873649 44.5749364,45.8564268 C44.5966031,45.8342045 44.5693808,45.8430934 44.5671586,45.8280934 C44.5616031,45.7825379 44.6093808,45.7369823 44.6271586,45.6969823 C44.5766031,45.6853156 44.6232697,45.5869823 44.6560475,45.5703156 C44.6916031,45.5519823 44.7982697,45.5814268 44.8049364,45.5530934 C44.8260475,45.5653156 44.8460475,45.5819823 44.8721586,45.5825379 C44.9299364,45.5830934 44.9693808,45.5842045 45.0093808,45.6314268 C45.030492,45.6553156 45.0593808,45.7103156 45.0960475,45.7142045 C45.0949364,45.7547601 45.1416031,45.7853156 45.0927142,45.8197601 C45.0543808,45.8464268 45.0010475,45.8397601 44.9849364,45.8914268 C44.9749364,45.923649 44.9443808,45.9380934 44.990492,45.9608712 C45.0116031,45.9708712 45.0360475,45.973649 45.0588253,45.9730934 C45.0638253,46.0053156 45.0793808,46.0458712 45.1199364,46.0408712 C45.1988253,46.0314268 45.2149364,45.9330934 45.2782697,45.8997601 C45.3793808,45.8464268 45.3632697,46.0780934 45.4521586,46.0192045 C45.4732697,46.0058712 45.4732697,45.9469823 45.4832697,45.9247601 C45.5043808,45.8769823 45.5299364,45.8297601 45.560492,45.7869823 C45.5988253,45.7342045 45.6477142,45.678649 45.6310475,45.608649 C45.6221586,45.5703156 45.5477142,45.5542045 45.5138253,45.5269823 C45.4727142,45.4942045 45.4343808,45.458649 45.4093808,45.4125379 C45.3949364,45.3853156 45.3860475,45.3747601 45.4099364,45.3619823 C45.4238253,45.3542045 45.4188253,45.338649 45.4138253,45.3280934 C45.3888253,45.2742045 45.3193808,45.1792045 45.4160475,45.1464268 C45.4371586,45.1392045 45.4821586,45.0703156 45.4849364,45.0430934 C45.4899364,44.9980934 45.4221586,44.9580934 45.4527142,44.9130934 C45.475492,44.8792045 45.5310475,44.858649 45.560492,44.8247601 C45.5738253,44.8092045 45.5899364,44.7953156 45.6110475,44.7908712 C45.6127142,44.7725379 45.6166031,44.7525379 45.6321586,44.7403156 C45.6582697,44.7214268 45.6982697,44.7303156 45.7277142,44.7214268 C45.7760475,44.7064268 45.7977142,44.6558712 45.8360475,44.6275379 C45.8671586,44.6042045 45.9038253,44.6125379 45.9371586,44.5964268 C45.955492,44.5875379 45.9632697,44.5680934 45.9810475,44.5597601 C46.025492,44.5380934 46.0760475,44.5725379 46.0960475,44.6103156 C46.1443808,44.7003156 46.2038253,44.838649 46.3399364,44.8025379 C46.3943808,44.7875379 46.435492,44.7425379 46.4527142,44.6908712 C46.4693808,44.6419823 46.4499364,44.6014268 46.4527142,44.5525379 C46.4571586,44.4680934 46.5416031,44.413649 46.5499364,44.328649 C46.4866031,44.3297601 46.5193808,44.2897601 46.4988253,44.2542045 C46.475492,44.2130934 46.4221586,44.2430934 46.3866031,44.2364268 C46.4227142,44.1503156 46.4227142,44.1192045 46.3377142,44.0780934 C46.3010475,44.0597601 46.2393808,43.973649 46.2116031,43.9769823 C46.2349364,43.9447601 46.2899364,43.998649 46.3066031,44.0142045 C46.3432697,44.0492045 46.3760475,44.0653156 46.4282697,44.0692045 C46.4138253,44.0464268 46.4060475,44.0058712 46.4160475,43.9803156 C46.425492,43.9564268 46.4016031,43.9319823 46.4038253,43.9019823 C46.4632697,43.9780934 46.4532697,44.0653156 46.4877142,44.148649 C46.5021586,44.1842045 46.5360475,44.2075379 46.5510475,44.243649 C46.570492,44.2892045 46.5582697,44.2892045 46.5988253,44.3169823 C46.6227142,44.3330934 46.6310475,44.363649 46.635492,44.3903156 C46.6427142,44.4375379 46.6599364,44.4169823 46.6849364,44.443649 C46.6999364,44.4597601 46.7382697,44.4625379 46.730492,44.4953156 C46.725492,44.518649 46.7099364,44.538649 46.7066031,44.5619823 C46.6966031,44.633649 46.8360475,44.5375379 46.8493808,44.5275379 C46.8788253,44.5053156 46.9277142,44.5003156 46.9516031,44.473649 C46.975492,44.4458712 46.9699364,44.4058712 46.9960475,44.3792045 C47.0277142,44.3469823 47.0582697,44.3692045 47.0971586,44.3625379 C47.1410475,44.3558712 47.1793808,44.3208712 47.2121586,44.293649 C47.2821586,44.2353156 47.3260475,44.1708712 47.3849364,44.103649 C47.3599364,44.1097601 47.2721586,44.1730934 47.2677142,44.1147601 C47.2338253,44.113649 47.1582697,44.108649 47.1460475,44.0719823 C47.1371586,44.0453156 47.140492,44.0164268 47.1399364,43.988649 C47.1393808,43.9603156 47.1043808,43.9697601 47.0810475,43.9547601 C47.0343808,43.9242045 47.0110475,43.8697601 46.9610475,43.8430934 C46.8816031,43.8014268 46.8332697,43.7347601 46.7860475,43.6630934 C46.7582697,43.6208712 46.6610475,43.5358712 46.6677142,43.4853156 C46.6727142,43.4525379 46.6999364,43.4169823 46.6982697,43.383649 C46.6966031,43.3547601 46.6732697,43.338649 46.6760475,43.3053156 C46.6799364,43.2664268 46.5860475,43.1992045 46.6677142,43.1914268 C46.6943808,43.1892045 46.6977142,43.158649 46.7260475,43.1414268 C46.7582697,43.1219823 46.7510475,43.1053156 46.7860475,43.1147601 C46.8421586,43.1308712 46.8816031,43.0697601 46.9199364,43.0364268 C46.985492,42.978649 46.880492,42.9769823 46.8749364,42.9342045 C46.8693808,42.8897601 46.8438253,42.8575379 46.8371586,42.8064268 C46.8327142,42.7692045 46.7977142,42.783649 46.7777142,42.7930934 C46.7499364,42.8058712 46.7227142,42.7869823 46.6960475,42.7814268 C46.6710475,42.7764268 46.6510475,42.7358712 46.6221586,42.748649 C46.5999364,42.758649 46.600492,42.7864268 46.5688253,42.7830934 C46.5449364,42.7803156 46.530492,42.7575379 46.5066031,42.7542045 C46.4710475,42.7492045 46.5027142,42.7830934 46.4627142,42.7869823 C46.4338253,42.788649 46.3421586,42.7497601 46.3399364,42.7869823 C46.310492,42.7375379 46.2993808,42.8208712 46.2682697,42.8297601 C46.2349364,42.8397601 46.1982697,42.8303156 46.1649364,42.843649 C46.0910475,42.873649 46.1143808,42.9475379 46.1860475,42.958649 C46.2432697,42.9680934 46.1688253,43.0069823 46.1877142,43.0458712 C46.2043808,43.0803156 46.2099364,43.103649 46.2471586,43.1175379 C46.3093808,43.1403156 46.3749364,43.158649 46.3532697,43.2380934 C46.325492,43.3364268 46.2566031,43.4292045 46.1599364,43.4742045 C46.0677142,43.5169823 46.0427142,43.3997601 45.9727142,43.3703156 C45.9293808,43.3525379 45.8799364,43.358649 45.8349364,43.3642045 C45.8266031,43.3758712 45.8988253,43.3975379 45.9099364,43.418649 C45.9316031,43.4603156 45.8732697,43.4553156 45.8688253,43.4864268 C45.8643808,43.513649 45.8293808,43.5330934 45.8482697,43.5603156 C45.8282697,43.5347601 45.7888253,43.568649 45.775492,43.5830934 C45.755492,43.6053156 45.7593808,43.618649 45.7677142,43.6453156 C45.7849364,43.7003156 45.7027142,43.7569823 45.6532697,43.7497601 C45.6121586,43.7442045 45.5727142,43.7469823 45.5338253,43.7280934 C45.4866031,43.7053156 45.5032697,43.7192045 45.4932697,43.6664268 C45.4838253,43.6175379 45.4160475,43.5964268 45.4560475,43.5353156 C45.4843808,43.4897601 45.4710475,43.4947601 45.465492,43.4514268 C45.4593808,43.4064268 45.4799364,43.4008712 45.515492,43.393649 C45.5543808,43.3858712 45.5716031,43.3175379 45.595492,43.283649 C45.6010475,43.2758712 45.6243808,43.2142045 45.5871586,43.2303156 C45.5649364,43.2392045 45.5816031,43.2658712 45.5488253,43.2714268 C45.5243808,43.2747601 45.5016031,43.2592045 45.4777142,43.2592045 C45.4499364,43.2592045 45.420492,43.2730934 45.395492,43.2553156 C45.4077142,43.2403156 45.4999364,43.1664268 45.4271586,43.1503156 C45.3982697,43.1442045 45.4227142,43.1914268 45.3843808,43.1853156 C45.3760475,43.2230934 45.3310475,43.218649 45.3088253,43.2430934 C45.3177142,43.2030934 45.4027142,43.173649 45.3749364,43.1414268 C45.4349364,43.0880934 45.4471586,43.0792045 45.365492,43.0503156 C45.2371586,43.0053156 45.2471586,42.8742045 45.335492,42.7942045 C45.4160475,42.7214268 45.5488253,42.6292045 45.6266031,42.7503156 C45.710492,42.8797601 45.7638253,42.7858712 45.8327142,42.7025379 C45.8093808,42.6925379 45.830492,42.6864268 45.8227142,42.6580934 C45.7466031,42.688649 45.6788253,42.5903156 45.7310475,42.5308712 C45.7621586,42.4958712 45.8116031,42.5058712 45.8532697,42.4953156 C45.8916031,42.4853156 45.9249364,42.4480934 45.940492,42.4147601 C45.9088253,42.4230934 45.9127142,42.403649 45.9249364,42.3892045 C45.905492,42.3875379 45.8849364,42.3792045 45.8688253,42.373649 C45.8232697,42.3580934 45.8260475,42.3225379 45.7782697,42.3158712 C45.6621586,42.298649 45.8982697,42.1664268 45.7827142,42.1658712 C45.7460475,42.1653156 45.7121586,42.108649 45.6849364,42.1197601 C45.665492,42.1275379 45.6616031,42.1425379 45.6377142,42.1325379 C45.6216031,42.1258712 45.6021586,42.1119823 45.5838253,42.1230934 C45.5416031,42.1492045 45.5338253,42.1175379 45.4932697,42.1303156 C45.4610475,42.1403156 45.4421586,42.1719823 45.4043808,42.163649 C45.4421586,42.1125379 45.4888253,42.0703156 45.5227142,42.0158712 C45.545492,41.9797601 45.5721586,41.9469823 45.6099364,41.9247601 C45.6299364,41.9119823 45.6871586,41.9008712 45.690492,41.8725379 C45.695492,41.8269823 45.6666031,41.8303156 45.6349364,41.8492045 C45.5527142,41.898649 45.4671586,41.9514268 45.3860475,42.003649 C45.3377142,42.0347601 45.2988253,42.0614268 45.2382697,42.0525379 C45.1921586,42.0453156 45.1732697,42.0969823 45.1360475,42.0919823 C45.1182697,42.0208712 44.7199364,42.2808712 44.6716031,42.3025379 C44.5943808,42.3375379 44.5066031,42.3964268 44.4243808,42.4164268 C44.3899364,42.4247601 44.3193808,42.5047601 44.3232697,42.4164268 C44.2816031,42.4114268 44.2499364,42.453649 44.2210475,42.4758712 C44.1810475,42.5069823 44.1327142,42.5253156 44.0899364,42.5525379 C43.9960475,42.6125379 43.9082697,42.683649 43.8210475,42.7514268 C43.7382697,42.8158712 43.655492,42.8908712 43.5688253,42.948649 C43.5388253,42.968649 43.4293808,43.0247601 43.4332697,43.0664268 C43.5110475,43.0808712 43.7743808,42.7508712 43.8482697,42.8403156 C43.8671586,42.8630934 43.7366031,42.9292045 43.7143808,42.9419823 C43.6966031,42.9519823 43.675492,42.9514268 43.6582697,42.9608712 C43.6338253,42.973649 43.6193808,42.9975379 43.5966031,43.0125379 C43.5382697,43.0503156 43.4882697,43.098649 43.4449364,43.1525379 C43.4132697,43.1919823 43.3910475,43.2425379 43.3577142,43.2792045 C43.3632697,43.2408712 43.355492,43.2130934 43.3566031,43.1758712 C43.3127142,43.2030934 43.2949364,43.2508712 43.2343808,43.2369823 C43.1799364,43.2247601 43.1360475,43.2803156 43.0966031,43.3103156 C43.005492,43.3797601 42.9443808,43.4569823 42.8677142,43.538649 C42.825492,43.5842045 42.7799364,43.6164268 42.7532697,43.673649 C42.7249364,43.7342045 42.6843808,43.7892045 42.6466031,43.8453156 C42.5766031,43.9492045 42.4949364,44.0447601 42.4243808,44.148649 C42.2816031,44.3614268 42.1871586,44.6058712 42.0699364,44.8319823 C42.0082697,44.9497601 41.9499364,45.0664268 41.9260475,45.1969823 C41.9043808,45.3108712 41.9032697,45.4269823 41.9066031,45.5419823 C41.970492,45.4914268 41.9671586,45.5975379 41.9510475,45.6280934 C41.9260475,45.6764268 41.9171586,45.7308712 41.9088253,45.783649 C41.8982697,45.853649 41.8860475,45.923649 41.8860475,45.9942045 C41.8866031,46.053649 41.8671586,46.1097601 41.865492,46.1675379 C41.8427142,46.1508712 41.8916031,46.0780934 41.8488253,46.0880934 C41.8277142,46.0930934 41.8266031,46.1247601 41.8216031,46.1403156 C41.8043808,46.1958712 41.7288253,46.1908712 41.7177142,46.253649 C41.710492,46.2930934 41.7066031,46.3147601 41.680492,46.3464268 C41.6616031,46.3697601 41.6799364,46.3803156 41.6838253,46.403649 C41.6938253,46.4603156 41.6216031,46.5369823 41.6410475,46.5819823 C41.6593808,46.6253156 41.6460475,46.6747601 41.6660475,46.7164268 C41.6766031,46.7380934 41.7010475,46.7658712 41.6910475,46.7925379 C41.6466031,46.8014268 41.7043808,46.9058712 41.7082697,46.9380934 C41.7149364,46.9908712 41.7610475,47.1553156 41.8127142,47.178649 C41.8760475,47.2769823 41.9627142,47.4125379 42.0749364,47.4614268 C42.1549364,47.4958712 42.1838253,47.393649 42.2282697,47.3503156 C42.2849364,47.2947601 42.3571586,47.2597601 42.4321586,47.233649 C42.4949364,47.2114268 42.7560475,47.1353156 42.630492,47.0453156 Z M48.0699364,48.1330934 C48.0499364,48.1330934 48.0143808,48.1469823 47.9982697,48.1608712 C47.9660475,48.1880934 48.0343808,48.2047601 48.0610475,48.2125379 C48.090492,48.2292045 48.1321586,48.2380934 48.1616031,48.2547601 C48.1860475,48.2725379 48.2027142,48.2975379 48.230492,48.3097601 C48.2643808,48.3253156 48.310492,48.3330934 48.3477142,48.3430934 C48.3643808,48.348649 48.3882697,48.3469823 48.4093808,48.3514268 C48.4327142,48.3647601 48.4432697,48.3864268 48.4621586,48.4008712 C48.4949364,48.4314268 48.5416031,48.438649 48.585492,48.4375379 C48.6266031,48.4414268 48.6577142,48.4480934 48.6949364,48.438649 C48.7366031,48.4280934 48.7671586,48.4497601 48.8066031,48.4492045 C48.8216031,48.4503156 48.8377142,48.4364268 48.8527142,48.4375379 C48.8727142,48.4375379 48.8749364,48.4464268 48.8843808,48.4630934 C48.9010475,48.4880934 48.945492,48.5253156 48.975492,48.5275379 C48.9949364,48.5269823 49.0093808,48.523649 49.025492,48.5292045 C49.0427142,48.5392045 49.050492,48.5397601 49.0632697,48.5508712 C49.085492,48.5597601 49.1043808,48.5669823 49.1143808,48.583649 C49.1316031,48.6130934 49.1310475,48.6469823 49.1566031,48.6697601 C49.175492,48.6842045 49.1938253,48.6992045 49.2127142,48.713649 C49.225492,48.7247601 49.2149364,48.7225379 49.2343808,48.7225379 C49.245492,48.7247601 49.2649364,48.7242045 49.2788253,48.7208712 C49.3321586,48.7169823 49.3027142,48.6425379 49.2866031,48.6175379 C49.275492,48.5958712 49.2660475,48.5792045 49.2710475,48.558649 C49.2738253,48.533649 49.2849364,48.5169823 49.2693808,48.4964268 C49.2610475,48.4842045 49.2493808,48.4775379 49.2377142,48.4708712 C49.2310475,48.4630934 49.2282697,48.453649 49.220492,48.4414268 C49.2049364,48.4208712 49.1738253,48.4147601 49.1538253,48.3953156 C49.1193808,48.3603156 49.1021586,48.3119823 49.0588253,48.2792045 C49.035492,48.2658712 49.0132697,48.2764268 48.985492,48.2642045 C48.9738253,48.2575379 48.9671586,48.2497601 48.950492,48.2442045 C48.9343808,48.238649 48.9199364,48.2419823 48.905492,48.2408712 C48.8749364,48.238649 48.8482697,48.2119823 48.8188253,48.2147601 C48.785492,48.2180934 48.7799364,48.2530934 48.7599364,48.2725379 C48.7438253,48.2864268 48.7238253,48.2864268 48.7182697,48.2642045 C48.715492,48.2358712 48.7260475,48.218649 48.7410475,48.2003156 C48.7643808,48.1753156 48.7410475,48.1619823 48.710492,48.1597601 C48.6710475,48.1603156 48.6643808,48.1908712 48.6482697,48.223649 C48.6227142,48.2592045 48.6060475,48.2342045 48.570492,48.228649 C48.5460475,48.2303156 48.5288253,48.2397601 48.5066031,48.2303156 C48.4899364,48.2253156 48.4866031,48.2114268 48.4749364,48.2047601 C48.4577142,48.1947601 48.4432697,48.1980934 48.430492,48.2064268 C48.4121586,48.2108712 48.4121586,48.2108712 48.3943808,48.2008712 C48.3782697,48.1953156 48.3749364,48.1814268 48.3538253,48.1775379 C48.3227142,48.1708712 48.2899364,48.1980934 48.2632697,48.1903156 C48.2516031,48.183649 48.2421586,48.1669823 48.2260475,48.1614268 C48.2082697,48.1514268 48.2110475,48.1603156 48.1993808,48.1730934 C48.1799364,48.1925379 48.1516031,48.1992045 48.1327142,48.1847601 C48.1088253,48.1669823 48.1060475,48.138649 48.0699364,48.1330934 Z M48.8121586,48.8430934 C48.805492,48.8297601 48.8127142,48.8164268 48.8127142,48.803649 C48.8093808,48.7803156 48.8027142,48.773649 48.8066031,48.7508712 C48.8132697,48.7375379 48.8132697,48.7175379 48.8099364,48.7008712 C48.8038253,48.6875379 48.7938253,48.6780934 48.7832697,48.6680934 C48.7838253,48.6614268 48.780492,48.6514268 48.7738253,48.6447601 C48.7599364,48.6314268 48.7466031,48.6508712 48.7327142,48.6575379 C48.7227142,48.6675379 48.7021586,48.673649 48.6982697,48.683649 C48.6882697,48.7003156 48.6949364,48.713649 48.6949364,48.7269823 L48.6977142,48.7430934 C48.6738253,48.7664268 48.6971586,48.8292045 48.6932697,48.8525379 C48.6932697,48.878649 48.655492,48.9542045 48.7066031,48.9314268 C48.7199364,48.9247601 48.730492,48.9153156 48.7438253,48.908649 C48.7610475,48.898649 48.7810475,48.8992045 48.8016031,48.8925379 C48.8082697,48.8925379 48.845492,48.8897601 48.845492,48.8830934 C48.8460475,48.8697601 48.815492,48.8597601 48.8121586,48.8430934 Z M49.2532697,49.4003156 C49.2616031,49.3942045 49.2743808,49.4003156 49.2810475,49.3980934 C49.2982697,49.3953156 49.3066031,49.378649 49.3216031,49.3808712 C49.3388253,49.3780934 49.3299364,49.3947601 49.3410475,49.4053156 C49.3521586,49.4153156 49.3627142,49.4153156 49.3732697,49.4153156 C49.3927142,49.4192045 49.4293808,49.4203156 49.4371586,49.403649 C49.445492,49.3764268 49.400492,49.3708712 49.3899364,49.3497601 C49.3782697,49.3192045 49.4032697,49.2892045 49.4116031,49.2619823 C49.4238253,49.2258712 49.3788253,49.2103156 49.3827142,49.1808712 C49.3816031,49.1492045 49.4032697,49.138649 49.3960475,49.1097601 C49.3916031,49.0869823 49.3716031,49.0619823 49.3588253,49.0453156 C49.345492,49.0292045 49.3216031,49.0125379 49.3232697,48.9875379 C49.3249364,48.9625379 49.3721586,48.963649 49.350492,48.9325379 C49.3371586,48.9058712 49.3027142,48.9108712 49.2727142,48.9069823 C49.2621586,48.9069823 49.2516031,48.9075379 49.240492,48.8969823 C49.2316031,48.8825379 49.235492,48.8742045 49.235492,48.863649 C49.2288253,48.8347601 49.2071586,48.8247601 49.1810475,48.8125379 C49.1721586,48.808649 49.1599364,48.8025379 49.1549364,48.7903156 C49.150492,48.7775379 49.1632697,48.7730934 49.1588253,48.7608712 C49.145492,48.733649 49.0949364,48.7725379 49.0732697,48.7619823 C49.0582697,48.7603156 49.0599364,48.7458712 49.0510475,48.7308712 L49.025492,48.7192045 C48.990492,48.7030934 48.9760475,48.7325379 48.9832697,48.7614268 C48.9993808,48.8258712 49.0471586,48.8692045 49.0416031,48.9342045 C49.0443808,48.9614268 49.0488253,48.973649 49.0577142,48.998649 C49.0671586,49.0342045 49.0766031,49.0514268 49.060492,49.0853156 C49.0349364,49.1042045 49.0577142,49.1280934 49.0671586,49.1530934 C49.0716031,49.1864268 49.080492,49.2114268 49.0788253,49.2469823 C49.0738253,49.3119823 49.0532697,49.3747601 49.0588253,49.4397601 C49.0616031,49.4664268 49.0593808,49.4919823 49.0688253,49.5169823 C49.0738253,49.5497601 49.0993808,49.5619823 49.1277142,49.5803156 C49.1538253,49.6030934 49.2771586,49.6758712 49.2410475,49.5869823 C49.2299364,49.5658712 49.2127142,49.5369823 49.2077142,49.5142045 C49.1988253,49.4892045 49.2288253,49.4719823 49.230492,49.4469823 C49.2338253,49.4175379 49.2127142,49.4075379 49.2532697,49.4003156 Z M48.2932697,41.9630934 C48.3249364,41.9964268 48.3632697,42.0064268 48.3560475,42.0603156 C48.3971586,42.0653156 48.4227142,42.0808712 48.4460475,42.0464268 C48.4599364,42.0247601 48.4827142,42.0080934 48.5066031,41.9992045 C48.5366031,41.9875379 48.6588253,41.988649 48.6532697,42.0380934 C48.650492,42.0630934 48.6349364,42.0853156 48.6310475,42.1092045 C48.6260475,42.1430934 48.6621586,42.118649 48.6793808,42.1275379 C48.660492,42.1397601 48.6382697,42.1480934 48.6160475,42.153649 C48.6260475,42.1592045 48.6316031,42.1680934 48.6332697,42.1792045 C48.605492,42.1858712 48.5916031,42.2625379 48.5432697,42.2775379 C48.5132697,42.2869823 48.470492,42.2669823 48.440492,42.2630934 C48.405492,42.258649 48.3788253,42.248649 48.3438253,42.2464268 C48.3093808,42.2442045 48.3371586,42.1997601 48.2949364,42.2080934 C48.2871586,42.2375379 48.3027142,42.3142045 48.3071586,42.343649 C48.3132697,42.3814268 48.3443808,42.4030934 48.3816031,42.4092045 C48.4332697,42.4175379 48.4582697,42.4347601 48.5027142,42.4597601 C48.5377142,42.4797601 48.5771586,42.4680934 48.615492,42.4714268 C48.6399364,42.473649 48.6616031,42.483649 48.680492,42.498649 C48.6766031,42.5108712 48.6677142,42.5303156 48.6732697,42.5430934 C48.6810475,42.5597601 48.7349364,42.5403156 48.7482697,42.5392045 C48.7860475,42.5353156 48.8216031,42.493649 48.8577142,42.4992045 C48.8716031,42.5014268 48.935492,42.5208712 48.9327142,42.5364268 C48.8982697,42.5225379 48.8760475,42.5647601 48.8499364,42.5414268 C48.8260475,42.5208712 48.7632697,42.5392045 48.8038253,42.5675379 C48.8077142,42.5703156 48.8182697,42.6508712 48.8171586,42.6592045 C48.8143808,42.6897601 48.760492,42.7214268 48.7643808,42.7408712 C48.7716031,42.7419823 48.820492,42.7458712 48.8316031,42.7547601 C48.8349364,42.7414268 48.8249364,42.7364268 48.8549364,42.7280934 C48.8771586,42.7214268 48.9032697,42.7192045 48.925492,42.7297601 C48.9349364,42.7669823 48.9071586,42.8058712 48.9588253,42.7964268 C49.0077142,42.7869823 49.0288253,42.818649 49.0799364,42.7858712 C49.1110475,42.7664268 49.1460475,42.7703156 49.1732697,42.7969823 C49.2099364,42.8342045 49.1349364,42.8842045 49.1777142,42.9192045 C49.1949364,42.933649 49.2088253,42.9764268 49.225492,42.9842045 C49.2371586,42.9892045 49.3010475,42.9669823 49.3127142,42.9608712 C49.3327142,42.9980934 49.3510475,42.9425379 49.3677142,42.938649 C49.3727142,42.9158712 49.4027142,42.8880934 49.4316031,42.8853156 C49.4743808,42.8814268 49.4749364,42.888649 49.5038253,42.908649 C49.5871586,42.9669823 49.575492,42.8253156 49.6193808,42.7892045 C49.6982697,42.7247601 49.7371586,42.6642045 49.7910475,42.5808712 C49.8338253,42.5153156 49.8927142,42.498649 49.9677142,42.4869823 C50.0277142,42.4775379 50.1182697,42.463649 50.1427142,42.3997601 C50.1716031,42.3247601 50.1032697,42.283649 50.0421586,42.2614268 C49.9743808,42.2375379 49.8977142,42.2119823 49.9277142,42.1264268 C49.9627142,42.0269823 49.9321586,41.9692045 49.8266031,41.9369823 C49.6027142,41.8675379 49.4010475,41.7503156 49.1732697,41.6864268 C48.9716031,41.6303156 48.7682697,41.6092045 48.5610475,41.5980934 C48.4688253,41.5664268 48.2743808,41.5630934 48.2177142,41.6414268 C48.1810475,41.6919823 48.2277142,41.7358712 48.2232697,41.788649 C48.2177142,41.8514268 48.2488253,41.9158712 48.2932697,41.9630934 Z M47.8549364,50.1964268 C47.8977142,50.2175379 47.9710475,50.1764268 48.0121586,50.1630934 C48.0627142,50.1475379 48.1432697,50.0942045 48.195492,50.123649 C48.2166031,50.1353156 48.2266031,50.1597601 48.2499364,50.1697601 C48.2771586,50.1814268 48.3099364,50.1708712 48.3371586,50.1647601 C48.3660475,50.1580934 48.3982697,50.153649 48.4249364,50.1408712 C48.4482697,50.1297601 48.4632697,50.1103156 48.4832697,50.0947601 C48.5360475,50.0558712 48.580492,50.0925379 48.6377142,50.0842045 C48.6699364,50.0797601 48.700492,50.063649 48.7327142,50.0558712 C48.7560475,50.0497601 48.7971586,50.0503156 48.815492,50.0330934 C48.8360475,50.013649 48.8266031,49.9719823 48.8260475,49.9475379 C48.8249364,49.9142045 48.8277142,49.8803156 48.815492,49.848649 C48.7921586,49.7875379 48.7077142,49.7158712 48.7949364,49.6680934 C48.8121586,49.5608712 48.6877142,49.5792045 48.6516031,49.5019823 C48.6282697,49.4514268 48.620492,49.4130934 48.5538253,49.4075379 C48.4966031,49.4025379 48.4638253,49.4319823 48.4143808,49.4514268 C48.3593808,49.4725379 48.3171586,49.4542045 48.2693808,49.4269823 C48.2410475,49.4108712 48.1799364,49.373649 48.1682697,49.4230934 C48.1582697,49.4658712 48.1988253,49.5075379 48.1649364,49.5458712 C48.135492,49.5797601 48.0838253,49.5953156 48.0410475,49.6047601 C47.9488253,49.6242045 47.8766031,49.6942045 47.8099364,49.7553156 L47.8166031,49.7619823 C47.790492,49.7614268 47.750492,49.8314268 47.7493808,49.8530934 C47.7599364,49.8558712 47.770492,49.8592045 47.7816031,49.8625379 C47.7799364,49.8992045 47.8221586,49.8747601 47.8243808,49.848649 C47.8332697,49.8503156 47.8421586,49.8558712 47.850492,49.8575379 C47.8577142,49.8592045 47.8738253,49.858649 47.8810475,49.8614268 C47.8982697,49.8692045 47.9016031,49.8864268 47.9243808,49.888649 C47.9110475,49.9442045 47.9227142,50.0030934 47.895492,50.0547601 C47.8788253,50.0875379 47.7927142,50.1669823 47.8549364,50.1964268 Z M42.7410475,50.5614268 C42.7532697,50.5408712 42.7432697,50.4858712 42.720492,50.4675379 C42.6638253,50.4208712 42.6421586,50.5319823 42.6777142,50.5647601 C42.6916031,50.5997601 42.7249364,50.588649 42.7410475,50.5614268 Z M45.9971586,42.3703156 C45.9777142,42.3692045 45.9566031,42.3708712 45.9388253,42.378649 C45.9343808,42.3803156 45.9288253,42.3847601 45.9249364,42.3892045 C45.9316031,42.3903156 45.9382697,42.3903156 45.9443808,42.388649 C45.9632697,42.3847601 45.9777142,42.3714268 45.9971586,42.3703156 Z M48.0010475,41.6769823 C48.0016031,41.6792045 48.0021586,41.6819823 48.0027142,41.6842045 C48.035492,41.6769823 48.0677142,41.6869823 48.0988253,41.6747601 C48.1149364,41.6680934 48.1660475,41.6497601 48.1632697,41.628649 C48.1577142,41.5892045 47.9893808,41.6147601 47.9621586,41.6264268 C47.9532697,41.6508712 47.9793808,41.6703156 48.0010475,41.6769823 Z M45.9310475,42.053649 C45.9727142,42.0225379 45.995492,41.9653156 46.0621586,41.9769823 C46.0599364,42.0342045 46.1160475,42.0414268 46.1571586,42.0630934 C46.1443808,42.0969823 46.0982697,42.0958712 46.0777142,42.1225379 C46.0521586,42.1553156 46.0982697,42.1847601 46.1216031,42.1992045 C46.1671586,42.2275379 46.1427142,42.258649 46.1332697,42.2992045 C46.120492,42.3547601 46.2410475,42.3397601 46.2671586,42.338649 C46.3132697,42.3358712 46.385492,42.3442045 46.4282697,42.3219823 C46.475492,42.2969823 46.4999364,42.2397601 46.5482697,42.2119823 C46.5882697,42.1892045 46.645492,42.1758712 46.690492,42.1919823 C46.7371586,42.2080934 46.7316031,42.2675379 46.7693808,42.293649 C46.8149364,42.3242045 46.8627142,42.3342045 46.9016031,42.2880934 C46.925492,42.258649 46.980492,42.2225379 46.9827142,42.1919823 C46.9866031,42.1375379 47.0021586,42.0942045 47.0632697,42.0825379 C47.1110475,42.0730934 47.1010475,42.1203156 47.1349364,42.128649 C47.2093808,42.1475379 47.245492,41.918649 47.3277142,41.9864268 C47.3471586,42.0025379 47.3527142,42.0642045 47.3849364,42.0592045 C47.4188253,42.053649 47.4199364,42.003649 47.4566031,42.0030934 C47.4682697,42.0369823 47.3932697,42.0775379 47.3832697,42.1130934 C47.4293808,42.0758712 47.4516031,42.0808712 47.5038253,42.0758712 C47.5177142,42.1114268 47.415492,42.168649 47.3893808,42.1753156 C47.3521586,42.183649 47.3299364,42.1630934 47.3016031,42.1825379 C47.2788253,42.198649 47.2482697,42.1969823 47.2221586,42.1997601 C47.1843808,42.2030934 47.1149364,42.2525379 47.115492,42.293649 C47.115492,42.3097601 47.1282697,42.3458712 47.1149364,42.3592045 C47.1016031,42.3725379 47.0716031,42.3597601 47.0677142,42.3458712 C47.035492,42.3930934 46.9949364,42.3108712 46.9682697,42.3769823 C47.0110475,42.3869823 47.0493808,42.428649 47.0977142,42.4403156 C47.1449364,42.4519823 47.190492,42.4630934 47.2371586,42.4769823 C47.3138253,42.5003156 47.4293808,42.4075379 47.4893808,42.3619823 C47.5449364,42.3203156 47.6160475,42.2258712 47.6310475,42.1580934 C47.6477142,42.0853156 47.7288253,42.0003156 47.7121586,41.9269823 C47.6966031,41.8592045 47.6860475,41.8264268 47.7632697,41.8025379 C47.7966031,41.7919823 47.875492,41.7747601 47.8871586,41.7364268 C47.9043808,41.6808712 47.7299364,41.6958712 47.7032697,41.683649 C47.6149364,41.6442045 47.5771586,41.5997601 47.475492,41.6392045 C47.4221586,41.6597601 47.3699364,41.6769823 47.3149364,41.6919823 C47.2860475,41.6997601 47.2571586,41.7019823 47.2416031,41.7258712 C47.235492,41.7358712 47.2271586,41.743649 47.2166031,41.748649 C47.1710475,41.7664268 47.2271586,41.6803156 47.2321586,41.6747601 C47.2471586,41.6580934 47.2716031,41.6053156 47.2238253,41.6164268 C47.1538253,41.6330934 47.1032697,41.7403156 47.0271586,41.7458712 C46.9699364,41.7497601 46.9882697,41.6992045 47.0038253,41.6703156 C47.0316031,41.6180934 46.9499364,41.6119823 46.9138253,41.6114268 C46.8638253,41.6114268 46.8249364,41.6403156 46.7766031,41.6453156 C46.7310475,41.6503156 46.6782697,41.6592045 46.6327142,41.6575379 C46.540492,41.6542045 46.480492,41.7075379 46.3899364,41.6775379 C46.2949364,41.6464268 46.1916031,41.7258712 46.0999364,41.7369823 C46.0693808,41.7408712 46.0243808,41.7342045 46.0121586,41.7708712 C46.0016031,41.8008712 46.0121586,41.8475379 46.035492,41.8697601 L46.0427142,41.863649 C46.0227142,41.8830934 46.0199364,41.9119823 45.9910475,41.9225379 C45.9621586,41.9330934 45.9332697,41.9703156 45.9182697,41.9953156 C45.9066031,42.0158712 45.8738253,42.0953156 45.9310475,42.053649 Z M46.0216031,43.2903156 C46.045492,43.2597601 45.9877142,43.2325379 45.9543808,43.2358712 C45.9632697,43.1980934 46.010492,43.178649 45.9982697,43.1314268 C45.9860475,43.0825379 45.9271586,43.0919823 45.895492,43.118649 C45.8660475,43.143649 45.8510475,43.1864268 45.8266031,43.2147601 C45.8132697,43.2314268 45.7882697,43.2364268 45.7788253,43.2564268 C45.7699364,43.2747601 45.780492,43.3053156 45.7799364,43.3253156 C45.8188253,43.3303156 45.860492,43.318649 45.8888253,43.2908712 L45.9077142,43.2819823 C45.9038253,43.2858712 45.9010475,43.293649 45.8993808,43.2975379 C45.9182697,43.3214268 46.0032697,43.3147601 46.0216031,43.2903156 Z M43.5977142,52.0842045 C43.6038253,52.0658712 43.575492,52.0453156 43.5560475,52.0469823 C43.5388253,52.0480934 43.5232697,52.0708712 43.5160475,52.083649 C43.4960475,52.1214268 43.5193808,52.1719823 43.5682697,52.1719823 C43.5793808,52.1514268 43.5749364,52.1114268 43.605492,52.108649 C43.6038253,52.0992045 43.5971586,52.0958712 43.5882697,52.0925379 L43.5977142,52.0842045 Z M53.2532697,50.4553156 C53.2510475,50.4464268 53.2488253,50.4369823 53.2471586,50.4275379 C53.2038253,50.4147601 53.175492,50.4603156 53.135492,50.4269823 C53.0582697,50.478649 53.1410475,50.583649 53.0138253,50.5764268 C53.035492,50.6030934 53.0332697,50.633649 53.0232697,50.6642045 C53.0066031,50.7130934 52.9949364,50.708649 52.9616031,50.7142045 C52.8932697,50.7258712 52.8593808,50.6825379 52.8388253,50.6253156 C52.7710475,50.6264268 52.6777142,50.7319823 52.6238253,50.7647601 C52.6110475,50.7730934 52.5860475,50.7953156 52.5710475,50.8042045 C52.5593808,50.8114268 52.5310475,50.8253156 52.5160475,50.8330934 C52.480492,50.8519823 52.4027142,50.8764268 52.3982697,50.9169823 C52.380492,50.9142045 52.3527142,50.9247601 52.335492,50.9225379 C52.3293808,50.9314268 52.3293808,50.9408712 52.335492,50.9497601 C52.4177142,50.9642045 52.4610475,50.9364268 52.530492,50.9058712 C52.6043808,50.873649 52.6832697,50.8803156 52.7516031,50.8547601 C52.7843808,50.8430934 52.785492,50.8064268 52.8393808,50.828649 C52.8627142,50.838649 52.8910475,50.8730934 52.8960475,50.8969823 C52.9066031,50.9503156 52.850492,51.0292045 52.7943808,51.0330934 C52.7810475,50.9997601 52.7999364,50.9658712 52.8060475,50.938649 C52.7327142,50.913649 52.610492,51.018649 52.5899364,51.0808712 C52.6671586,51.0964268 52.6982697,51.208649 52.6577142,51.2692045 C52.6438253,51.2842045 52.6282697,51.303649 52.6010475,51.3119823 C52.5577142,51.3247601 52.5371586,51.2847601 52.4949364,51.3164268 C52.4393808,51.3580934 52.500492,51.4725379 52.4688253,51.5369823 C52.4443808,51.5858712 52.4027142,51.6042045 52.3682697,51.638649 C52.3443808,51.6630934 52.3310475,51.6897601 52.2988253,51.7114268 C52.2571586,51.7392045 52.1566031,51.798649 52.165492,51.8569823 C52.2560475,51.8875379 52.4449364,51.7292045 52.5238253,51.6769823 C52.5738253,51.643649 52.6038253,51.5919823 52.6549364,51.5597601 C52.7121586,51.523649 52.7877142,51.5058712 52.8249364,51.4419823 C52.8460475,51.4064268 52.8293808,51.3742045 52.8421586,51.3380934 C52.8532697,51.3058712 52.8760475,51.2953156 52.8971586,51.2703156 C52.9360475,51.2230934 52.9727142,51.208649 53.0171586,51.1692045 C53.0721586,51.1203156 53.0593808,51.0430934 53.0843808,50.9780934 C53.1066031,50.9208712 53.150492,50.8780934 53.180492,50.8247601 C53.2288253,50.7392045 53.3538253,50.5375379 53.3016031,50.4430934 C53.2882697,50.4530934 53.2643808,50.4508712 53.2532697,50.4553156 Z M51.8121586,51.8869823 L51.810492,51.8853156 C51.8138253,51.8914268 51.8127142,51.9003156 51.8132697,51.9080934 C51.8549364,51.9080934 51.8727142,51.9453156 51.9171586,51.9319823 C51.9632697,51.918649 51.9888253,51.8753156 51.9527142,51.8392045 C51.9216031,51.8075379 51.8938253,51.7814268 51.8466031,51.7897601 C51.7921586,51.8003156 51.8043808,51.8442045 51.8121586,51.8869823 Z M42.780492,47.3764268 C42.7943808,47.3864268 42.8127142,47.3953156 42.8266031,47.4047601 C42.840492,47.4147601 42.8621586,47.4319823 42.8788253,47.4364268 C42.9177142,47.4575379 42.9777142,47.4814268 43.0066031,47.4364268 C43.015492,47.4180934 43.0210475,47.4064268 43.010492,47.3908712 C42.9988253,47.3747601 42.9821586,47.3703156 42.9777142,47.3575379 C42.9727142,47.3442045 42.9843808,47.3347601 42.970492,47.3247601 C42.9549364,47.3108712 42.9432697,47.3197601 42.9421586,47.2903156 C42.9432697,47.2653156 42.9449364,47.2147601 42.9110475,47.2469823 C42.9021586,47.2497601 42.9143808,47.2553156 42.900492,47.2603156 C42.8916031,47.263649 42.8838253,47.2564268 42.8777142,47.2530934 C42.8593808,47.2453156 42.8482697,47.243649 42.835492,47.2630934 C42.8249364,47.2769823 42.825492,47.2914268 42.8088253,47.3025379 L42.7821586,47.3114268 C42.7727142,47.3147601 42.7443808,47.3347601 42.7432697,47.3447601 C42.7388253,47.3614268 42.7638253,47.3725379 42.780492,47.3764268 Z M52.1093808,51.7808712 C52.115492,51.7503156 52.1788253,51.6397601 52.1199364,51.623649 C52.0988253,51.6175379 52.080492,51.6503156 52.0610475,51.6558712 C52.0360475,51.663649 52.0088253,51.6475379 51.9849364,51.6603156 C51.9632697,51.6714268 51.9438253,51.7058712 51.9310475,51.7253156 C51.9149364,51.7492045 51.9210475,51.7580934 51.9466031,51.7714268 C51.9727142,51.7853156 52.005492,51.7919823 52.0199364,51.8197601 C52.0332697,51.8447601 52.0266031,51.8769823 52.0227142,51.9030934 C52.0238253,51.9014268 52.0271586,51.8980934 52.0282697,51.893649 C52.0327142,51.8925379 52.0399364,51.8914268 52.0443808,51.8925379 L52.0388253,51.903649 C52.1071586,51.9130934 52.1016031,51.8253156 52.1093808,51.7808712 Z M54.3488253,48.1230934 C54.3282697,48.0014268 54.3982697,47.8325379 54.335492,47.7158712 C54.3082697,47.6647601 54.3499364,47.5169823 54.3493808,47.4564268 C54.3482697,47.3442045 54.3149364,47.258649 54.2993808,47.153649 C54.2888253,47.0558712 54.2760475,46.7947601 54.3116031,46.7058712 C54.3621586,46.5803156 54.1143808,46.3680934 54.100492,46.2325379 C54.0882697,46.1153156 54.0210475,46.003649 53.9249364,45.9358712 C53.885492,45.9075379 53.800492,45.5192045 53.750492,45.5369823 C53.725492,45.548649 53.7771586,45.6342045 53.7738253,45.6608712 C53.760492,45.7558712 53.7127142,45.6614268 53.6621586,45.6825379 C53.570492,45.7203156 53.4732697,45.8080934 53.4610475,45.8997601 C53.4171586,46.2369823 53.1782697,45.888649 53.1971586,45.8742045 C53.250492,45.8330934 53.2693808,45.8458712 53.3310475,45.8369823 C53.400492,45.8125379 53.2932697,45.763649 53.4021586,45.7530934 C53.3766031,45.6842045 53.4332697,45.6614268 53.405492,45.6064268 C53.3643808,45.5264268 53.3349364,45.5358712 53.3766031,45.4497601 C53.3943808,45.4019823 53.2866031,45.253649 53.2777142,45.1964268 C53.2688253,45.1408712 53.2677142,45.068649 53.260492,45.0064268 C53.2560475,44.9669823 53.3210475,44.928649 53.3099364,44.8980934 C53.3071586,44.7875379 53.3316031,44.6680934 53.2927142,44.5619823 C53.265492,44.488649 53.2338253,44.3930934 53.1871586,44.3308712 C53.170492,44.308649 53.120492,44.1980934 53.1160475,44.1680934 C53.1027142,44.0992045 53.0721586,44.1253156 53.0332697,44.0975379 C53.0121586,44.0714268 52.9166031,43.9825379 52.8888253,43.9697601 C52.8638253,43.9580934 52.6793808,43.7897601 52.6749364,43.7764268 C52.6599364,43.7275379 52.5560475,43.6903156 52.5671586,43.6380934 C52.5843808,43.5580934 52.3071586,43.3525379 52.2299364,43.3397601 C52.1799364,43.3308712 52.3827142,43.5719823 52.3816031,43.5675379 C52.3849364,43.5814268 52.5121586,43.7425379 52.5116031,43.7425379 C52.5410475,43.7525379 52.610492,43.9503156 52.6082697,43.9758712 C52.600492,44.0514268 52.4066031,43.8530934 52.3921586,43.8264268 C52.2971586,43.7092045 52.1232697,43.6153156 52.0143808,43.5369823 C51.9371586,43.4658712 51.975492,43.4247601 51.8432697,43.3642045 C51.7938253,43.3414268 51.6616031,43.2325379 51.620492,43.2308712 C51.570492,43.2275379 51.6271586,43.3330934 51.6288253,43.3447601 C51.6366031,43.4197601 51.7182697,43.4253156 51.7666031,43.4747601 C51.8060475,43.5147601 51.840492,43.5653156 51.8066031,43.6097601 C51.8060475,43.6103156 51.745492,43.7214268 51.7421586,43.7114268 C51.7593808,43.758649 51.8999364,43.8630934 51.9349364,43.9019823 C51.9288253,43.8914268 52.1199364,44.1408712 52.1360475,44.008649 C52.1421586,43.9608712 52.0882697,43.9025379 52.095492,43.8614268 C52.100492,43.8347601 52.3138253,44.103649 52.3249364,44.1275379 C52.3777142,44.2697601 52.3732697,44.1058712 52.4271586,44.1225379 C52.4721586,44.1369823 52.5916031,44.2853156 52.4927142,44.2914268 C52.3399364,44.3019823 52.5199364,44.433649 52.5621586,44.453649 C52.650492,44.4964268 52.7099364,44.5958712 52.8021586,44.6330934 C52.9449364,44.6914268 52.9149364,44.7942045 52.9877142,44.8992045 C53.010492,44.9325379 52.5760475,44.898649 52.5416031,44.918649 C52.4843808,44.9625379 52.7599364,45.2708712 52.7616031,45.3169823 C52.7638253,45.4130934 52.8182697,45.483649 52.8327142,45.5803156 C52.8421586,45.6692045 52.8310475,45.7858712 52.8893808,45.8575379 C52.9371586,45.9014268 52.9816031,45.7847601 53.0566031,45.853649 C53.0843808,45.8664268 53.1238253,45.8975379 53.1327142,45.9214268 C53.1571586,45.9875379 53.3016031,46.393649 53.1288253,46.3630934 C53.0527142,46.3497601 53.0960475,46.6819823 53.1010475,46.7303156 C53.125492,46.8353156 53.1671586,46.8275379 53.1399364,46.9597601 C53.1427142,47.0703156 53.0532697,47.1258712 52.990492,47.2047601 C52.9610475,47.2408712 52.9416031,47.283649 52.9271586,47.3297601 C52.8921586,47.2958712 52.875492,47.2414268 52.8271586,47.2230934 C52.7738253,47.2030934 52.6571586,47.2664268 52.610492,47.2897601 C52.4977142,47.3458712 52.580492,47.448649 52.5366031,47.5375379 C52.5049364,47.6025379 52.4088253,47.6347601 52.3471586,47.6669823 C52.2716031,47.7053156 52.1710475,47.7464268 52.0960475,47.6847601 C52.0327142,47.6325379 52.060492,47.5292045 51.9932697,47.4842045 C51.9177142,47.433649 51.9110475,47.5464268 51.8938253,47.5908712 C51.8610475,47.6758712 51.7627142,47.7047601 51.7877142,47.8108712 C51.7982697,47.8542045 51.820492,47.893649 51.8293808,47.9375379 C51.8399364,47.9925379 51.8127142,48.0419823 51.8088253,48.0964268 C51.8032697,48.1892045 51.8999364,48.2108712 51.9260475,48.2875379 C51.9482697,48.3547601 51.9249364,48.4614268 51.8482697,48.4875379 C51.7666031,48.5158712 51.6766031,48.4419823 51.5960475,48.433649 C51.5149364,48.4253156 51.4127142,48.4480934 51.3971586,48.5397601 C51.3832697,48.6214268 51.4666031,48.6875379 51.4266031,48.7708712 C51.4093808,48.8058712 51.3799364,48.833649 51.3577142,48.8658712 C51.3210475,48.9192045 51.300492,48.9819823 51.265492,49.0364268 C51.3066031,49.0375379 51.3027142,49.0119823 51.3393808,49.0197601 C51.3788253,49.0275379 51.4132697,48.988649 51.4466031,48.9753156 C51.4521586,49.0008712 51.4493808,49.0275379 51.4527142,49.053649 C51.4799364,49.0619823 51.5077142,49.0508712 51.5321586,49.0414268 C51.535492,49.0642045 51.5249364,49.0903156 51.5343808,49.1125379 C51.5421586,49.1319823 51.5638253,49.1380934 51.5766031,49.1530934 C51.6099364,49.1930934 51.5693808,49.2580934 51.5460475,49.2919823 C51.4788253,49.3903156 51.3649364,49.4453156 51.290492,49.5369823 C51.220492,49.6230934 51.2138253,49.7264268 51.1621586,49.8197601 C51.1443808,49.8514268 51.1260475,49.8953156 51.1743808,49.9108712 C51.1838253,49.893649 51.2010475,49.8797601 51.2221586,49.8797601 C51.255492,49.8792045 51.2432697,49.9030934 51.2593808,49.9230934 C51.3288253,50.0103156 51.4021586,49.8653156 51.4310475,49.823649 C51.4616031,49.7797601 51.5843808,49.7119823 51.6188253,49.7869823 C51.6449364,49.843649 51.6143808,49.9242045 51.5877142,49.9753156 C51.6316031,49.9953156 51.6193808,50.0275379 51.6293808,50.0658712 C51.6432697,50.1192045 51.6927142,50.153649 51.6927142,50.2119823 C51.6927142,50.2830934 51.5377142,50.4242045 51.5977142,50.4769823 C51.6732697,50.5430934 51.7627142,50.3597601 51.7916031,50.3203156 C51.8482697,50.2442045 51.9766031,50.233649 52.0077142,50.1397601 C52.0432697,50.0342045 52.0310475,49.9714268 52.1732697,49.9680934 C52.2327142,49.9669823 52.275492,49.928649 52.3316031,49.9175379 C52.3932697,49.9053156 52.420492,49.898649 52.4599364,49.8514268 C52.5166031,49.7847601 52.5666031,49.8647601 52.5693808,49.9180934 C52.5716031,49.9730934 52.5471586,50.0419823 52.580492,50.0903156 C52.6221586,50.1508712 52.6682697,50.0708712 52.7099364,50.0330934 C52.7060475,50.0730934 52.760492,50.0969823 52.7916031,50.1097601 C52.8410475,50.0764268 52.8716031,50.0219823 52.9238253,49.9919823 C52.9482697,49.978649 52.975492,49.9730934 53.0027142,49.9680934 C53.010492,50.0119823 53.0177142,50.0592045 53.0571586,50.0808712 C53.1132697,50.1130934 53.0443808,50.1442045 53.1182697,50.178649 C53.2110475,50.213649 53.2416031,50.3069823 53.2866031,50.3819823 C53.3071586,50.4158712 53.4999364,50.2080934 53.5749364,50.1997601 C53.820492,50.1719823 53.9482697,49.8525379 54.0399364,49.6625379 C54.1716031,49.3919823 54.230492,49.0908712 54.2649364,48.8108712 C54.3488253,48.6392045 54.3827142,48.323649 54.3488253,48.1230934 Z M52.9038253,50.4425379 C52.9049364,50.4747601 52.9399364,50.4703156 52.9643808,50.4625379 C52.9849364,50.4558712 52.9988253,50.4375379 53.0121586,50.4208712 C53.0316031,50.3969823 53.0427142,50.3719823 53.0266031,50.343649 C53.0088253,50.3130934 52.9982697,50.2914268 52.9899364,50.2564268 C52.9760475,50.2642045 52.9593808,50.2764268 52.9449364,50.2819823 C52.9293808,50.2869823 52.9277142,50.2825379 52.9110475,50.2819823 C52.8716031,50.2797601 52.8793808,50.3103156 52.8627142,50.3347601 C52.8488253,50.3558712 52.8182697,50.3647601 52.8299364,50.3925379 C52.8388253,50.413649 52.8738253,50.4325379 52.8932697,50.4414268 L52.8988253,50.4358712 C52.8977142,50.4369823 52.8966031,50.438649 52.895492,50.4403156 C52.8982697,50.4414268 52.9010475,50.4419823 52.9038253,50.4425379 Z M42.7499364,50.8092045 C42.7449364,50.7747601 42.7316031,50.7530934 42.705492,50.7330934 C42.7043808,50.7358712 42.7032697,50.738649 42.7032697,50.7425379 C42.6677142,50.7247601 42.6627142,50.6564268 42.6149364,50.6614268 C42.5743808,50.5975379 42.4716031,50.6258712 42.440492,50.6853156 C42.4193808,50.7247601 42.4443808,50.7419823 42.4682697,50.7714268 C42.4977142,50.8075379 42.4921586,50.8353156 42.5027142,50.8780934 C42.5293808,50.9858712 42.6193808,50.9114268 42.6832697,50.9542045 C42.7071586,50.9692045 42.7177142,51.0158712 42.7527142,51.0075379 C42.7932697,50.998649 42.7910475,50.938649 42.780492,50.9114268 C42.7660475,50.8730934 42.755492,50.8508712 42.7499364,50.8092045 Z M42.6960475,50.7253156 C42.6993808,50.7280934 42.7027142,50.7303156 42.705492,50.7330934 C42.7082697,50.7275379 42.7121586,50.723649 42.7143808,50.7164268 L42.6960475,50.7253156 Z M52.7827142,52.1864268 C52.740492,52.2058712 52.665492,52.1958712 52.6321586,52.2269823 C52.6138253,52.243649 52.6188253,52.263649 52.6110475,52.2842045 C52.6010475,52.308649 52.5638253,52.3175379 52.5438253,52.3308712 C52.5116031,52.3519823 52.5010475,52.3992045 52.4666031,52.4114268 C52.4516031,52.3892045 52.4288253,52.3253156 52.3988253,52.3808712 C52.3799364,52.4153156 52.3927142,52.4503156 52.3632697,52.4830934 C52.335492,52.513649 52.3371586,52.5497601 52.3171586,52.5830934 C52.2860475,52.6353156 52.2560475,52.6664268 52.2038253,52.6975379 C52.1616031,52.7225379 52.1538253,52.7675379 52.1238253,52.8025379 C52.0916031,52.8392045 52.0438253,52.853649 51.9999364,52.8725379 C51.9693808,52.8858712 51.9182697,52.9225379 51.8838253,52.9014268 C51.8360475,52.8725379 51.900492,52.8214268 51.9227142,52.8030934 C51.9432697,52.7858712 52.0438253,52.7253156 52.0210475,52.6908712 C52.005492,52.668649 51.9471586,52.6714268 51.9243808,52.673649 C51.8771586,52.678649 51.840492,52.7264268 51.800492,52.7497601 C51.755492,52.7753156 51.7160475,52.7969823 51.6666031,52.8142045 C51.6088253,52.8342045 51.6038253,52.8875379 51.5588253,52.9203156 C51.5221586,52.9475379 51.4766031,52.9692045 51.4293808,52.9692045 C51.3677142,52.968649 51.3710475,52.9180934 51.3532697,52.8753156 C51.3299364,52.8780934 51.3093808,52.908649 51.2877142,52.9180934 C51.2510475,52.9330934 51.2282697,52.9492045 51.2432697,52.9897601 C51.2588253,53.0297601 51.0943808,53.0680934 51.0660475,53.0880934 C51.0588253,53.0680934 51.0910475,53.0458712 51.1038253,53.0347601 C51.0527142,53.0303156 50.9960475,53.0742045 50.9443808,53.0814268 C50.8943808,53.088649 50.8327142,53.1219823 50.825492,53.1742045 C50.8199364,53.2142045 50.7649364,53.213649 50.7316031,53.2275379 C50.675492,53.2503156 50.6988253,53.2842045 50.6877142,53.3292045 C50.6666031,53.4142045 50.4899364,53.3492045 50.5799364,53.2369823 C50.6116031,53.1975379 50.6593808,53.1719823 50.6882697,53.1308712 C50.7210475,53.0842045 50.7260475,53.0247601 50.7516031,52.9742045 C50.695492,52.9903156 50.6527142,53.0225379 50.6049364,53.053649 C50.5510475,53.0892045 50.505492,53.0808712 50.445492,53.068649 C50.375492,53.053649 50.3271586,53.0892045 50.2621586,53.1047601 C50.220492,53.1147601 50.1282697,53.1114268 50.1232697,53.1714268 C50.1199364,53.2097601 50.1866031,53.2180934 50.2088253,53.2403156 C50.2421586,53.2742045 50.2771586,53.3247601 50.305492,53.3630934 C50.3282697,53.393649 50.410492,53.4353156 50.405492,53.473649 C50.3960475,53.5480934 50.3027142,53.5353156 50.2510475,53.548649 C50.1999364,53.5614268 50.1927142,53.6069823 50.155492,53.6347601 C50.1138253,53.6653156 50.0566031,53.6297601 50.0093808,53.648649 C49.9616031,53.6675379 49.9277142,53.7092045 49.8860475,53.7380934 C49.8160475,53.7853156 49.7710475,53.7330934 49.6988253,53.7303156 C49.6410475,53.7280934 49.5871586,53.7542045 49.5321586,53.7664268 C49.4821586,53.7775379 49.4227142,53.7858712 49.3832697,53.8214268 C49.300492,53.8958712 49.5699364,53.8764268 49.5943808,53.873649 C49.5832697,53.9169823 49.5721586,53.9669823 49.5327142,53.9953156 C49.4838253,54.0303156 49.4149364,54.0219823 49.3588253,54.0347601 C49.3149364,54.0447601 49.2543808,54.093649 49.3182697,54.1269823 C49.3749364,54.1564268 49.4488253,54.1408712 49.505492,54.1208712 C49.5716031,54.0980934 49.6332697,54.0630934 49.7021586,54.048649 C49.7743808,54.0325379 49.8493808,54.0397601 49.9221586,54.0264268 C49.9993808,54.0125379 50.0671586,53.9697601 50.1388253,53.9408712 C50.2077142,53.9130934 50.2793808,53.9030934 50.3538253,53.9003156 C50.3382697,53.9269823 50.2660475,53.9258712 50.2388253,53.9308712 C50.1843808,53.9419823 50.1477142,53.9892045 50.0910475,53.9864268 C50.0277142,53.983649 50.035492,54.0292045 49.9893808,54.0397601 C49.9599364,54.0464268 49.8893808,54.1153156 49.8671586,54.0730934 C49.8499364,54.0397601 49.8177142,54.0464268 49.8066031,54.0864268 C49.7982697,54.1164268 49.815492,54.1275379 49.7743808,54.1280934 C49.7416031,54.1280934 49.7277142,54.1153156 49.6982697,54.1080934 C49.6416031,54.093649 49.6160475,54.1519823 49.575492,54.1669823 C49.5160475,54.1892045 49.4527142,54.1825379 49.3960475,54.2197601 C49.3627142,54.2414268 49.3266031,54.2475379 49.2877142,54.2592045 C49.2160475,54.2808712 49.1493808,54.308649 49.0788253,54.3325379 C49.0232697,54.3508712 48.9682697,54.3742045 48.9088253,54.3753156 C48.8843808,54.3758712 48.7877142,54.3592045 48.7716031,54.3875379 C48.7416031,54.4408712 48.8327142,54.4208712 48.8521586,54.4103156 C48.9060475,54.3814268 48.9710475,54.3980934 49.0316031,54.3980934 C49.1060475,54.3980934 49.1682697,54.3830934 49.2343808,54.348649 C49.2527142,54.3397601 49.3727142,54.313649 49.3782697,54.3280934 C49.3893808,54.3342045 49.4693808,54.3053156 49.4827142,54.3019823 C49.5471586,54.2875379 49.6116031,54.2730934 49.6749364,54.2569823 C49.8710475,54.2064268 50.0643808,54.1303156 50.2560475,54.0653156 C50.6366031,53.9364268 50.9866031,53.7197601 51.3166031,53.4992045 C51.4666031,53.3992045 51.5882697,53.268649 51.750492,53.1875379 C51.9138253,53.1058712 52.0599364,52.9980934 52.2099364,52.8964268 C52.3566031,52.7964268 52.4677142,52.6597601 52.5893808,52.5342045 C52.7121586,52.4075379 52.8166031,52.2869823 52.8710475,52.1180934 C52.8399364,52.1114268 52.8082697,52.1742045 52.7827142,52.1864268 Z M48.9610475,53.198649 C49.0593808,53.0614268 48.9949364,52.8830934 49.075492,52.7403156 C49.1149364,52.6708712 49.1843808,52.6269823 49.2466031,52.578649 C49.3221586,52.5208712 49.3888253,52.453649 49.4488253,52.3803156 C49.4138253,52.3719823 49.4116031,52.3380934 49.3899364,52.3169823 C49.355492,52.283649 49.3038253,52.3058712 49.2960475,52.2553156 C49.2888253,52.2103156 49.2516031,52.2003156 49.215492,52.1819823 C49.1327142,52.1408712 49.0988253,52.0597601 49.0321586,52.0025379 C48.9582697,51.938649 48.8588253,51.9603156 48.770492,51.9425379 C48.6916031,51.9269823 48.6121586,51.8025379 48.5282697,51.8558712 C48.475492,51.888649 48.450492,51.9742045 48.4866031,52.0258712 C48.5149364,52.0669823 48.5643808,52.0869823 48.5838253,52.1353156 C48.5549364,52.1614268 48.5482697,52.1792045 48.5827142,52.203649 C48.6243808,52.2330934 48.7043808,52.2625379 48.6843808,52.3264268 C48.6732697,52.3603156 48.6449364,52.3930934 48.6077142,52.4003156 C48.580492,52.4053156 48.5160475,52.3897601 48.5299364,52.438649 C48.500492,52.358649 48.4166031,52.4825379 48.3716031,52.4180934 C48.3343808,52.3653156 48.3110475,52.318649 48.2527142,52.2830934 C48.1766031,52.2364268 48.2827142,52.1925379 48.2699364,52.1219823 C48.2510475,52.0203156 48.1271586,52.0480934 48.0821586,51.973649 C48.0566031,51.9303156 48.0927142,51.8964268 48.1093808,51.858649 C48.1260475,51.8203156 48.1721586,51.8508712 48.1960475,51.8625379 C48.275492,51.8997601 48.3927142,51.8864268 48.4593808,51.8308712 C48.490492,51.8053156 48.5582697,51.6864268 48.4716031,51.6875379 C48.4121586,51.6875379 48.3666031,51.7397601 48.310492,51.7430934 C48.3021586,51.6753156 48.2710475,51.5603156 48.3432697,51.5158712 C48.4088253,51.4753156 48.5466031,51.4280934 48.4877142,51.3225379 C48.4671586,51.2864268 48.4299364,51.3464268 48.3993808,51.3203156 C48.3860475,51.308649 48.3949364,51.2842045 48.3999364,51.2725379 C48.3793808,51.253649 48.360492,51.2319823 48.3499364,51.2064268 C48.3032697,51.0930934 48.4410475,51.003649 48.3832697,50.8842045 C48.3588253,50.833649 48.3171586,50.803649 48.2716031,50.7719823 C48.2260475,50.7403156 48.2238253,50.7003156 48.2071586,50.6519823 C48.1977142,50.6247601 48.1538253,50.5619823 48.115492,50.5775379 C48.0827142,50.5908712 48.0727142,50.6469823 48.0482697,50.6708712 C47.990492,50.7269823 47.870492,50.7475379 47.7927142,50.7297601 C47.7321586,50.7164268 47.7349364,50.6925379 47.705492,50.6514268 C47.695492,50.6369823 47.6743808,50.6353156 47.6588253,50.6292045 C47.630492,50.618649 47.6282697,50.5942045 47.6216031,50.5692045 C47.5960475,50.4764268 47.4110475,50.5914268 47.3827142,50.4614268 C47.3766031,50.4314268 47.3866031,50.3803156 47.3438253,50.373649 C47.2943808,50.3658712 47.2927142,50.318649 47.2927142,50.2792045 C47.2921586,50.2469823 47.295492,50.2019823 47.2610475,50.183649 C47.2171586,50.1603156 47.2066031,50.1708712 47.1932697,50.1214268 C47.1766031,50.0580934 47.1332697,50.1242045 47.0977142,50.1103156 C47.0216031,50.0814268 47.0349364,50.118649 46.9738253,50.1508712 C46.8688253,50.2058712 46.8577142,49.9480934 46.820492,49.8975379 C46.750492,49.8014268 46.7693808,50.0169823 46.7216031,50.043649 C46.6771586,50.0675379 46.6321586,50.0119823 46.6166031,49.978649 C46.6071586,49.958649 46.6010475,49.9375379 46.5893808,49.9180934 C46.5721586,49.8903156 46.5399364,49.878649 46.5221586,49.8508712 C46.5066031,49.8258712 46.4849364,49.7969823 46.4743808,49.7703156 C46.4649364,49.7469823 46.4666031,49.7169823 46.4488253,49.6975379 C46.4266031,49.673649 46.4532697,49.6325379 46.4699364,49.6014268 C46.4977142,49.5914268 46.5388253,49.6125379 46.5588253,49.6297601 C46.6088253,49.6725379 46.6843808,49.8547601 46.7710475,49.8214268 C46.7532697,49.7975379 46.7643808,49.768649 46.7516031,49.7430934 C46.7388253,49.7164268 46.7116031,49.7003156 46.6921586,49.6780934 C46.6466031,49.6269823 46.5977142,49.5764268 46.5682697,49.5142045 C46.5427142,49.4597601 46.5310475,49.403649 46.4788253,49.3664268 C46.4349364,49.3342045 46.3449364,49.3042045 46.3688253,49.2342045 C46.4049364,49.2408712 46.4299364,49.2692045 46.4560475,49.2919823 C46.4932697,49.323649 46.540492,49.3403156 46.5843808,49.3614268 C46.6643808,49.4003156 46.7560475,49.4275379 46.8243808,49.4858712 C46.8660475,49.5219823 46.8443808,49.5997601 46.8971586,49.6442045 C46.9360475,49.6764268 46.995492,49.7847601 47.0666031,49.7419823 C47.0938253,49.7253156 47.1049364,49.6942045 47.1310475,49.6764268 C47.1599364,49.6564268 47.2082697,49.6375379 47.240492,49.6225379 C47.2610475,49.613649 47.2943808,49.6158712 47.3110475,49.5997601 C47.3360475,49.5758712 47.275492,49.5053156 47.2616031,49.4869823 C47.2077142,49.4175379 47.1582697,49.3419823 47.0860475,49.2892045 C47.0488253,49.2619823 47.0127142,49.2330934 46.9699364,49.2153156 C46.945492,49.2058712 46.9049364,49.2069823 46.8999364,49.1742045 C46.9116031,49.1819823 46.9188253,49.1797601 46.9210475,49.1680934 C46.9199364,49.1464268 46.8888253,49.1458712 46.8738253,49.1414268 C46.8382697,49.1314268 46.8138253,49.1314268 46.7993808,49.1019823 C46.7816031,49.0647601 46.7177142,49.0664268 46.6838253,49.0575379 C46.6327142,49.0447601 46.5927142,49.0064268 46.5443808,48.9875379 C46.4866031,48.9653156 46.4432697,48.988649 46.3877142,49.0008712 C46.3310475,49.0130934 46.1093808,49.553649 46.0532697,49.5675379 C45.9982697,49.5814268 45.9193808,49.5358712 45.9116031,49.4530934 C45.9371586,49.4880934 45.7666031,49.263649 45.7738253,49.2503156 C45.4832697,49.7653156 45.785492,49.4464268 45.7871586,49.4192045 C45.7882697,49.3897601 45.880492,49.2375379 46.0571586,48.8608712 C46.0943808,48.7797601 46.0116031,49.6203156 45.9282697,49.6592045 C45.8593808,49.6914268 45.7871586,49.6364268 45.7066031,49.6203156 C45.6610475,49.6114268 45.6943808,49.2264268 45.6438253,49.2580934 C45.315492,49.4669823 45.6538253,49.7625379 45.5893808,49.7697601 C45.4888253,49.7803156 45.1749364,49.8158712 45.0710475,49.8030934 C45.0243808,49.7975379 45.0349364,49.6919823 44.8871586,49.4608712 C44.8682697,49.4314268 44.7827142,49.1308712 44.765492,49.1625379 C44.7427142,49.2025379 44.6332697,49.5842045 44.6243808,49.5475379 C44.5832697,49.5797601 44.6849364,48.9958712 44.5910475,49.1125379 C44.5688253,49.1403156 44.2343808,49.5142045 44.2010475,49.5169823 C44.1766031,49.5197601 44.4682697,49.1347601 44.4482697,49.1419823 C44.3988253,49.1592045 44.0688253,49.6108712 44.0277142,49.6403156 C43.9721586,49.6803156 44.0288253,49.4903156 43.9616031,49.543649 C43.9016031,49.5919823 44.0810475,49.3692045 44.0249364,49.4214268 C43.9860475,49.4569823 43.9099364,49.4997601 43.9038253,49.5569823 C43.8977142,49.6214268 43.930492,49.6658712 43.8827142,49.7258712 C43.8638253,49.7492045 43.8416031,49.7697601 43.8243808,49.7942045 C43.810492,49.8142045 43.8071586,49.8475379 43.7832697,49.8597601 C43.7182697,49.893649 43.7349364,49.913649 43.7543808,49.9842045 C43.7710475,50.043649 43.7649364,50.1553156 43.7510475,50.2164268 C43.7410475,50.2630934 43.6938253,50.3764268 43.6366031,50.3492045 C43.6066031,50.3353156 43.5749364,50.323649 43.5471586,50.3503156 C43.5349364,50.3625379 43.5266031,50.3769823 43.5227142,50.393649 C43.5032697,50.3942045 43.4843808,50.3964268 43.465492,50.4008712 C43.4299364,50.4080934 43.3910475,50.4169823 43.3566031,50.4019823 C43.3216031,50.3869823 43.275492,50.3580934 43.2360475,50.3708712 C43.2027142,50.3819823 43.1377142,50.4103156 43.1238253,50.4447601 C43.1182697,50.4603156 43.1382697,50.4997601 43.1377142,50.5197601 C43.1371586,50.5564268 43.1660475,50.6108712 43.1543808,50.6447601 C43.1271586,50.6308712 43.0882697,50.6264268 43.0699364,50.6008712 C43.0527142,50.5775379 43.0277142,50.5825379 43.0088253,50.5592045 C43.0043808,50.6003156 42.9893808,50.6564268 42.9416031,50.6675379 C42.8938253,50.678649 42.8482697,50.6408712 42.800492,50.6530934 C42.6760475,50.6858712 42.8760475,50.8419823 42.9027142,50.8725379 C42.9471586,50.9225379 42.9627142,50.988649 42.995492,51.0458712 C43.030492,51.1075379 43.105492,51.128649 43.1488253,51.1830934 C43.1849364,51.228649 43.1927142,51.2914268 43.2438253,51.3253156 C43.3016031,51.3642045 43.3532697,51.4003156 43.3771586,51.4675379 C43.4016031,51.443649 43.4521586,51.5547601 43.5038253,51.4658712 C43.5321586,51.4175379 43.5799364,51.3758712 43.6110475,51.4547601 C43.6371586,51.5214268 43.6116031,51.5653156 43.6688253,51.623649 C43.7143808,51.6692045 43.7116031,51.723649 43.635492,51.7192045 C43.6482697,51.753649 43.6688253,51.7869823 43.6360475,51.8180934 C43.620492,51.8330934 43.5782697,51.8642045 43.6093808,51.8858712 C43.6438253,51.8697601 43.6816031,51.8603156 43.7160475,51.8447601 C43.7538253,51.8280934 43.7916031,51.7892045 43.835492,51.7903156 C43.8388253,51.8042045 43.7771586,51.8458712 43.8149364,51.848649 C43.8466031,51.8514268 43.8916031,51.8180934 43.9177142,51.8458712 C43.9466031,51.8764268 43.910492,51.9219823 43.9271586,51.9558712 C43.9438253,51.9897601 44.0010475,51.9642045 44.0293808,51.9692045 C44.0166031,51.9992045 43.9727142,51.993649 43.9488253,52.0053156 C44.0049364,52.0742045 43.9316031,52.1753156 43.8499364,52.1764268 C43.810492,52.1769823 43.6710475,52.0180934 43.665492,52.1203156 C43.6638253,52.1508712 43.6732697,52.1875379 43.6832697,52.2164268 C43.6960475,52.253649 43.7877142,52.2380934 43.8199364,52.2519823 C43.8671586,52.2714268 43.9232697,52.3169823 43.9416031,52.3647601 C43.960492,52.4142045 44.0016031,52.4464268 44.0177142,52.4942045 C44.0471586,52.5814268 44.1316031,52.5930934 44.2188253,52.618649 C44.3349364,52.6530934 44.2777142,52.8325379 44.2716031,52.9180934 C44.2660475,53.0025379 44.385492,53.0225379 44.4399364,53.0703156 C44.4999364,53.1225379 44.5099364,53.2247601 44.4093808,53.2330934 C44.3582697,53.2375379 44.2738253,53.2142045 44.2549364,53.2792045 C44.2277142,53.3719823 44.3677142,53.3614268 44.4282697,53.3825379 C44.4571586,53.3919823 44.5727142,53.4042045 44.5832697,53.4308712 C44.5993808,53.4719823 44.5860475,53.528649 44.6010475,53.573649 C44.6371586,53.6847601 44.7421586,53.7603156 44.8421586,53.8158712 C45.0582697,53.9364268 45.3149364,54.0130934 45.5527142,54.0808712 C45.6849364,54.118649 45.8199364,54.1480934 45.9560475,54.1669823 C46.0866031,54.1847601 46.2032697,54.173649 46.3149364,54.2469823 C46.3921586,54.2969823 46.4416031,54.2597601 46.5210475,54.2730934 C46.5549364,54.2792045 46.5699364,54.3103156 46.5949364,54.3292045 C46.6238253,54.3508712 46.6566031,54.3225379 46.6877142,54.3342045 C46.6921586,54.3142045 46.6899364,54.2942045 46.6810475,54.2753156 C46.7410475,54.2975379 46.815492,54.3619823 46.8788253,54.3103156 C46.910492,54.2847601 46.9321586,54.2492045 46.9649364,54.2247601 C47.0038253,54.2275379 47.0427142,54.2297601 47.0816031,54.2303156 C47.2466031,54.2308712 47.3760475,54.1553156 47.5071586,54.0675379 C47.6477142,53.973649 47.8177142,53.9714268 47.9810475,53.9558712 C48.1538253,53.9392045 48.3377142,53.9169823 48.4949364,53.8414268 C48.6332697,53.7753156 48.6682697,53.6542045 48.7071586,53.5203156 C48.7493808,53.3747601 48.8771586,53.3158712 48.9610475,53.198649 Z M51.5738253,52.108649 C51.6182697,52.1203156 51.6893808,52.0925379 51.7171586,52.0553156 C51.7549364,52.0042045 51.7232697,51.9369823 51.6577142,51.9753156 C51.6332697,51.9897601 51.6360475,52.0169823 51.6177142,52.0342045 C51.5966031,52.0525379 51.5943808,52.0308712 51.5749364,52.0264268 C51.545492,52.0197601 51.4993808,52.0580934 51.4899364,52.083649 C51.450492,52.0825379 51.415492,52.1264268 51.4338253,52.1614268 C51.485492,52.1419823 51.5171586,52.0930934 51.5738253,52.108649 Z"/>
     </g>
+    <g id="loop-icon">
+      <path fill-rule="evenodd" clip-rule="evenodd" d="M8 .5c-4.418 0-8 3.134-8 7 0 2.022.986 3.839 2.553 5.116-.079.268-.186.56-.339.871-.718 1.464-1.214 2.013-1.214 2.013s1.803-.34 3.339-1.104c.275-.137.498-.269.7-.397.916.32 1.914.501 2.961.501 4.418 0 8-3.134 8-7s-3.582-7-8-7zm2.5 3.938c.725 0 1.312.588 1.312 1.312s-.587 1.313-1.312 1.313-1.312-.588-1.312-1.313.587-1.312 1.312-1.312zm-5 0c.725 0 1.312.588 1.312 1.312s-.587 1.313-1.312 1.313-1.312-.588-1.312-1.313.587-1.312 1.312-1.312zm2.5 7.612c-4.065 0-5-3.55-5-3.55s2.115 1 5 1c2.84 0 5-1 5-1s-.935 3.55-5 3.55z"/>
+    </g>
   </defs>
   <use id="add" xlink:href="#add-shape"/>
   <use id="add-hover" xlink:href="#add-shape"/>
   <use id="add-active" xlink:href="#add-shape"/>
   <use id="audio" xlink:href="#audio-shape"/>
   <use id="block" xlink:href="#block-shape"/>
   <use id="block-red" xlink:href="#block-shape"/>
   <use id="block-hover" xlink:href="#block-shape"/>
@@ -93,16 +96,18 @@
   <use id="globe" xlink:href="#globe-shape"/>
   <use id="google" xlink:href="#google-shape"/>
   <use id="google-hover" xlink:href="#google-shape"/>
   <use id="google-active" xlink:href="#google-shape"/>
   <use id="history" xlink:href="#history-shape"/>
   <use id="history-hover" xlink:href="#history-shape"/>
   <use id="history-active" xlink:href="#history-shape"/>
   <use id="leave" xlink:href="#leave-shape"/>
+  <use id="loop-icon-still" xlink:href="#loop-icon"/>
+  <use id="loop-icon-white" xlink:href="#loop-icon"/>
   <use id="settings" xlink:href="#settings-shape"/>
   <use id="settings-hover" xlink:href="#settings-shape"/>
   <use id="settings-active" xlink:href="#settings-shape"/>
   <use id="share-darkgrey" xlink:href="#share-shape"/>
   <use id="tag" xlink:href="#tag-shape"/>
   <use id="tag-hover" xlink:href="#tag-shape"/>
   <use id="tag-active" xlink:href="#tag-shape"/>
   <use id="trash" xlink:href="#trash-shape"/>
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/pause-12x12.svg
@@ -0,0 +1,1 @@
+<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-pause-12x12</title><desc>Created with Sketch.</desc><path d="M5 .5v11c0 .136-.05.253-.149.351-.099.099-.215.149-.351.149h-4c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.217-.148.352-.148h4c.136 0 .252.05.351.148.099.099.149.217.149.352zm7 0v11c0 .136-.05.253-.149.351-.099.099-.216.149-.351.149h-4c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.216-.148.352-.148h4c.136 0 .252.05.351.148.099.099.149.217.149.352z" sketch:type="MSShapeGroup" fill="#333"/></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/play-12x12.svg
@@ -0,0 +1,1 @@
+<svg width="11" height="12" viewBox="0 0 11 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-play-12x12</title><desc>Created with Sketch.</desc><path d="M10.695 6.24l-10.263 5.704c-.118.066-.22.074-.305.023-.085-.052-.127-.144-.127-.278v-11.377c0-.134.043-.228.127-.278.085-.052.186-.044.305.022l10.263 5.704c.118.066.178.147.178.239 0 .094-.06.174-.178.241z" sketch:type="MSShapeGroup" fill="#fff"/></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/stop-12x12.svg
@@ -0,0 +1,1 @@
+<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-stop-12x12</title><desc>Created with Sketch.</desc><path d="M12 .5v11c0 .136-.05.253-.149.351-.099.099-.216.149-.351.149h-11c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.217-.148.352-.148h11c.136 0 .252.05.351.148.099.099.149.217.149.352z" sketch:type="MSShapeGroup" fill="#D92215"/></svg>
\ No newline at end of file
--- a/browser/components/loop/jar.mn
+++ b/browser/components/loop/jar.mn
@@ -84,16 +84,19 @@ browser.jar:
   content/browser/loop/shared/img/hello_logo.svg                (content/shared/img/hello_logo.svg)
   content/browser/loop/shared/img/telefonica@2x.png             (content/shared/img/telefonica@2x.png)
   content/browser/loop/shared/img/ellipsis-v.svg                (content/shared/img/ellipsis-v.svg)
   content/browser/loop/shared/img/empty_conversations.svg       (content/shared/img/empty_conversations.svg)
   content/browser/loop/shared/img/empty_search.svg              (content/shared/img/empty_search.svg)
   content/browser/loop/shared/img/animated-spinner.svg          (content/shared/img/animated-spinner.svg)
   content/browser/loop/shared/img/avatars.svg                   (content/shared/img/avatars.svg)
   content/browser/loop/shared/img/firefox-avatar.svg            (content/shared/img/firefox-avatar.svg)
+  content/browser/loop/shared/img/pause-12x12.svg               (content/shared/img/pause-12x12.svg)
+  content/browser/loop/shared/img/play-12x12.svg                (content/shared/img/play-12x12.svg)
+  content/browser/loop/shared/img/stop-12x12.svg                (content/shared/img/stop-12x12.svg)
 
   # Shared scripts
   content/browser/loop/shared/js/actions.js             (content/shared/js/actions.js)
   content/browser/loop/shared/js/store.js               (content/shared/js/store.js)
   content/browser/loop/shared/js/activeRoomStore.js     (content/shared/js/activeRoomStore.js)
   content/browser/loop/shared/js/dispatcher.js          (content/shared/js/dispatcher.js)
   content/browser/loop/shared/js/linkifiedTextView.js   (content/shared/js/linkifiedTextView.js)
   content/browser/loop/shared/js/loopapi-client.js      (content/shared/js/loopapi-client.js)
--- a/browser/components/loop/modules/MozLoopService.jsm
+++ b/browser/components/loop/modules/MozLoopService.jsm
@@ -870,20 +870,21 @@ var MozLoopServiceInternal = {
     return "about:loopconversation#" + chatWindowId;
   },
 
   /**
    * Hangup and close all chat windows that are open.
    */
   hangupAllChatWindows() {
     let isLoopURL = ({ src }) => /^about:loopconversation#/.test(src);
-    [...Chat.chatboxes].filter(isLoopURL).forEach(chatbox => {
+    let loopChatWindows = [...Chat.chatboxes].filter(isLoopURL);
+    for (let chatbox of loopChatWindows) {
       let window = chatbox.content.contentWindow;
       window.dispatchEvent(new window.CustomEvent("LoopHangupNow"));
-    });
+    }
   },
 
   /**
    * Determines if a chat window is already open for a given window id.
    *
    * @param  {String}  chatWindowId The window id.
    * @return {Boolean}              True if the window is opened.
    */
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -77,17 +77,20 @@ describe("loop.panel", function() {
       GetUserProfile: function() { return null; }
     });
 
     roomName = "First Room Name";
     roomData = {
       roomToken: "QzBbvGmIZWU",
       roomUrl: "http://sample/QzBbvGmIZWU",
       decryptedContext: {
-        roomName: roomName
+        roomName: roomName,
+        urls: [{
+          location: "http://testurl.com"
+        }]
       },
       maxSize: 2,
         participants: [{
         displayName: "Alexis",
           account: "alexis@example.com",
           roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
       }, {
         displayName: "Adam",
@@ -660,22 +663,33 @@ describe("loop.panel", function() {
 
         TestUtils.Simulate.click(node, fakeEvent);
 
         expect(view.state.showMenu).to.eql(!prevState);
       });
     });
 
     describe("Copy button", function() {
-      var roomEntry;
+      var roomEntry, openURLStub;
 
       beforeEach(function() {
         // Stub to prevent warnings where no stores are set up to handle the
         // actions we are testing.
         sandbox.stub(dispatcher, "dispatch");
+        openURLStub = sinon.stub();
+
+        LoopMochaUtils.stubLoopRequest({
+          GetSelectedTabMetadata: function() {
+            return {
+              url: "http://invalid.com",
+              description: "fakeSite"
+            };
+          },
+          OpenURL: openURLStub
+        });
 
         roomEntry = mountRoomEntry({
           deleteRoom: sandbox.stub(),
           isOpenedRoom: false,
           room: new loop.store.Room(roomData)
         });
       });
 
@@ -712,16 +726,42 @@ describe("loop.panel", function() {
             isOpenedRoom: true,
             room: new loop.store.Room(roomData)
           });
 
           TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
 
           sinon.assert.notCalled(dispatcher.dispatch);
         });
+
+        it("should open a new tab with the room context if it is not the same as the currently open tab", function() {
+          TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
+          sinon.assert.calledOnce(openURLStub);
+          sinon.assert.calledWithExactly(openURLStub, "http://testurl.com");
+        });
+
+        it("should not open a new tab if the context is the same as the currently open tab", function() {
+          LoopMochaUtils.stubLoopRequest({
+            GetSelectedTabMetadata: function() {
+              return {
+                url: "http://testurl.com",
+                description: "fakeSite"
+              };
+            }
+          });
+
+          roomEntry = mountRoomEntry({
+            deleteRoom: sandbox.stub(),
+            isOpenedRoom: false,
+            room: new loop.store.Room(roomData)
+          });
+
+          TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
+          sinon.assert.notCalled(openURLStub);
+        });
       });
     });
 
     describe("Context Indicator", function() {
       var roomEntry;
 
       function mountEntryForContext() {
         return mountRoomEntry({
--- a/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
@@ -151,40 +151,32 @@ add_task(function* test_infoBar() {
     let bar = getInfoBar();
 
     // Start with some basic assertions for the bar.
     Assert.ok(bar, "The notification bar should be visible");
     Assert.strictEqual(bar.hidden, false, "Again, the notification bar should be visible");
 
     let button = bar.querySelector(".notification-button");
     Assert.ok(button, "There should be a button present");
-    Assert.strictEqual(button.type, "menu-button", "We're expecting a menu-button");
-    Assert.strictEqual(button.getAttribute("anchor"), "dropmarker",
-      "The popup should be opening anchored to the dropmarker");
-    Assert.strictEqual(button.getElementsByTagNameNS(kNSXUL, "menupopup").length, 1,
-      "There should be a popup attached to the button");
   };
 
   testBarProps();
 
   // When we switch tabs, the infobar should move along with it. We use `selectedIndex`
   // here, because that's the setter that triggers the 'select' event. This event
   // is what LoopUI listens to and moves the infobar.
   gBrowser.selectedIndex = Array.indexOf(gBrowser.tabs, createdTabs[0]);
 
   // We now know that the second tab is selected and should be displaying the
   // infobar.
   testBarProps();
 
   // Test hiding the infoBar.
-  getInfoBar().querySelector(".notification-button")
-              .getElementsByTagNameNS(kNSXUL, "menuitem")[0].click();
+  getInfoBar().querySelector(".notification-button-default").click();
   Assert.equal(getInfoBar(), null, "The notification should be hidden now");
-  Assert.strictEqual(Services.prefs.getBoolPref(kPrefBrowserSharingInfoBar), false,
-    "The pref should be set to false when the menu item is clicked");
 
   gBrowser.selectedIndex = Array.indexOf(gBrowser.tabs, createdTabs[1]);
 
   Assert.equal(getInfoBar(), null, "The notification should still be hidden");
 
   // Cleanup.
   gHandlers.RemoveBrowserSharingListener();
   yield removeTabs();
--- a/browser/components/loop/test/shared/loop_mocha_utils.js
+++ b/browser/components/loop/test/shared/loop_mocha_utils.js
@@ -59,16 +59,18 @@ var LoopMochaUtils = (function(global, _
       }
       return this;
     };
 
     this.reject = function(result) {
       throw result;
     };
 
+    this.catch = function() {};
+
     asyncFn(this.resolve.bind(this), this.reject.bind(this));
   }
 
   syncThenable.all = function(promises) {
     return new syncThenable(function(resolve) {
       var results = [];
 
       promises.forEach(function(promise) {
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -2,24 +2,26 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 Cu.import("resource:///modules/MSMigrationUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
 const kEdgeRegistryRoot = "SOFTWARE\\Classes\\Local Settings\\Software\\" +
   "Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\" +
   "microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge";
+const kEdgeReadingListPath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
 
 function EdgeTypedURLMigrator() {
 }
 
 EdgeTypedURLMigrator.prototype = {
   type: MigrationUtils.resourceTypes.HISTORY,
 
   get _typedURLs() {
@@ -74,26 +76,133 @@ EdgeTypedURLMigrator.prototype = {
       handleError: function() {},
       handleCompletion: function() {
         aCallback(this._success);
       }
     });
   },
 }
 
+function EdgeReadingListMigrator() {
+}
+
+EdgeReadingListMigrator.prototype = {
+  type: MigrationUtils.resourceTypes.BOOKMARKS,
+
+  get exists() {
+    return !!MSMigrationUtils.getEdgeLocalDataFolder();
+  },
+
+  migrate(callback) {
+    this._migrateReadingList(PlacesUtils.bookmarks.menuGuid).then(
+      () => callback(true),
+      ex => {
+        Cu.reportError(ex);
+        callback(false);
+      }
+    );
+  },
+
+  _migrateReadingList: Task.async(function*(parentGuid) {
+    let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
+    if (!edgeDir) {
+      return;
+    }
+    this._readingListExtractor = Cc["@mozilla.org/profile/migrator/edgereadinglistextractor;1"].
+                                 createInstance(Ci.nsIEdgeReadingListExtractor);
+    edgeDir.appendRelativePath(kEdgeReadingListPath);
+    let errorProduced = null;
+    if (edgeDir.exists() && edgeDir.isReadable() && edgeDir.isDirectory()) {
+      let expectedDir = edgeDir.clone();
+      expectedDir.appendRelativePath("nouser1\\120712-0049");
+      if (expectedDir.exists() && expectedDir.isReadable() && expectedDir.isDirectory()) {
+        yield this._migrateReadingListDB(expectedDir, parentGuid).catch(ex => {
+          if (!errorProduced)
+            errorProduced = ex;
+        });
+      } else {
+        let getSubdirs = someDir => {
+          let subdirs = someDir.directoryEntries;
+          let rv = [];
+          while (subdirs.hasMoreElements()) {
+            let subdir = subdirs.getNext().QueryInterface(Ci.nsIFile);
+            if (subdir.isDirectory() && subdir.isReadable()) {
+              rv.push(subdir);
+            }
+          }
+          return rv;
+        };
+        let dirs = getSubdirs(edgeDir).map(getSubdirs);
+        for (let dir of dirs) {
+          yield this._migrateReadingListDB(dir, parentGuid).catch(ex => {
+            if (!errorProduced)
+              errorProduced = ex;
+          });
+        }
+      }
+    }
+    if (errorProduced) {
+      throw errorProduced;
+    }
+  }),
+  _migrateReadingListDB: Task.async(function*(dbFile, parentGuid) {
+    dbFile.appendRelativePath("DBStore\\spartan.edb");
+
+    if (!dbFile.exists() || !dbFile.isReadable() || !dbFile.isFile()) {
+      return;
+    }
+    let readingListItems;
+    try {
+      readingListItems = this._readingListExtractor.extract(dbFile.path);
+    } catch (ex) {
+      Cu.reportError("Failed to extract Edge reading list information from " +
+                     "the database at " + dbFile.path + " due to the following error: " + ex);
+      // Deliberately make this fail so we expose failure in the UI:
+      throw ex;
+      return;
+    }
+    if (!readingListItems.length) {
+      return;
+    }
+    let destFolderGuid = yield this._ensureReadingListFolder(parentGuid);
+    for (let i = 0; i < readingListItems.length; i++) {
+      let readingListItem = readingListItems.queryElementAt(i, Ci.nsIPropertyBag2);
+      let url = readingListItem.get("uri");
+      let title = readingListItem.get("title");
+      let time = readingListItem.get("time");
+      // time is a PRTime, which is microseconds (since unix epoch), or null.
+      // We need milliseconds for the date constructor, so divide by 1000:
+      let dateAdded = time ? new Date(time / 1000) : new Date();
+      yield PlacesUtils.bookmarks.insert({
+        parentGuid: destFolderGuid, url: url, title, dateAdded
+      });
+    }
+  }),
+
+  _ensureReadingListFolder: Task.async(function*(parentGuid) {
+    if (!this.__readingListFolderGuid) {
+      let folderTitle = MigrationUtils.getLocalizedString("importedEdgeReadingList");
+      let folderSpec = {type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title: folderTitle};
+      this.__readingListFolderGuid = (yield PlacesUtils.bookmarks.insert(folderSpec)).guid;
+    }
+    return this.__readingListFolderGuid;
+  }),
+};
+
 function EdgeProfileMigrator() {
 }
 
 EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
 
 EdgeProfileMigrator.prototype.getResources = function() {
   let resources = [
     MSMigrationUtils.getBookmarksMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
     MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
     new EdgeTypedURLMigrator(),
+    new EdgeReadingListMigrator(),
   ];
   let windowsVaultFormPasswordsMigrator =
     MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
   windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
   resources.push(windowsVaultFormPasswordsMigrator);
   return resources.filter(r => r.exists);
 };
 
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -21,17 +21,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
 
 const EDGE_COOKIE_PATH_OPTIONS = ["", "#!001\\", "#!002\\"];
 const EDGE_COOKIES_SUFFIX = "MicrosoftEdge\\Cookies";
 const EDGE_FAVORITES = "AC\\MicrosoftEdge\\User\\Default\\Favorites";
-const EDGE_READINGLIST = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
 const FREE_CLOSE_FAILED = 0;
 const INTERNET_EXPLORER_EDGE_GUID = [0x3CCD5499,
                                      0x4B1087A8,
                                      0x886015A2,
                                      0x553BDD88];
 const RESULT_SUCCESS = 0;
 const VAULT_ENUMERATE_ALL_ITEMS = 512;
 const WEB_CREDENTIALS_VAULT_ID = [0x4BF4C442,
@@ -371,30 +370,27 @@ Bookmarks.prototype = {
     return Task.spawn(function* () {
       // Import to the bookmarks menu.
       let folderGuid = PlacesUtils.bookmarks.menuGuid;
       if (!MigrationUtils.isStartupMigration) {
         folderGuid =
           yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
       }
       yield this._migrateFolder(this._favoritesFolder, folderGuid);
-
-      if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_EDGE) {
-        yield this._migrateEdgeReadingList(PlacesUtils.bookmarks.menuGuid);
-      }
     }.bind(this)).then(() => aCallback(true),
                         e => { Cu.reportError(e); aCallback(false) });
   },
 
   _migrateFolder: Task.async(function* (aSourceFolder, aDestFolderGuid) {
     // TODO (bug 741993): the favorites order is stored in the Registry, at
     // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
     // for IE, and in a similar location for Edge.
     // Until we support it, bookmarks are imported in alphabetical order.
     let entries = aSourceFolder.directoryEntries;
+    let succeeded = true;
     while (entries.hasMoreElements()) {
       let entry = entries.getNext().QueryInterface(Ci.nsIFile);
       try {
         // Make sure that entry.path == entry.target to not follow .lnk folder
         // shortcuts which could lead to infinite cycles.
         // Don't use isSymlink(), since it would throw for invalid
         // lnk files pointing to URLs or to unresolvable paths.
         if (entry.path == entry.target && entry.isDirectory()) {
@@ -434,92 +430,24 @@ Bookmarks.prototype = {
 
             yield PlacesUtils.bookmarks.insert({
               parentGuid: aDestFolderGuid, url: uri, title
             });
           }
         }
       } catch (ex) {
         Components.utils.reportError("Unable to import " + this.importedAppLabel + " favorite (" + entry.leafName + "): " + ex);
+        succeeded = false;
       }
     }
+    if (!succeeded) {
+      throw new Error("Failed to import all bookmarks correctly.");
+    }
   }),
 
-  _migrateEdgeReadingList: Task.async(function*(parentGuid) {
-    let edgeDir = getEdgeLocalDataFolder();
-    if (!edgeDir) {
-      return;
-    }
-
-    this._readingListExtractor = Cc["@mozilla.org/profile/migrator/edgereadinglistextractor;1"].
-                                 createInstance(Ci.nsIEdgeReadingListExtractor);
-    edgeDir.appendRelativePath(EDGE_READINGLIST);
-    if (edgeDir.exists() && edgeDir.isReadable() && edgeDir.isDirectory()) {
-      let expectedDir = edgeDir.clone();
-      expectedDir.appendRelativePath("nouser1\\120712-0049");
-      if (expectedDir.exists() && expectedDir.isReadable() && expectedDir.isDirectory()) {
-        yield this._migrateEdgeReadingListDB(expectedDir, parentGuid);
-      } else {
-        let getSubdirs = someDir => {
-          let subdirs = someDir.directoryEntries;
-          let rv = [];
-          while (subdirs.hasMoreElements()) {
-            let subdir = subdirs.getNext().QueryInterface(Ci.nsIFile);
-            if (subdir.isDirectory() && subdir.isReadable()) {
-              rv.push(subdir);
-            }
-          }
-          return rv;
-        };
-        let dirs = getSubdirs(edgeDir).map(getSubdirs);
-        for (let dir of dirs) {
-          yield this._migrateEdgeReadingListDB(dir, parentGuid);
-        }
-      }
-    }
-  }),
-  _migrateEdgeReadingListDB: Task.async(function*(dbFile, parentGuid) {
-    dbFile.appendRelativePath("DBStore\\spartan.edb");
-    if (!dbFile.exists() || !dbFile.isReadable() || !dbFile.isFile()) {
-      return;
-    }
-    let readingListItems;
-    try {
-      readingListItems = this._readingListExtractor.extract(dbFile.path);
-    } catch (ex) {
-      Cu.reportError("Failed to extract Edge reading list information from " +
-                     "the database at " + dbPath + " due to the following error: " + ex);
-      return;
-    }
-    if (!readingListItems.length) {
-      return;
-    }
-    let destFolderGuid = yield this._ensureEdgeReadingListFolder(parentGuid);
-    for (let i = 0; i < readingListItems.length; i++) {
-      let readingListItem = readingListItems.queryElementAt(i, Ci.nsIPropertyBag2);
-      let url = readingListItem.get("uri");
-      let title = readingListItem.get("title");
-      let time = readingListItem.get("time");
-      // time is a PRTime, which is microseconds (since unix epoch), or null.
-      // We need milliseconds for the date constructor, so divide by 1000:
-      let dateAdded = time ? new Date(time / 1000) : new Date();
-      yield PlacesUtils.bookmarks.insert({
-        parentGuid: destFolderGuid, url: url, title, dateAdded
-      });
-    }
-  }),
-
-  _ensureEdgeReadingListFolder: Task.async(function*(parentGuid) {
-    if (!this.__edgeReadingListFolderGuid) {
-      let folderTitle = MigrationUtils.getLocalizedString("importedEdgeReadingList");
-      let folderSpec = {type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title: folderTitle};
-      this.__edgeReadingListFolderGuid = (yield PlacesUtils.bookmarks.insert(folderSpec)).guid;
-    }
-    return this.__edgeReadingListFolderGuid;
-  }),
 };
 
 function Cookies(migrationType) {
   this._migrationType = migrationType;
 }
 
 Cookies.prototype = {
   type: MigrationUtils.resourceTypes.COOKIES,
@@ -943,9 +871,10 @@ var MSMigrationUtils = {
   },
   getCookiesMigrator(migrationType = this.MIGRATION_TYPE_IE) {
     return new Cookies(migrationType);
   },
   getWindowsVaultFormPasswordsMigrator() {
     return new WindowsVaultFormPasswords();
   },
   getTypedURLs,
+  getEdgeLocalDataFolder,
 };
--- a/browser/components/migration/nsEdgeReadingListExtractor.cpp
+++ b/browser/components/migration/nsEdgeReadingListExtractor.cpp
@@ -192,13 +192,19 @@ nsEdgeReadingListExtractor::ConvertJETEr
       return NS_ERROR_FILE_IS_LOCKED;
     case JET_errPermissionDenied:
     case JET_errAccessDenied:
       return NS_ERROR_FILE_ACCESS_DENIED;
     case JET_errInvalidFilename:
       return NS_ERROR_FILE_INVALID_PATH;
     case JET_errFileNotFound:
       return NS_ERROR_FILE_NOT_FOUND;
+    case JET_errDatabaseDirtyShutdown:
+      return NS_ERROR_FILE_CORRUPTED;
     default:
+      nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+      wchar_t* msg = new wchar_t[80];
+      swprintf(msg, 80, MOZ_UTF16("Unexpected JET error from ESE database: %ld"), aError);
+      consoleService->LogStringMessage(msg);
       return NS_ERROR_FAILURE;
   }
 }
 
--- a/browser/components/places/tests/unit/distribution.ini
+++ b/browser/components/places/tests/unit/distribution.ini
@@ -3,19 +3,25 @@
 
 [Global]
 id=516444
 version=1.0
 about=Test distribution file
 
 [BookmarksToolbar]
 item.1.title=Toolbar Link Before
-item.1.link=http://mozilla.com/
+item.1.link=https://example.org/toolbar/before/
+item.1.keyword=e:t:b
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
 item.2.type=default
 item.3.title=Toolbar Link After
-item.3.link=http://mozilla.com/
+item.3.link=https://example.org/toolbar/after/
+item.3.keyword=e:t:a
 
 [BookmarksMenu]
 item.1.title=Menu Link Before
-item.1.link=http://mozilla.com/
+item.1.link=https://example.org/menu/before/
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
 item.2.type=default
 item.3.title=Menu Link After
-item.3.link=http://mozilla.com/
\ No newline at end of file
+item.3.link=https://example.org/menu/after/
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -114,8 +114,39 @@ function rebuildSmartBookmarks() {
   });
   Cc["@mozilla.org/browser/browserglue;1"]
     .getService(Ci.nsIObserver)
     .observe(null, "browser-glue-test", "smart-bookmarks-init");
   return promiseTopicObserved("test-smart-bookmarks-done").then(() => {
     Services.console.unregisterListener(consoleListener);
   });
 }
+
+const SINGLE_TRY_TIMEOUT = 100;
+const NUMBER_OF_TRIES = 30;
+
+/**
+ * Similar to waitForConditionPromise, but poll for an asynchronous value
+ * every SINGLE_TRY_TIMEOUT ms, for no more than tryCount times.
+ *
+ * @param promiseFn
+ *        A function to generate a promise, which resolves to the expected
+ *        asynchronous value.
+ * @param timeoutMsg
+ *        The reason to reject the returned promise with.
+ * @param [optional] tryCount
+ *        Maximum times to try before rejecting the returned promise with
+ *        timeoutMsg, defaults to NUMBER_OF_TRIES.
+ * @return {Promise}
+ * @resolves to the asynchronous value being polled.
+ * @rejects if the asynchronous value is not available after tryCount attempts.
+ */
+var waitForResolvedPromise = Task.async(function* (promiseFn, timeoutMsg, tryCount=NUMBER_OF_TRIES) {
+  let tries = 0;
+  do {
+    try {
+      let value = yield promiseFn();
+      return value;
+    } catch (ex) {}
+    yield new Promise(resolve => do_timeout(SINGLE_TRY_TIMEOUT, resolve));
+  } while (++tries <= tryCount);
+  throw(timeoutMsg);
+});
--- a/browser/components/places/tests/unit/test_browserGlue_distribution.js
+++ b/browser/components/places/tests/unit/test_browserGlue_distribution.js
@@ -71,23 +71,51 @@ add_task(function* () {
   Assert.equal(menuItem.title, "Menu Link Before");
 
   menuItem = yield PlacesUtils.bookmarks.fetch({
     parentGuid: PlacesUtils.bookmarks.menuGuid,
     index: 1 + DEFAULT_BOOKMARKS_ON_MENU
   });
   Assert.equal(menuItem.title, "Menu Link After");
 
+  // Check no favicon or keyword exists for this bookmark
+  yield Assert.rejects(waitForResolvedPromise(() => {
+    return PlacesUtils.promiseFaviconData(menuItem.url.href);
+  }, "Favicon not found", 10), /Favicon\snot\sfound/, "Favicon not found");
+
+  let keywordItem = yield PlacesUtils.keywords.fetch({
+    url: menuItem.url.href
+  });
+  Assert.strictEqual(keywordItem, null);
+
   // Check the custom bookmarks exist on toolbar.
   let toolbarItem = yield PlacesUtils.bookmarks.fetch({
     parentGuid: PlacesUtils.bookmarks.toolbarGuid,
     index: 0
   });
   Assert.equal(toolbarItem.title, "Toolbar Link Before");
 
+  // Check the custom favicon and keyword exist for this bookmark
+  let faviconItem = yield waitForResolvedPromise(() => {
+    return PlacesUtils.promiseFaviconData(toolbarItem.url.href);
+  }, "Favicon not found", 10);
+  Assert.equal(faviconItem.uri.spec, "https://example.org/favicon.png");
+  Assert.greater(faviconItem.dataLen, 0);
+  Assert.equal(faviconItem.mimeType, "image/png");
+
+  let base64Icon = "data:image/png;base64," +
+      base64EncodeString(String.fromCharCode.apply(String, faviconItem.data));
+  Assert.equal(base64Icon, SMALLPNG_DATA_URI.spec);
+
+  keywordItem = yield PlacesUtils.keywords.fetch({
+    url: toolbarItem.url.href
+  });
+  Assert.notStrictEqual(keywordItem, null);
+  Assert.equal(keywordItem.keyword, "e:t:b");
+
   toolbarItem = yield PlacesUtils.bookmarks.fetch({
     parentGuid: PlacesUtils.bookmarks.toolbarGuid,
     index: 1 + DEFAULT_BOOKMARKS_ON_TOOLBAR
   });
   Assert.equal(toolbarItem.title, "Toolbar Link After");
 
   // Check the bmprocessed pref has been created.
   Assert.ok(Services.prefs.getBoolPref(PREF_BMPROCESSED));
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1245,32 +1245,45 @@ var SessionStoreInternal = {
       // Store the window's close date to figure out when each individual tab
       // was closed. This timestamp should allow re-arranging data based on how
       // recently something was closed.
       winData.closedAt = Date.now();
 
       // we don't want to save the busy state
       delete winData.busy;
 
+      // When closing windows one after the other until Firefox quits, we
+      // will move those closed in series back to the "open windows" bucket
+      // before writing to disk. If however there is only a single window
+      // with tabs we deem not worth saving then we might end up with a
+      // random closed or even a pop-up window re-opened. To prevent that
+      // we explicitly allow saving an "empty" window state.
+      let isLastWindow =
+        Object.keys(this._windows).length == 1 &&
+        !this._closedWindows.some(win => win._shouldRestore || false);
+
+      // clear this window from the list, since it has definitely been closed.
+      delete this._windows[aWindow.__SSi];
+
       // Now we have to figure out if this window is worth saving in the _closedWindows
       // Object.
       //
       // We're about to flush the tabs from this window, but it's possible that we
       // might never hear back from the content process(es) in time before the user
       // chooses to restore the closed window. So we do the following:
       //
       // 1) Use the tab state cache to determine synchronously if the window is
       //    worth stashing in _closedWindows.
       // 2) Flush the window.
       // 3) When the flush is complete, revisit our decision to store the window
       //    in _closedWindows, and add/remove as necessary.
       if (!winData.isPrivate) {
         // Remove any open private tabs the window may contain.
         PrivacyFilter.filterPrivateTabs(winData);
-        this.maybeSaveClosedWindow(winData);
+        this.maybeSaveClosedWindow(winData, isLastWindow);
       }
 
       // The tabbrowser binding will go away once the window is closed,
       // so we'll hold a reference to the browsers in the closure here.
       let browsers = tabbrowser.browsers;
 
       TabStateFlusher.flushWindow(aWindow).then(() => {
         // At this point, aWindow is closed! You should probably not try to
@@ -1287,38 +1300,35 @@ var SessionStoreInternal = {
         }
 
         // Save non-private windows if they have at
         // least one saveable tab or are the last window.
         if (!winData.isPrivate) {
           // It's possible that a tab switched its privacy state at some point
           // before our flush, so we need to filter again.
           PrivacyFilter.filterPrivateTabs(winData);
-          this.maybeSaveClosedWindow(winData);
+          this.maybeSaveClosedWindow(winData, isLastWindow);
         }
 
-        // clear this window from the list
-        delete this._windows[aWindow.__SSi];
         // Update the tabs data now that we've got the most
         // recent information.
         this.cleanUpWindow(aWindow, winData);
 
         // save the state without this window to disk
         this.saveStateDelayed();
       });
     } else {
       this.cleanUpWindow(aWindow, winData);
     }
 
     for (let i = 0; i < tabbrowser.tabs.length; i++) {
       this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
     }
   },
 
-
   /**
    * Clean up the message listeners on a window that has finally
    * gone away. Call this once you're sure you don't want to hear
    * from any of this windows tabs from here forward.
    *
    * @param aWindow
    *        The browser window we're cleaning up.
    * @param winData
@@ -1340,32 +1350,29 @@ var SessionStoreInternal = {
    * Decides whether or not a closed window should be put into the
    * _closedWindows Object. This might be called multiple times per
    * window, and will do the right thing of moving the window data
    * in or out of _closedWindows if the winData indicates that our
    * need for saving it has changed.
    *
    * @param winData
    *        The data for the closed window that we might save.
+   * @param isLastWindow
+   *        Whether or not the window being closed is the last
+   *        browser window. Callers of this function should pass
+   *        in the value of SessionStoreInternal.atLastWindow for
+   *        this argument, and pass in the same value if they happen
+   *        to call this method again asynchronously (for example, after
+   *        a window flush).
    */
-  maybeSaveClosedWindow(winData) {
+  maybeSaveClosedWindow(winData, isLastWindow) {
     if (RunState.isRunning) {
       // Determine whether the window has any tabs worth saving.
       let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);
 
-      // When closing windows one after the other until Firefox quits, we
-      // will move those closed in series back to the "open windows" bucket
-      // before writing to disk. If however there is only a single window
-      // with tabs we deem not worth saving then we might end up with a
-      // random closed or even a pop-up window re-opened. To prevent that
-      // we explicitly allow saving an "empty" window state.
-      let isLastWindow =
-        Object.keys(this._windows).length == 1 &&
-        !this._closedWindows.some(win => win._shouldRestore || false);
-
       // Note that we might already have this window stored in
       // _closedWindows from a previous call to this function.
       let winIndex = this._closedWindows.indexOf(winData);
       let alreadyStored = (winIndex != -1);
       let shouldStore = (hasSaveableTabs || isLastWindow);
 
       if (shouldStore && !alreadyStored) {
         let index = this._closedWindows.findIndex(win => {
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -212,8 +212,9 @@ skip-if = true
 # Disabled on OS X:
 [browser_625016.js]
 skip-if = os == "mac"
 
 [browser_911547.js]
 [browser_send_async_message_oom.js]
 [browser_multiple_navigateAndRestore.js]
 run-if = e10s
+[browser_async_window_flushing.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_async_window_flushing.js
@@ -0,0 +1,38 @@
+/**
+ * Tests that when we close a window, it is immediately removed from the
+ * _windows array.
+ */
+add_task(function* test_synchronously_remove_window_state() {
+  // Depending on previous tests, we might already have some closed
+  // windows stored. We'll use its length to determine whether or not
+  // the window was added or not.
+  let state = JSON.parse(ss.getBrowserState());
+  ok(state, "Make sure we can get the state");
+  let initialWindows = state.windows.length;
+
+  // Open a new window and send the first tab somewhere
+  // interesting.
+  let newWin = yield BrowserTestUtils.openNewBrowserWindow();
+  let browser = newWin.gBrowser.selectedBrowser;
+  browser.loadURI("http://example.com");
+  yield BrowserTestUtils.browserLoaded(browser);
+  yield TabStateFlusher.flush(browser);
+
+  state = JSON.parse(ss.getBrowserState());
+  is(state.windows.length, initialWindows + 1,
+     "The new window to be in the state");
+
+  // Now close the window, and make sure that the window was removed
+  // from the windows list from the SessionState. We're specifically
+  // testing the case where the window is _not_ removed in between
+  // the close-initiated flush request and the flush response.
+  let windowClosed = BrowserTestUtils.windowClosed(newWin);
+  newWin.close();
+
+  state = JSON.parse(ss.getBrowserState());
+  is(state.windows.length, initialWindows,
+     "The new window should have been removed from the state");
+
+  // Wait for our window to go away
+  yield windowClosed;
+});
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -193,21 +193,24 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
 rooms_room_joined_label=Someone has joined the conversation!
 rooms_room_join_label=Join the conversation
 rooms_signout_alert=Open conversations will be closed
 room_name_untitled_page=Untitled Page
 
 # Infobar strings
 
-infobar_screenshare_browser_message=Users in your conversation will now be able to see the contents of any tab you click on.
-infobar_button_gotit_label=Got it!
-infobar_button_gotit_accesskey=G
-infobar_menuitem_dontshowagain_label=Don't show this again
-infobar_menuitem_dontshowagain_accesskey=D
+infobar_screenshare_browser_message=You are sharing your tabs. Any tab you click on can be seen by your friends
+infobar_screenshare_paused_browser_message=Tab sharing is paused
+infobar_button_pause_label=Pause
+infobar_button_resume_label=Resume
+infobar_button_stop_label=Stop
+infobar_button_pause_accesskey=P
+infobar_button_stop_accesskey=S
+infobar_button_resume_accesskey=R
 
 # Context in conversation strings
 
 ## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
 ## has no conversations available.
 no_conversations_message_heading2=No conversations yet.
 ## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
 ## user to start a new conversation.
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1113,26 +1113,16 @@ notification[value="translation"] button
 notification[value="translation"] button:not([type="menu"]) > .button-box {
   -moz-margin-end: 3ch;
 }
 
 notification[value="translation"] menulist > .menulist-dropmarker {
   display: block;
 }
 
-/* Loop/ Hello browser styles */
-
-notification[value="loop-sharing-notification"] .button-menubutton-button {
-  min-width: 0;
-}
-
-notification[value="loop-sharing-notification"] .messageImage {
-  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
-}
-
 #treecolAutoCompleteImage {
   max-width : 36px;
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/autocomplete-star.png");
   width: 16px;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3207,34 +3207,47 @@ menulist.translate-infobar-element > .me
   }
 
   menulist.translate-infobar-element > .menulist-dropmarker > .dropmarker-icon {
     width: 8px;
   }
 }
 
 /* Loop/ Hello browser styles */
+notification[value="loop-sharing-notification"] {
+  background: #00a9dc;
+  padding: 0;
+  border: 0;
+}
+
+notification[value="loop-sharing-notification"].paused {
+  background: #ebebeb;
+}
 
 notification[value="loop-sharing-notification"] .notification-button {
-  padding: 1px 5px;
-}
-
-notification[value="loop-sharing-notification"] .button-menubutton-button {
-  -moz-appearance: none;
-  min-width: 0;
-  margin: 0;
-}
-
-notification[value="loop-sharing-notification"] .messageImage {
-  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-menubar.png);
-}
-@media (min-resolution: 2dppx) {
-  notification[value="loop-sharing-notification"] .messageImage {
-    list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-menubar@2x.png);
-  }
+  background: #fff;
+  border-radius: 0;
+}
+
+notification[value="loop-sharing-notification"].paused .notification-button {
+  background: #57bd35;
+}
+
+notification[value="loop-sharing-notification"].paused .notification-button:hover {
+  background: #39a017;
+}
+
+notification[value="loop-sharing-notification"] .notification-button:hover,
+notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
+  background: #ebebeb;
+}
+
+notification[value="loop-sharing-notification"] .notification-button-default,
+notification[value="loop-sharing-notification"].paused .notification-button-default {
+  background: #fff;
 }
 
 .popup-notification-body[popupid="addon-progress"],
 .popup-notification-body[popupid="addon-install-confirmation"] {
   width: 28em;
   max-width: 28em;
 }
 
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -440,8 +440,88 @@
     list-style-image: url(chrome://browser/skin/social/services-64@2x.png);
   }
 
   #servicesInstall-notification-icon {
     list-style-image: url(chrome://browser/skin/social/services-16@2x.png);
   }
 %endif
 }
+
+/* Loop notification */
+notification[value="loop-sharing-notification"] {
+  -moz-appearance: none;
+  height: 40px;
+  background-color: #00a9dc;
+  box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
+}
+
+notification[value="loop-sharing-notification"].paused {
+  background-color: #ebebeb;
+}
+
+notification[value="loop-sharing-notification"] .notification-inner {
+  color: #fff;
+  padding: 0;
+}
+
+notification[value="loop-sharing-notification"].paused .notification-inner {
+  color: #00a9dc;
+}
+
+notification[value="loop-sharing-notification"] .notification-button {
+  -moz-appearance: none;
+  background-color: #fff;
+  border: 0;
+  border-right: solid 1px #ebebeb;
+  width: 100px;
+  height: 40px;
+  margin: 0;
+  list-style-image: url(chrome://browser/content/loop/shared/img/pause-12x12.svg);
+  box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
+  text-shadow: none;
+}
+
+notification[value="loop-sharing-notification"] .notification-button:-moz-locale-dir(rtl) {
+  border-right: 0;
+  border-left: solid 1px #ebebeb;
+}
+
+notification[value="loop-sharing-notification"].paused .notification-button {
+  background-color: #57bd35;
+  color: #fff;
+  list-style-image: url(chrome://browser/content/loop/shared/img/play-12x12.svg);
+}
+
+notification[value="loop-sharing-notification"].paused .notification-button:hover {
+  background-color: #39a017;
+}
+
+notification[value="loop-sharing-notification"] .notification-button:hover,
+notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
+  background-color: #ebebeb;
+}
+
+notification[value="loop-sharing-notification"] .notification-button-default,
+notification[value="loop-sharing-notification"].paused .notification-button-default {
+  color: #d92215;
+  background-color: #fff;
+  border-right: 0;
+  list-style-image: url(chrome://browser/content/loop/shared/img/stop-12x12.svg);
+}
+
+notification[value="loop-sharing-notification"] .notification-button .button-icon {
+  display: block;
+  -moz-margin-end: 6px;
+}
+
+notification[value="loop-sharing-notification"] .button-menubutton-button {
+  min-width: 0;
+}
+
+notification[value="loop-sharing-notification"] .messageImage {
+  list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-white);
+  margin-inline-start: 14px;
+}
+
+notification[value="loop-sharing-notification"].paused .messageImage {
+  list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-still);
+}
\ No newline at end of file
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2311,29 +2311,16 @@ notification[value="translation"] {
     -moz-margin-end: 3ch;
   }
 }
 
 .translation-menupopup {
   -moz-appearance: none;
 }
 
-/* Loop/ Hello browser styles */
-
-notification[value="loop-sharing-notification"] .button-menubutton-button {
-  -moz-appearance: none;
-  min-width: 0;
-  border: 0;
-  margin: 0;
-}
-
-notification[value="loop-sharing-notification"] .messageImage {
-  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
-}
-
 /* Bookmarks roots menu-items */
 #subscribeToPageMenuitem:not([disabled]),
 #subscribeToPageMenupopup,
 #BMB_subscribeToPageMenuitem:not([disabled]),
 #BMB_subscribeToPageMenupopup {
   list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
 }
 
--- a/build/autoconf/wrapper.m4
+++ b/build/autoconf/wrapper.m4
@@ -61,15 +61,15 @@ if test "$COMPILER_WRAPPER" != "no"; the
         ;;
     *)
         CC="$COMPILER_WRAPPER $CC"
         CXX="$COMPILER_WRAPPER $CXX"
         _SUBDIR_CC="$CC"
         _SUBDIR_CXX="$CXX"
         ac_cv_prog_CC="$CC"
         ac_cv_prog_CXX="$CXX"
-        MOZ_USING_COMPILER_WRAPPER=1
         ;;
     esac
+    MOZ_USING_COMPILER_WRAPPER=1
 fi
 
 AC_SUBST(MOZ_USING_COMPILER_WRAPPER)
 ])
--- a/configure.in
+++ b/configure.in
@@ -6982,17 +6982,17 @@ if test -n "$MOZ_OPTIMIZE"; then
 fi
 fi # COMPILE_ENVIRONMENT
 
 AC_SUBST(MOZ_OPTIMIZE)
 AC_SUBST(MOZ_FRAMEPTR_FLAGS)
 AC_SUBST(MOZ_OPTIMIZE_FLAGS)
 AC_SUBST(MOZ_OPTIMIZE_RUSTFLAGS)
 AC_SUBST(MOZ_OPTIMIZE_LDFLAGS)
-AC_SUBST(MOZ_ALLOW_HEAP_EXECUTE_FLAGS)
+AC_SUBST_LIST(MOZ_ALLOW_HEAP_EXECUTE_FLAGS)
 AC_SUBST(MOZ_PGO)
 AC_SUBST(MOZ_PGO_OPTIMIZE_FLAGS)
 
 dnl ========================================================
 dnl = Enable NS_StackWalk.
 dnl ========================================================
 
 # On Windows, NS_StackWalk will only work correctly if we have frame pointers
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -7,17 +7,18 @@ html, body {
   width: 100%;
 }
 
 h2, h3, h4 {
   margin-bottom: 5px;
 }
 
 button {
-  width: 100px;
+  padding-left: 20px;
+  padding-right: 20px;
 }
 
 #body {
   display: flex;
   flex-direction: row;
 }
 
 /* Category tabs */
@@ -75,8 +76,17 @@ label {
 
 .inverted-icons .target-icon {
   filter: invert(30%);
 }
 
 .target-details {
   flex: 1;
 }
+
+.addon-controls {
+  display: flex;
+  flex-direction: row;
+}
+
+.addon-options {
+  flex: 1;
+}
--- a/devtools/client/aboutdebugging/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/aboutdebugging.js
@@ -3,32 +3,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 /* global AddonsComponent, DebuggerClient, DebuggerServer, React,
    RuntimesComponent, WorkersComponent */
 
 "use strict";
 
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 const { loader } = Components.utils.import(
   "resource://devtools/shared/Loader.jsm", {});
 
 loader.lazyRequireGetter(this, "AddonsComponent",
   "devtools/client/aboutdebugging/components/addons", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "DebuggerServer",
   "devtools/server/main", true);
 loader.lazyRequireGetter(this, "Telemetry",
   "devtools/client/shared/telemetry");
 loader.lazyRequireGetter(this, "WorkersComponent",
   "devtools/client/aboutdebugging/components/workers", true);
 loader.lazyRequireGetter(this, "Services");
 
+loader.lazyImporter(this, "AddonManager",
+  "resource://gre/modules/AddonManager.jsm");
+
+const Strings = Services.strings.createBundle(
+  "chrome://devtools/locale/aboutdebugging.properties");
+
 var AboutDebugging = {
+  _prefListeners: [],
+
   _categories: null,
   get categories() {
     // If needed, initialize the list of available categories.
     if (!this._categories) {
       let elements = document.querySelectorAll(".category");
       this._categories = Array.map(elements, element => {
         let value = element.getAttribute("value");
         element.addEventListener("click", this.showTab.bind(this, value));
@@ -67,43 +76,79 @@ var AboutDebugging = {
 
     // Link checkboxes to prefs.
     let elements = document.querySelectorAll("input[type=checkbox][data-pref]");
     Array.map(elements, element => {
       let pref = element.dataset.pref;
       let updatePref = () => {
         Services.prefs.setBoolPref(pref, element.checked);
       };
+      element.addEventListener("change", updatePref, false);
       let updateCheckbox = () => {
         element.checked = Services.prefs.getBoolPref(pref);
       };
-      element.addEventListener("change", updatePref, false);
       Services.prefs.addObserver(pref, updateCheckbox, false);
+      this._prefListeners.push([pref, updateCheckbox]);
       updateCheckbox();
     });
 
+    // Link buttons to their associated actions.
+    let loadAddonButton = document.getElementById("load-addon-from-file");
+    loadAddonButton.addEventListener("click", this.loadAddonFromFile);
+
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.allowChromeProcess = true;
     let client = new DebuggerClient(DebuggerServer.connectPipe());
 
     client.connect(() => {
       React.render(React.createElement(AddonsComponent, { client }),
         document.querySelector("#addons"));
       React.render(React.createElement(WorkersComponent, { client }),
         document.querySelector("#workers"));
     });
   },
 
+  loadAddonFromFile() {
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    fp.init(window,
+      Strings.GetStringFromName("selectAddonFromFile"),
+      Ci.nsIFilePicker.modeOpen);
+    let res = fp.show();
+    if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
+      return;
+    }
+    let file = fp.file;
+    // AddonManager.installTemporaryAddon accepts either
+    // addon directory or final xpi file.
+    if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
+      file = file.parent;
+    }
+    try {
+      AddonManager.installTemporaryAddon(file);
+    } catch(e) {
+      alert("Error while installing the addon:\n" + e.message + "\n");
+      throw e;
+    }
+  },
+
   destroy() {
     let telemetry = this._telemetry;
     telemetry.toolClosed("aboutdebugging");
     telemetry.destroy();
+
+    this._prefListeners.forEach(([pref, listener]) => {
+      Services.prefs.removeObserver(pref, listener);
+    });
+    this._prefListeners = [];
+
+    React.unmountComponentAtNode(document.querySelector("#addons"));
+    React.unmountComponentAtNode(document.querySelector("#workers"));
   },
 };
 
 window.addEventListener("DOMContentLoaded", function load() {
   window.removeEventListener("DOMContentLoaded", load);
   AboutDebugging.init();
 });
 
--- a/devtools/client/aboutdebugging/aboutdebugging.xhtml
+++ b/devtools/client/aboutdebugging/aboutdebugging.xhtml
@@ -29,18 +29,23 @@
         <div class="category-name">&aboutDebugging.workers;</div>
       </div>
     </div>
     <div class="main-content">
       <div id="tab-addons" class="tab active">
         <div class="header">
           <h1 class="header-name">&aboutDebugging.addons;</h1>
         </div>
-        <input id="enable-addon-debugging" type="checkbox" data-pref="devtools.chrome.enabled"/>
-        <label for="enable-addon-debugging" title="&aboutDebugging.addonDebugging.tooltip;">&aboutDebugging.addonDebugging.label;</label>
+        <div class="addon-controls">
+          <div class="addon-options">
+            <input id="enable-addon-debugging" type="checkbox" data-pref="devtools.chrome.enabled"/>
+            <label for="enable-addon-debugging" title="&aboutDebugging.addonDebugging.tooltip;">&aboutDebugging.addonDebugging.label;</label>
+          </div>
+          <button id="load-addon-from-file">&aboutDebugging.loadTemporaryAddon;</button>
+        </div>
         <div id="addons"></div>
       </div>
       <div id="tab-workers" class="tab">
         <div class="header">
           <h1 class="header-name">&aboutDebugging.workers;</h1>
         </div>
         <input id="enable-worker-debugging" type="checkbox" data-pref="devtools.debugger.workers"/>
         <label for="enable-worker-debugging" title="&options.enableWorkers.tooltip;">&options.enableWorkers.label;</label>
copy from devtools/client/locales/en-US/aboutdebugging.properties
copy to devtools/client/aboutdebugging/moz.build
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/aboutdebugging/moz.build
@@ -1,12 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-debug = Debug
+DIRS += [
+    'components',
+]
 
-extensions = Extensions
-serviceWorkers = Service Workers
-sharedWorkers = Shared Workers
-otherWorkers = Other Workers
-
-nothing = Nothing yet.
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini'
+]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/addons/unpacked/bootstrap.js
@@ -0,0 +1,7 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+function startup() {
+  Services.obs.notifyObservers(null, "test-devtools", null);
+}
+function shutdown() {}
+function install() {}
+function uninstall() {}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/addons/unpacked/install.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+  <Description about="urn:mozilla:install-manifest"
+               em:id="test-devtools@mozilla.org"
+               em:name="test-devtools"
+               em:version="1.0"
+               em:type="2"
+               em:creator="Mozilla">
+
+    <em:bootstrap>true</em:bootstrap>
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>44.0a1</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+  head.js
+  addons/unpacked/bootstrap.js
+  addons/unpacked/install.rdf
+
+[browser_addons_install.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser_addons_install.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+var {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+
+const ADDON_ID = "test-devtools@mozilla.org";
+const ADDON_NAME = "test-devtools";
+
+add_task(function *() {
+  let { tab, document } = yield openAboutDebugging("addons");
+
+  // Mock the file picker to select a test addon
+  let MockFilePicker = SpecialPowers.MockFilePicker;
+  MockFilePicker.init(null);
+  let file = get_supports_file("addons/unpacked/install.rdf");
+  MockFilePicker.returnFiles = [file.file];
+
+  // Wait for a message sent by the addon's bootstrap.js file
+  let promise = new Promise(done => {
+    Services.obs.addObserver(function listener() {
+      Services.obs.removeObserver(listener, "test-devtools", false);
+      ok(true, "Addon installed and running its bootstrap.js file");
+      done();
+    }, "test-devtools", false);
+  });
+  // Trigger the file picker by clicking on the button
+  document.getElementById("load-addon-from-file").click();
+
+  // Wait for the addon execution
+  yield promise;
+
+  // Check that the addon appears in the UI
+  let names = [...document.querySelectorAll("#addons .target-name")];
+  names = names.map(element => element.textContent);
+  ok(names.includes(ADDON_NAME), "The addon name appears in the list of addons: " + names);
+
+  // Now uninstall this addon
+  yield new Promise(done => {
+    AddonManager.getAddonByID(ADDON_ID, addon => {
+      let listener = {
+        onUninstalled: function(aUninstalledAddon) {
+          if (aUninstalledAddon != addon) {
+            return;
+          }
+          AddonManager.removeAddonListener(listener);
+          done();
+        }
+      };
+      AddonManager.addAddonListener(listener);
+      addon.uninstall();
+    });
+  });
+
+  // Ensure that the UI removes the addon from the list
+  names = [...document.querySelectorAll("#addons .target-name")];
+  names = names.map(element => element.textContent);
+  ok(!names.includes(ADDON_NAME), "After uninstall, the addon name disappears from the list of addons: " + names);
+
+  yield closeAboutDebugging(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
+
+const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+DevToolsUtils.testing = true;
+
+const CHROME_ROOT = gTestPath.substr(0, gTestPath.lastIndexOf("/") + 1);
+
+registerCleanupFunction(() => {
+  DevToolsUtils.testing = false;
+});
+
+function openAboutDebugging() {
+  info("opening about:debugging");
+  return addTab("about:debugging").then(tab => {
+    let browser = tab.linkedBrowser;
+    return {
+      tab,
+      document: browser.contentDocument,
+      window: browser.contentWindow
+    };
+  });
+}
+
+function closeAboutDebugging(tab) {
+  info("Closing about:debugging");
+  return removeTab(tab);
+}
+
+function addTab(aUrl, aWindow) {
+  info("Adding tab: " + aUrl);
+
+  return new Promise(done => {
+    let targetWindow = aWindow || window;
+    let targetBrowser = targetWindow.gBrowser;
+
+    targetWindow.focus();
+    let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
+    let linkedBrowser = tab.linkedBrowser;
+
+    linkedBrowser.addEventListener("load", function onLoad() {
+      linkedBrowser.removeEventListener("load", onLoad, true);
+      info("Tab added and finished loading: " + aUrl);
+      done(tab);
+    }, true);
+  });
+}
+
+function removeTab(aTab, aWindow) {
+  info("Removing tab.");
+
+  return new Promise(done => {
+    let targetWindow = aWindow || window;
+    let targetBrowser = targetWindow.gBrowser;
+    let tabContainer = targetBrowser.tabContainer;
+
+    tabContainer.addEventListener("TabClose", function onClose(aEvent) {
+      tabContainer.removeEventListener("TabClose", onClose, false);
+      info("Tab removed and finished closing.");
+      done();
+    }, false);
+
+    targetBrowser.removeTab(aTab);
+  });
+}
+
+function get_supports_file(path) {
+  let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
+  getService(Ci.nsIChromeRegistry);
+  let fileurl = cr.convertChromeURL(Services.io.newURI(CHROME_ROOT + path, null, null));
+  return fileurl.QueryInterface(Ci.nsIFileURL);
+}
--- a/devtools/client/animationinspector/components.js
+++ b/devtools/client/animationinspector/components.js
@@ -560,16 +560,17 @@ function AnimationsTimeline(inspector) {
   this.targetNodes = [];
   this.timeBlocks = [];
   this.inspector = inspector;
   this.onAnimationStateChanged = this.onAnimationStateChanged.bind(this);
   this.onScrubberMouseDown = this.onScrubberMouseDown.bind(this);
   this.onScrubberMouseUp = this.onScrubberMouseUp.bind(this);
   this.onScrubberMouseOut = this.onScrubberMouseOut.bind(this);
   this.onScrubberMouseMove = this.onScrubberMouseMove.bind(this);
+  this.onAnimationSelected = this.onAnimationSelected.bind(this);
   EventEmitter.decorate(this);
 }
 
 exports.AnimationsTimeline = AnimationsTimeline;
 
 AnimationsTimeline.prototype = {
   init: function(containerEl) {
     this.win = containerEl.ownerDocument.defaultView;
@@ -628,39 +629,60 @@ AnimationsTimeline.prototype = {
     this.rootWrapperEl = null;
     this.timeHeaderEl = null;
     this.animationsEl = null;
     this.scrubberEl = null;
     this.scrubberHandleEl = null;
     this.win = null;
     this.inspector = null;
   },
+
   destroyTargetNodes: function() {
     for (let targetNode of this.targetNodes) {
       targetNode.destroy();
     }
     this.targetNodes = [];
   },
+
   destroyTimeBlocks: function() {
     for (let timeBlock of this.timeBlocks) {
+      timeBlock.off("selected", this.onAnimationSelected);
       timeBlock.destroy();
     }
     this.timeBlocks = [];
   },
 
   unrender: function() {
     for (let animation of this.animations) {
       animation.off("changed", this.onAnimationStateChanged);
     }
     TimeScale.reset();
     this.destroyTargetNodes();
     this.destroyTimeBlocks();
     this.animationsEl.innerHTML = "";
   },
 
+  onAnimationSelected: function(e, animation) {
+    // Unselect the previously selected animation if any.
+    [...this.rootWrapperEl.querySelectorAll(".animation.selected")].forEach(el => {
+      el.classList.remove("selected");
+    });
+
+    // Select the new animation.
+    let index = this.animations.indexOf(animation);
+    if (index === -1) {
+      return;
+    }
+    this.rootWrapperEl.querySelectorAll(".animation")[index]
+                      .classList.toggle("selected");
+
+    // Relay the event to the parent component.
+    this.emit("selected", animation);
+  },
+
   onScrubberMouseDown: function(e) {
     this.moveScrubberTo(e.pageX);
     this.win.addEventListener("mouseup", this.onScrubberMouseUp);
     this.win.addEventListener("mouseout", this.onScrubberMouseOut);
     this.win.addEventListener("mousemove", this.onScrubberMouseMove);
 
     // Prevent text selection while dragging.
     e.preventDefault();
@@ -759,16 +781,18 @@ AnimationsTimeline.prototype = {
           "class": "time-block"
         }
       });
       // Draw the animation time block.
       let timeBlock = new AnimationTimeBlock();
       timeBlock.init(timeBlockEl);
       timeBlock.render(animation);
       this.timeBlocks.push(timeBlock);
+
+      timeBlock.on("selected", this.onAnimationSelected);
     }
     // Use the document's current time to position the scrubber (if the server
     // doesn't provide it, hide the scrubber entirely).
     // Note that because the currentTime was sent via the protocol, some time
     // may have gone by since then, and so the scrubber might be a bit late.
     if (!documentCurrentTime) {
       this.scrubberEl.style.display = "none";
     } else {
@@ -867,25 +891,31 @@ AnimationsTimeline.prototype = {
     }
   }
 };
 
 /**
  * UI component responsible for displaying a single animation timeline, which
  * basically looks like a rectangle that shows the delay and iterations.
  */
-function AnimationTimeBlock() {}
+function AnimationTimeBlock() {
+  EventEmitter.decorate(this);
+  this.onClick = this.onClick.bind(this);
+}
 
 exports.AnimationTimeBlock = AnimationTimeBlock;
 
 AnimationTimeBlock.prototype = {
   init: function(containerEl) {
     this.containerEl = containerEl;
+    this.containerEl.addEventListener("click", this.onClick);
   },
+
   destroy: function() {
+    this.containerEl.removeEventListener("click", this.onClick);
     while (this.containerEl.firstChild) {
       this.containerEl.firstChild.remove();
     }
     this.containerEl = null;
     this.animation = null;
   },
 
   render: function(animation) {
@@ -994,12 +1024,16 @@ AnimationTimeBlock.prototype = {
 
     // Adding a note that the animation is running on the compositor thread if
     // needed.
     if (state.isRunningOnCompositor) {
       text += L10N.getStr("player.runningOnCompositorTooltip");
     }
 
     return text;
+  },
+
+  onClick: function() {
+    this.emit("selected", this.animation);
   }
 };
 
 let sortedUnique = arr => [...new Set(arr)].sort((a, b) => a > b);
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -4,16 +4,17 @@ subsuite = devtools
 support-files =
   doc_body_animation.html
   doc_frame_script.js
   doc_modify_playbackRate.html
   doc_negative_animation.html
   doc_simple_animation.html
   head.js
 
+[browser_animation_click_selects_animation.js]
 [browser_animation_controller_exposes_document_currentTime.js]
 [browser_animation_empty_on_invalid_nodes.js]
 [browser_animation_mutations_with_same_names.js]
 [browser_animation_panel_exists.js]
 [browser_animation_participate_in_inspector_update.js]
 [browser_animation_playerFronts_are_refreshed.js]
 [browser_animation_playerWidgets_appear_on_panel_init.js]
 [browser_animation_playerWidgets_target_nodes.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_click_selects_animation.js
@@ -0,0 +1,47 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that animations displayed in the timeline can be selected by clicking
+// them, and that this emits the right events and adds the right classes.
+
+add_task(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
+  let {panel} = yield openAnimationInspector();
+  let timeline = panel.animationsTimelineComponent;
+
+  let selected = timeline.rootWrapperEl.querySelectorAll(".animation.selected");
+  ok(!selected.length, "There are no animations selected by default");
+
+  info("Click on the first animation, expect the right event and right class");
+  let animation0 = yield clickToSelect(timeline, 0);
+  is(animation0, timeline.animations[0],
+     "The selected event was emitted with the right animation");
+  ok(isTimeBlockSelected(timeline, 0),
+     "The time block has the right selected class");
+
+  info("Click on the second animation, expect the first one to be unselected");
+  let animation1 = yield clickToSelect(timeline, 1);
+  is(animation1, timeline.animations[1],
+     "The selected event was emitted with the right animation");
+  ok(isTimeBlockSelected(timeline, 1),
+     "The second time block has the right selected class");
+  ok(!isTimeBlockSelected(timeline, 0),
+     "The first time block has been unselected");
+});
+
+function* clickToSelect(timeline, index) {
+  info("Click on animation " + index + " in the timeline");
+  let onSelected = timeline.once("selected");
+  let timeBlock = timeline.rootWrapperEl.querySelectorAll(".time-block")[index];
+  EventUtils.sendMouseEvent({type: "click"}, timeBlock,
+                            timeBlock.ownerDocument.defaultView);
+  return yield onSelected;
+}
+
+function isTimeBlockSelected(timeline, index) {
+  let animation = timeline.rootWrapperEl.querySelectorAll(".animation")[index];
+  return animation.classList.contains("selected");
+}
--- a/devtools/client/canvasdebugger/canvasdebugger.xul
+++ b/devtools/client/canvasdebugger/canvasdebugger.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/canvasdebugger.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % canvasDebuggerDTD SYSTEM "chrome://devtools/locale/canvasdebugger.dtd">
   %canvasDebuggerDTD;
 ]>
--- a/devtools/client/commandline/test/browser.ini
+++ b/devtools/client/commandline/test/browser.ini
@@ -70,16 +70,17 @@ support-files =
   browser_cmd_media.html
 [browser_cmd_pagemod_export.js]
 support-files =
   browser_cmd_pagemod_export.html
 [browser_cmd_paintflashing.js]
 [browser_cmd_pref1.js]
 [browser_cmd_pref2.js]
 [browser_cmd_pref3.js]
+[browser_cmd_qsa.js]
 [browser_cmd_restart.js]
 [browser_cmd_rulers.js]
 [browser_cmd_screenshot.js]
 support-files =
   browser_cmd_screenshot.html
 [browser_cmd_settings.js]
 [browser_gcli_async.js]
 [browser_gcli_canon.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/commandline/test/browser_cmd_qsa.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the qsa commands work as they should.
+
+const TEST_URI = "data:text/html;charset=utf-8,<body></body>";
+
+function test() {
+ helpers.addTabWithToolbar(TEST_URI, function(options) {
+   return helpers.audit(options, [
+     {
+       setup: 'qsa',
+       check: {
+         input:  'qsa',
+         hints:  ' [query]',
+         markup: 'VVV',
+         status: 'VALID'
+       }
+     },
+     {
+       setup: 'qsa body',
+       check: {
+         input:  'qsa body',
+         hints:  '',
+         markup: 'VVVVVVVV',
+         status: 'VALID'
+       }
+     }
+   ]);
+ }).then(finish, helpers.handleError);
+}
--- a/devtools/client/debugger/debugger.xul
+++ b/devtools/client/debugger/debugger.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="debugger.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/debugger.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/debugger.dtd">
   %debuggerDTD;
--- a/devtools/client/framework/dev-edition-promo/dev-edition-promo.xul
+++ b/devtools/client/framework/dev-edition-promo/dev-edition-promo.xul
@@ -1,16 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 <!DOCTYPE window [
 <!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
  %toolboxDTD;
 ]>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet rel="stylesheet" href="chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="dev-edition-promo">
   <vbox id="doorhanger-container">
     <hbox flex="1" id="top-panel">
       <image id="icon" />
       <vbox id="info">
         <h1>Using Developer Tools in your browser?</h1>
--- a/devtools/client/framework/test/browser_toolbox_sidebar_tool.xul
+++ b/devtools/client/framework/test/browser_toolbox_sidebar_tool.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
   <box flex="1" class="devtools-responsive-container theme-body">
     <vbox flex="1" class="devtools-main-content" id="content">test</vbox>
     <splitter class="devtools-side-splitter"/>
--- a/devtools/client/framework/toolbox-process-window.xul
+++ b/devtools/client/framework/toolbox-process-window.xul
@@ -2,16 +2,18 @@
 <!-- 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/. -->
 <!DOCTYPE window [
 <!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
  %toolboxDTD;
 ]>
 
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="devtools-toolbox-window"
         macanimationtype="document"
         fullscreenbutton="true"
         windowtype="devtools:toolbox"
         width="900" height="600"
         persist="screenX screenY width height sizemode">
 
--- a/devtools/client/framework/toolbox-window.xul
+++ b/devtools/client/framework/toolbox-window.xul
@@ -2,16 +2,18 @@
 <!-- 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/. -->
 <!DOCTYPE window [
 <!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
  %toolboxDTD;
 ]>
 
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="devtools-toolbox-window"
         macanimationtype="document"
         fullscreenbutton="true"
         windowtype="devtools:toolbox"
         width="900" height="320"
         persist="screenX screenY width height sizemode">
 
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/inspector/inspector.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/inspector.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % inspectorDTD SYSTEM "chrome://devtools/locale/inspector.dtd" >
    %inspectorDTD;
--- a/devtools/client/locales/en-US/aboutdebugging.dtd
+++ b/devtools/client/locales/en-US/aboutdebugging.dtd
@@ -1,9 +1,10 @@
 <!-- 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/. -->
 
 <!ENTITY aboutDebugging.title                      "about:debugging">
 <!ENTITY aboutDebugging.addons                     "Add-ons">
 <!ENTITY aboutDebugging.addonDebugging.label       "Enable add-on debugging">
 <!ENTITY aboutDebugging.addonDebugging.tooltip     "Turning this on will allow you to debug add-ons and various other parts of the browser chrome">
+<!ENTITY aboutDebugging.loadTemporaryAddon         "Load Temporary Add-on">
 <!ENTITY aboutDebugging.workers                    "Workers">
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/locales/en-US/aboutdebugging.properties
@@ -1,12 +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/.
 
 debug = Debug
 
 extensions = Extensions
+selectAddonFromFile = Select Add-on Directory or XPI File
 serviceWorkers = Service Workers
 sharedWorkers = Shared Workers
 otherWorkers = Other Workers
 
 nothing = Nothing yet.
--- a/devtools/client/markupview/test/browser_markupview_dragdrop_autoscroll.js
+++ b/devtools/client/markupview/test/browser_markupview_dragdrop_autoscroll.js
@@ -13,55 +13,61 @@ add_task(function*() {
   let {inspector} = yield addTab(TEST_URL).then(openInspector);
   let markup = inspector.markup;
   let viewHeight = markup.doc.documentElement.clientHeight;
 
   info("Pretend the markup-view is dragging");
   markup.isDragging = true;
 
   info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
-  let onScrolled = waitForViewScroll(markup);
+  let onScrolled = waitForScrollStop(markup);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
     pageY: viewHeight
   });
 
   let bottomScrollPos = yield onScrolled;
   ok(bottomScrollPos > 0, "The view was scrolled down");
 
   info("Simulate a mousemove at the top and expect more scrolling");
-  onScrolled = waitForViewScroll(markup);
+  onScrolled = waitForScrollStop(markup);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
     pageY: 0
   });
 
   let topScrollPos = yield onScrolled;
   ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
   is(topScrollPos, 0, "The view was scrolled up to the top");
 
   info("Simulate a mouseup to stop dragging");
   markup._onMouseUp();
 });
 
-function waitForViewScroll(markup) {
+/**
+ * Waits until the element has not scrolled for 30 consecutive frames.
+ */
+function* waitForScrollStop(markup) {
   let el = markup.doc.documentElement;
-  let startPos = el.scrollTop;
+  let win = markup.doc.defaultView;
+  let lastScrollTop = el.scrollTop;
+  let stopFrameCount = 0;
+  while (stopFrameCount < 30) {
+    // Wait for a frame.
+    yield new Promise(resolve => win.requestAnimationFrame(resolve));
 
-  return new Promise(resolve => {
-    let isDone = () => {
-      if (el.scrollTop === startPos) {
-        resolve(el.scrollTop);
-      } else {
-        startPos = el.scrollTop;
-        // Continue checking every 50ms.
-        setTimeout(isDone, 50);
-      }
-    };
+    // Check if the element has scrolled.
+    if (lastScrollTop == el.scrollTop) {
+      // No scrolling since the last frame.
+      stopFrameCount++;
+    } else {
+      // The element has scrolled. Reset the frame counter.
+      stopFrameCount = 0;
+      lastScrollTop = el.scrollTop;
+    }
+  }
 
-    // Start checking if the view scrolled after a while.
-    setTimeout(isDone, 50);
-  });
+  return lastScrollTop;
 }
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -2,17 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include('../templates.mozbuild')
 
 DIRS += [
-    'aboutdebugging/components',
+    'aboutdebugging',
     'animationinspector',
     'canvasdebugger',
     'commandline',
     'debugger',
     'eyedropper',
     'fontinspector',
     'framework',
     'inspector',
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/netmonitor/netmonitor.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/netmonitor.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % netmonitorDTD SYSTEM "chrome://devtools/locale/netmonitor.dtd">
   %netmonitorDTD;
--- a/devtools/client/performance/performance.xul
+++ b/devtools/client/performance/performance.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/performance.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % performanceDTD SYSTEM "chrome://devtools/locale/performance.dtd">
   %performanceDTD;
 ]>
--- a/devtools/client/shadereditor/shadereditor.xul
+++ b/devtools/client/shadereditor/shadereditor.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/shadereditor.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/shadereditor.dtd">
   %debuggerDTD;
 ]>
--- a/devtools/client/shared/test/doc_options-view.xul
+++ b/devtools/client/shared/test/doc_options-view.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <!DOCTYPE window []>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <popupset id="options-popupset">
--- a/devtools/client/storage/storage.xul
+++ b/devtools/client/storage/storage.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/storage.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -233,32 +233,37 @@ body {
   height: var(--timeline-animation-height);
   position: relative;
 }
 
 .animation-timeline .animation:nth-child(2n) {
   background-color: var(--even-animation-timeline-background-color);
 }
 
+.animation-timeline .animation.selected {
+  background-color: var(--theme-selection-background-semitransparent);
+}
+
 .animation-timeline .animation .target {
   width: var(--timeline-sidebar-width);
   overflow: hidden;
   height: 100%;
 }
 
 .animation-timeline .animation-target {
   background-color: transparent;
 }
 
 .animation-timeline .animation .time-block {
   position: absolute;
   top: 2px;
   left: var(--timeline-sidebar-width);
   right: 0;
   height: var(--timeline-animation-height);
+  cursor: pointer;
 }
 
 /* Animation iterations */
 
 .animation-timeline .animation .iterations {
   position: relative;
   height: 100%;
   box-sizing: border-box;
--- a/devtools/client/webaudioeditor/webaudioeditor.xul
+++ b/devtools/client/webaudioeditor/webaudioeditor.xul
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/webaudioeditor.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/webaudioeditor.dtd">
   %debuggerDTD;
 ]>
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -153,18 +153,22 @@ WebConsoleActor.prototype =
    * @type boolean
    */
   get _parentIsContentActor() {
     return "ContentActor" in DebuggerServer &&
             this.parentActor instanceof DebuggerServer.ContentActor;
   },
 
   /**
-   * The window we work with.
-   * @type nsIDOMWindow
+   * The window or sandbox we work with.
+   * Note that even if it is named `window` it refers to the current
+   * global we are debugging, which can be a Sandbox for addons
+   * or browser content toolbox.
+   *
+   * @type nsIDOMWindow or Sandbox
    */
   get window() {
     if (this.parentActor.isRootActor) {
       return this._getWindowForBrowserConsole();
     }
     return this.parentActor.window;
   },
 
@@ -731,17 +735,18 @@ WebConsoleActor.prototype =
     while (types.length > 0) {
       let type = types.shift();
       switch (type) {
         case "ConsoleAPI": {
           if (!this.consoleAPIListener) {
             break;
           }
 
-          let requestStartTime = this.window ?
+          // See `window` definition. It isn't always a DOM Window.
+          let requestStartTime = this.window && this.window.performance ?
             this.window.performance.timing.requestStart : 0;
 
           let cache = this.consoleAPIListener
                       .getCachedMessages(!this.parentActor.isRootActor);
           cache.forEach((aMessage) => {
             // Filter out messages that came from a ServiceWorker but happened
             // before the page was requested.
             if (aMessage.innerID === "ServiceWorker" &&
--- a/devtools/shared/gcli/commands/index.js
+++ b/devtools/shared/gcli/commands/index.js
@@ -64,16 +64,17 @@ exports.devtoolsModules = [
   "devtools/shared/gcli/commands/highlight",
   "devtools/shared/gcli/commands/inject",
   "devtools/shared/gcli/commands/jsb",
   "devtools/shared/gcli/commands/listen",
   "devtools/shared/gcli/commands/measure",
   "devtools/shared/gcli/commands/media",
   "devtools/shared/gcli/commands/pagemod",
   "devtools/shared/gcli/commands/paintflashing",
+  "devtools/shared/gcli/commands/qsa",
   "devtools/shared/gcli/commands/restart",
   "devtools/shared/gcli/commands/rulers",
   "devtools/shared/gcli/commands/screenshot",
   "devtools/shared/gcli/commands/security",
   "devtools/shared/gcli/commands/tools",
 ];
 
 /**
--- a/devtools/shared/gcli/commands/moz.build
+++ b/devtools/shared/gcli/commands/moz.build
@@ -16,14 +16,15 @@ DevToolsModules(
     'index.js',
     'inject.js',
     'jsb.js',
     'listen.js',
     'measure.js',
     'media.js',
     'pagemod.js',
     'paintflashing.js',
+    'qsa.js',
     'restart.js',
     'rulers.js',
     'screenshot.js',
     'security.js',
     'tools.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/shared/gcli/commands/qsa.js
@@ -0,0 +1,24 @@
+/* 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 l10n = require("gcli/l10n");
+
+exports.items = [
+  {
+    item: "command",
+    runAt: "server",
+    name: "qsa",
+    description: l10n.lookup("qsaDesc"),
+    params: [{
+      name: "query",
+      type: "nodelist",
+      description: l10n.lookup("qsaQueryDesc")
+    }],
+    exec: function(args, context) {
+      return args.query.length;
+    }
+  }
+];
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -52,30 +52,17 @@ using ::google::protobuf::io::ArrayInput
 using ::google::protobuf::io::CodedInputStream;
 using ::google::protobuf::io::GzipInputStream;
 using ::google::protobuf::io::ZeroCopyInputStream;
 
 using JS::ubi::AtomOrTwoByteChars;
 
 /*** Cycle Collection Boilerplate *****************************************************************/
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(HeapSnapshot)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HeapSnapshot)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HeapSnapshot)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HeapSnapshot)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HeapSnapshot, mParent)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(HeapSnapshot)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(HeapSnapshot)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HeapSnapshot)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
--- a/devtools/shared/locales/en-US/gclicommands.properties
+++ b/devtools/shared/locales/en-US/gclicommands.properties
@@ -1552,16 +1552,21 @@ unlistenOutput=All TCP ports closed
 # mediaEmulateType, mediaResetDesc, mediaResetManual) These strings describe
 # the 'media' commands and all available parameters.
 mediaDesc=CSS media type emulation
 mediaEmulateDesc=Emulate a specified CSS media type
 mediaEmulateManual=View the document as if rendered on a device supporting the given media type, with the relevant CSS rules applied.
 mediaEmulateType=The media type to emulate
 mediaResetDesc=Stop emulating a CSS media type
 
+# LOCALIZATION NOTE (qsaDesc, qsaQueryDesc)
+# These strings describe the 'qsa' commands and all available parameters.
+qsaDesc=Perform querySelectorAll on the current document and return number of matches
+qsaQueryDesc=CSS selectors separated by comma
+
 # LOCALIZATION NOTE (injectDesc, injectManual, injectLibraryDesc, injectLoaded,
 # injectFailed) These strings describe the 'inject' commands and all available
 # parameters.
 injectDesc=Inject common libraries into the page
 injectManual2=Inject common libraries into the content of the page which can also be accessed from the console.
 injectLibraryDesc=Select the library to inject or enter a valid script URI to inject
 injectLoaded=%1$S loaded
 injectFailed=Failed to load %1$S - Invalid URI
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1053,16 +1053,17 @@ Animation::UpdateFinishedState(SeekFlag 
   mPreviousCurrentTime = GetCurrentTime();
 }
 
 void
 Animation::UpdateEffect()
 {
   if (mEffect) {
     UpdateRelevance();
+    mEffect->NotifyAnimationTimingUpdated();
   }
 }
 
 void
 Animation::FlushStyle() const
 {
   nsIDocument* doc = GetRenderedDocument();
   if (doc) {
new file mode 100644
--- /dev/null
+++ b/dom/animation/EffectSet.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "EffectSet.h"
+#include "mozilla/dom/Element.h" // For Element
+#include "nsCycleCollectionNoteChild.h" // For CycleCollectionNoteChild
+
+namespace mozilla {
+
+/* static */ void
+EffectSet::PropertyDtor(void* aObject, nsIAtom* aPropertyName,
+                        void* aPropertyValue, void* aData)
+{
+  EffectSet* effectSet = static_cast<EffectSet*>(aPropertyValue);
+
+#ifdef DEBUG
+  MOZ_ASSERT(!effectSet->mCalledPropertyDtor, "Should not call dtor twice");
+  effectSet->mCalledPropertyDtor = true;
+#endif
+
+  delete effectSet;
+}
+
+void
+EffectSet::Traverse(nsCycleCollectionTraversalCallback& aCallback)
+{
+  for (auto iter = mEffects.Iter(); !iter.Done(); iter.Next()) {
+    CycleCollectionNoteChild(aCallback, iter.Get()->GetKey(),
+                             "EffectSet::mEffects[]", aCallback.Flags());
+  }
+}
+
+/* static */ EffectSet*
+EffectSet::GetEffectSet(dom::Element* aElement,
+                        nsCSSPseudoElements::Type aPseudoType)
+{
+  nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
+  return static_cast<EffectSet*>(aElement->GetProperty(propName));
+}
+
+/* static */ EffectSet*
+EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
+                                nsCSSPseudoElements::Type aPseudoType)
+{
+  EffectSet* effectSet = GetEffectSet(aElement, aPseudoType);
+  if (effectSet) {
+    return effectSet;
+  }
+
+  nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
+  effectSet = new EffectSet();
+
+  nsresult rv = aElement->SetProperty(propName, effectSet,
+                                      &EffectSet::PropertyDtor, true);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("SetProperty failed");
+    // The set must be destroyed via PropertyDtor, otherwise
+    // mCalledPropertyDtor assertion is triggered in destructor.
+    EffectSet::PropertyDtor(aElement, propName, effectSet, nullptr);
+    return nullptr;
+  }
+
+  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
+    aElement->SetMayHaveAnimations();
+  }
+
+  return effectSet;
+}
+
+/* static */ nsIAtom**
+EffectSet::GetEffectSetPropertyAtoms()
+{
+  static nsIAtom* effectSetPropertyAtoms[] =
+    {
+      nsGkAtoms::animationEffectsProperty,
+      nsGkAtoms::animationEffectsForBeforeProperty,
+      nsGkAtoms::animationEffectsForAfterProperty,
+      nullptr
+    };
+
+  return effectSetPropertyAtoms;
+}
+
+/* static */ nsIAtom*
+EffectSet::GetEffectSetPropertyAtom(nsCSSPseudoElements::Type aPseudoType)
+{
+  switch (aPseudoType) {
+    case nsCSSPseudoElements::ePseudo_NotPseudoElement:
+      return nsGkAtoms::animationEffectsProperty;
+
+    case nsCSSPseudoElements::ePseudo_before:
+      return nsGkAtoms::animationEffectsForBeforeProperty;
+
+    case nsCSSPseudoElements::ePseudo_after:
+      return nsGkAtoms::animationEffectsForAfterProperty;
+
+    default:
+      NS_NOTREACHED("Should not try to get animation effects for a pseudo "
+                    "other that :before or :after");
+      return nullptr;
+  }
+}
+
+void
+EffectSet::AddEffect(dom::KeyframeEffectReadOnly& aEffect)
+{
+  mEffects.PutEntry(&aEffect);
+}
+
+void
+EffectSet::RemoveEffect(dom::KeyframeEffectReadOnly& aEffect)
+{
+  mEffects.RemoveEntry(&aEffect);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/animation/EffectSet.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_EffectSet_h
+#define mozilla_EffectSet_h
+
+#include "nsCSSPseudoElements.h" // For nsCSSPseudoElements::Type
+#include "nsHashKeys.h" // For nsPtrHashKey
+#include "nsTHashtable.h" // For nsTHashtable
+
+namespace mozilla {
+
+namespace dom {
+class Element;
+class KeyframeEffectReadOnly;
+} // namespace dom
+
+// A wrapper around a hashset of AnimationEffect objects to handle
+// storing the set as a property of an element.
+class EffectSet
+{
+public:
+  EffectSet()
+#ifdef DEBUG
+    : mCalledPropertyDtor(false)
+#endif
+  {
+    MOZ_COUNT_CTOR(EffectSet);
+  }
+
+  ~EffectSet()
+  {
+    MOZ_ASSERT(mCalledPropertyDtor,
+               "must call destructor through element property dtor");
+    MOZ_COUNT_DTOR(EffectSet);
+  }
+  static void PropertyDtor(void* aObject, nsIAtom* aPropertyName,
+                           void* aPropertyValue, void* aData);
+
+  // Methods for supporting cycle-collection
+  void Traverse(nsCycleCollectionTraversalCallback& aCallback);
+
+  static EffectSet* GetEffectSet(dom::Element* aElement,
+                                 nsCSSPseudoElements::Type aPseudoType);
+  static EffectSet* GetOrCreateEffectSet(dom::Element* aElement,
+                                         nsCSSPseudoElements::Type aPseudoType);
+
+  void AddEffect(dom::KeyframeEffectReadOnly& aEffect);
+  void RemoveEffect(dom::KeyframeEffectReadOnly& aEffect);
+
+private:
+  typedef nsTHashtable<nsRefPtrHashKey<dom::KeyframeEffectReadOnly>>
+    OwningEffectSet;
+
+public:
+  // A simple iterator to support iterating over the effects in this object in
+  // range-based for loops.
+  //
+  // This allows us to avoid exposing mEffects directly and saves the
+  // caller from having to dereference hashtable iterators using
+  // the rather complicated: iter.Get()->GetKey().
+  class Iterator
+  {
+  public:
+    explicit Iterator(OwningEffectSet::Iterator&& aHashIterator)
+      : mHashIterator(mozilla::Move(aHashIterator))
+      , mIsEndIterator(false) { }
+    Iterator(Iterator&& aOther)
+      : mHashIterator(mozilla::Move(aOther.mHashIterator))
+      , mIsEndIterator(aOther.mIsEndIterator) { }
+
+    static Iterator EndIterator(OwningEffectSet::Iterator&& aHashIterator)
+    {
+      Iterator result(mozilla::Move(aHashIterator));
+      result.mIsEndIterator = true;
+      return result;
+    }
+
+    bool operator!=(const Iterator& aOther) const {
+      if (Done() || aOther.Done()) {
+        return Done() != aOther.Done();
+      }
+      return mHashIterator.Get() != aOther.mHashIterator.Get();
+    }
+
+    Iterator& operator++() {
+      MOZ_ASSERT(!Done());
+      mHashIterator.Next();
+      return *this;
+    }
+
+    dom::KeyframeEffectReadOnly* operator* ()
+    {
+      MOZ_ASSERT(!Done());
+      return mHashIterator.Get()->GetKey();
+    }
+
+  private:
+    Iterator() = delete;
+    Iterator(const Iterator&) = delete;
+    Iterator& operator=(const Iterator&) = delete;
+    Iterator& operator=(const Iterator&&) = delete;
+
+    bool Done() const {
+      return mIsEndIterator || mHashIterator.Done();
+    }
+
+    OwningEffectSet::Iterator mHashIterator;
+    bool mIsEndIterator;
+  };
+
+  Iterator begin() { return Iterator(mEffects.Iter()); }
+  Iterator end()
+  {
+    return Iterator::EndIterator(mEffects.Iter());
+  }
+
+  static nsIAtom** GetEffectSetPropertyAtoms();
+
+private:
+  static nsIAtom* GetEffectSetPropertyAtom(nsCSSPseudoElements::Type
+                                             aPseudoType);
+
+  OwningEffectSet mEffects;
+
+#ifdef DEBUG
+  bool mCalledPropertyDtor;
+#endif
+};
+
+} // namespace mozilla
+
+#endif // mozilla_EffectSet_h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -122,16 +122,19 @@ KeyframeEffectReadOnly::SetTiming(const 
 {
   if (mTiming == aTiming) {
     return;
   }
   mTiming = aTiming;
   if (mAnimation) {
     mAnimation->NotifyEffectTimingUpdated();
   }
+  // NotifyEffectTimingUpdated will eventually cause
+  // NotifyAnimationTimingUpdated to be called on this object which will
+  // update our registration with the target element.
 }
 
 Nullable<TimeDuration>
 KeyframeEffectReadOnly::GetLocalTime() const
 {
   // Since the *animation* start time is currently always zero, the local
   // time is equal to the parent time.
   Nullable<TimeDuration> result;
@@ -331,16 +334,17 @@ KeyframeEffectReadOnly::IsInEffect() con
   ComputedTiming computedTiming = GetComputedTiming();
   return !computedTiming.mProgress.IsNull();
 }
 
 void
 KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
 {
   mAnimation = aAnimation;
+  NotifyAnimationTimingUpdated();
 }
 
 const AnimationProperty*
 KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (size_t propIdx = 0, propEnd = mProperties.Length();
        propIdx != propEnd; ++propIdx) {
     if (aProperty == mProperties[propIdx].mProperty) {
@@ -506,30 +510,56 @@ KeyframeEffectReadOnly::SetIsRunningOnCo
   for (size_t i = 0; i < ArrayLength(mIsPropertyRunningOnCompositor); i++) {
     if (info[i].mProperty == aProperty) {
       mIsPropertyRunningOnCompositor[i] = aIsRunning;
       return;
     }
   }
 }
 
-// We need to define this here since Animation is an incomplete type
-// (forward-declared) in the header.
 KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
 {
 }
 
 void
 KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
 {
   for (bool& isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
     isPropertyRunningOnCompositor = false;
   }
 }
 
+void
+KeyframeEffectReadOnly::UpdateTargetRegistration()
+{
+  if (!mTarget) {
+    return;
+  }
+
+  bool isRelevant = mAnimation && mAnimation->IsRelevant();
+
+  // Animation::IsRelevant() returns a cached value. It only updates when
+  // something calls Animation::UpdateRelevance. Whenever our timing changes,
+  // we should be notifying our Animation before calling this, so
+  // Animation::IsRelevant() should be up-to-date by the time we get here.
+  MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
+             "Out of date Animation::IsRelevant value");
+
+  if (isRelevant) {
+    EffectSet* effectSet = EffectSet::GetOrCreateEffectSet(mTarget,
+                                                           mPseudoType);
+    effectSet->AddEffect(*this);
+  } else {
+    EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+    if (effectSet) {
+      effectSet->RemoveEffect(*this);
+    }
+  }
+}
+
 #ifdef DEBUG
 void
 DumpAnimationProperties(nsTArray<AnimationProperty>& aAnimationProperties)
 {
   for (auto& p : aAnimationProperties) {
     printf("%s\n", nsCSSProps::GetStringValue(p.mProperty).get());
     for (auto& s : p.mSegments) {
       nsString fromValue, toValue;
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -214,16 +214,17 @@ public:
   CompositeOperation Composite() const;
   void GetSpacing(nsString& aRetVal) const {
     aRetVal.AssignLiteral("distribute");
   }
 
   const AnimationTiming& Timing() const { return mTiming; }
   AnimationTiming& Timing() { return mTiming; }
   void SetTiming(const AnimationTiming& aTiming);
+  void NotifyAnimationTimingUpdated() { UpdateTargetRegistration(); }
 
   Nullable<TimeDuration> GetLocalTime() const;
 
   // This function takes as input the timing parameters of an animation and
   // returns the computed timing at the specified local time.
   //
   // The local time may be null in which case only static parameters such as the
   // active duration are calculated. All other members of the returned object
@@ -251,16 +252,17 @@ public:
   static StickyTimeDuration
   ActiveDuration(const AnimationTiming& aTiming);
 
   bool IsInPlay() const;
   bool IsCurrent() const;
   bool IsInEffect() const;
 
   void SetAnimation(Animation* aAnimation);
+  Animation* GetAnimation() const { return mAnimation; }
 
   const AnimationProperty*
   GetAnimationOfProperty(nsCSSProperty aProperty) const;
   bool HasAnimationOfProperty(nsCSSProperty aProperty) const {
     return GetAnimationOfProperty(aProperty) != nullptr;
   }
   bool HasAnimationOfProperties(const nsCSSProperty* aProperties,
                                 size_t aPropertyCount) const;
@@ -292,16 +294,27 @@ public:
   nsPresContext* GetPresContext() const;
 
   inline AnimationCollection* GetCollection() const;
 
 protected:
   virtual ~KeyframeEffectReadOnly();
   void ResetIsRunningOnCompositor();
 
+  // This effect is registered with its target element so long as:
+  //
+  // (a) It has a target element, and
+  // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but
+  //     filling forwards)
+  //
+  // As a result, we need to make sure this gets called whenever anything
+  // changes with regards to this effects's timing including changes to the
+  // owning Animation's timing.
+  void UpdateTargetRegistration();
+
   static AnimationTiming ConvertKeyframeEffectOptions(
     const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions);
 
   static void BuildAnimationPropertyList(
     JSContext* aCx,
     Element* aTarget,
     const Optional<JS::Handle<JSObject*>>& aFrames,
     InfallibleTArray<AnimationProperty>& aResult,
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -14,25 +14,27 @@ EXPORTS.mozilla.dom += [
     'DocumentTimeline.h',
     'KeyframeEffect.h',
 ]
 
 EXPORTS.mozilla += [
     'AnimationComparator.h',
     'AnimationUtils.h',
     'ComputedTimingFunction.h',
+    'EffectSet.h',
     'PendingAnimationTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffectReadOnly.cpp',
     'AnimationTimeline.cpp',
     'ComputedTimingFunction.cpp',
     'DocumentTimeline.cpp',
+    'EffectSet.cpp',
     'KeyframeEffect.cpp',
     'PendingAnimationTracker.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -49,16 +49,17 @@
 #include "nsXBLPrototypeBinding.h"
 #include "nsError.h"
 #include "nsDOMString.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/AnimationComparator.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ContentEvents.h"
+#include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "nsNodeUtils.h"
@@ -3300,34 +3301,32 @@ Element::MozRequestPointerLock()
 void
 Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
 {
   nsIDocument* doc = GetComposedDoc();
   if (doc) {
     doc->FlushPendingNotifications(Flush_Style);
   }
 
-  nsIAtom* properties[] = { nsGkAtoms::transitionsProperty,
-                            nsGkAtoms::animationsProperty };
-  for (size_t propIdx = 0; propIdx < MOZ_ARRAY_LENGTH(properties);
-       propIdx++) {
-    AnimationCollection* collection =
-      static_cast<AnimationCollection*>(
-        GetProperty(properties[propIdx]));
-    if (!collection) {
-      continue;
-    }
-    for (size_t animIdx = 0;
-         animIdx < collection->mAnimations.Length();
-         animIdx++) {
-      Animation* anim = collection->mAnimations[animIdx];
-      if (anim->IsRelevant()) {
-        aAnimations.AppendElement(anim);
-      }
-    }
+  EffectSet* effects = EffectSet::GetEffectSet(this,
+                         nsCSSPseudoElements::ePseudo_NotPseudoElement);
+  if (!effects) {
+    return;
+  }
+
+  for (KeyframeEffectReadOnly* effect : *effects) {
+    MOZ_ASSERT(effect && effect->GetAnimation(),
+               "Only effects associated with an animation should be "
+               "added to an element's effect set");
+    Animation* animation = effect->GetAnimation();
+
+    MOZ_ASSERT(animation->IsRelevant(),
+               "Only relevant animations should be added to an element's "
+               "effect set");
+    aAnimations.AppendElement(animation);
   }
 
   aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
 }
 
 NS_IMETHODIMP
 Element::GetInnerHTML(nsAString& aInnerHTML)
 {
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StaticPtr.h"
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "mozilla/dom/Event.h"
@@ -1346,16 +1347,21 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
   // which is dispatched in UnbindFromTree.
 
   if (tmp->HasProperties()) {
     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
       for (uint32_t i = 0; props[i]; ++i) {
         tmp->DeleteProperty(*props[i]);
       }
+      // Bug 1226091: Call MayHaveAnimations() first
+      nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
+      for (uint32_t i = 0; effectProps[i]; ++i) {
+        tmp->DeleteProperty(effectProps[i]);
+      }
     }
   }
 
   // Unlink child content (and unbind our subtree).
   if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
     uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
     if (childCount) {
       // Don't allow script to run while we're unbinding everything.
@@ -1896,16 +1902,25 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (tmp->HasProperties()) {
     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
       for (uint32_t i = 0; props[i]; ++i) {
         nsISupports* property =
           static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
         cb.NoteXPCOMChild(property);
       }
+      // Bug 1226091: Check MayHaveAnimations() first
+      nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
+      for (uint32_t i = 0; effectProps[i]; ++i) {
+        EffectSet* effectSet =
+          static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
+        if (effectSet) {
+          effectSet->Traverse(cb);
+        }
+      }
     }
   }
 
   // Traverse attribute names and child content.
   {
     uint32_t i;
     uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
     for (i = 0; i < attrs; i++) {
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2094,16 +2094,19 @@ GK_ATOM(ongamepadaxismove, "ongamepadaxi
 GK_ATOM(ongamepadconnected, "ongamepadconnected")
 GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
 #endif
 
 // Content property names
 GK_ATOM(animationsProperty, "AnimationsProperty")        // FrameAnimations*
 GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations*
 GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
+GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet*
+GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet*
+GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
 GK_ATOM(transitionsProperty, "TransitionsProperty")        // FrameTransitions*
 GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
 GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
 GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
 GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
 GK_ATOM(baseURIProperty, "baseURIProperty")
 GK_ATOM(lockedStyleStates, "lockedStyleStates")
 GK_ATOM(apzCallbackTransform, "apzCallbackTransform")
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -143,82 +143,80 @@ CropAndCopyDataSourceSurface(DataSourceS
 
   return dstDataSurface.forget();
 }
 
 /*
  * Encapsulate the given _aSurface_ into a layers::CairoImage.
  */
 static already_AddRefed<layers::Image>
-CreateImageFromSurface(SourceSurface* aSurface, ErrorResult& aRv)
+CreateImageFromSurface(SourceSurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
-  RefPtr<layers::CairoImage> image = new layers::CairoImage(aSurface->GetSize(), aSurface);
+  RefPtr<layers::CairoImage> image =
+    new layers::CairoImage(aSurface->GetSize(), aSurface);
+
   return image.forget();
 }
 
 /*
  * CreateImageFromRawData(), CreateSurfaceFromRawData() and
  * CreateImageFromRawDataInMainThreadSyncTask are helpers for
  * create-from-ImageData case
  */
 static already_AddRefed<SourceSurface>
 CreateSurfaceFromRawData(const gfx::IntSize& aSize,
                          uint32_t aStride,
                          gfx::SurfaceFormat aFormat,
                          uint8_t* aBuffer,
                          uint32_t aBufferLength,
-                         const Maybe<IntRect>& aCropRect,
-                         ErrorResult& aRv)
+                         const Maybe<IntRect>& aCropRect)
 {
   MOZ_ASSERT(!aSize.IsEmpty());
   MOZ_ASSERT(aBuffer);
 
   // Wrap the source buffer into a SourceSurface.
   RefPtr<DataSourceSurface> dataSurface =
     Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize, aFormat);
 
   if (NS_WARN_IF(!dataSurface)) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // The temporary cropRect variable is equal to the size of source buffer if we
   // do not need to crop, or it equals to the given cropping size.
   const IntRect cropRect = aCropRect.valueOr(IntRect(0, 0, aSize.width, aSize.height));
 
   // Copy the source buffer in the _cropRect_ area into a new SourceSurface.
   RefPtr<DataSourceSurface> result = CropAndCopyDataSourceSurface(dataSurface, cropRect);
 
   if (NS_WARN_IF(!result)) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   return result.forget();
 }
 
 static already_AddRefed<layers::Image>
 CreateImageFromRawData(const gfx::IntSize& aSize,
                        uint32_t aStride,
                        gfx::SurfaceFormat aFormat,
                        uint8_t* aBuffer,
                        uint32_t aBufferLength,
-                       const Maybe<IntRect>& aCropRect,
-                       ErrorResult& aRv)
+                       const Maybe<IntRect>& aCropRect)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Copy and crop the source buffer into a SourceSurface.
   RefPtr<SourceSurface> rgbaSurface =
     CreateSurfaceFromRawData(aSize, aStride, aFormat,
                              aBuffer, aBufferLength,
-                             aCropRect, aRv);
+                             aCropRect);
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!rgbaSurface)) {
     return nullptr;
   }
 
   // Convert RGBA to BGRA
   RefPtr<DataSourceSurface> rgbaDataSurface = rgbaSurface->GetDataSurface();
   RefPtr<DataSourceSurface> bgraDataSurface =
     Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface->GetSize(),
                                                SurfaceFormat::B8G8R8A8,
@@ -236,17 +234,21 @@ CreateImageFromRawData(const gfx::IntSiz
                      bgraMap.mData, bgraMap.mStride,
                      bgraDataSurface->GetSize().width,
                      bgraDataSurface->GetSize().height);
 
   rgbaDataSurface->Unmap();
   bgraDataSurface->Unmap();
 
   // Create an Image from the BGRA SourceSurface.
-  RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface, aRv);
+  RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface);
+
+  if (NS_WARN_IF(!image)) {
+    return nullptr;
+  }
 
   return image.forget();
 }
 
 /*
  * This is a synchronous task.
  * This class is used to create a layers::CairoImage from raw data in the main
  * thread. While creating an ImageBitmap from an ImageData, we need to create
@@ -261,56 +263,53 @@ class CreateImageFromRawDataInMainThread
 {
 public:
   CreateImageFromRawDataInMainThreadSyncTask(uint8_t* aBuffer,
                                              uint32_t aBufferLength,
                                              uint32_t aStride,
                                              gfx::SurfaceFormat aFormat,
                                              const gfx::IntSize& aSize,
                                              const Maybe<IntRect>& aCropRect,
-                                             ErrorResult& aError,
                                              layers::Image** aImage)
   : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate())
   , mImage(aImage)
   , mBuffer(aBuffer)
   , mBufferLength(aBufferLength)
   , mStride(aStride)
   , mFormat(aFormat)
   , mSize(aSize)
   , mCropRect(aCropRect)
-  , mError(aError)
   {
+    MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromRawDataInMainThreadSyncTask.");
   }
 
   bool MainThreadRun() override
   {
     RefPtr<layers::Image> image =
       CreateImageFromRawData(mSize, mStride, mFormat,
                              mBuffer, mBufferLength,
-                             mCropRect,
-                             mError);
+                             mCropRect);
 
-    if (NS_WARN_IF(mError.Failed())) {
+    if (NS_WARN_IF(!image)) {
       return false;
     }
 
     image.forget(mImage);
 
     return true;
   }
 
 private:
   layers::Image** mImage;
   uint8_t* mBuffer;
   uint32_t mBufferLength;
   uint32_t mStride;
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
   const Maybe<IntRect>& mCropRect;
-  ErrorResult& mError;
 };
 
 static bool
 CheckSecurityForHTMLElements(bool aIsWriteOnly, bool aCORSUsed, nsIPrincipal* aPrincipal)
 {
   MOZ_ASSERT(aPrincipal);
 
   if (aIsWriteOnly) {
@@ -514,19 +513,20 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   // checking.
   RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl, aRv);
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   // Create ImageBitmap.
-  RefPtr<layers::Image> data = CreateImageFromSurface(surface, aRv);
+  RefPtr<layers::Image> data = CreateImageFromSurface(surface);
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!data)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
@@ -618,19 +618,20 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   }
 
   if (NS_WARN_IF(!croppedSurface)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // Create an Image from the SourceSurface.
-  RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface, aRv);
+  RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface);
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!data)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(cropRect, aRv);
@@ -664,31 +665,31 @@ ImageBitmap::CreateInternal(nsIGlobalObj
     return nullptr;
   }
 
   // Create and Crop the raw data into a layers::Image
   RefPtr<layers::Image> data;
   if (NS_IsMainThread()) {
     data = CreateImageFromRawData(imageSize, imageStride, FORMAT,
                                   array.Data(), dataLength,
-                                  aCropRect, aRv);
+                                  aCropRect);
   } else {
     RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task
       = new CreateImageFromRawDataInMainThreadSyncTask(array.Data(),
                                                        dataLength,
                                                        imageStride,
                                                        FORMAT,
                                                        imageSize,
                                                        aCropRect,
-                                                       aRv,
                                                        getter_AddRefs(data));
     task->Dispatch(aRv);
   }
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!data)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // Create an ImageBimtap.
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // The cropping information has been handled in the CreateImageFromRawData()
   // function.
@@ -714,19 +715,20 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   }
 
   const IntSize surfaceSize = surface->GetSize();
   if (surfaceSize.width == 0 || surfaceSize.height == 0) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  RefPtr<layers::Image> data = CreateImageFromSurface(surface, aRv);
+  RefPtr<layers::Image> data = CreateImageFromSurface(surface);
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!data)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
@@ -818,66 +820,64 @@ AsyncFulfillImageBitmapPromise(Promise* 
   } else {
     RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
       new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
     task->Dispatch(GetCurrentThreadWorkerPrivate()->GetJSContext()); // Actually, to the current worker-thread.
   }
 }
 
 static already_AddRefed<SourceSurface>
-DecodeBlob(Blob& aBlob, ErrorResult& aRv)
+DecodeBlob(Blob& aBlob)
 {
   // Get the internal stream of the blob.
   nsCOMPtr<nsIInputStream> stream;
-  aBlob.Impl()->GetInternalStream(getter_AddRefs(stream), aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+  ErrorResult error;
+  aBlob.Impl()->GetInternalStream(getter_AddRefs(stream), error);
+  if (NS_WARN_IF(error.Failed())) {
     return nullptr;
   }
 
   // Get the MIME type string of the blob.
   // The type will be checked in the DecodeImage() method.
   nsAutoString mimeTypeUTF16;
   aBlob.GetType(mimeTypeUTF16);
 
   // Get the Component object.
   nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
   if (NS_WARN_IF(!imgtool)) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // Decode image.
   NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeTypeUTF16); // NS_ConvertUTF16toUTF8 ---|> nsAutoCString
   nsCOMPtr<imgIContainer> imgContainer;
   nsresult rv = imgtool->DecodeImage(stream, mimeTypeUTF8, getter_AddRefs(imgContainer));
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   // Get the surface out.
   uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE;
   uint32_t whichFrame = imgIContainer::FRAME_FIRST;
   RefPtr<SourceSurface> surface = imgContainer->GetFrame(whichFrame, frameFlags);
 
   if (NS_WARN_IF(!surface)) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   return surface.forget();
 }
 
 static already_AddRefed<layers::Image>
-DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect, ErrorResult& aRv)
+DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
 {
   // Decode the blob into a SourceSurface.
-  RefPtr<SourceSurface> surface = DecodeBlob(aBlob, aRv);
+  RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
 
-  if (NS_WARN_IF(aRv.Failed())) {
+  if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   // Crop the source surface if needed.
   RefPtr<SourceSurface> croppedSurface = surface;
 
   if (aCropRect.isSome()) {
     // The blob is just decoded into a RasterImage and not optimized yet, so the
@@ -893,22 +893,25 @@ DecodeAndCropBlob(Blob& aBlob, Maybe<Int
     //       decode the blob off the main thread. Re-check if we should do
     //       cropping at this moment again there.
     RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
     croppedSurface = CropAndCopyDataSourceSurface(dataSurface, aCropRect.ref());
     aCropRect->MoveTo(0, 0);
   }
 
   if (NS_WARN_IF(!croppedSurface)) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   // Create an Image from the source surface.
-  RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface, aRv);
+  RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
+
+  if (NS_WARN_IF(!image)) {
+    return nullptr;
+  }
 
   return image.forget();
 }
 
 class CreateImageBitmapFromBlob
 {
 protected:
   CreateImageBitmapFromBlob(Promise* aPromise,
@@ -980,21 +983,20 @@ public:
   {
     DoCreateImageBitmapFromBlob();
     return NS_OK;
   }
 
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
-    ErrorResult rv;
-    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, rv);
+    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect);
 
-    if (NS_WARN_IF(rv.Failed())) {
-      mPromise->MaybeReject(rv);
+    if (NS_WARN_IF(!data)) {
+      mPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
       return nullptr;
     }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
     return imageBitmap.forget();
   }
 };
@@ -1004,43 +1006,40 @@ class CreateImageBitmapFromBlobWorkerTas
 {
   // This is a synchronous task.
   class DecodeBlobInMainThreadSyncTask final : public WorkerMainThreadRunnable
   {
   public:
     DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
                                    Blob& aBlob,
                                    Maybe<IntRect>& aCropRect,
-                                   ErrorResult& aError,
                                    layers::Image** aImage)
     : WorkerMainThreadRunnable(aWorkerPrivate)
     , mBlob(aBlob)
     , mCropRect(aCropRect)
-    , mError(aError)
     , mImage(aImage)
     {
     }
 
     bool MainThreadRun() override
     {
-      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mError);
+      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect);
 
-      if (NS_WARN_IF(mError.Failed())) {
+      if (NS_WARN_IF(!image)) {
         return true;
       }
 
       image.forget(mImage);
 
       return true;
     }
 
   private:
     Blob& mBlob;
     Maybe<IntRect>& mCropRect;
-    ErrorResult& mError;
     layers::Image** mImage;
   };
 
 public:
   CreateImageBitmapFromBlobWorkerTask(Promise* aPromise,
                                   nsIGlobalObject* aGlobal,
                                   mozilla::dom::Blob& aBlob,
                                   const Maybe<IntRect>& aCropRect)
@@ -1057,24 +1056,29 @@ public:
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
     RefPtr<layers::Image> data;
 
     ErrorResult rv;
     RefPtr<DecodeBlobInMainThreadSyncTask> task =
       new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
-                                         rv, getter_AddRefs(data));
+                                         getter_AddRefs(data));
     task->Dispatch(rv); // This is a synchronous call.
 
     if (NS_WARN_IF(rv.Failed())) {
       // XXXbz does this really make sense if we're shutting down?  Ah, well.
       mPromise->MaybeReject(rv);
       return nullptr;
     }
+    
+    if (NS_WARN_IF(!data)) {
+      mPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+      return nullptr;
+    }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
     return imageBitmap.forget();
   }
 
 };
 
deleted file mode 100644
--- a/dom/contacts/tests/contacts_chromescript.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Ensure the contacts service is running in the parent.
-Components.utils.import("resource://gre/modules/ContactService.jsm");
--- a/dom/contacts/tests/mochitest.ini
+++ b/dom/contacts/tests/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-support-files = shared.js contacts_chromescript.js
+support-files = shared.js
 
 [test_contacts_basics.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_basics2.html]
 skip-if = (toolkit == 'gonk' && debug) || (os == 'win' && os_version == '5.1') #debug-only failure, bug 967258 on XP
 [test_contacts_blobs.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_events.html]
--- a/dom/contacts/tests/shared.js
+++ b/dom/contacts/tests/shared.js
@@ -1,16 +1,12 @@
 "use strict";
 
 // Fix the environment to run Contacts tests
-if (SpecialPowers.isMainProcess()) {
-  SpecialPowers.Cu.import("resource://gre/modules/ContactService.jsm");
-} else {
-  SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('contacts_chromescript.js'));
-}
+SpecialPowers.importInMainProcess("resource://gre/modules/ContactService.jsm");
 
 // Some helpful global vars
 var isAndroid = (navigator.userAgent.indexOf("Android") !== -1);
 
 var defaultOptions = {
   sortBy: "givenName",
 };
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -477,16 +477,40 @@ EventStateManager::OnStopObservingConten
 void
 EventStateManager::TryToFlushPendingNotificationsToIME()
 {
   if (mIMEContentObserver) {
     mIMEContentObserver->TryToFlushPendingNotifications();
   }
 }
 
+static bool
+IsMessageMouseUserActivity(EventMessage aMessage)
+{
+  return aMessage == eMouseMove ||
+         aMessage == eMouseUp ||
+         aMessage == eMouseDown ||
+         aMessage == eMouseDoubleClick ||
+         aMessage == eMouseClick ||
+         aMessage == eMouseActivate ||
+         aMessage == eMouseLongTap;
+}
+
+static bool
+IsMessageGamepadUserActivity(EventMessage aMessage)
+{
+#ifndef MOZ_GAMEPAD
+  return false;
+#else
+  return aMessage == eGamepadButtonDown ||
+         aMessage == eGamepadButtonUp ||
+         aMessage == eGamepadAxisMove;
+#endif
+}
+
 nsresult
 EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
                                   WidgetEvent* aEvent,
                                   nsIFrame* aTargetFrame,
                                   nsIContent* aTargetContent,
                                   nsEventStatus* aStatus)
 {
   NS_ENSURE_ARG_POINTER(aStatus);
@@ -505,22 +529,22 @@ EventStateManager::PreHandleEvent(nsPres
   mCurrentTarget = aTargetFrame;
   mCurrentTargetContent = nullptr;
 
   // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
   // a page when user is not active doesn't change the state to active.
   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   if (aEvent->mFlags.mIsTrusted &&
       ((mouseEvent && mouseEvent->IsReal() &&
-        mouseEvent->mMessage != eMouseEnterIntoWidget &&
-        mouseEvent->mMessage != eMouseExitFromWidget) ||
+        IsMessageMouseUserActivity(mouseEvent->mMessage)) ||
        aEvent->mClass == eWheelEventClass ||
        aEvent->mClass == ePointerEventClass ||
        aEvent->mClass == eTouchEventClass ||
-       aEvent->mClass == eKeyboardEventClass)) {
+       aEvent->mClass == eKeyboardEventClass ||
+       IsMessageGamepadUserActivity(aEvent->mMessage))) {
     if (gMouseOrKeyboardEventCounter == 0) {
       nsCOMPtr<nsIObserverService> obs =
         mozilla::services::GetObserverService();
       if (obs) {
         obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
         UpdateUserActivityTimer();
       }
     }
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -328,16 +328,17 @@ PositionError::NotifyCallback(const GeoP
 {
   nsAutoMicroTask mt;
   if (aCallback.HasWebIDLCallback()) {
     PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
 
     if (callback) {
       ErrorResult err;
       callback->Call(*this, err);
+      err.SuppressException();
     }
   } else {
     nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
     if (callback) {
       callback->HandleEvent(this);
     }
   }
 }
@@ -656,16 +657,17 @@ nsGeolocationRequest::SendLocation(nsIDO
 
   nsAutoMicroTask mt;
   if (mCallback.HasWebIDLCallback()) {
     ErrorResult err;
     PositionCallback* callback = mCallback.GetWebIDLCallback();
 
     MOZ_ASSERT(callback);
     callback->Call(*wrapped, err);
+    err.SuppressException();
   } else {
     nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
 
     MOZ_ASSERT(callback);
     callback->HandleEvent(aPosition);
   }
 
   StopTimeoutTimer();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4759,18 +4759,17 @@ HTMLMediaElement::IsPlayingThroughTheAud
   }
 
   // A loop always is playing
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
     return true;
   }
 
   // If we are actually playing...
-  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
-      !IsPlaybackEnded()) {
+  if (IsCurrentlyPlaying()) {
     return true;
   }
 
   // If we are seeking, we consider it as playing
   if (mPlayingThroughTheAudioChannelBeforeSeek) {
     return true;
   }
 
@@ -5114,10 +5113,31 @@ HTMLMediaElement::ComputedVolume() const
 }
 
 bool
 HTMLMediaElement::ComputedMuted() const
 {
   return (mMuted & MUTED_BY_AUDIO_CHANNEL);
 }
 
+bool
+HTMLMediaElement::IsCurrentlyPlaying() const
+{
+  // We have playable data, but we still need to check whether data is "real"
+  // current data.
+  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
+      !IsPlaybackEnded()) {
+
+    // Restart the video after ended, it needs to seek to the new position.
+    // In b2g, the cache is not large enough to store whole video data, so we
+    // need to download data again. In this case, although the ready state is
+    // "HAVE_CURRENT_DATA", it is the previous old data. Actually we are not
+    // yet have enough currently data.
+    if (mDecoder && mDecoder->IsSeeking() && !mPlayingBeforeSeek) {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1065,16 +1065,19 @@ protected:
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
   // next block of audio samples) preceeding seek target.
   void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
 
   // A method to check if we are playing through the AudioChannel.
   bool IsPlayingThroughTheAudioChannel() const;
 
+  // A method to check whether we are currently playing.
+  bool IsCurrentlyPlaying() const;
+
   // Update the audio channel playing state
   void UpdateAudioChannelPlayingState();
 
   // Adds to the element's list of pending text tracks each text track
   // in the element's list of text tracks whose text track mode is not disabled
   // and whose text track readiness state is loading.
   void PopulatePendingTextTrackList();
 
--- a/dom/indexedDB/test/file_app_isolation.js
+++ b/dom/indexedDB/test/file_app_isolation.js
@@ -148,21 +148,14 @@ function startTest() {
       is(event.target.result.name, 'foo', 'data have been written');
       db.close();
 
       gTestRunner.next();
     };
   }
 }
 
-// test_ipc.html executes all the tests in this directory in content process.
-// It will fail on this one for the moment.
-if (!SpecialPowers.isMainProcess()) {
-  todo(false, "We should make this work on content process");
-  SimpleTest.finish();
-} else {
-  // TODO: remove unsetting network.disable.ipc.security as part of bug 820712
-  SpecialPowers.pushPrefEnv({
-    "set": [
-      ["network.disable.ipc.security", true],
-    ]
-  }, startTest);
-}
+// TODO: remove unsetting network.disable.ipc.security as part of bug 820712
+SpecialPowers.pushPrefEnv({
+  "set": [
+    ["network.disable.ipc.security", true],
+  ]
+}, startTest);
--- a/dom/indexedDB/test/test_bfcache.html
+++ b/dom/indexedDB/test/test_bfcache.html
@@ -3,35 +3,16 @@
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="text/javascript">
-    function _runTest() {
-      // Work around Win2k3 debug tinderboxes that may be too slow to complete this test. (Bug 614474)
-      if (navigator.oscpu == "Windows NT 5.2") {
-        todo(false, "Test disabled on (too slow debug) Windows 2003 (tinderboxes)");
-
-        finishTest();
-        return;
-      }
-
-      if (!SpecialPowers.isMainProcess()) {
-        todo(false, "Test disabled in child processes, for now");
-        finishTest();
-        return;
-      }
-
-      runTest();
-    }
-  </script>
   <script type="text/javascript;version=1.7">
     var gOrigMaxTotalViewers = undefined;
     function setCachePref(enabled) {
       if (enabled) {
         is(typeof gOrigMaxTotalViewers, "undefined",
            "don't double-enable bfcache");
         SpecialPowers.setBoolPref("browser.sessionhistory.cache_subframes",
                                   true);
@@ -74,13 +55,13 @@
       finishTest();
       yield undefined;
     }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
-<body onload="_runTest();">
+<body onload="runTest();">
   <iframe id="iframe"></iframe>
 </body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_compat.html
+++ b/dom/indexedDB/test/test_filehandle_compat.html
@@ -38,16 +38,15 @@
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_getFile.html
+++ b/dom/indexedDB/test/test_filehandle_getFile.html
@@ -39,16 +39,15 @@
     ok(request.lockedFile === undefined, "Property should not exist");
     ok(!('onprogress' in request), "Property should not exist");
     ok(request.onprogress === undefined, "Property should not exist");
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_lifetimes.html
+++ b/dom/indexedDB/test/test_filehandle_lifetimes.html
@@ -43,16 +43,15 @@
       is(e.name, "FileHandleInactiveError", "Good error.");
       is(e.code, 0, "Good error code.");
     }
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html
+++ b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html
@@ -55,16 +55,15 @@
 
     continueToNextStep();
     yield undefined;
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_location.html
+++ b/dom/indexedDB/test/test_filehandle_location.html
@@ -90,16 +90,15 @@
     event = yield undefined;
 
     is(fileHandle.location, 0, "Correct location");
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_ordering.html
+++ b/dom/indexedDB/test/test_filehandle_ordering.html
@@ -48,16 +48,15 @@
 
     event = yield undefined;
     is(event.target.result, "2", "File handles were ordered properly.");
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_overlapping.html
+++ b/dom/indexedDB/test/test_filehandle_overlapping.html
@@ -59,16 +59,15 @@
 
       is(stepNumber, 4, "All callbacks received");
     }
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html
+++ b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html
@@ -67,16 +67,15 @@
     catch (e) {
       ok(true, "Flushing a readonly file handle failed");
     }
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_request_readyState.html
+++ b/dom/indexedDB/test/test_filehandle_request_readyState.html
@@ -55,16 +55,15 @@
 
     ok(event.target.result, "Got something");
     is(request.readyState, "done", "Correct readyState");
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html
+++ b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html
@@ -60,16 +60,15 @@
     while (thread.hasPendingEvents()) {
       thread.processNextEvent(false);
     }
 
     finishTest();
     yield undefined;
   }
   </script>
-  <script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_leaving_page.html
+++ b/dom/indexedDB/test/test_leaving_page.html
@@ -10,39 +10,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 
 <body onload="runTest();">
   <iframe id="inner"></iframe>
   <a id="a" href="leaving_page_iframe.html"></a>
 
   <script type="text/javascript;version=1.7">
-    if (!SpecialPowers.isMainProcess()) {
-      window.runTest = function() {
-        todo(false, "Figure out this test for child processes!");
-        finishTest();
-      }
-    }
-
     onmessage = function(e) {
       ok(false, "gotmessage: " + e.data);
     }
 
     function testSteps()
     {
       var iframe = $("inner");
       iframe.src = "leaving_page_iframe.html";
       iframe.onload = continueToNextStep;
       yield undefined;
       is(iframe.contentWindow.location.href, $("a").href,
          "should navigate to iframe page");
       yield undefined;
       is(iframe.contentWindow.location.href, "about:blank",
          "should nagivate to about:blank");
-         
+
       let request = indexedDB.open(location, 1);
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       let db = event.target.result;
       db.transaction(["mystore"]).objectStore("mystore").get(42).onsuccess =
         grabEventAndContinueHandler;
       event = yield undefined;
--- a/dom/indexedDB/test/webapp_clearBrowserData.js
+++ b/dom/indexedDB/test/webapp_clearBrowserData.js
@@ -105,22 +105,16 @@ function testSteps()
   ok(testValue == event.target.result, "data still exists");
 
   finishTest();
   yield undefined;
 }
 
 function start()
 {
-  if (!SpecialPowers.isMainProcess()) {
-    todo(false, "Test disabled in child processes, for now");
-    SimpleTest.finish();
-    return;
-  }
-
   SpecialPowers.addPermission("browser", true, document);
   SpecialPowers.addPermission("browser", true, { manifestURL: manifestURL });
   SpecialPowers.addPermission("embed-apps", true, document);
   SpecialPowers.addPermission("indexedDB", true, { manifestURL: manifestURL });
 
   SpecialPowers.setAllAppsLaunchable(true);
 
   window.addEventListener("unload", function cleanup(event) {
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -143,35 +143,35 @@ MP3TrackDemuxer::Init() {
 
   MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
          mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
          mInfo->mDuration);
 
   return mSamplesPerSecond && mChannels;
 }
 
+media::TimeUnit
+MP3TrackDemuxer::SeekPosition() const {
+  TimeUnit pos = Duration(mFrameIndex);
+  if (Duration() > TimeUnit()) {
+    pos = std::min(Duration(), pos);
+  }
+  return pos;
+}
+
 #ifdef ENABLE_TESTS
 const FrameParser::Frame&
 MP3TrackDemuxer::LastFrame() const {
   return mParser.PrevFrame();
 }
 
 RefPtr<MediaRawData>
 MP3TrackDemuxer::DemuxSample() {
   return GetNextFrame(FindNextFrame());
 }
-
-media::TimeUnit
-MP3TrackDemuxer::SeekPosition() const {
-  TimeUnit pos = Duration(mFrameIndex);
-  if (Duration() > TimeUnit()) {
-    pos = std::min(Duration(), pos);
-  }
-  return pos;
-}
 #endif
 
 const ID3Parser::ID3Header&
 MP3TrackDemuxer::ID3Header() const {
   return mParser.ID3Header();
 }
 
 const FrameParser::VBRHeader&
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/MP3Demuxer.h
@@ -365,20 +365,22 @@ public:
 
   // Returns the estimated stream duration, or a 0-duration if unknown.
   media::TimeUnit Duration() const;
 
   // Returns the estimated duration up to the given frame number,
   // or a 0-duration if unknown.
   media::TimeUnit Duration(int64_t aNumFrames) const;
 
+  // Returns the estimated current seek position time.
+  media::TimeUnit SeekPosition() const;
+
 #ifdef ENABLE_TESTS
   const FrameParser::Frame& LastFrame() const;
   RefPtr<MediaRawData> DemuxSample();
-  media::TimeUnit SeekPosition() const;
 #endif
 
   const ID3Parser::ID3Header& ID3Header() const;
   const FrameParser::VBRHeader& VBRInfo() const;
 
   // MediaTrackDemuxer interface.
   UniquePtr<TrackInfo> GetInfo() const override;
   RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -387,17 +387,17 @@ MediaDecoderStateMachine::CreateMediaSin
   //       is extracted. It is tricky that the implementation of DecodedStream
   //       happens to allow reuse after shutdown without creating a new one.
   RefPtr<media::MediaSink> audioSink = aAudioCaptured ?
     mStreamSink : CreateAudioSink();
 
   RefPtr<media::MediaSink> mediaSink =
     new VideoSink(mTaskQueue, audioSink, mVideoQueue,
                   mDecoder->GetVideoFrameContainer(), mRealTime,
-                  mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
+                  mDecoder->GetFrameStatistics(),
                   sVideoQueueSendToCompositorSize);
   return mediaSink.forget();
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
@@ -889,29 +889,16 @@ MediaDecoderStateMachine::OnVideoDecoded
       Push(video, MediaData::VIDEO_DATA);
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       if (mIsVideoPrerolling && DonePrerollingVideo()) {
         StopPrerollingVideo();
       }
 
-      // Schedule the state machine to send stream data as soon as possible if
-      // the VideoQueue() is empty or contains one frame before the Push().
-      //
-      // The state machine threads requires a frame in VideoQueue() that is `in
-      // the future` to gather precise timing information. The head of
-      // VideoQueue() is always `in the past`.
-      //
-      // Schedule the state machine as soon as possible to render the video
-      // frame or delay the state machine thread accurately.
-      if (VideoQueue().GetSize() <= 2) {
-        ScheduleStateMachine();
-      }
-
       // For non async readers, if the requested video sample was slow to
       // arrive, increase the amount of audio we buffer to ensure that we
       // don't run out of audio. This is unnecessary for async readers,
       // since they decode audio and video on different threads so they
       // are unlikely to run out of decoded audio.
       if (mReader->IsAsync()) {
         return;
       }
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -93,16 +93,33 @@ struct nsIMediaDevice::COMTypeInfo<mozil
 };
 const nsIID nsIMediaDevice::COMTypeInfo<mozilla::VideoDevice, void>::kIID = NS_IMEDIADEVICE_IID;
 template<>
 struct nsIMediaDevice::COMTypeInfo<mozilla::AudioDevice, void> {
   static const nsIID kIID;
 };
 const nsIID nsIMediaDevice::COMTypeInfo<mozilla::AudioDevice, void>::kIID = NS_IMEDIADEVICE_IID;
 
+namespace {
+already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
+  nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
+  MOZ_RELEASE_ASSERT(svc);
+
+  nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
+  nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
+  if (!shutdownPhase) {
+    // We are probably in a content process.
+    rv = svc->GetContentChildShutdown(getter_AddRefs(shutdownPhase));
+  }
+  MOZ_RELEASE_ASSERT(shutdownPhase);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  return shutdownPhase.forget();
+}
+}
+
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 LogModule*
 GetMediaManagerLog()
@@ -1555,44 +1572,38 @@ MediaManager::Get() {
       prefs->AddObserver("media.navigator.video.default_width", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_height", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_fps", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_minfps", sSingleton, false);
     }
 
     // Prepare async shutdown
 
-    nsCOMPtr<nsIAsyncShutdownClient> profileBeforeChange;
-    {
-      nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
-      MOZ_RELEASE_ASSERT(svc);
-      nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(profileBeforeChange));
-      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
-    }
+    nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
 
     class Blocker : public media::ShutdownBlocker
     {
     public:
       Blocker()
       : media::ShutdownBlocker(NS_LITERAL_STRING(
           "Media shutdown: blocking on media thread")) {}
 
-      NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override
+      NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient*) override
       {
         MOZ_RELEASE_ASSERT(MediaManager::GetIfExists());
         MediaManager::GetIfExists()->Shutdown();
         return NS_OK;
       }
     };
 
     sSingleton->mShutdownBlocker = new Blocker();
-    nsresult rv = profileBeforeChange->AddBlocker(sSingleton->mShutdownBlocker,
-                                                  NS_LITERAL_STRING(__FILE__),
-                                                  __LINE__,
-                                                  NS_LITERAL_STRING("Media shutdown"));
+    nsresult rv = shutdownPhase->AddBlocker(sSingleton->mShutdownBlocker,
+                                            NS_LITERAL_STRING(__FILE__),
+                                            __LINE__,
+                                            NS_LITERAL_STRING("Media shutdown"));
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 #ifdef MOZ_B2G
     // Init MediaPermissionManager before sending out any permission requests.
     (void) MediaPermissionManager::GetInstance();
 #endif //MOZ_B2G
   }
   return sSingleton;
 }
@@ -2649,24 +2660,18 @@ MediaManager::Shutdown()
       media::NewRunnableFrom([this, that]() mutable {
     LOG(("MediaManager shutdown lambda running, releasing MediaManager singleton and thread"));
     if (mMediaThread) {
       mMediaThread->Stop();
     }
 
     // Remove async shutdown blocker
 
-    nsCOMPtr<nsIAsyncShutdownClient> profileBeforeChange;
-    {
-      nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
-      MOZ_RELEASE_ASSERT(svc);
-      nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(profileBeforeChange));
-      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
-    }
-    profileBeforeChange->RemoveBlocker(sSingleton->mShutdownBlocker);
+    nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
+    shutdownPhase->RemoveBlocker(sSingleton->mShutdownBlocker);
 
     // we hold a ref to 'that' which is the same as sSingleton
     sSingleton = nullptr;
 
     return NS_OK;
   })));
 }
 
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -41,25 +41,25 @@ public:
     return nsDeque::GetSize();
   }
 
   inline void Push(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::Push(aItem);
-    mPushEvent.Notify();
+    mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline void PushFront(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::PushFront(aItem);
-    mPushEvent.Notify();
+    mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline already_AddRefed<T> PopFront() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopEvent.Notify(rv);
     }
@@ -156,28 +156,28 @@ public:
     }
     return frames;
   }
 
   MediaEventSource<RefPtr<T>>& PopEvent() {
     return mPopEvent;
   }
 
-  MediaEventSource<void>& PushEvent() {
+  MediaEventSource<RefPtr<T>>& PushEvent() {
     return mPushEvent;
   }
 
   MediaEventSource<void>& FinishEvent() {
     return mFinishEvent;
   }
 
 private:
   mutable ReentrantMonitor mReentrantMonitor;
   MediaEventProducer<RefPtr<T>> mPopEvent;
-  MediaEventProducer<void> mPushEvent;
+  MediaEventProducer<RefPtr<T>> mPushEvent;
   MediaEventProducer<void> mFinishEvent;
   // True when we've decoded the last frame of data in the
   // bitstream for which we're queueing frame data.
   bool mEndOfStream;
 };
 
 } // namespace mozilla
 
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1316,27 +1316,37 @@ PeerConnectionObserver.prototype = {
   //                 This is more aggressive than failed, and may trigger
   //                 intermittently (and resolve itself without action) on a
   //                 flaky network.
   //
   //   closed        The ICE Agent has shut down and is no longer responding to
   //                 STUN requests.
 
   handleIceConnectionStateChange: function(iceConnectionState) {
-    var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
+    if (this._dompc.iceConnectionState === 'new') {
+      var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
+      if (iceConnectionState === 'checking') {
+        checking_histogram.add(true);
+      } else if (iceConnectionState === 'failed') {
+        checking_histogram.add(false);
+      }
+    } else if (this._dompc.iceConnectionState === 'checking') {
+      var success_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
+      if (iceConnectionState === 'completed' ||
+          iceConnectionState === 'connected') {
+        success_histogram.add(true);
+      } else if (iceConnectionState === 'failed') {
+        success_histogram.add(false);
+      }
+    }
 
     if (iceConnectionState === 'failed') {
-      histogram.add(false);
       this._dompc.logError("ICE failed, see about:webrtc for more details", null, 0);
     }
-    if (this._dompc.iceConnectionState === 'checking' &&
-        (iceConnectionState === 'completed' ||
-         iceConnectionState === 'connected')) {
-          histogram.add(true);
-    }
+
     this._dompc.changeIceConnectionState(iceConnectionState);
   },
 
   // This method is responsible for updating iceGatheringState. This
   // state is defined in the WebRTC specification as follows:
   //
   // iceGatheringState:
   // ------------------
--- a/dom/media/eme/MediaKeyStatusMap.cpp
+++ b/dom/media/eme/MediaKeyStatusMap.cpp
@@ -103,18 +103,20 @@ MediaKeyStatusMap::Size() const
 }
 
 static MediaKeyStatus
 ToMediaKeyStatus(GMPMediaKeyStatus aStatus) {
   switch (aStatus) {
     case kGMPUsable: return MediaKeyStatus::Usable;
     case kGMPExpired: return MediaKeyStatus::Expired;
     case kGMPOutputDownscaled: return MediaKeyStatus::Output_downscaled;
-    case kGMPOutputNotAllowed: return MediaKeyStatus::Output_not_allowed;
+    case kGMPOutputRestricted: return MediaKeyStatus::Output_restricted;
     case kGMPInternalError: return MediaKeyStatus::Internal_error;
+    case kGMPReleased: return MediaKeyStatus::Released;
+    case kGMPStatusPending: return MediaKeyStatus::Status_pending;
     default: return MediaKeyStatus::Internal_error;
   }
 }
 
 void
 MediaKeyStatusMap::Update(const nsTArray<CDMCaps::KeyStatus>& aKeys)
 {
   mStatuses.Clear();
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -89,20 +89,22 @@ enum GMPSessionMessageType {
   kGMPIndividualizationRequest = 3,
   kGMPMessageInvalid = 4 // Must always be last.
 };
 
 enum GMPMediaKeyStatus {
   kGMPUsable = 0,
   kGMPExpired = 1,
   kGMPOutputDownscaled = 2,
-  kGMPOutputNotAllowed = 3,
+  kGMPOutputRestricted = 3,
   kGMPInternalError = 4,
-  kGMPUnknown = 5,
-  kGMPMediaKeyStatusInvalid = 6 // Must always be last.
+  kGMPUnknown = 5, // Removes key from MediaKeyStatusMap
+  kGMPReleased = 6,
+  kGMPStatusPending = 7,
+  kGMPMediaKeyStatusInvalid = 8 // Must always be last.
 };
 
 // Time in milliseconds, as offset from epoch, 1 Jan 1970.
 typedef int64_t GMPTimestamp;
 
 // Capability definitions. The capabilities of the EME GMP are reported
 // to Gecko by calling the GMPDecryptorCallback::SetCapabilities()
 // callback and specifying the logical OR of the GMP_EME_CAP_* flags below.
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -21,29 +21,27 @@ using namespace mozilla::layers;
 namespace media {
 
 VideoSink::VideoSink(AbstractThread* aThread,
                      MediaSink* aAudioSink,
                      MediaQueue<MediaData>& aVideoQueue,
                      VideoFrameContainer* aContainer,
                      bool aRealTime,
                      FrameStatistics& aFrameStats,
-                     int aDelayDuration,
                      uint32_t aVQueueSentToCompositerSize)
   : mOwnerThread(aThread)
   , mAudioSink(aAudioSink)
   , mVideoQueue(aVideoQueue)
   , mContainer(aContainer)
   , mProducerID(ImageContainer::AllocateProducerID())
   , mRealTime(aRealTime)
   , mFrameStats(aFrameStats)
   , mVideoFrameEndTime(-1)
   , mHasVideo(false)
   , mUpdateScheduler(aThread)
-  , mDelayDuration(aDelayDuration)
   , mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
 {
   MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
 }
 
 VideoSink::~VideoSink()
 {
 }
@@ -213,22 +211,28 @@ VideoSink::Shutdown()
   AssertOwnerThread();
   MOZ_ASSERT(!mAudioSink->IsStarted(), "must be called after playback stops.");
   VSINK_LOG("[%s]", __func__);
 
   mAudioSink->Shutdown();
 }
 
 void
-VideoSink::OnVideoQueueEvent()
+VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
 {
   AssertOwnerThread();
   // Listen to push event, VideoSink should try rendering ASAP if first frame
   // arrives but update scheduler is not triggered yet.
-  TryUpdateRenderedVideoFrames();
+  VideoData* v = aSample->As<VideoData>();
+  if (!v->mSentToCompositor) {
+    // Since we push rendered frames back to the queue, we will receive
+    // push events for them. We only need to trigger render loop
+    // when this frame is not rendered yet.
+    TryUpdateRenderedVideoFrames();
+  }
 }
 
 void
 VideoSink::Redraw()
 {
   AssertOwnerThread();
   RenderVideoFrames(1);
 }
@@ -333,17 +337,17 @@ VideoSink::UpdateRenderedVideoFrames()
 
   TimeStamp nowTime;
   const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame and drop
   // the current frame.
   NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
 
-  int64_t remainingTime = mDelayDuration;
+  int64_t remainingTime = -1;
   if (VideoQueue().GetSize() > 0) {
     RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
     int32_t framesRemoved = 0;
     while (VideoQueue().GetSize() > 0) {
       MediaData* nextFrame = VideoQueue().PeekFront();
       if (!mRealTime && nextFrame->mTime > clockTime) {
         remainingTime = nextFrame->mTime - clockTime;
         break;
@@ -360,17 +364,24 @@ VideoSink::UpdateRenderedVideoFrames()
     if (framesRemoved > 0) {
       mVideoFrameEndTime = currentFrame->GetEndTime();
       mFrameStats.NotifyPresentedFrame();
     }
   }
 
   RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
 
-  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);
+  // No next fame to render. There is no need to schedule next render
+  // loop. We will run render loops again upon incoming frames.
+  if (remainingTime < 0) {
+    return;
+  }
+
+  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
+    remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);
 
   RefPtr<VideoSink> self = this;
   mUpdateScheduler.Ensure(target, [self] () {
     self->UpdateRenderedVideoFramesByTimer();
   }, [self] () {
     self->UpdateRenderedVideoFramesByTimer();
   });
 }
--- a/dom/media/mediasink/VideoSink.h
+++ b/dom/media/mediasink/VideoSink.h
@@ -30,17 +30,16 @@ class VideoSink : public MediaSink
   typedef mozilla::layers::ImageContainer::ProducerID ProducerID;
 public:
   VideoSink(AbstractThread* aThread,
             MediaSink* aAudioSink,
             MediaQueue<MediaData>& aVideoQueue,
             VideoFrameContainer* aContainer,
             bool aRealTime,
             FrameStatistics& aFrameStats,
-            int aDelayDuration,
             uint32_t aVQueueSentToCompositerSize);
 
   const PlaybackParams& GetPlaybackParams() const override;
 
   void SetPlaybackParams(const PlaybackParams& aParams) override;
 
   RefPtr<GenericPromise> OnEnded(TrackType aType) override;
 
@@ -69,17 +68,17 @@ public:
   bool IsPlaying() const override;
 
   void Shutdown() override;
 
 private:
   virtual ~VideoSink();
 
   // VideoQueue listener related.
-  void OnVideoQueueEvent();
+  void OnVideoQueueEvent(RefPtr<MediaData>&& aSample);
   void ConnectListener();
   void DisconnectListener();
 
   // Sets VideoQueue images into the VideoFrameContainer. Called on the shared
   // state machine thread. The first aMaxFrames (at most) are set.
   // aClockTime and aClockTimeStamp are used as the baseline for deriving
   // timestamps for the frames; when omitted, aMaxFrames must be 1 and
   // a null timestamp is passed to the VideoFrameContainer.
@@ -134,20 +133,16 @@ private:
   MediaEventListener mPushListener;
 
   // True if this sink is going to handle video track.
   bool mHasVideo;
 
   // Used to trigger another update of rendered frames in next round.
   DelayedScheduler mUpdateScheduler;
 
-  // A delay duration to trigger next time UpdateRenderedVideoFrames().
-  // Based on the default value in MDSM.
-  const int mDelayDuration;
-
   // Max frame number sent to compositor at a time.
   // Based on the pref value obtained in MDSM.
   const uint32_t mVideoQueueSendToCompositorSize;
 };
 
 } // namespace media
 } // namespace mozilla
 
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -656,16 +656,20 @@ AudioBufferSourceNode::Start(double aWhe
     // Nothing to play, or we're already dead for some reason
     return;
   }
 
   // Remember our arguments so that we can use them when we get a new buffer.
   mOffset = aOffset;
   mDuration = aDuration.WasPassed() ? aDuration.Value()
                                     : std::numeric_limits<double>::min();
+
+  WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(),
+                    NodeType(), Id(), aWhen, aOffset, mDuration);
+
   // We can't send these parameters without a buffer because we don't know the
   // buffer's sample rate or length.
   if (mBuffer) {
     SendOffsetAndDurationParametersToStream(ns);
   }
 
   // Don't set parameter unnecessarily
   if (aWhen > 0.0) {
@@ -737,16 +741,19 @@ AudioBufferSourceNode::Stop(double aWhen
     return;
   }
 
   if (!mStartCalled) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
+  WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(),
+                    NodeType(), Id(), aWhen);
+
   AudioNodeStream* ns = mStream;
   if (!ns || !Context()) {
     // We've already stopped and had our stream shut down
     return;
   }
 
   ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
 }
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -193,16 +193,20 @@ AudioNode::Connect(AudioNode& aDestinati
   }
 
   if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) !=
       nsTArray<AudioNode::InputNode>::NoIndex) {
     // connection already exists.
     return &aDestination;
   }
 
+  WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u",
+                    Context()->CurrentTime(), NodeType(), Id(),
+                    aDestination.NodeType(), aDestination.Id());
+
   // The MediaStreamGraph will handle cycle detection. We don't need to do it
   // here.
 
   mOutputNodes.AppendElement(&aDestination);
   InputNode* input = aDestination.mInputNodes.AppendElement();
   input->mInputNode = this;
   input->mInputPort = aInput;
   input->mOutputPort = aOutput;
@@ -295,16 +299,19 @@ AudioNode::SendChannelMixingParametersTo
 void
 AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
 {
   if (aOutput >= NumberOfOutputs()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
+  WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
+                    NodeType(), Id());
+
   // An upstream node may be starting to play on the graph thread, and the
   // engine for a downstream node may be sending a PlayingRefChangeHandler
   // ADDREF message to this (main) thread.  Wait for a round trip before
   // releasing nodes, to give engines receiving sound now time to keep their
   // nodes alive.
   class RunnableRelease final : public nsRunnable
   {
   public:
--- a/dom/media/webaudio/AudioParam.cpp
+++ b/dom/media/webaudio/AudioParam.cpp
@@ -110,19 +110,58 @@ AudioParam::Stream()
 
   // Send the stream to the timeline on the MSG side.
   AudioTimelineEvent event(mStream);
   SendEventToEngine(event);
 
   return mStream;
 }
 
+static const char*
+ToString(AudioTimelineEvent::Type aType)
+{
+  switch (aType) {
+    case AudioTimelineEvent::SetValue:
+      return "SetValue";
+    case AudioTimelineEvent::SetValueAtTime:
+      return "SetValueAtTime";
+    case AudioTimelineEvent::LinearRamp:
+      return "LinearRamp";
+    case AudioTimelineEvent::ExponentialRamp:
+      return "ExponentialRamp";
+    case AudioTimelineEvent::SetTarget:
+      return "SetTarget";
+    case AudioTimelineEvent::SetValueCurve:
+      return "SetValueCurve";
+    case AudioTimelineEvent::Stream:
+      return "Stream";
+    case AudioTimelineEvent::Cancel:
+      return "Cancel";
+    default:
+      return "unknown AudioTimelineEvent";
+  }
+}
+
 void
 AudioParam::SendEventToEngine(const AudioTimelineEvent& aEvent)
 {
+  WEB_AUDIO_API_LOG("%f: %s for %u %s %s=%g time=%f %s=%g",
+                    GetParentObject()->CurrentTime(),
+                    mName, ParentNodeId(), ToString(aEvent.mType),
+                    aEvent.mType == AudioTimelineEvent::SetValueCurve ?
+                      "length" : "value",
+                    aEvent.mType == AudioTimelineEvent::SetValueCurve ?
+                      static_cast<double>(aEvent.mCurveLength) :
+                      static_cast<double>(aEvent.mValue),
+                    aEvent.Time<double>(),
+                    aEvent.mType == AudioTimelineEvent::SetValueCurve ?
+                      "duration" : "constant",
+                    aEvent.mType == AudioTimelineEvent::SetValueCurve ?
+                      aEvent.mDuration : aEvent.mTimeConstant);
+
   AudioNodeStream* stream = mNode->GetStream();
   if (stream) {
     stream->SendTimelineEvent(mIndex, aEvent);
   }
 }
 
 float
 AudioParamTimeline::AudioNodeInputValue(size_t aCounter) const
--- a/dom/media/webaudio/WebAudioUtils.cpp
+++ b/dom/media/webaudio/WebAudioUtils.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebAudioUtils.h"
 #include "AudioNodeStream.h"
 #include "blink/HRTFDatabaseLoader.h"
 
 namespace mozilla {
 
+LazyLogModule gWebAudioAPILog("WebAudioAPI");
+
 namespace dom {
 
 void WebAudioUtils::ConvertAudioTimelineEventToTicks(AudioTimelineEvent& aEvent,
                                                      AudioNodeStream* aDest)
 {
   aEvent.SetTimeInTicks(
       aDest->SecondsToNearestStreamTime(aEvent.Time<double>()));
   aEvent.mTimeConstant *= aDest->SampleRate();
--- a/dom/media/webaudio/WebAudioUtils.h
+++ b/dom/media/webaudio/WebAudioUtils.h
@@ -15,16 +15,20 @@
 
 // Forward declaration
 typedef struct SpeexResamplerState_ SpeexResamplerState;
 
 namespace mozilla {
 
 class AudioNodeStream;
 
+extern LazyLogModule gWebAudioAPILog;
+#define WEB_AUDIO_API_LOG(...) \
+  MOZ_LOG(gWebAudioAPILog, LogLevel::Debug, (__VA_ARGS__))
+
 namespace dom {
 
 struct AudioTimelineEvent;
 
 namespace WebAudioUtils {
   // 32 is the minimum required by the spec for createBuffer() and
   // createScriptProcessor() and matches what is used by Blink.  The limit
   // protects against large memory allocations.
--- a/dom/plugins/test/testplugin/testplugin.mozbuild
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -47,17 +47,17 @@ USE_STATIC_LIBS = True
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     RCFILE  = 'nptest.rc'
     RESFILE = 'nptest.res'
     DEFFILE = SRCDIR + '/nptest.def'
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and '64' in CONFIG['OS_TEST']:
-    EXTRA_DSO_LDOPTS += ['-framework Carbon']
+    OS_LIBS += ['-framework Carbon']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
     CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
     CFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
     OS_LIBS += CONFIG['MOZ_GTK2_LIBS']
     OS_LIBS += CONFIG['XLDFLAGS']
     OS_LIBS += CONFIG['XLIBS']
     OS_LIBS += CONFIG['XEXT_LIBS']
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -249,18 +249,21 @@ PresentationIPCService::NotifyReceiverRe
   if (NS_WARN_IF(aWindowId == 0)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Track the responding info for an OOP receiver page.
   mRespondingSessionIds.Put(aWindowId, new nsAutoString(aSessionId));
   mRespondingWindowIds.Put(aSessionId, aWindowId);
 
+  NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsAutoString(aSessionId)));
+
+  // Release mCallback after using aSessionId
+  // because aSessionId is held by mCallback.
   mCallback = nullptr;
-  NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsAutoString(aSessionId)));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId)
 {
   // Remove the OOP responding info (if it has never been used).
   uint64_t windowId = 0;
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -41,16 +41,21 @@ static bool SchemeIs(nsIURI* aURI, const
 
   bool isScheme = false;
   return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
 }
 
 static nsresult
 DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
 {
+  // Bug 1228117: determine the correct security policy for DTD loads
+  if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DTD) {
+    return NS_OK;
+  }
+
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->LoadingPrincipal();
   uint32_t flags = nsIScriptSecurityManager::STANDARD;
   if (aLoadInfo->GetAllowChrome()) {
     flags |= nsIScriptSecurityManager::ALLOW_CHROME;
   }
 
--- a/dom/webidl/MediaKeyStatusMap.webidl
+++ b/dom/webidl/MediaKeyStatusMap.webidl
@@ -8,18 +8,20 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
 enum MediaKeyStatus {
   "usable",
   "expired",
+  "released",
+  "output-restricted",
   "output-downscaled",
-  "output-not-allowed",
+  "status-pending",
   "internal-error"
 };
 
 [Pref="media.eme.apiVisible"]
 interface MediaKeyStatusMap {
   iterable<ArrayBuffer,MediaKeyStatus>;
   readonly attribute unsigned long size;
   boolean has (BufferSource keyId);
--- a/editor/composer/nsComposeTxtSrvFilter.cpp
+++ b/editor/composer/nsComposeTxtSrvFilter.cpp
@@ -40,16 +40,17 @@ nsComposeTxtSrvFilter::Skip(nsIDOMNode* 
         if (!*_retval) {
           *_retval = content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::_class,
                                           nsGkAtoms::mozsignature, eCaseMatters);
         }
       }
     } else if (content->IsAnyOfHTMLElements(nsGkAtoms::script,
                                             nsGkAtoms::textarea,
                                             nsGkAtoms::select,
+                                            nsGkAtoms::style,
                                             nsGkAtoms::map)) {
       *_retval = true;
     } else if (content->IsHTMLElement(nsGkAtoms::table)) {
       if (mIsForMail) {
         *_retval =
           content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::_class,
                                NS_LITERAL_STRING("moz-email-headers-table"),
                                eCaseMatters);
--- a/editor/composer/test/chrome.ini
+++ b/editor/composer/test/chrome.ini
@@ -6,8 +6,9 @@ skip-if = buildapp == 'b2g' || os == 'an
 [test_bug434998.xul]
 [test_bug678842.html]
 [test_bug697981.html]
 [test_bug717433.html]
 [test_bug1204147.html]
 [test_bug1200533.html]
 [test_bug1205983.html]
 [test_bug1209414.html]
+[test_bug1219928.html]
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/test_bug1219928.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1219928
+-->
+<head>
+  <title>Test for Bug 1219928</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1219928">Mozilla Bug 1219928</a>
+<p id="display"></p>
+
+<div contenteditable id="en-US" lang="en-US">
+<p>And here a missspelled word</p>
+<style>
+<!-- and here another onnee in a style comment -->
+</style>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1219928 **/
+/* Very simple test to check that <style> blocks are skipped in the spell check */
+
+var spellchecker;
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  Components.utils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
+
+  var elem = document.getElementById('en-US');
+  elem.focus();
+
+  onSpellCheck(elem, function () {
+    var Ci = Components.interfaces;
+    var editingSession = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIWebNavigation)
+                               .QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIEditingSession);
+    var editor = editingSession.getEditorForWindow(window);
+    var selcon = editor.selectionController;
+    var sel = selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+
+    is(sel.toString(), "missspelled", "one misspelled word expected: missspelled");
+
+    spellchecker = Components.classes['@mozilla.org/editor/editorspellchecker;1'].createInstance(Components.interfaces.nsIEditorSpellCheck);
+    var filterContractId = "@mozilla.org/editor/txtsrvfilter;1";
+    spellchecker.setFilter(Components.classes[filterContractId].createInstance(Components.interfaces.nsITextServicesFilter));
+    spellchecker.InitSpellChecker(editor, false, spellCheckStarted);
+  });
+});
+
+function spellCheckStarted() {
+  var misspelledWord = spellchecker.GetNextMisspelledWord();
+  is(misspelledWord, "missspelled", "first misspelled word expected: missspelled");
+
+  // Without the fix, the next misspelled word was 'onnee', so we check that we don't get it.
+  misspelledWord = spellchecker.GetNextMisspelledWord();
+  isnot(misspelledWord, "onnee", "second misspelled word should not be: onnee");
+
+  spellchecker = "";
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -501,18 +501,20 @@ Factory::GetMaxSurfaceSize(BackendType a
   switch (aType) {
   case BackendType::CAIRO:
   case BackendType::COREGRAPHICS:
     return DrawTargetCairo::GetMaxSurfaceSize();
 #ifdef XP_MACOSX
   case BackendType::COREGRAPHICS_ACCELERATED:
     return DrawTargetCG::GetMaxSurfaceSize();
 #endif
+#ifdef USE_SKIA
   case BackendType::SKIA:
     return DrawTargetSkia::GetMaxSurfaceSize();
+#endif
 #ifdef WIN32
   case BackendType::DIRECT2D:
     return DrawTargetD2D::GetMaxSurfaceSize();
   case BackendType::DIRECT2D1_1:
     return DrawTargetD2D1::GetMaxSurfaceSize();
 #endif
   default:
     return 0;
@@ -862,20 +864,20 @@ Factory::CreateCGGlyphRenderingOptions(c
 }
 #endif
 
 already_AddRefed<DataSourceSurface>
 Factory::CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride,
                                          const IntSize &aSize,
                                          SurfaceFormat aFormat)
 {
-  MOZ_ASSERT(aData);
   if (aSize.width <= 0 || aSize.height <= 0) {
     return nullptr;
   }
+  MOZ_ASSERT(aData);
 
   RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
 
   newSurf->InitWrappingData(aData, aSize, aStride, aFormat, false);
   return newSurf.forget();
 }
 
 already_AddRefed<DataSourceSurface>
--- a/gfx/angle/src/libANGLE/moz.build
+++ b/gfx/angle/src/libANGLE/moz.build
@@ -305,18 +305,18 @@ DEFINES['LIBANGLE_IMPLEMENTATION'] = "1"
 DEFINES['ANGLE_ENABLE_HLSL'] = "1"
 DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
 DEFINES['ANGLE_DEFAULT_D3D11'] = "0"
 
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
   OS_LIBS += [ 'd3d9', 'dxguid' ]
 else:
   EXTRA_DSO_LDOPTS += [
-    '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
-    '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
   ]
 
 Library('libANGLE')
 
 
 SOURCES['renderer/d3d/HLSLCompiler.cpp'].flags += ['-DANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES=\'{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }\'']
 
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -61,18 +61,18 @@ USE_LIBS += [ 'libGLESv2' ]
 DEFINES['LIBANGLE_IMPLEMENTATION'] = "1"
 DEFINES['ANGLE_ENABLE_HLSL'] = "1"
 DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
 
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
   OS_LIBS += [ 'd3d9', 'dxguid' ]
 else:
   EXTRA_DSO_LDOPTS += [
-    '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
-    '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
   ]
 
 
 GeckoSharedLibrary('libEGL', linkage=None)
 
 RCFILE = SRCDIR + '/libEGL.rc'
 DEFFILE = SRCDIR + '/libEGL.def'
 
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -66,18 +66,18 @@ LOCAL_INCLUDES += [ '../../include', '..
 DEFINES['LIBANGLE_IMPLEMENTATION'] = "1"
 DEFINES['ANGLE_ENABLE_HLSL'] = "1"
 DEFINES['ANGLE_ENABLE_KEYEDMUTEX'] = "1"
 
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
   OS_LIBS += [ 'd3d9', 'dxguid' ]
 else:
   EXTRA_DSO_LDOPTS += [
-    '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
-    '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
+    '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
   ]
 
 GeckoSharedLibrary('libGLESv2', linkage=None)
 
 RCFILE = SRCDIR + '/libGLESv2.rc'
 DEFFILE = SRCDIR + '/libGLESv2.def'
 
 USE_LIBS += ['libANGLE']
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -294,16 +294,30 @@ public:
     MOZ_ASSERT(IsValid());
 
     if (AtBottomLayer()) {
       return mLayer->GetTransform();
     }
     return gfx::Matrix4x4();
   }
 
+  bool TransformIsPerspective() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    // mLayer->GetTransformIsPerspective() tells us whether
+    // mLayer->GetTransform() is a perspective transform. Since
+    // mLayer->GetTransform() is only used at the bottom layer, we only
+    // need to check GetTransformIsPerspective() at the bottom layer too.
+    if (AtBottomLayer()) {
+      return mLayer->GetTransformIsPerspective();
+    }
+    return false;
+  }
+
   EventRegions GetEventRegions() const
   {
     MOZ_ASSERT(IsValid());
 
     if (AtBottomLayer()) {
       return mLayer->GetEventRegions();
     }
     return EventRegions();
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -240,16 +240,17 @@ Layer::Layer(LayerManager* aManager, voi
   mPostXScale(1.0f),
   mPostYScale(1.0f),
   mOpacity(1.0),
   mMixBlendMode(CompositionOp::OP_OVER),
   mForceIsolatedGroup(false),
   mContentFlags(0),
   mUseTileSourceRect(false),
   mIsFixedPosition(false),
+  mTransformIsPerspective(false),
   mFixedPositionData(nullptr),
   mStickyPositionData(nullptr),
   mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
   mScrollbarDirection(ScrollDirection::NONE),
   mScrollbarThumbRatio(0.0f),
   mIsScrollbarContainer(false),
   mDebugColorIndex(0),
   mAnimationGeneration(0)
@@ -1881,16 +1882,19 @@ Layer::PrintInfo(std::stringstream& aStr
     AppendToString(aStream, *mClipRect, " [clip=", "]");
   }
   if (1.0 != mPostXScale || 1.0 != mPostYScale) {
     aStream << nsPrintfCString(" [postScale=%g, %g]", mPostXScale, mPostYScale).get();
   }
   if (!mTransform.IsIdentity()) {
     AppendToString(aStream, mTransform, " [transform=", "]");
   }
+  if (mTransformIsPerspective) {
+    aStream << " [perspective]";
+  }
   if (!mLayerBounds.IsEmpty()) {
     AppendToString(aStream, mLayerBounds, " [bounds=", "]");
   }
   if (!mVisibleRegion.IsEmpty()) {
     AppendToString(aStream, mVisibleRegion, " [visible=", "]");
   } else {
     aStream << " [not visible]";
   }
@@ -1901,16 +1905,19 @@ Layer::PrintInfo(std::stringstream& aStr
     aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get();
   }
   if (GetContentFlags() & CONTENT_OPAQUE) {
     aStream << " [opaqueContent]";
   }
   if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
     aStream << " [componentAlpha]";
   }
+  if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+    aStream << " [backfaceHidden]";
+  }
   if (GetScrollbarDirection() == VERTICAL) {
     aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
   }
   if (GetScrollbarDirection() == HORIZONTAL) {
     aStream << nsPrintfCString(" [hscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
   }
   if (GetIsFixedPosition()) {
     LayerPoint anchor = GetFixedPositionAnchor();
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1108,16 +1108,31 @@ public:
   {
     if (mIsFixedPosition != aFixedPosition) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) IsFixedPosition", this));
       mIsFixedPosition = aFixedPosition;
       Mutated();
     }
   }
 
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * This flag is true when the transform on the layer is a perspective
+   * transform. The compositor treats perspective transforms specially
+   * for async scrolling purposes.
+   */
+  void SetTransformIsPerspective(bool aTransformIsPerspective)
+  {
+    if (mTransformIsPerspective != aTransformIsPerspective) {
+      MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) TransformIsPerspective", this));
+      mTransformIsPerspective = aTransformIsPerspective;
+      Mutated();
+    }
+  }
+
   // Call AddAnimation to add a new animation to this layer from layout code.
   // Caller must fill in all the properties of the returned animation.
   // A later animation overrides an earlier one.
   Animation* AddAnimation();
   // ClearAnimations clears animations on this layer.
   void ClearAnimations();
   // This is only called when the layer tree is updated. Do not call this from
   // layout code.  To add an animation to this layer, use AddAnimation.
@@ -1261,16 +1276,17 @@ public:
   virtual Layer* GetFirstChild() const { return nullptr; }
   virtual Layer* GetLastChild() const { return nullptr; }
   const gfx::Matrix4x4 GetTransform() const;
   const gfx::Matrix4x4& GetBaseTransform() const { return mTransform; }
   // Note: these are virtual because ContainerLayerComposite overrides them.
   virtual float GetPostXScale() const { return mPostXScale; }
   virtual float GetPostYScale() const { return mPostYScale; }
   bool GetIsFixedPosition() { return mIsFixedPosition; }
+  bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
   bool GetIsStickyPosition() { return mStickyPositionData; }
   FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mFixedPositionData ? mFixedPositionData->mScrollId : FrameMetrics::NULL_SCROLL_ID; }
   LayerPoint GetFixedPositionAnchor() { return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint(); }
   int32_t GetFixedPositionSides() { return mFixedPositionData ? mFixedPositionData->mSides : eSideBitsNone; }
   bool IsClipFixed() { return mFixedPositionData ? mFixedPositionData->mIsClipFixed : false; }
   FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; }
   const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
   const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
@@ -1791,16 +1807,17 @@ protected:
   bool mForceIsolatedGroup;
   Maybe<ParentLayerIntRect> mClipRect;
   gfx::IntRect mTileSourceRect;
   nsIntRegion mInvalidRegion;
   nsTArray<RefPtr<AsyncPanZoomController> > mApzcs;
   uint32_t mContentFlags;
   bool mUseTileSourceRect;
   bool mIsFixedPosition;
+  bool mTransformIsPerspective;
   struct FixedPositionData {
     FrameMetrics::ViewID mScrollId;
     LayerPoint mAnchor;
     int32_t mSides;
     bool mIsClipFixed;
   };
   UniquePtr<FixedPositionData> mFixedPositionData;
   struct StickyPositionData {
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -587,18 +587,19 @@ APZCTreeManager::UpdateHitTestingTree(Tr
 
   // Accumulate the CSS transform between layers that have an APZC.
   // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
   // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
   // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
   // transform to layer L when we recurse into the children below. If we are at a layer
   // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
   // the new accumulation as we go down.
-  Matrix4x4 transform = aLayer.GetTransform();
-  Matrix4x4 ancestorTransform = transform;
+  // If a transform is a perspective transform, it's ignored for this purpose
+  // (see bug 1168263).
+  Matrix4x4 ancestorTransform = aLayer.TransformIsPerspective() ? Matrix4x4() : aLayer.GetTransform();
   if (!apzc) {
     ancestorTransform = ancestorTransform * aAncestorTransform;
   }
 
   // Note that |node| at this point will not have any children, otherwise we
   // we would have to set next to node->GetFirstChild().
   MOZ_ASSERT(!node->GetFirstChild());
   aParent = node;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -131,19 +131,16 @@ using mozilla::gfx::PointTyped;
  *
  * \li\b apz.content_response_timeout
  * Amount of time before we timeout response from content. For example, if
  * content is being unruly/slow and we don't get a response back within this
  * time, we will just pretend that content did not preventDefault any touch
  * events we dispatched to it.\n
  * Units: milliseconds
  *
- * \li\b apz.cross_slide_enabled
- * Pref that enables integration with the Metro "cross-slide" gesture.
- *
  * \li\b apz.danger_zone_x
  * \li\b apz.danger_zone_y
  * When drawing high-res tiles, we drop down to drawing low-res tiles
  * when we know we can't keep up with the scrolling. The way we determine
  * this is by checking if we are entering the "danger zone", which is the
  * boundary of the painted content. For example, if the painted content
  * goes from y=0...1000 and the visible portion is y=250...750 then
  * we're far from checkerboarding. If we get to y=490...990 though then we're
@@ -1223,18 +1220,16 @@ nsEventStatus AsyncPanZoomController::On
       break;
     }
     case TOUCHING:
     case PANNING:
     case PANNING_LOCKED_X:
     case PANNING_LOCKED_Y:
     case PANNING_LOCKED_X_SMOOTH_SCROLL:
     case PANNING_LOCKED_Y_SMOOTH_SCROLL:
-    case CROSS_SLIDING_X:
-    case CROSS_SLIDING_Y:
     case PINCHING:
       NS_WARNING("Received impossible touch in OnTouchStart");
       break;
     default:
       NS_WARNING("Unhandled case in OnTouchStart");
       break;
   }
 
@@ -1247,22 +1242,16 @@ nsEventStatus AsyncPanZoomController::On
     case FLING:
     case SMOOTH_SCROLL:
     case NOTHING:
     case ANIMATING_ZOOM:
       // May happen if the user double-taps and drags without lifting after the
       // second tap. Ignore the move if this happens.
       return nsEventStatus_eIgnore;
 
-    case CROSS_SLIDING_X:
-    case CROSS_SLIDING_Y:
-      // While cross-sliding, we don't want to consume any touchmove events for
-      // panning or zooming, and let the caller handle them instead.
-      return nsEventStatus_eIgnore;
-
     case TOUCHING: {
       ScreenCoord panThreshold = GetTouchStartTolerance();
       UpdateWithTouchAtDevicePoint(aEvent);
 
       if (PanDistance() < panThreshold) {
         return nsEventStatus_eIgnore;
       }
 
@@ -1326,18 +1315,16 @@ nsEventStatus AsyncPanZoomController::On
   case ANIMATING_ZOOM:
   case SMOOTH_SCROLL:
   case NOTHING:
     // May happen if the user double-taps and drags without lifting after the
     // second tap. Ignore if this happens.
     return nsEventStatus_eIgnore;
 
   case TOUCHING:
-  case CROSS_SLIDING_X:
-  case CROSS_SLIDING_Y:
     // We may have some velocity stored on the axis from move events
     // that were not big enough to trigger scrolling. Clear that out.
     mX.SetVelocity(0);
     mY.SetVelocity(0);
     APZC_LOG("%p still has %u touch points active\n", this,
         CurrentTouchBlock()->GetActiveTouchCount());
     // In cases where the user is panning, then taps the second finger without
     // entering a pinch, we will arrive here when the second finger is lifted.
@@ -2150,36 +2137,29 @@ void AsyncPanZoomController::HandlePanni
   ReentrantMonitorAutoEnter lock(mMonitor);
   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
     GetInputQueue()->CurrentBlock()->GetOverscrollHandoffChain();
   bool canScrollHorizontal = !mX.IsAxisLocked() &&
     overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL);
   bool canScrollVertical = !mY.IsAxisLocked() &&
     overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
 
-  if (!gfxPrefs::APZCrossSlideEnabled() &&
-      (!canScrollHorizontal || !canScrollVertical)) {
+  if (!canScrollHorizontal || !canScrollVertical) {
     SetState(PANNING);
   } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
     mY.SetAxisLocked(true);
     if (canScrollHorizontal) {
       SetState(PANNING_LOCKED_X);
       overscrollHandoffChain->RequestSnapOnLock(Layer::VERTICAL);
-    } else {
-      SetState(CROSS_SLIDING_X);
-      mX.SetAxisLocked(true);
     }
   } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
     mX.SetAxisLocked(true);
     if (canScrollVertical) {
       SetState(PANNING_LOCKED_Y);
       overscrollHandoffChain->RequestSnapOnLock(Layer::HORIZONTAL);
-    } else {
-      SetState(CROSS_SLIDING_Y);
-      mY.SetAxisLocked(true);
     }
   } else {
     SetState(PANNING);
   }
 }
 
 void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
   // If we're axis-locked, check if the user is trying to break the lock
@@ -2199,22 +2179,22 @@ void AsyncPanZoomController::HandlePanni
         }
         return;
       }
     }
 
     float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
 
     if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
-      if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
+      if (mState == PANNING_LOCKED_X) {
         if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
           mY.SetAxisLocked(false);
           SetState(PANNING);
         }
-      } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
+      } else if (mState == PANNING_LOCKED_Y) {
         if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) {
           mX.SetAxisLocked(false);
           SetState(PANNING);
         }
       }
     }
   }
 }
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -749,21 +749,16 @@ protected:
     TOUCHING,                 /* one touch-start event received */
 
     PANNING,                  /* panning the frame */
     PANNING_LOCKED_X,         /* touch-start followed by move (i.e. panning with axis lock) X axis */
     PANNING_LOCKED_Y,         /* as above for Y axis */
 
     PAN_MOMENTUM,             /* like PANNING, but controlled by momentum PanGestureInput events */
 
-    CROSS_SLIDING_X,          /* Panning disabled while user does a horizontal gesture
-                                 on a vertically-scrollable view. This used for the
-                                 Windows Metro "cross-slide" gesture. */
-    CROSS_SLIDING_Y,          /* as above for Y axis */
-
     PINCHING,                 /* nth touch-start, where n > 1. this mode allows pan and zoom */
     ANIMATING_ZOOM,           /* animated zoom to a new rect */
     OVERSCROLL_ANIMATION,     /* Spring-based animation used to relieve overscroll once
                                  the finger is lifted. */
     SMOOTH_SCROLL,            /* Smooth scrolling to destination. Used by
                                  CSSOM-View smooth scroll-behavior */
 
     PANNING_LOCKED_X_SMOOTH_SCROLL, /* Smooth scrolling animation initiated
@@ -1051,17 +1046,18 @@ public:
   }
 
   bool IsPannedIntoOverscroll() const {
     return IsOverscrolled() && IsInPanningState();
   }
 
 private:
   /* This is the cumulative CSS transform for all the layers from (and including)
-   * the parent APZC down to (but excluding) this one. */
+   * the parent APZC down to (but excluding) this one, and excluding any
+   * perspective transforms. */
   Matrix4x4 mAncestorTransform;
 
 
   /* ===================================================================
    * The functions and members in this section are used for sharing the
    * FrameMetrics across processes for the progressive tiling code.
    */
 private:
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -499,17 +499,17 @@ AsyncCompositionManager::AlignFixedAndSt
   // those.
   bool adjustClipRect = aLayer != aTransformedSubtreeRoot &&
                         aLayer->IsClipFixed();
   TranslateShadowLayer(aLayer, ThebesPoint(translation.ToUnknownPoint()), adjustClipRect);
 }
 
 static void
 SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
-            StyleAnimationValue& aEnd, Animatable* aValue)
+            StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
 {
   StyleAnimationValue interpolatedValue;
   NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
                aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
                aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
                "Must have same unit");
   StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
                                 aPortion, interpolatedValue);
@@ -520,31 +520,31 @@ SampleValue(float aPortion, Animation& a
 
   nsCSSValueSharedList* interpolatedList =
     interpolatedValue.GetCSSValueSharedListValue();
 
   TransformData& data = aAnimation.data().get_TransformData();
   nsPoint origin = data.origin();
   // we expect all our transform data to arrive in device pixels
   Point3D transformOrigin = data.transformOrigin();
-  Point3D perspectiveOrigin = data.perspectiveOrigin();
   nsDisplayTransform::FrameTransformProperties props(interpolatedList,
-                                                     transformOrigin,
-                                                     perspectiveOrigin,
-                                                     data.perspective());
+                                                     transformOrigin);
+
+  // If our parent layer is a perspective layer, then the offset into reference
+  // frame coordinates is already on that layer. If not, then we need to ask
+  // for it to be added here.
+  uint32_t flags = 0;
+  if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
+    flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
+  }
+
   Matrix4x4 transform =
     nsDisplayTransform::GetResultingTransformMatrix(props, origin,
                                                     data.appUnitsPerDevPixel(),
-                                                    &data.bounds());
-  Point3D scaledOrigin =
-    Point3D(NS_round(NSAppUnitsToFloatPixels(origin.x, data.appUnitsPerDevPixel())),
-            NS_round(NSAppUnitsToFloatPixels(origin.y, data.appUnitsPerDevPixel())),
-            0.0f);
-
-  transform.PreTranslate(scaledOrigin);
+                                                    flags, &data.bounds());
 
   InfallibleTArray<TransformFunction> functions;
   functions.AppendElement(TransformMatrix(transform));
   *aValue = functions;
 }
 
 static bool
 SampleAnimations(Layer* aLayer, TimeStamp aPoint)
@@ -612,17 +612,17 @@ SampleAnimations(Layer* aLayer, TimeStam
       (segment->endPortion() - segment->startPortion());
 
     double portion =
       animData.mFunctions[segmentIndex]->GetValue(positionInSegment);
 
     // interpolate the property
     Animatable interpolatedValue;
     SampleValue(portion, animation, animData.mStartValues[segmentIndex],
-                animData.mEndValues[segmentIndex], &interpolatedValue);
+                animData.mEndValues[segmentIndex], &interpolatedValue, aLayer);
     LayerComposite* layerComposite = aLayer->AsLayerComposite();
     switch (animation.property()) {
     case eCSSProperty_opacity:
     {
       layerComposite->SetShadowOpacity(interpolatedValue.get_float());
       break;
     }
     case eCSSProperty_transform:
@@ -766,25 +766,42 @@ MoveScrollbarForLayerMargin(Layer* aRoot
       // assuming a particular layer tree structure but short of adding more
       // flags to the layer there doesn't appear to be a good way to do this.
       ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
     }
   }
 }
 #endif
 
+template <typename Units>
+Maybe<IntRectTyped<Units>>
+IntersectMaybeRects(const Maybe<IntRectTyped<Units>>& a,
+                    const Maybe<IntRectTyped<Units>>& b)
+{
+  if (!a) {
+    return b;
+  } else if (!b) {
+    return a;
+  } else {
+    return Some(a->Intersect(*b));
+  }
+}
+
 bool
 AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
-                                                          bool* aOutFoundRoot)
+                                                          bool* aOutFoundRoot,
+                                                          Maybe<ParentLayerIntRect>& aClipDeferredToParent)
 {
+  Maybe<ParentLayerIntRect> clipDeferredFromChildren;
   bool appliedTransform = false;
   for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
     appliedTransform |=
-      ApplyAsyncContentTransformToTree(child, aOutFoundRoot);
+      ApplyAsyncContentTransformToTree(child, aOutFoundRoot,
+          clipDeferredFromChildren);
   }
 
   Matrix4x4 oldTransform = aLayer->GetTransform();
 
   Matrix4x4 combinedAsyncTransform;
   bool hasAsyncTransform = false;
   ScreenMargin fixedLayerMargins;
 
@@ -881,20 +898,25 @@ AsyncCompositionManager::ApplyAsyncConte
       asyncClip = Some(TransformTo<ParentLayerPixel>(asyncTransform, *asyncClip));
     }
 
     // Combine the local clip with the ancestor scrollframe clip. This is not
     // included in the async transform above, since the ancestor clip should not
     // move with this APZC.
     if (metrics.HasClipRect()) {
       ParentLayerIntRect clip = metrics.ClipRect();
-      if (asyncClip) {
-        asyncClip = Some(clip.Intersect(*asyncClip));
+      if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) {
+        // If our parent layer has a perspective transform, we want to apply
+        // our scroll clip to it instead of to this layer (see bug 1168263).
+        // A layer with a perspective transform shouldn't have multiple
+        // children with FrameMetrics, nor a child with multiple FrameMetrics.
+        MOZ_ASSERT(!aClipDeferredToParent);
+        aClipDeferredToParent = Some(clip);
       } else {
-        asyncClip = Some(clip);
+        asyncClip = IntersectMaybeRects(Some(clip), asyncClip);
       }
     }
 
     // Do the same for the ancestor mask layers: ancestorMaskLayers contains
     // the ancestor mask layers for scroll frames *inside* the current scroll
     // frame, so these are the ones we need to shift by our async transform.
     for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
       SetShadowTransform(ancestorMaskLayer,
@@ -921,21 +943,22 @@ AsyncCompositionManager::ApplyAsyncConte
         AdjustForClip(asyncTransformWithoutOverscroll, aLayer);
 
     // Since fixed/sticky layers are relative to their nearest scrolling ancestor,
     // we use the ViewID from the bottommost scrollable metrics here.
     AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
                               transformWithoutOverscrollOrOmta, fixedLayerMargins);
   }
 
+  if (hasAsyncTransform || clipDeferredFromChildren) {
+    aLayer->AsLayerComposite()->SetShadowClipRect(
+        IntersectMaybeRects(asyncClip, clipDeferredFromChildren));
+  }
+
   if (hasAsyncTransform) {
-    if (asyncClip) {
-      aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
-    }
-
     // Apply the APZ transform on top of GetLocalTransform() here (rather than
     // GetTransform()) in case the OMTA code in SampleAnimations already set a
     // shadow transform; in that case we want to apply ours on top of that one
     // rather than clobber it.
     SetShadowTransform(aLayer,
         aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
 
     // Do the same for the layer's own mask layer, if it has one.
@@ -1343,17 +1366,18 @@ AsyncCompositionManager::TransformShadow
     // async using Gecko). If this fails, fall back to transforming the
     // primary scrollable layer.  "Failing" here means that we don't
     // find a frame that is async scrollable.  Note that the fallback
     // code also includes Fennec which is rendered async.  Fennec uses
     // its own platform-specific async rendering that is done partially
     // in Gecko and partially in Java.
     wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
     bool foundRoot = false;
-    if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+    Maybe<ParentLayerIntRect> clipDeferredFromChildren;
+    if (ApplyAsyncContentTransformToTree(root, &foundRoot, clipDeferredFromChildren)) {
 #if defined(MOZ_ANDROID_APZ)
       MOZ_ASSERT(foundRoot);
       if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
         MoveScrollbarForLayerMargin(root, mRootScrollableId, mFixedLayerMargins);
       }
 #endif
     } else {
       nsAutoTArray<Layer*,1> scrollableLayers;
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -127,18 +127,21 @@ public:
   void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
 
 private:
   void TransformScrollableLayer(Layer* aLayer);
   // Return true if an AsyncPanZoomController content transform was
   // applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
   // one of the metrics on one of the layers was determined to be the "root"
   // and its state was synced to the Java front-end. |aOutFoundRoot| must be
-  // non-null.
-  bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
+  // non-null. As the function recurses over the layer tree, a layer may
+  // populate |aClipDeferredToParent| a clip rect it wants to set on its parent.
+  bool ApplyAsyncContentTransformToTree(Layer* aLayer,
+                                        bool* aOutFoundRoot,
+                                        Maybe<ParentLayerIntRect>& aClipDeferredToParent);
   /**
    * Update the shadow transform for aLayer assuming that is a scrollbar,
    * so that it stays in sync with the content that is being scrolled by APZ.
    */
   void ApplyAsyncTransformToScrollbar(Layer* aLayer);
 
   void SetFirstPaintViewport(const LayerIntPoint& aOffset,
                              const CSSToLayerScale& aZoom,
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -41,16 +41,19 @@ Atomic<int32_t> CompositableForwarder::s
 CompositorChild::CompositorChild(ClientLayerManager *aLayerManager)
   : mLayerManager(aLayerManager)
   , mCanSend(false)
 {
 }
 
 CompositorChild::~CompositorChild()
 {
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+
   if (mCanSend) {
     gfxCriticalError() << "CompositorChild was not deinitialized";
   }
 }
 
 static void DeferredDestroyCompositor(RefPtr<CompositorParent> aCompositorParent,
                                       RefPtr<CompositorChild> aCompositorChild)
 {
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -268,16 +268,19 @@ ImageBridgeChild::ImageBridgeChild()
   SetMessageLoopToPostDestructionTo(MessageLoop::current());
 
   mTxn = new CompositableTransaction();
 }
 ImageBridgeChild::~ImageBridgeChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+
   delete mTxn;
 }
 
 void
 ImageBridgeChild::MarkShutDown()
 {
   MOZ_ASSERT(!mShuttingDown);
   mShuttingDown = true;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -323,16 +323,17 @@ LayerTransactionParent::RecvUpdate(Infal
       const CommonLayerAttributes& common = attrs.common();
       layer->SetLayerBounds(common.layerBounds());
       layer->SetVisibleRegion(common.visibleRegion());
       layer->SetEventRegions(common.eventRegions());
       layer->SetContentFlags(common.contentFlags());
       layer->SetOpacity(common.opacity());
       layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing());
       layer->SetBaseTransform(common.transform().value());
+      layer->SetTransformIsPerspective(common.transformIsPerspective());
       layer->SetPostScale(common.postXScale(), common.postYScale());
       layer->SetIsFixedPosition(common.isFixedPosition());
       if (common.isFixedPosition()) {
         layer->SetFixedPositionData(common.fixedPositionScrollContainerId(),
                                     common.fixedPositionAnchor(),
                                     common.fixedPositionSides(),
                                     common.isClipFixed());
       }
@@ -738,24 +739,26 @@ LayerTransactionParent::RecvGetAnimation
         Point3D(NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)),
                 NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)),
                 0.0f);
       transformOrigin = data.transformOrigin();
       break;
     }
   }
 
-  // Undo the translation to the origin of the reference frame applied by
-  // AsyncCompositionManager::SampleValue
-  transform.PreTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+  // If our parent isn't a perspective layer, then the offset into reference
+  // frame coordinates will have been applied to us. Add an inverse translation
+  // to cancel it out.
+  if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
+    transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+  }
 
   // Undo the rebasing applied by
   // nsDisplayTransform::GetResultingTransformMatrixInternal
-  Point3D basis = -scaledOrigin - transformOrigin;
-  transform.ChangeBasis(basis.x, basis.y, basis.z);
+  transform.ChangeBasis(-transformOrigin);
 
   // Convert to CSS pixels (this undoes the operations performed by
   // nsStyleTransformMatrix::ProcessTranslatePart which is called from
   // nsDisplayTransform::GetResultingTransformMatrix)
   double devPerCss =
     double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
   transform._41 *= devPerCss;
   transform._42 *= devPerCss;
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -159,20 +159,17 @@ struct AnimationSegment {
 
 // Transforms need extra information to correctly convert the list of transform
 // functions to a Matrix4x4 that can be applied directly to the layer.
 struct TransformData {
   // the origin of the frame being transformed in app units
   nsPoint origin;
   // the transform-origin property for the transform in device pixels
   Point3D transformOrigin;
-  // the perspective-origin property for the transform in device pixels
-  Point3D perspectiveOrigin;
   nsRect bounds;
-  nscoord perspective;
   int32_t appUnitsPerDevPixel;
 };
 
 union AnimationData {
   null_t;
   TransformData;
 };
 
@@ -203,16 +200,17 @@ struct Animation {
 };
 
 // Change a layer's attributes
 struct CommonLayerAttributes {
   IntRect layerBounds;
   nsIntRegion visibleRegion;
   EventRegions eventRegions;
   TransformMatrix transform;
+  bool transformIsPerspective;
   float postXScale;
   float postYScale;
   uint32_t contentFlags;
   float opacity;
   bool useClipRect;
   ParentLayerIntRect clipRect;
   bool isFixedPosition;
   uint64_t fixedPositionScrollContainerId;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -584,16 +584,17 @@ ShadowLayerForwarder::EndTransaction(Inf
     LayerAttributes attrs;
     CommonLayerAttributes& common = attrs.common();
     common.layerBounds() = mutant->GetLayerBounds();
     common.visibleRegion() = mutant->GetVisibleRegion();
     common.eventRegions() = mutant->GetEventRegions();
     common.postXScale() = mutant->GetPostXScale();
     common.postYScale() = mutant->GetPostYScale();
     common.transform() = mutant->GetBaseTransform();
+    common.transformIsPerspective() = mutant->GetTransformIsPerspective();
     common.contentFlags() = mutant->GetContentFlags();
     common.opacity() = mutant->GetOpacity();
     common.useClipRect() = !!mutant->GetClipRect();
     common.clipRect() = (common.useClipRect() ?
                          *mutant->GetClipRect() : ParentLayerIntRect());
     common.isFixedPosition() = mutant->GetIsFixedPosition();
     if (mutant->GetIsFixedPosition()) {
       common.fixedPositionScrollContainerId() = mutant->GetFixedPositionScrollContainerId();
--- a/gfx/tests/gtest/TestJobScheduler.cpp
+++ b/gfx/tests/gtest/TestJobScheduler.cpp
@@ -43,17 +43,17 @@ struct SanityChecker {
       mAdvancements.push_back(0);
     }
   }
 
   virtual void Check(uint64_t aJobId, uint64_t aCmdId)
   {
     MaybeYieldThread();
     CriticalSectionAutoEnter lock(&mSection);
-    ASSERT_EQ(mAdvancements[aJobId], aCmdId-1);
+    MOZ_RELEASE_ASSERT(mAdvancements[aJobId] == aCmdId-1);
     mAdvancements[aJobId] = aCmdId;
   }
 };
 
 /// Run checks that are specific to TestSchulerJoin.
 struct JoinTestSanityCheck : public SanityChecker {
   bool mSpecialJobHasRun;
 
@@ -61,30 +61,30 @@ struct JoinTestSanityCheck : public Sani
   : SanityChecker(aNumCmdBuffers)
   , mSpecialJobHasRun(false)
   {}
 
   virtual void Check(uint64_t aJobId, uint64_t aCmdId) override
   {
     // Job 0 is the special task executed when everything is joined after task 1
     if (aCmdId == 0) {
-      ASSERT_FALSE(mSpecialJobHasRun);
+      MOZ_RELEASE_ASSERT(!mSpecialJobHasRun);
       mSpecialJobHasRun = true;
       for (auto advancement : mAdvancements) {
         // Because of the synchronization point (beforeFilter), all
         // task buffers should have run task 1 when task 0 is run.
-        ASSERT_EQ(advancement, (uint32_t)1);
+        MOZ_RELEASE_ASSERT(advancement == 1);
       }
     } else {
       // This check does not apply to task 0.
       SanityChecker::Check(aJobId, aCmdId);
     }
 
     if (aCmdId == 2) {
-      ASSERT_TRUE(mSpecialJobHasRun);
+      MOZ_RELEASE_ASSERT(mSpecialJobHasRun);
     }
   }
 };
 
 class TestJob : public Job
 {
 public:
   TestJob(uint64_t aCmdId, uint64_t aJobId, SanityChecker* aChecker,
@@ -151,17 +151,17 @@ void TestSchedulerJoin(uint32_t aNumThre
 
   MaybeYieldThread();
 
   waitForCompletion->Wait();
 
   MaybeYieldThread();
 
   for (auto advancement : check.mAdvancements) {
-    ASSERT_TRUE(advancement == 2);
+    EXPECT_TRUE(advancement == 2);
   }
 }
 
 /// This test creates several chains of 10 task, tasks of a given chain are executed
 /// sequentially, and chains are exectuted in parallel.
 /// This simulates the typical scenario where we want to process sequences of drawing
 /// commands for several tiles in parallel.
 void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
@@ -209,17 +209,17 @@ void TestSchedulerChain(uint32_t aNumThr
   auto evtJob = new SetEventJob(waitForCompletion, completion);
   JobScheduler::SubmitJob(evtJob);
 
   MaybeYieldThread();
 
   waitForCompletion->Wait();
 
   for (auto advancement : check.mAdvancements) {
-    ASSERT_TRUE(advancement == numJobs);
+    EXPECT_TRUE(advancement == numJobs);
   }
 }
 
 } // namespace test_scheduler
 
 TEST(Moz2D, JobScheduler_Join) {
   srand(time(nullptr));
   for (uint32_t threads = 1; threads < 8; ++threads) {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -140,17 +140,16 @@ private:
   DECL_GFX_PREF(Live, "apz.allow_checkerboarding",             APZAllowCheckerboarding, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_zooming",                     APZAllowZooming, bool, false);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle",              APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.mode",                    APZAxisLockMode, int32_t, 0);
   DECL_GFX_PREF(Live, "apz.content_response_timeout",          APZContentResponseTimeout, int32_t, 300);
-  DECL_GFX_PREF(Live, "apz.cross_slide.enabled",               APZCrossSlideEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.danger_zone_x",                     APZDangerZoneX, int32_t, 50);
   DECL_GFX_PREF(Live, "apz.danger_zone_y",                     APZDangerZoneY, int32_t, 100);
   DECL_GFX_PREF(Live, "apz.drag.enabled",                      APZDragEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped",  APZEnlargeDisplayPortWhenClipped, bool, false);
   DECL_GFX_PREF(Live, "apz.fling_accel_base_mult",             APZFlingAccelBaseMultiplier, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms",           APZFlingAccelInterval, int32_t, 500);
   DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult",     APZFlingAccelSupplementalMultiplier, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x1",           APZCurveFunctionX1, float, 0.0f);
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -101,21 +101,25 @@ Downscaler::BeginFrame(const nsIntSize& 
 
   skia::resize::ComputeFilters(resizeMethod,
                                mOriginalSize.height, mTargetSize.height,
                                0, mTargetSize.height,
                                mYFilter.get());
 
   // Allocate the buffer, which contains scanlines of the original image.
   // pad by 15 to handle overreads by the simd code
-  mRowBuffer.reset(new (fallible) uint8_t[mOriginalSize.width * sizeof(uint32_t) + 15]);
+  size_t bufferLen = mOriginalSize.width * sizeof(uint32_t) + 15;
+  mRowBuffer.reset(new (fallible) uint8_t[bufferLen]);
   if (MOZ_UNLIKELY(!mRowBuffer)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  // Zero buffer to keep valgrind happy.
+  memset(mRowBuffer.get(), 0, bufferLen);
+
   // Allocate the window, which contains horizontally downscaled scanlines. (We
   // can store scanlines which are already downscale because our downscaling
   // filter is separable.)
   mWindowCapacity = mYFilter->max_filter();
   mWindow.reset(new (fallible) uint8_t*[mWindowCapacity]);
   if (MOZ_UNLIKELY(!mWindow)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/image/decoders/GIF2.h
+++ b/image/decoders/GIF2.h
@@ -70,16 +70,18 @@ typedef struct gif_struct {
     int ipass;                  // Interlace pass; Ranges 1-4 if interlaced.
     unsigned rows_remaining;    // Rows remaining to be output
     unsigned irow;              // Current output row, starting at zero
     uint8_t* rowp;              // Current output pointer
 
     // Parameters for image frame currently being decoded
     unsigned x_offset, y_offset; // With respect to "screen" origin
     unsigned height, width;
+    unsigned clamped_height;    // Size of the frame rectangle clamped to the
+    unsigned clamped_width;     //  global logical size after x_ and y_offset.
     int tpixel;                 // Index of transparent pixel
     int32_t disposal_method;    // Restore to background, leave in place, etc.
     uint32_t* local_colormap;   // Per-image colormap
     int local_colormap_size;    // Size of local colormap array.
     uint32_t delay_time;        // Display time, in milliseconds,
                                 // for this image in a multi-image GIF
 
     // Global (multi-image) state
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -176,21 +176,21 @@ nsGIFDecoder2::FlushImageData()
       if (mCurrentRow - mLastFlushedRow) {
         FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
       }
       break;
 
     case 1:  // one pass on - need to handle bottom & top rects
       FlushImageData(0, mCurrentRow + 1);
       FlushImageData(mLastFlushedRow + 1,
-                     mGIFStruct.height - (mLastFlushedRow + 1));
+                     mGIFStruct.clamped_height - (mLastFlushedRow + 1));
       break;
 
     default: // more than one pass on - push the whole frame
-      FlushImageData(0, mGIFStruct.height);
+      FlushImageData(0, mGIFStruct.clamped_height);
   }
 }
 
 //******************************************************************************
 // GIF decoder callback methods. Part of public API for GIF2
 //******************************************************************************
 
 //******************************************************************************
@@ -333,21 +333,21 @@ nsGIFDecoder2::EndImageFrame()
     if (!mGIFStruct.is_transparent && !mSawTransparency) {
       opacity = Opacity::OPAQUE;
     }
   }
   mCurrentRow = mLastFlushedRow = -1;
   mCurrentPass = mLastFlushedPass = 0;
 
   // Only add frame if we have any rows at all
-  if (mGIFStruct.rows_remaining != mGIFStruct.height) {
+  if (mGIFStruct.rows_remaining != mGIFStruct.clamped_height) {
     if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
       // Clear the remaining rows (only needed for the animation frames)
       uint8_t* rowp =
-        mImageData + ((mGIFStruct.height - mGIFStruct.rows_remaining) *
+        mImageData + ((mGIFStruct.clamped_height - mGIFStruct.rows_remaining) *
                       mGIFStruct.width);
       memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
     }
   }
 
   // Unconditionally increment images_decoded, because we unconditionally
   // append frames in BeginImageFrame(). This ensures that images_decoded
   // always refers to the frame in mImage we're currently decoding,
@@ -376,17 +376,17 @@ nsGIFDecoder2::OutputRow()
 {
   // Initialize the region in which we're duplicating rows (for the
   // Haeberli-inspired hack below) to |irow|, which is the row we're writing to
   // now.
   int drow_start = mGIFStruct.irow;
   int drow_end = mGIFStruct.irow;
 
   // Protect against too much image data
-  if ((unsigned)drow_start >= mGIFStruct.height) {
+  if ((unsigned)drow_start >= mGIFStruct.clamped_height) {
     NS_WARNING("GIF2.cpp::OutputRow - too much image data");
     return 0;
   }
 
   if (!mGIFStruct.images_decoded) {
     // Haeberli-inspired hack for interlaced GIFs: Replicate lines while
     // displaying to diminish the "venetian-blind" effect as the image is
     // loaded. Adjust pixel vertical positions to avoid the appearance of the
@@ -396,44 +396,44 @@ nsGIFDecoder2::OutputRow()
       // ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0
       const uint32_t row_dup = 15 >> mGIFStruct.ipass;
       const uint32_t row_shift = row_dup >> 1;
 
       drow_start -= row_shift;
       drow_end = drow_start + row_dup;
 
       // Extend if bottom edge isn't covered because of the shift upward.
-      if (((mGIFStruct.height - 1) - drow_end) <= row_shift) {
-        drow_end = mGIFStruct.height - 1;
+      if (((mGIFStruct.clamped_height - 1) - drow_end) <= row_shift) {
+        drow_end = mGIFStruct.clamped_height - 1;
       }
 
       // Clamp first and last rows to upper and lower edge of image.
       if (drow_start < 0) {
         drow_start = 0;
       }
-      if ((unsigned)drow_end >= mGIFStruct.height) {
-        drow_end = mGIFStruct.height - 1;
+      if ((unsigned)drow_end >= mGIFStruct.clamped_height) {
+        drow_end = mGIFStruct.clamped_height - 1;
       }
     }
 
     // Row to process
     uint8_t* rowp = GetCurrentRowBuffer();
 
     // Convert color indices to Cairo pixels
-    uint8_t* from = rowp + mGIFStruct.width;
-    uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.width;
+    uint8_t* from = rowp + mGIFStruct.clamped_width;
+    uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.clamped_width;
     uint32_t* cmap = mColormap;
-    for (uint32_t c = mGIFStruct.width; c > 0; c--) {
+    for (uint32_t c = mGIFStruct.clamped_width; c > 0; c--) {
       *--to = cmap[*--from];
     }
 
     // check for alpha (only for first frame)
     if (mGIFStruct.is_transparent && !mSawTransparency) {
       const uint32_t* rgb = (uint32_t*)rowp;
-      for (uint32_t i = mGIFStruct.width; i > 0; i--) {
+      for (uint32_t i = mGIFStruct.clamped_width; i > 0; i--) {
         if (*rgb++ == 0) {
           mSawTransparency = true;
           break;
         }
       }
     }
 
     // If we're downscaling but not deinterlacing, we're done with this row and
@@ -444,17 +444,17 @@ nsGIFDecoder2::OutputRow()
     }
 
     if (drow_end > drow_start) {
       // Duplicate rows if needed to reduce the "venetian blind" effect mentioned
       // above. This writes out scanlines of the image in a way that isn't ordered
       // vertically, which is incompatible with the filter that we use for
       // downscale-during-decode, so we can't do this if we're downscaling.
       MOZ_ASSERT_IF(mDownscaler, mDeinterlacer);
-      const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
+      const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.clamped_width;
       for (int r = drow_start; r <= drow_end; r++) {
         // Skip the row we wrote to above; that's what we're copying *from*.
         if (r != int(mGIFStruct.irow)) {
           memcpy(GetRowBuffer(r), rowp, bpr);
         }
       }
     }
   }
@@ -470,22 +470,22 @@ nsGIFDecoder2::OutputRow()
     mGIFStruct.irow++;
   } else {
     static const uint8_t kjump[5] = { 1, 8, 8, 4, 2 };
     int currentPass = mGIFStruct.ipass;
 
     do {
       // Row increments resp. per 8,8,4,2 rows
       mGIFStruct.irow += kjump[mGIFStruct.ipass];
-      if (mGIFStruct.irow >= mGIFStruct.height) {
+      if (mGIFStruct.irow >= mGIFStruct.clamped_height) {
         // Next pass starts resp. at row 4,2,1,0
         mGIFStruct.irow = 8 >> mGIFStruct.ipass;
         mGIFStruct.ipass++;
       }
-    } while (mGIFStruct.irow >= mGIFStruct.height);
+    } while (mGIFStruct.irow >= mGIFStruct.clamped_height);
 
     // We've finished a pass. If we're downscaling, it's time to propagate the
     // rows we've decoded so far from our Deinterlacer to our Downscaler.
     if (mGIFStruct.ipass > currentPass && mDownscaler) {
       MOZ_ASSERT(mDeinterlacer);
       mDeinterlacer->PropagatePassToDownscaler(*mDownscaler);
       FlushImageData();
       mDownscaler->ResetForNextProgressivePass();
@@ -520,24 +520,24 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
   uint8_t firstchar = mGIFStruct.firstchar;
   int32_t datum     = mGIFStruct.datum;
   uint16_t* prefix  = mGIFStruct.prefix;
   uint8_t* stackp   = mGIFStruct.stackp;
   uint8_t* suffix   = mGIFStruct.suffix;
   uint8_t* stack    = mGIFStruct.stack;
   uint8_t* rowp     = mGIFStruct.rowp;
 
-  uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.width;
+  uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.clamped_width;
 
 #define OUTPUT_ROW()                                        \
   PR_BEGIN_MACRO                                            \
     if (!OutputRow())                                       \
       goto END;                                             \
     rowp = GetCurrentRowBuffer();                           \
-    rowend = rowp + mGIFStruct.width;                       \
+    rowend = rowp + mGIFStruct.clamped_width;               \
   PR_END_MACRO
 
   for (const uint8_t* ch = q; count-- > 0; ch++) {
     // Feed the next byte into the decoder's 32-bit input buffer.
     datum += ((int32_t)* ch) << bits;
     bits += 8;
 
     // Check for underflow of decoder's 32-bit input buffer.
@@ -620,16 +620,20 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
       }
       oldcode = incode;
 
       // Copy the decoded data out to the scanline buffer.
       do {
         *rowp++ = *--stackp & mColorMask; // ensure index is within colormap
         if (rowp == rowend) {
           OUTPUT_ROW();
+
+          // Consume decoded data that falls past the end of the clamped width.
+          stackp -= mGIFStruct.width - mGIFStruct.clamped_width;
+          stackp = std::max(stackp, stack);
         }
       } while (stackp > stack);
     }
   }
 
   END:
 
   // Home the local copies of the GIF decoder state variables
@@ -1103,16 +1107,31 @@ nsGIFDecoder2::WriteInternal(const char*
         mGIFStruct.height = mGIFStruct.screen_height;
         mGIFStruct.width = mGIFStruct.screen_width;
         if (!mGIFStruct.height || !mGIFStruct.width) {
           mGIFStruct.state = gif_error;
           break;
         }
       }
 
+      // Hack around GIFs with frame rects outside the given screen bounds.
+      IntRect clampedRect =
+        ClampToImageRect(IntRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
+                                 mGIFStruct.width, mGIFStruct.height));
+      if (clampedRect.IsEmpty()) {
+        // XXX Bug 1227546 - Maybe we should treat this as valid?
+        mGIFStruct.state = gif_error;
+        break;
+      }
+      mGIFStruct.clamped_width = clampedRect.width;
+      mGIFStruct.clamped_height = clampedRect.height;
+
+      MOZ_ASSERT(mGIFStruct.clamped_width <= mGIFStruct.width);
+      MOZ_ASSERT(mGIFStruct.clamped_height <= mGIFStruct.height);
+
       // Depth of colors is determined by colormap
       // (q[8] & 0x80) indicates local colormap
       // bits per pixel is (q[8]&0x07 + 1) when local colormap is set
       uint32_t depth = mGIFStruct.global_colormap_depth;
       if (q[8] & 0x80) {
         depth = (q[8]&0x07) + 1;
       }
       uint32_t realDepth = depth;
@@ -1165,17 +1184,17 @@ nsGIFDecoder2::WriteInternal(const char*
         mGIFStruct.ipass = 0;
       }
 
       // Only apply the Haeberli display hack on the first frame
       mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
 
       // Clear state from last image
       mGIFStruct.irow = 0;
-      mGIFStruct.rows_remaining = mGIFStruct.height;
+      mGIFStruct.rows_remaining = mGIFStruct.clamped_height;
       mGIFStruct.rowp = GetCurrentRowBuffer();
 
       // Depth of colors is determined by colormap
       // (q[8] & 0x80) indicates local colormap
       // bits per pixel is (q[8]&0x07 + 1) when local colormap is set
       uint32_t depth = mGIFStruct.global_colormap_depth;
       if (q[8] & 0x80) {
         depth = (q[8]&0x07) + 1;
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -106,17 +106,17 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_
         'sha256.c',
     ]
 
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
-LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']]
+LDFLAGS += CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']
 
 # Control the default heap size.
 # This is the heap returned by GetProcessHeap().
 # As we use the CRT heap, the default size is too large and wastes VM.
 #
 # The default heap size is 1MB on Win32.
 # The heap will grow if need be.
 #
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -99,66 +99,38 @@ bool
 ObjectToIdMap::init()
 {
     return table_.initialized() || table_.init(32);
 }
 
 void
 ObjectToIdMap::trace(JSTracer* trc)
 {
-    for (Table::Enum e(table_); !e.empty(); e.popFront()) {
-        JSObject* obj = e.front().key();
-        JS_CallUnbarrieredObjectTracer(trc, &obj, "ipc-object");
-        if (obj != e.front().key())
-            e.rekeyFront(obj);
-    }
+    table_.trace(trc);
 }
 
 void
 ObjectToIdMap::sweep()
 {
-    for (Table::Enum e(table_); !e.empty(); e.popFront()) {
-        JSObject* obj = e.front().key();
-        JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
-        if (!obj)
-            e.removeFront();
-        else if (obj != e.front().key())
-            e.rekeyFront(obj);
-    }
+    table_.sweep();
 }
 
 ObjectId
 ObjectToIdMap::find(JSObject* obj)
 {
     Table::Ptr p = table_.lookup(obj);
     if (!p)
         return ObjectId::nullId();
     return p->value();
 }
 
 bool
 ObjectToIdMap::add(JSContext* cx, JSObject* obj, ObjectId id)
 {
-    if (!table_.put(obj, id))
-        return false;
-    JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, &table_);
-    return true;
-}
-
-/*
- * This function is called during minor GCs for each key in the HashMap that has
- * been moved.
- */
-/* static */ void
-ObjectToIdMap::keyMarkCallback(JSTracer* trc, JSObject* key, void* data)
-{
-    Table* table = static_cast<Table*>(data);
-    JSObject* prior = key;
-    JS_CallUnbarrieredObjectTracer(trc, &key, "ObjectIdCache::table_ key");
-    table->rekeyIfMoved(prior, key);
+    return table_.put(obj, id);
 }
 
 void
 ObjectToIdMap::remove(JSObject* obj)
 {
     table_.remove(obj);
 }
 
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_jsipc_JavaScriptShared_h__
 #define mozilla_jsipc_JavaScriptShared_h__
 
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/jsipc/PJavaScript.h"
+#include "js/GCHashTable.h"
 #include "nsJSUtils.h"
 
 namespace mozilla {
 namespace jsipc {
 
 class ObjectId {
   public:
     // Use 47 bits at most, to be safe, since jsval privates are encoded as
@@ -46,16 +47,20 @@ class ObjectId {
         return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0));
     }
 
     static ObjectId nullId() { return ObjectId(); }
     static ObjectId deserialize(uint64_t data) {
         return ObjectId(data >> FLAG_BITS, data & 1);
     }
 
+    // For use with StructGCPolicy.
+    void trace(JSTracer*) const {}
+    bool needsSweep() const { return false; }
+
   private:
     ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}
 
     uint64_t serialNumber_ : SERIAL_NUMBER_BITS;
     bool hasXrayWaiver_ : 1;
 };
 
 class JavaScriptShared;
@@ -98,35 +103,33 @@ class IdToObjectMap
 
   private:
     Table table_;
 };
 
 // Map JSObjects -> ids
 class ObjectToIdMap
 {
-    typedef js::PointerHasher<JSObject*, 3> Hasher;
-    typedef js::HashMap<JSObject*, ObjectId, Hasher, js::SystemAllocPolicy> Table;
+    using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>;
+    using Table = js::GCHashMap<JS::Heap<JSObject*>, ObjectId, Hasher, js::SystemAllocPolicy>;
 
   public:
     explicit ObjectToIdMap(JSRuntime* rt);
     ~ObjectToIdMap();
 
     bool init();
     void trace(JSTracer* trc);
     void sweep();
 
     bool add(JSContext* cx, JSObject* obj, ObjectId id);
     ObjectId find(JSObject* obj);
     void remove(JSObject* obj);
     void clear();
 
   private:
-    static void keyMarkCallback(JSTracer* trc, JSObject* key, void* data);
-
     JSRuntime* rt_;
     Table table_;
 };
 
 class Logging;
 
 class JavaScriptShared : public CPOWManager
 {
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -60,20 +60,17 @@ class GCHashMap : public HashMap<Key, Va
     explicit GCHashMap(AllocPolicy a = AllocPolicy()) : Base(a)  {}
 
     static void trace(GCHashMap* map, JSTracer* trc) { map->trace(trc); }
     void trace(JSTracer* trc) {
         if (!this->initialized())
             return;
         for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
             GCPolicy::ValuePolicy::trace(trc, &e.front().value(), "hashmap value");
-            Key key = e.front().key();
-            GCPolicy::KeyPolicy::trace(trc, &key, "hashmap key");
-            if (key != e.front().key())
-                e.rekeyFront(key);
+            GCPolicy::KeyPolicy::trace(trc, &e.front().mutableKey(), "hashmap key");
         }
     }
 
     void sweep() {
         if (!this->initialized())
             return;
 
         for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
@@ -205,22 +202,18 @@ class GCHashSet : public HashSet<T, Hash
 
   public:
     explicit GCHashSet(AllocPolicy a = AllocPolicy()) : Base(a)  {}
 
     static void trace(GCHashSet* set, JSTracer* trc) { set->trace(trc); }
     void trace(JSTracer* trc) {
         if (!this->initialized())
             return;
-        for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
-            T elem = e.front();
-            GCPolicy::trace(trc, &elem, "hashset element");
-            if (elem != e.front())
-                e.rekeyFront(elem);
-        }
+        for (typename Base::Enum e(*this); !e.empty(); e.popFront())
+            GCPolicy::trace(trc, &e.mutableFront(), "hashset element");
     }
 
     void sweep() {
         if (!this->initialized())
             return;
         for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
             if (GCPolicy::needsSweep(&e.mutableFront()))
                 e.removeFront();
--- a/js/src/asmjs/AsmJSCompile.cpp
+++ b/js/src/asmjs/AsmJSCompile.cpp
@@ -12,208 +12,156 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/AsmJSCompile.h"
-#include "asmjs/AsmJSGlobals.h"
 
 #include "jit/CodeGenerator.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
-
-namespace js {
-// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
-// the course of an ModuleCompiler object's lifetime, many FunctionCompiler
-// objects will be created and destroyed in sequence, one for each function in
-// the module.
-//
-// *** asm.js FFI calls ***
-//
-// asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type
-// system does not place any constraints on the FFI call. In particular:
-//  - an FFI call's target is not known or speculated at module-compile time;
-//  - a single external function can be called with different signatures.
-//
-// If performance didn't matter, all FFI calls could simply box their arguments
-// and call js::Invoke. However, we'd like to be able to specialize FFI calls
-// to be more efficient in several cases:
-//
-//  - for calls to JS functions which have been jitted, we'd like to call
-//    directly into JIT code without going through C++.
-//
-//  - for calls to certain builtins, we'd like to be call directly into the C++
-//    code for the builtin without going through the general call path.
-//
-// All of this requires dynamic specialization techniques which must happen
-// after module compilation. To support this, at module-compilation time, each
-// FFI call generates a call signature according to the system ABI, as if the
-// callee was a C++ function taking/returning the same types as the caller was
-// passing/expecting. The callee is loaded from a fixed offset in the global
-// data array which allows the callee to change at runtime. Initially, the
-// callee is stub which boxes its arguments and calls js::Invoke.
-//
-// To do this, we need to generate a callee stub for each pairing of FFI callee
-// and signature. We call this pairing an "exit". For example, this code has
-// two external functions and three exits:
-//
-//  function f(global, imports) {
-//    "use asm";
-//    var foo = imports.foo;
-//    var bar = imports.bar;
-//    function g() {
-//      foo(1);      // Exit #1: (int) -> void
-//      foo(1.5);    // Exit #2: (double) -> void
-//      bar(1)|0;    // Exit #3: (int) -> int
-//      bar(2)|0;    // Exit #3: (int) -> int
-//    }
-//  }
-//
-// The ModuleCompiler maintains a hash table (ExitMap) which allows a call site
-// to add a new exit or reuse an existing one. The key is an index into the
-// Vector<Exit> stored in the AsmJSModule and the value is the signature of
-// that exit's variant.
-//
-// Although ModuleCompiler isn't a MOZ_STACK_CLASS, it has the same rooting
-// properties as the ModuleValidator, and a shorter lifetime: so it is marked
-// as rooted in the in the rooting analysis. Don't add non-JSATom pointers, or
-// this will break!
-class ModuleCompiler
-{
-    ModuleCompileInputs                     compileInputs_;
-    ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
-
-  public:
-    explicit ModuleCompiler(const ModuleCompileInputs& inputs)
-      : compileInputs_(inputs)
-    {}
-
-    bool init() {
-        compileResults_.reset(js_new<ModuleCompileResults>());
-        return !!compileResults_;
-    }
-
-    /*************************************************** Read-only interface */
-
-    MacroAssembler& masm()          { return compileResults_->masm(); }
-    Label& stackOverflowLabel()     { return compileResults_->stackOverflowLabel(); }
-    Label& asyncInterruptLabel()    { return compileResults_->asyncInterruptLabel(); }
-    Label& syncInterruptLabel()     { return compileResults_->syncInterruptLabel(); }
-    Label& onOutOfBoundsLabel()     { return compileResults_->onOutOfBoundsLabel(); }
-    Label& onConversionErrorLabel() { return compileResults_->onConversionErrorLabel(); }
-    int64_t usecBefore()            { return compileResults_->usecBefore(); }
-
-    bool usesSignalHandlersForOOB() const   { return compileInputs_.usesSignalHandlersForOOB; }
-    CompileRuntime* runtime() const         { return compileInputs_.runtime; }
-    CompileCompartment* compartment() const { return compileInputs_.compartment; }
-
-    /***************************************************** Mutable interface */
-
-    void finish(ScopedJSDeletePtr<ModuleCompileResults>* results) {
-        *results = compileResults_.forget();
-    }
-};
-
-} // namespace js
-
 enum class AsmType : uint8_t {
     Int32,
     Float32,
     Float64,
     Int32x4,
     Float32x4
 };
 
 typedef Vector<size_t, 1, SystemAllocPolicy> LabelVector;
 typedef Vector<MBasicBlock*, 8, SystemAllocPolicy> BlockVector;
 
 // Encapsulates the compilation of a single function in an asm.js module. The
 // function compiler handles the creation and final backend compilation of the
-// MIR graph. Also see ModuleCompiler comment.
+// MIR graph.
 class FunctionCompiler
 {
   private:
     typedef HashMap<uint32_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> LabeledBlockMap;
     typedef HashMap<size_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> UnlabeledBlockMap;
     typedef Vector<size_t, 4, SystemAllocPolicy> PositionStack;
     typedef Vector<Type, 4, SystemAllocPolicy> LocalVarTypes;
 
-    ModuleCompiler &         m_;
-    LifoAlloc &              lifo_;
-
+    ModuleCompileInputs      inputs_;
     const AsmFunction &      func_;
     size_t                   pc_;
 
-    TempAllocator *          alloc_;
-    MIRGraph *               graph_;
-    CompileInfo *            info_;
+    TempAllocator &          alloc_;
+    MIRGraph &               graph_;
+    const CompileInfo &      info_;
     MIRGenerator *           mirGen_;
     Maybe<JitContext>        jitContext_;
 
     MBasicBlock *            curBlock_;
 
     PositionStack            loopStack_;
     PositionStack            breakableStack_;
     UnlabeledBlockMap        unlabeledBreaks_;
     UnlabeledBlockMap        unlabeledContinues_;
     LabeledBlockMap          labeledBreaks_;
     LabeledBlockMap          labeledContinues_;
 
     LocalVarTypes            localVarTypes_;
 
+    FunctionCompileResults&  compileResults_;
+
   public:
-    FunctionCompiler(ModuleCompiler& m, const AsmFunction& func, LifoAlloc& lifo)
-      : m_(m),
-        lifo_(lifo),
+    FunctionCompiler(ModuleCompileInputs inputs, const AsmFunction& func, const CompileInfo& info,
+                     TempAllocator* alloc, MIRGraph* graph, MIRGenerator* mirGen,
+                     FunctionCompileResults& compileResults)
+      : inputs_(inputs),
         func_(func),
         pc_(0),
-        alloc_(nullptr),
-        graph_(nullptr),
-        info_(nullptr),
-        mirGen_(nullptr),
-        curBlock_(nullptr)
+        alloc_(*alloc),
+        graph_(*graph),
+        info_(info),
+        mirGen_(mirGen),
+        curBlock_(nullptr),
+        compileResults_(compileResults)
     {}
 
-    ModuleCompiler & m() const            { return m_; }
-    TempAllocator &  alloc() const        { return *alloc_; }
-    LifoAlloc &      lifo() const         { return lifo_; }
+    TempAllocator &  alloc() const        { return alloc_; }
+    MacroAssembler & masm() const         { return compileResults_.masm(); }
     RetType          returnedType() const { return func_.returnedType(); }
 
-    bool init()
+    bool init(const VarTypeVector& argTypes)
     {
-        return unlabeledBreaks_.init() &&
-               unlabeledContinues_.init() &&
-               labeledBreaks_.init() &&
-               labeledContinues_.init();
+        if (!unlabeledBreaks_.init() ||
+            !unlabeledContinues_.init() ||
+            !labeledBreaks_.init() ||
+            !labeledContinues_.init())
+        {
+            return false;
+        }
+
+        const AsmFunction::VarInitializerVector& varInitializers = func_.varInitializers();
+
+        // Prepare data structures
+        jitContext_.emplace(inputs_.runtime, /* CompileCompartment = */ nullptr, &alloc_);
+        MOZ_ASSERT(func_.numLocals() == argTypes.length() + varInitializers.length());
+
+        if (!newBlock(/* pred = */ nullptr, &curBlock_))
+            return false;
+
+        // Emit parameters and local variables
+        for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
+            MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
+            curBlock_->add(ins);
+            curBlock_->initSlot(info().localSlot(i.index()), ins);
+            if (!mirGen_->ensureBallast())
+                return false;
+            localVarTypes_.append(argTypes[i.index()].toType());
+        }
+
+        unsigned firstLocalSlot = argTypes.length();
+        for (unsigned i = 0; i < varInitializers.length(); i++) {
+            const AsmJSNumLit& lit = varInitializers[i];
+            Type type = Type::Of(lit);
+            MIRType mirType = type.toMIRType();
+
+            MInstruction* ins;
+            if (lit.isSimd())
+               ins = MSimdConstant::New(alloc(), lit.simdValue(), mirType);
+            else
+               ins = MConstant::NewAsmJS(alloc(), lit.scalarValue(), mirType);
+
+            curBlock_->add(ins);
+            curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
+            if (!mirGen_->ensureBallast())
+                return false;
+            localVarTypes_.append(type);
+        }
+
+        return true;
     }
 
     void checkPostconditions()
     {
         MOZ_ASSERT(loopStack_.empty());
         MOZ_ASSERT(unlabeledBreaks_.empty());
         MOZ_ASSERT(unlabeledContinues_.empty());
         MOZ_ASSERT(labeledBreaks_.empty());
         MOZ_ASSERT(labeledContinues_.empty());
         MOZ_ASSERT(inDeadCode());
         MOZ_ASSERT(pc_ == func_.size(), "all bytecode must be consumed");
     }
 
     /************************* Read-only interface (after local scope setup) */
 
-    MIRGenerator & mirGen() const     { MOZ_ASSERT(mirGen_); return *mirGen_; }
-    MIRGraph &     mirGraph() const   { MOZ_ASSERT(graph_); return *graph_; }
-    CompileInfo &  info() const       { MOZ_ASSERT(info_); return *info_; }
+    MIRGenerator &      mirGen() const     { MOZ_ASSERT(mirGen_); return *mirGen_; }
+    MIRGraph &          mirGraph() const   { return graph_; }
+    const CompileInfo & info() const       { return info_; }
 
     MDefinition* getLocalDef(unsigned slot)
     {
         if (inDeadCode())
             return nullptr;
         return curBlock_->getSlot(info().localSlot(slot));
     }
 
@@ -609,17 +557,17 @@ class FunctionCompiler
     }
 
     void addInterruptCheck(unsigned lineno, unsigned column)
     {
         if (inDeadCode())
             return;
 
         CallSiteDesc callDesc(lineno, column, CallSiteDesc::Relative);
-        curBlock_->add(MAsmJSInterruptCheck::New(alloc(), &m().syncInterruptLabel(), callDesc));
+        curBlock_->add(MAsmJSInterruptCheck::New(alloc(), masm().asmSyncInterruptLabel(), callDesc));
     }
 
     MDefinition* extractSimdElement(SimdLane lane, MDefinition* base, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(base->type()));
@@ -1218,89 +1166,16 @@ class FunctionCompiler
 #ifdef DEBUG
         MOZ_ASSERT(Stmt(readU8()) == Stmt::DebugCheckPoint);
 #endif
     }
 
     bool done() const { return pc_ == func_.size(); }
     size_t pc() const { return pc_; }
 
-    bool prepareEmitMIR(const VarTypeVector& argTypes)
-    {
-        const AsmFunction::VarInitializerVector& varInitializers = func_.varInitializers();
-        size_t numLocals = func_.numLocals();
-
-        // Prepare data structures
-        alloc_  = lifo_.new_<TempAllocator>(&lifo_);
-        if (!alloc_)
-            return false;
-        jitContext_.emplace(m().runtime(), /* CompileCompartment = */ nullptr, alloc_);
-        graph_  = lifo_.new_<MIRGraph>(alloc_);
-        if (!graph_)
-            return false;
-        MOZ_ASSERT(numLocals == argTypes.length() + varInitializers.length());
-        info_   = lifo_.new_<CompileInfo>(numLocals);
-        if (!info_)
-            return false;
-        const OptimizationInfo* optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
-        const JitCompileOptions options;
-        mirGen_ = lifo_.new_<MIRGenerator>(m().compartment(),
-                                           options, alloc_,
-                                           graph_, info_, optimizationInfo,
-                                           &m().onOutOfBoundsLabel(),
-                                           &m().onConversionErrorLabel(),
-                                           m().usesSignalHandlersForOOB());
-        if (!mirGen_)
-            return false;
-
-        if (!newBlock(/* pred = */ nullptr, &curBlock_))
-            return false;
-
-        // Emit parameters and local variables
-        for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
-            MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
-            curBlock_->add(ins);
-            curBlock_->initSlot(info().localSlot(i.index()), ins);
-            if (!mirGen_->ensureBallast())
-                return false;
-            localVarTypes_.append(argTypes[i.index()].toType());
-        }
-
-        unsigned firstLocalSlot = argTypes.length();
-        for (unsigned i = 0; i < varInitializers.length(); i++) {
-            const AsmJSNumLit& lit = varInitializers[i];
-            Type type = Type::Of(lit);
-            MIRType mirType = type.toMIRType();
-
-            MInstruction* ins;
-            if (lit.isSimd())
-               ins = MSimdConstant::New(alloc(), lit.simdValue(), mirType);
-            else
-               ins = MConstant::NewAsmJS(alloc(), lit.scalarValue(), mirType);
-
-            curBlock_->add(ins);
-            curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
-            if (!mirGen_->ensureBallast())
-                return false;
-            localVarTypes_.append(type);
-        }
-
-        return true;
-    }
-
-    /*************************************************************************/
-
-    MIRGenerator* extractMIR()
-    {
-        MOZ_ASSERT(mirGen_ != nullptr);
-        MIRGenerator* mirGen = mirGen_;
-        mirGen_ = nullptr;
-        return mirGen;
-    }
-
     /*************************************************************************/
   private:
     bool newBlockWithDepth(MBasicBlock* pred, unsigned loopDepth, MBasicBlock** block)
     {
         *block = MBasicBlock::NewAsmJS(mirGraph(), info(), pred, MBasicBlock::NORMAL);
         if (!*block)
             return false;
         mirGraph().addBlock(*block);
@@ -3083,99 +2958,77 @@ EmitF32X4Expr(FunctionCompiler& f, MDefi
         return EmitSimdStore(f, AsmType::Float32x4, def);
       case F32X4::Bad:
         break;
     }
     MOZ_CRASH("unexpected float32x4 expression");
 }
 
 bool
-js::GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func, MIRGenerator** mir)
+js::CompileAsmFunction(LifoAlloc& lifo, ModuleCompileInputs inputs, const AsmFunction& func,
+                       FunctionCompileResults* results)
 {
     int64_t before = PRMJ_Now();
 
-    FunctionCompiler f(m, func, lifo);
-    if (!f.init())
-        return false;
-
-    if (!f.prepareEmitMIR(func.argTypes()))
-        return false;
-
-    while (!f.done()) {
-        if (!EmitStatement(f))
+    TempAllocator tempAlloc(&lifo);
+    MIRGraph graph(&tempAlloc);
+    CompileInfo compileInfo(func.numLocals());
+    const OptimizationInfo* optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
+    const JitCompileOptions options;
+    MIRGenerator mir(inputs.compartment, options, &tempAlloc, &graph, &compileInfo,
+                     optimizationInfo, results->masm().asmOnOutOfBoundsLabel(),
+                     results->masm().asmOnConversionErrorLabel(), inputs.usesSignalHandlersForOOB);
+
+    // Keep the FunctionCompiler in a separate scope so that its memory gets
+    // freed when it's done.
+    {
+        FunctionCompiler f(inputs, func, compileInfo, &tempAlloc, &graph, &mir, *results);
+        if (!f.init(func.argTypes()))
             return false;
+
+        while (!f.done()) {
+            if (!EmitStatement(f))
+                return false;
+        }
+
+        f.checkPostconditions();
     }
 
-    *mir = f.extractMIR();
-    if (!*mir)
-        return false;
-
-    jit::SpewBeginFunction(*mir, nullptr);
-
-    f.checkPostconditions();
-
-    func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
+    {
+        JitContext jitContext(inputs.runtime, /* CompileCompartment = */ nullptr, &mir.alloc());
+
+        jit::SpewBeginFunction(&mir, nullptr);
+        jit::AutoSpewEndFunction spewEndFunction(&mir);
+
+        if (!OptimizeMIR(&mir))
+            return false;
+
+        LIRGraph* lir = GenerateLIR(&mir);
+        if (!lir)
+            return false;
+
+        // Although each function gets to have its own MacroAssembler for
+        // compilation, parallel compilations may recycle an already existing
+        // MacroAssembler.
+        //
+        // To avoid spiking memory, a LifoAllocScope in the caller
+        // frees all MIR/LIR after each function is compiled. This method is
+        // responsible for cleaning out any dangling pointers that the
+        // MacroAssembler may have kept.
+        results->masm().resetForNewCodeGenerator(mir.alloc());
+
+        Label entry;
+        AsmJSFunctionLabels labels(entry, *results->masm().asmStackOverflowLabel());
+        CodeGenerator codegen(&mir, lir, &results->masm());
+        if (!codegen.generateAsmJS(&labels))
+            return false;
+
+        PropertyName* funcName = func.name();
+        unsigned line = func.lineno();
+
+        // Fill in the results of the function's compilation
+        AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
+        results->finishCodegen(codeRange, *codegen.extractScriptCounts());
+    }
+
+    results->setCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
     return true;
 }
-
-bool
-js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir,
-                            FunctionCompileResults* results)
-{
-    JitContext jitContext(m.runtime(), /* CompileCompartment = */ nullptr, &mir.alloc());
-
-    int64_t before = PRMJ_Now();
-
-    // A single MacroAssembler is reused for all function compilations so
-    // that there is a single linear code segment for each module. To avoid
-    // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
-    // after each function is compiled. This method is responsible for cleaning
-    // out any dangling pointers that the MacroAssembler may have kept.
-    m.masm().resetForNewCodeGenerator(mir.alloc());
-
-    ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm()));
-    if (!codegen)
-        return false;
-
-    Label entry;
-    AsmJSFunctionLabels labels(entry, m.stackOverflowLabel());
-    if (!codegen->generateAsmJS(&labels))
-        return false;
-
-    func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
-
-    PropertyName* funcName = func.name();
-    unsigned line = func.lineno();
-
-    // Fill in the results of the function's compilation
-    AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
-    results->finishCodegen(func, codeRange, *codegen->extractScriptCounts());
-
-    // Unlike regular IonMonkey, which links and generates a new JitCode for
-    // every function, we accumulate all the functions in the module in a
-    // single MacroAssembler and link at end. Linking asm.js doesn't require a
-    // CodeGenerator so we can destroy it now (via ScopedJSDeletePtr).
-    return true;
-}
-
-bool
-js::CreateAsmModuleCompiler(ModuleCompileInputs mci, AsmModuleCompilerScope* scope)
-{
-    auto* mc = js_new<ModuleCompiler>(mci);
-    if (!mc || !mc->init())
-        return false;
-    scope->setModule(mc);
-    return true;
-}
-
-AsmModuleCompilerScope::~AsmModuleCompilerScope()
-{
-    if (m_) {
-        js_delete(m_);
-        m_ = nullptr;
-    }
-}
-
-void
-js::FinishAsmModuleCompilation(ModuleCompiler& m, ScopedJSDeletePtr<ModuleCompileResults>* results)
-{
-    m.finish(results);
-}
--- a/js/src/asmjs/AsmJSCompile.h
+++ b/js/src/asmjs/AsmJSCompile.h
@@ -14,73 +14,1103 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef jit_AsmJSCompile_h
 #define jit_AsmJSCompile_h
 
-#include "jit/CompileWrappers.h"
+#include "asmjs/AsmJSCompileInputs.h"
+#include "asmjs/AsmJSModule.h"
 
 namespace js {
 
-class AsmFunction;
-class LifoAlloc;
-class ModuleCompiler;
-class ModuleCompileResults;
-class FunctionCompileResults;
-
 namespace jit {
     class LIRGraph;
     class MIRGenerator;
 }
 
-struct ModuleCompileInputs
+namespace wasm {
+
+enum NeedsBoundsCheck {
+    NO_BOUNDS_CHECK,
+    NEEDS_BOUNDS_CHECK
+};
+
+// Respresents the type of a general asm.js expression.
+class Type
 {
-    jit::CompileCompartment* compartment;
-    jit::CompileRuntime* runtime;
-    bool usesSignalHandlersForOOB;
+  public:
+    enum Which {
+        Fixnum = AsmJSNumLit::Fixnum,
+        Signed = AsmJSNumLit::NegativeInt,
+        Unsigned = AsmJSNumLit::BigUnsigned,
+        DoubleLit = AsmJSNumLit::Double,
+        Float = AsmJSNumLit::Float,
+        Int32x4 = AsmJSNumLit::Int32x4,
+        Float32x4 = AsmJSNumLit::Float32x4,
+        Double,
+        MaybeDouble,
+        MaybeFloat,
+        Floatish,
+        Int,
+        Intish,
+        Void
+    };
+
+  private:
+    Which which_;
+
+  public:
+    Type() : which_(Which(-1)) {}
+    static Type Of(const AsmJSNumLit& lit) {
+        MOZ_ASSERT(lit.hasType());
+        Which which = Type::Which(lit.which());
+        MOZ_ASSERT(which >= Fixnum && which <= Float32x4);
+        Type t;
+        t.which_ = which;
+        return t;
+    }
+    MOZ_IMPLICIT Type(Which w) : which_(w) {}
+    Which which() const { return which_; }
+    MOZ_IMPLICIT Type(AsmJSSimdType type) {
+        switch (type) {
+          case AsmJSSimdType_int32x4:
+            which_ = Int32x4;
+            return;
+          case AsmJSSimdType_float32x4:
+            which_ = Float32x4;
+            return;
+        }
+        MOZ_CRASH("unexpected AsmJSSimdType");
+    }
+
+    bool operator==(Type rhs) const { return which_ == rhs.which_; }
+    bool operator!=(Type rhs) const { return which_ != rhs.which_; }
+
+    inline bool operator<=(Type rhs) const {
+        switch (rhs.which_) {
+          case Signed:      return isSigned();
+          case Unsigned:    return isUnsigned();
+          case DoubleLit:   return isDoubleLit();
+          case Double:      return isDouble();
+          case Float:       return isFloat();
+          case Int32x4:     return isInt32x4();
+          case Float32x4:   return isFloat32x4();
+          case MaybeDouble: return isMaybeDouble();
+          case MaybeFloat:  return isMaybeFloat();
+          case Floatish:    return isFloatish();
+          case Int:         return isInt();
+          case Intish:      return isIntish();
+          case Fixnum:      return isFixnum();
+          case Void:        return isVoid();
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected this type");
+    }
+
+    bool isFixnum() const {
+        return which_ == Fixnum;
+    }
+
+    bool isSigned() const {
+        return which_ == Signed || which_ == Fixnum;
+    }
+
+    bool isUnsigned() const {
+        return which_ == Unsigned || which_ == Fixnum;
+    }
+
+    bool isInt() const {
+        return isSigned() || isUnsigned() || which_ == Int;
+    }
+
+    bool isIntish() const {
+        return isInt() || which_ == Intish;
+    }
+
+    bool isDoubleLit() const {
+        return which_ == DoubleLit;
+    }
+
+    bool isDouble() const {
+        return isDoubleLit() || which_ == Double;
+    }
+
+    bool isMaybeDouble() const {
+        return isDouble() || which_ == MaybeDouble;
+    }
+
+    bool isFloat() const {
+        return which_ == Float;
+    }
+
+    bool isMaybeFloat() const {
+        return isFloat() || which_ == MaybeFloat;
+    }
+
+    bool isFloatish() const {
+        return isMaybeFloat() || which_ == Floatish;
+    }
+
+    bool isVoid() const {
+        return which_ == Void;
+    }
+
+    bool isExtern() const {
+        return isDouble() || isSigned();
+    }
+
+    bool isInt32x4() const {
+        return which_ == Int32x4;
+    }
+
+    bool isFloat32x4() const {
+        return which_ == Float32x4;
+    }
+
+    bool isSimd() const {
+        return isInt32x4() || isFloat32x4();
+    }
+
+    bool isVarType() const {
+        return isInt() || isDouble() || isFloat() || isSimd();
+    }
+
+    jit::MIRType toMIRType() const {
+        switch (which_) {
+          case Double:
+          case DoubleLit:
+          case MaybeDouble:
+            return jit::MIRType_Double;
+          case Float:
+          case Floatish:
+          case MaybeFloat:
+            return jit::MIRType_Float32;
+          case Fixnum:
+          case Int:
+          case Signed:
+          case Unsigned:
+          case Intish:
+            return jit::MIRType_Int32;
+          case Int32x4:
+            return jit::MIRType_Int32x4;
+          case Float32x4:
+            return jit::MIRType_Float32x4;
+          case Void:
+            return jit::MIRType_None;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
+    }
+
+    AsmJSSimdType simdType() const {
+        MOZ_ASSERT(isSimd());
+        switch (which_) {
+          case Int32x4:
+            return AsmJSSimdType_int32x4;
+          case Float32x4:
+            return AsmJSSimdType_float32x4;
+          // Scalar types
+          case Double:
+          case DoubleLit:
+          case MaybeDouble:
+          case Float:
+          case MaybeFloat:
+          case Floatish:
+          case Fixnum:
+          case Int:
+          case Signed:
+          case Unsigned:
+          case Intish:
+          case Void:
+            break;
+        }
+        MOZ_CRASH("not a SIMD Type");
+    }
+
+    const char* toChars() const {
+        switch (which_) {
+          case Double:      return "double";
+          case DoubleLit:   return "doublelit";
+          case MaybeDouble: return "double?";
+          case Float:       return "float";
+          case Floatish:    return "floatish";
+          case MaybeFloat:  return "float?";
+          case Fixnum:      return "fixnum";
+          case Int:         return "int";
+          case Signed:      return "signed";
+          case Unsigned:    return "unsigned";
+          case Intish:      return "intish";
+          case Int32x4:     return "int32x4";
+          case Float32x4:   return "float32x4";
+          case Void:        return "void";
+        }
+        MOZ_CRASH("Invalid Type");
+    }
+};
+
+// Represents the subset of Type that can be used as a variable or
+// argument's type. Note: AsmJSCoercion and VarType are kept separate to
+// make very clear the signed/int distinction: a coercion may explicitly sign
+// an *expression* but, when stored as a variable, this signedness information
+// is explicitly thrown away by the asm.js type system. E.g., in
+//
+//   function f(i) {
+//     i = i | 0;             (1)
+//     if (...)
+//         i = foo() >>> 0;
+//     else
+//         i = bar() | 0;
+//     return i | 0;          (2)
+//   }
+//
+// the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when
+// translated to a VarType, the result is a plain Int since, as shown, it
+// is legal to assign both Signed and Unsigned (or some other Int) values to
+// it. For (2), the AsmJSCoercion is also Signed but, when translated to an
+// RetType, the result is Signed since callers (asm.js and non-asm.js) can
+// rely on the return value being Signed.
+class VarType
+{
+  public:
+    enum Which {
+        Int = Type::Int,
+        Double = Type::Double,
+        Float = Type::Float,
+        Int32x4 = Type::Int32x4,
+        Float32x4 = Type::Float32x4
+    };
+
+  private:
+    Which which_;
 
-    ModuleCompileInputs(jit::CompileCompartment* compartment,
-                        jit::CompileRuntime* runtime,
-                        bool usesSignalHandlersForOOB)
-      : compartment(compartment),
-        runtime(runtime),
-        usesSignalHandlersForOOB(usesSignalHandlersForOOB)
+  public:
+    VarType()
+      : which_(Which(-1)) {}
+    MOZ_IMPLICIT VarType(Which w)
+      : which_(w) {}
+    MOZ_IMPLICIT VarType(AsmJSCoercion coercion) {
+        switch (coercion) {
+          case AsmJS_ToInt32: which_ = Int; break;
+          case AsmJS_ToNumber: which_ = Double; break;
+          case AsmJS_FRound: which_ = Float; break;
+          case AsmJS_ToInt32x4: which_ = Int32x4; break;
+          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
+        }
+    }
+    static VarType Of(const AsmJSNumLit& lit) {
+        MOZ_ASSERT(lit.hasType());
+        switch (lit.which()) {
+          case AsmJSNumLit::Fixnum:
+          case AsmJSNumLit::NegativeInt:
+          case AsmJSNumLit::BigUnsigned:
+            return Int;
+          case AsmJSNumLit::Double:
+            return Double;
+          case AsmJSNumLit::Float:
+            return Float;
+          case AsmJSNumLit::Int32x4:
+            return Int32x4;
+          case AsmJSNumLit::Float32x4:
+            return Float32x4;
+          case AsmJSNumLit::OutOfRangeInt:
+            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("can't be out of range int");
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected literal type");
+    }
+
+    Which which() const {
+        return which_;
+    }
+    Type toType() const {
+        return Type::Which(which_);
+    }
+    jit::MIRType toMIRType() const {
+        switch(which_) {
+          case Int:       return jit::MIRType_Int32;
+          case Double:    return jit::MIRType_Double;
+          case Float:     return jit::MIRType_Float32;
+          case Int32x4:   return jit::MIRType_Int32x4;
+          case Float32x4: return jit::MIRType_Float32x4;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
+    }
+    AsmJSCoercion toCoercion() const {
+        switch(which_) {
+          case Int:       return AsmJS_ToInt32;
+          case Double:    return AsmJS_ToNumber;
+          case Float:     return AsmJS_FRound;
+          case Int32x4:   return AsmJS_ToInt32x4;
+          case Float32x4: return AsmJS_ToFloat32x4;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
+    }
+    static VarType FromCheckedType(Type type) {
+        MOZ_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish() || type.isSimd());
+        if (type.isMaybeDouble())
+            return Double;
+        else if (type.isFloatish())
+            return Float;
+        else if (type.isInt())
+            return Int;
+        else if (type.isInt32x4())
+            return Int32x4;
+        else if (type.isFloat32x4())
+            return Float32x4;
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unknown type in FromCheckedType");
+    }
+    bool operator==(VarType rhs) const { return which_ == rhs.which_; }
+    bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
+};
+
+// Represents the subset of Type that can be used as the return type of a
+// function.
+class RetType
+{
+  public:
+    enum Which {
+        Void      = Type::Void,
+        Signed    = Type::Signed,
+        Double    = Type::Double,
+        Float     = Type::Float,
+        Int32x4   = Type::Int32x4,
+        Float32x4 = Type::Float32x4
+    };
+
+  private:
+    Which which_;
+
+  public:
+    RetType() : which_(Which(-1)) {}
+    MOZ_IMPLICIT RetType(Which w) : which_(w) {}
+    MOZ_IMPLICIT RetType(AsmJSCoercion coercion) {
+        which_ = Which(-1);  // initialize to silence GCC warning
+        switch (coercion) {
+          case AsmJS_ToInt32:     which_ = Signed;    break;
+          case AsmJS_ToNumber:    which_ = Double;    break;
+          case AsmJS_FRound:      which_ = Float;     break;
+          case AsmJS_ToInt32x4:   which_ = Int32x4;   break;
+          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
+        }
+    }
+    Which which() const {
+        return which_;
+    }
+    Type toType() const {
+        return Type::Which(which_);
+    }
+    AsmJSModule::ReturnType toModuleReturnType() const {
+        switch (which_) {
+          case Void:      return AsmJSModule:: Return_Void;
+          case Signed:    return AsmJSModule:: Return_Int32;
+          case Float: // will be converted to a Double
+          case Double:    return AsmJSModule:: Return_Double;
+          case Int32x4:   return AsmJSModule:: Return_Int32x4;
+          case Float32x4: return AsmJSModule:: Return_Float32x4;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
+    }
+    jit::MIRType toMIRType() const {
+        switch (which_) {
+          case Void:      return jit::MIRType_None;
+          case Signed:    return jit::MIRType_Int32;
+          case Double:    return jit::MIRType_Double;
+          case Float:     return jit::MIRType_Float32;
+          case Int32x4:   return jit::MIRType_Int32x4;
+          case Float32x4: return jit::MIRType_Float32x4;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
+    }
+    bool operator==(RetType rhs) const { return which_ == rhs.which_; }
+    bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
+};
+
+inline jit::MIRType ToMIRType(jit::MIRType t) { return t; }
+inline jit::MIRType ToMIRType(VarType t) { return t.toMIRType(); }
+
+template <class VecT>
+class ABIArgIter
+{
+    jit::ABIArgGenerator gen_;
+    const VecT& types_;
+    unsigned i_;
+
+    void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); }
+
+  public:
+    explicit ABIArgIter(const VecT& types) : types_(types), i_(0) { settle(); }
+    void operator++(int) { MOZ_ASSERT(!done()); i_++; settle(); }
+    bool done() const { return i_ == types_.length(); }
+
+    jit::ABIArg* operator->() { MOZ_ASSERT(!done()); return &gen_.current(); }
+    jit::ABIArg& operator*() { MOZ_ASSERT(!done()); return gen_.current(); }
+
+    unsigned index() const { MOZ_ASSERT(!done()); return i_; }
+    jit::MIRType mirType() const { MOZ_ASSERT(!done()); return ToMIRType(types_[i_]); }
+    uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); }
+};
+
+typedef Vector<jit::MIRType, 8> MIRTypeVector;
+typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
+
+typedef Vector<VarType, 8, LifoAllocPolicy<Fallible>> VarTypeVector;
+typedef ABIArgIter<VarTypeVector> ABIArgTypeIter;
+
+class Signature
+{
+    VarTypeVector argTypes_;
+    RetType retType_;
+
+  public:
+    explicit Signature(LifoAlloc& alloc)
+      : argTypes_(alloc) {}
+    Signature(LifoAlloc& alloc, RetType retType)
+      : argTypes_(alloc), retType_(retType) {}
+    Signature(VarTypeVector&& argTypes, RetType retType)
+      : argTypes_(Move(argTypes)), retType_(Move(retType)) {}
+    Signature(Signature&& rhs)
+      : argTypes_(Move(rhs.argTypes_)), retType_(Move(rhs.retType_)) {}
+
+    bool copy(const Signature& rhs) {
+        if (!argTypes_.resize(rhs.argTypes_.length()))
+            return false;
+        for (unsigned i = 0; i < argTypes_.length(); i++)
+            argTypes_[i] = rhs.argTypes_[i];
+        retType_ = rhs.retType_;
+        return true;
+    }
+
+    bool appendArg(VarType type) { return argTypes_.append(type); }
+    VarType arg(unsigned i) const { return argTypes_[i]; }
+    const VarTypeVector& args() const { return argTypes_; }
+    VarTypeVector&& extractArgs() { return Move(argTypes_); }
+
+    RetType retType() const { return retType_; }
+};
+
+// Signature that can be only allocated with a LifoAlloc.
+class LifoSignature : public Signature
+{
+    explicit LifoSignature(Signature&& rhs)
+      : Signature(Move(rhs))
     {}
+
+    LifoSignature(const LifoSignature&) = delete;
+    LifoSignature(const LifoSignature&&) = delete;
+    LifoSignature& operator=(const LifoSignature&) = delete;
+    LifoSignature& operator=(const LifoSignature&&) = delete;
+
+  public:
+    static LifoSignature* new_(LifoAlloc& lifo, Signature&& sig) {
+        void* mem = lifo.alloc(sizeof(LifoSignature));
+        if (!mem)
+            return nullptr;
+        return new (mem) LifoSignature(Move(sig));
+    }
+};
+
+enum class Stmt : uint8_t {
+    Ret,
+
+    Block,
+
+    IfThen,
+    IfElse,
+    Switch,
+
+    While,
+    DoWhile,
+
+    ForInitInc,
+    ForInitNoInc,
+    ForNoInitNoInc,
+    ForNoInitInc,
+
+    Label,
+    Continue,
+    ContinueLabel,
+    Break,
+    BreakLabel,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    AtomicsFence,
+
+    // asm.js specific
+    // Expression statements (to be removed in the future)
+    I32Expr,
+    F32Expr,
+    F64Expr,
+    I32X4Expr,
+    F32X4Expr,
+
+    Id,
+    Noop,
+    InterruptCheckHead,
+    InterruptCheckLoop,
+
+    DebugCheckPoint,
+
+    Bad
 };
 
-class MOZ_RAII AsmModuleCompilerScope
-{
-    ModuleCompiler* m_;
+enum class I32 : uint8_t {
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+    GetGlobal,
+    SetGlobal,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Binary arith opcodes
+    Add,
+    Sub,
+    Mul,
+    SDiv,
+    SMod,
+    UDiv,
+    UMod,
+    Min,
+    Max,
+
+    // Unary arith opcodes
+    Not,
+    Neg,
+
+    // Bitwise opcodes
+    BitOr,
+    BitAnd,
+    BitXor,
+    BitNot,
+
+    Lsh,
+    ArithRsh,
+    LogicRsh,
+
+    // Conversion opcodes
+    FromF32,
+    FromF64,
+
+    // Math builtin opcodes
+    Clz,
+    Abs,
+
+    // Comparison opcodes
+    // Ordering matters (EmitComparison expects signed opcodes to be placed
+    // before unsigned opcodes)
+    EqI32,
+    NeI32,
+    SLtI32,
+    SLeI32,
+    SGtI32,
+    SGeI32,
+    ULtI32,
+    ULeI32,
+    UGtI32,
+    UGeI32,
+
+    EqF32,
+    NeF32,
+    LtF32,
+    LeF32,
+    GtF32,
+    GeF32,
+
+    EqF64,
+    NeF64,
+    LtF64,
+    LeF64,
+    GtF64,
+    GeF64,
+
+    // Heap accesses opcodes
+    SLoad8,
+    SLoad16,
+    SLoad32,
+    ULoad8,
+    ULoad16,
+    ULoad32,
+    Store8,
+    Store16,
+    Store32,
+
+    // Atomics opcodes
+    AtomicsCompareExchange,
+    AtomicsExchange,
+    AtomicsLoad,
+    AtomicsStore,
+    AtomicsBinOp,
+
+    // SIMD opcodes
+    I32X4SignMask,
+    F32X4SignMask,
+
+    I32X4ExtractLane,
+
+    // Specific to AsmJS
+    Id,
+
+    Bad
+};
+
+enum class F32 : uint8_t {
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+    GetGlobal,
+    SetGlobal,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Binary arith opcodes
+    Add,
+    Sub,
+    Mul,
+    Div,
+    Min,
+    Max,
+    Neg,
+
+    // Math builtin opcodes
+    Abs,
+    Sqrt,
+    Ceil,
+    Floor,
+
+    // Conversion opcodes
+    FromF64,
+    FromS32,
+    FromU32,
+
+    // Heap accesses opcodes
+    Load,
+    StoreF32,
+    StoreF64,
+
+    // SIMD opcodes
+    F32X4ExtractLane,
+
+    // asm.js specific
+    Id,
+    Bad
+};
+
+enum class F64 : uint8_t {
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+    GetGlobal,
+    SetGlobal,
 
-    AsmModuleCompilerScope(const AsmModuleCompilerScope&) = delete;
-    AsmModuleCompilerScope(const AsmModuleCompilerScope&&) = delete;
-    AsmModuleCompilerScope& operator=(const AsmModuleCompilerScope&&) = delete;
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Binary arith opcodes
+    Add,
+    Sub,
+    Mul,
+    Div,
+    Min,
+    Max,
+    Mod,
+    Neg,
+
+    // Math builtin opcodes
+    Abs,
+    Sqrt,
+    Ceil,
+    Floor,
+    Sin,
+    Cos,
+    Tan,
+    Asin,
+    Acos,
+    Atan,
+    Exp,
+    Log,
+    Pow,
+    Atan2,
+
+    // Conversions opcodes
+    FromF32,
+    FromS32,
+    FromU32,
+
+    // Heap accesses opcodes
+    Load,
+    StoreF32,
+    StoreF64,
+
+    // asm.js specific
+    Id,
+    Bad
+};
+
+enum class I32X4 : uint8_t {
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+
+    GetGlobal,
+    SetGlobal,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Specific opcodes
+    Ctor,
+
+    Unary,
+
+    Binary,
+    BinaryCompI32X4,
+    BinaryCompF32X4,
+    BinaryBitwise,
+    BinaryShift,
+
+    ReplaceLane,
+
+    FromF32X4,
+    FromF32X4Bits,
+
+    Swizzle,
+    Shuffle,
+    Select,
+    BitSelect,
+    Splat,
+
+    Load,
+    Store,
+
+    // asm.js specific
+    Id,
+    Bad
+};
+
+enum class F32X4 : uint8_t {
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+
+    GetGlobal,
+    SetGlobal,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Specific opcodes
+    Ctor,
+
+    Unary,
+
+    Binary,
+    BinaryBitwise,
+
+    ReplaceLane,
+
+    FromI32X4,
+    FromI32X4Bits,
+    Swizzle,
+    Shuffle,
+    Select,
+    BitSelect,
+    Splat,
+
+    Load,
+    Store,
+
+    // asm.js specific
+    Id,
+    Bad
+};
+
+} // namespace wasm
+
+class AsmFunction
+{
+  public:
+    typedef Vector<AsmJSNumLit, 8, LifoAllocPolicy<Fallible>> VarInitializerVector;
+
+  private:
+    typedef Vector<uint8_t, 4096, LifoAllocPolicy<Fallible>> Bytecode;
+
+    VarInitializerVector varInitializers_;
+    Bytecode bytecode_;
+
+    wasm::VarTypeVector argTypes_;
+    wasm::RetType returnedType_;
+
+    PropertyName* name_;
+
+    unsigned funcIndex_;
+    unsigned srcBegin_;
+    unsigned lineno_;
+    unsigned column_;
+    unsigned parseTime_;
 
   public:
-    AsmModuleCompilerScope()
-      : m_(nullptr)
+    explicit AsmFunction(LifoAlloc& alloc)
+      : varInitializers_(alloc),
+        bytecode_(alloc),
+        argTypes_(alloc),
+        returnedType_(wasm::RetType::Which(-1)),
+        name_(nullptr),
+        funcIndex_(-1),
+        srcBegin_(-1),
+        lineno_(-1),
+        column_(-1),
+        parseTime_(-1)
     {}
 
-    void setModule(ModuleCompiler* m) {
-        MOZ_ASSERT(m);
-        m_ = m;
+    bool init(const wasm::VarTypeVector& args) {
+        if (!argTypes_.initCapacity(args.length()))
+            return false;
+        for (size_t i = 0; i < args.length(); i++)
+            argTypes_.append(args[i]);
+        return true;
+    }
+
+    bool finish(const wasm::VarTypeVector& args, PropertyName* name, unsigned funcIndex,
+                unsigned srcBegin, unsigned lineno, unsigned column, unsigned parseTime)
+    {
+        if (!argTypes_.initCapacity(args.length()))
+            return false;
+        for (size_t i = 0; i < args.length(); i++)
+            argTypes_.infallibleAppend(args[i]);
+
+        MOZ_ASSERT(name_ == nullptr);
+        name_ = name;
+
+        MOZ_ASSERT(funcIndex_ == unsigned(-1));
+        funcIndex_ = funcIndex;
+
+        MOZ_ASSERT(srcBegin_ == unsigned(-1));
+        srcBegin_ = srcBegin;
+
+        MOZ_ASSERT(lineno_ == unsigned(-1));
+        lineno_ = lineno;
+
+        MOZ_ASSERT(column_ == unsigned(-1));
+        column_ = column;
+
+        MOZ_ASSERT(parseTime_ == unsigned(-1));
+        parseTime_ = parseTime;
+        return true;
+    }
+
+  private:
+    AsmFunction(const AsmFunction&) = delete;
+    AsmFunction(AsmFunction&& other) = delete;
+    AsmFunction& operator=(const AsmFunction&) = delete;
+
+    // Helper functions
+    template<class T> size_t writePrimitive(T v) {
+        size_t writeAt = bytecode_.length();
+        if (!bytecode_.append(reinterpret_cast<uint8_t*>(&v), sizeof(T)))
+            return -1;
+        return writeAt;
+    }
+
+    template<class T> T readPrimitive(size_t* pc) const {
+        MOZ_ASSERT(*pc + sizeof(T) <= bytecode_.length());
+        T ret;
+        memcpy(&ret, &bytecode_[*pc], sizeof(T));
+        *pc += sizeof(T);
+        return ret;
+    }
+
+  public:
+    size_t writeU8(uint8_t i)   { return writePrimitive<uint8_t>(i); }
+    size_t writeI32(int32_t i)  { return writePrimitive<int32_t>(i); }
+    size_t writeU32(uint32_t i) { return writePrimitive<uint32_t>(i); }
+    size_t writeF32(float f)    { return writePrimitive<float>(f); }
+    size_t writeF64(double d)   { return writePrimitive<double>(d); }
+
+    size_t writeI32X4(const int32_t* i4) {
+        size_t pos = bytecode_.length();
+        for (size_t i = 0; i < 4; i++)
+            writePrimitive<int32_t>(i4[i]);
+        return pos;
+    }
+    size_t writeF32X4(const float* f4) {
+        size_t pos = bytecode_.length();
+        for (size_t i = 0; i < 4; i++)
+            writePrimitive<float>(f4[i]);
+        return pos;
+    }
+
+    uint8_t  readU8 (size_t* pc) const { return readPrimitive<uint8_t>(pc); }
+    int32_t  readI32(size_t* pc) const { return readPrimitive<int32_t>(pc); }
+    float    readF32(size_t* pc) const { return readPrimitive<float>(pc); }
+    uint32_t readU32(size_t* pc) const { return readPrimitive<uint32_t>(pc); }
+    double   readF64(size_t* pc) const { return readPrimitive<double>(pc); }
+    wasm::LifoSignature* readSignature(size_t* pc) const { return readPrimitive<wasm::LifoSignature*>(pc); }
+
+    jit::SimdConstant readI32X4(size_t* pc) const {
+        int32_t x = readI32(pc);
+        int32_t y = readI32(pc);
+        int32_t z = readI32(pc);
+        int32_t w = readI32(pc);
+        return jit::SimdConstant::CreateX4(x, y, z, w);
+    }
+    jit::SimdConstant readF32X4(size_t* pc) const {
+        float x = readF32(pc);
+        float y = readF32(pc);
+        float z = readF32(pc);
+        float w = readF32(pc);
+        return jit::SimdConstant::CreateX4(x, y, z, w);
     }
 
-    ModuleCompiler& module() const {
-        MOZ_ASSERT(m_);
-        return *m_;
+#ifdef DEBUG
+    bool pcIsPatchable(size_t pc, unsigned size) const {
+        bool patchable = true;
+        for (unsigned i = 0; patchable && i < size; i++)
+            patchable &= wasm::Stmt(bytecode_[pc]) == wasm::Stmt::Bad;
+        return patchable;
+    }
+#endif // DEBUG
+
+    void patchU8(size_t pc, uint8_t i) {
+        MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
+        bytecode_[pc] = i;
+    }
+
+    template<class T>
+    void patch32(size_t pc, T i) {
+        static_assert(sizeof(T) == sizeof(uint32_t),
+                      "patch32 must be used with 32-bits wide types");
+        MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t)));
+        memcpy(&bytecode_[pc], &i, sizeof(uint32_t));
+    }
+
+    void patchSignature(size_t pc, const wasm::LifoSignature* ptr) {
+        MOZ_ASSERT(pcIsPatchable(pc, sizeof(wasm::LifoSignature*)));
+        memcpy(&bytecode_[pc], &ptr, sizeof(wasm::LifoSignature*));
+    }
+
+    // Setters
+    bool addVariable(const AsmJSNumLit& init) {
+        return varInitializers_.append(init);
+    }
+    void setReturnedType(wasm::RetType retType) {
+        MOZ_ASSERT(returnedType_ == wasm::RetType::Which(-1));
+        returnedType_ = retType;
     }
 
-    ~AsmModuleCompilerScope();
+    // Read-only interface
+    PropertyName* name() const { return name_; }
+    unsigned funcIndex() const { return funcIndex_; }
+    unsigned srcBegin() const { return srcBegin_; }
+    unsigned lineno() const { return lineno_; }
+    unsigned column() const { return column_; }
+    unsigned parseTime() const { return parseTime_; }
+
+    size_t size() const { return bytecode_.length(); }
+
+    const wasm::VarTypeVector& argTypes() const { return argTypes_; }
+
+    const VarInitializerVector& varInitializers() const { return varInitializers_; }
+    size_t numLocals() const { return argTypes_.length() + varInitializers_.length(); }
+    wasm::RetType returnedType() const {
+        MOZ_ASSERT(returnedType_ != wasm::RetType::Which(-1));
+        return returnedType_;
+    }
 };
 
-bool CreateAsmModuleCompiler(ModuleCompileInputs mci, AsmModuleCompilerScope* scope);
-bool GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func, jit::MIRGenerator** mir);
-bool GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, jit::MIRGenerator& mir,
-                             jit::LIRGraph& lir, FunctionCompileResults* results);
-void FinishAsmModuleCompilation(ModuleCompiler& m, ScopedJSDeletePtr<ModuleCompileResults>* results);
+class FunctionCompileResults
+{
+    jit::MacroAssembler masm_;
+
+    jit::IonScriptCounts* counts_;
+    AsmJSModule::FunctionCodeRange codeRange_;
+    unsigned compileTime_;
+
+  public:
+    FunctionCompileResults()
+      : masm_(jit::MacroAssembler::AsmJSToken()),
+        counts_(nullptr),
+        codeRange_(),
+        compileTime_(-1)
+    {}
+
+    const jit::MacroAssembler& masm() const { return masm_; }
+    jit::MacroAssembler& masm() { return masm_; }
+
+    const AsmJSModule::FunctionCodeRange& codeRange() const { return codeRange_; }
+    jit::IonScriptCounts* counts() const { return counts_; }
+
+    void finishCodegen(AsmJSModule::FunctionCodeRange codeRange, jit::IonScriptCounts& counts) {
+        codeRange_ = codeRange;
+        counts_ = &counts;
+    }
+    void setCompileTime(unsigned ms) {
+        MOZ_ASSERT(compileTime_ == unsigned(-1));
+        compileTime_ = ms;
+    }
+    unsigned compileTime() const {
+        return compileTime_;
+    }
+};
+
+struct AsmJSParallelTask
+{
+    mozilla::Maybe<FunctionCompileResults> results;
+    LifoAlloc lifo;                         // Provider of all heap memory used for compilation.
+    ModuleCompileInputs inputs;
+    JSRuntime* runtime;                     // Associated runtime.
+    AsmFunction* func;
+
+    AsmJSParallelTask(size_t defaultChunkSize, js::ModuleCompileInputs inputs)
+      : results(),
+        lifo(defaultChunkSize),
+        inputs(inputs),
+        runtime(nullptr),
+        func(nullptr)
+    { }
+
+    void init(JSRuntime* rt, js::AsmFunction* func) {
+        this->runtime = rt;
+        this->func = func;
+        results.emplace();
+    }
+};
+
+bool CompileAsmFunction(LifoAlloc& lifo, ModuleCompileInputs inputs, const AsmFunction& func,
+                        FunctionCompileResults* results);
 
 } // namespace js
 
-#endif // jit_AsmJSCompile_h
+#endif //jit_AsmJSCompile_h
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/AsmJSCompileInputs.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef jit_AsmJSCompileInputs_h
+#define jit_AsmJSCompileInputs_h
+
+namespace js {
+
+namespace jit {
+    class CompileCompartment;
+    class CompileRuntime;
+}
+
+struct ModuleCompileInputs
+{
+    jit::CompileCompartment* compartment;
+    jit::CompileRuntime* runtime;
+    bool usesSignalHandlersForOOB;
+
+    ModuleCompileInputs(jit::CompileCompartment* compartment,
+                        jit::CompileRuntime* runtime,
+                        bool usesSignalHandlersForOOB)
+      : compartment(compartment),
+        runtime(runtime),
+        usesSignalHandlersForOOB(usesSignalHandlersForOOB)
+    {}
+};
+
+} // namespace js
+
+#endif //jit_AsmJSCompileInputs_h
deleted file mode 100644
--- a/js/src/asmjs/AsmJSGlobals.h
+++ /dev/null
@@ -1,1108 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2015 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef jit_AsmJSGlobals_h
-#define jit_AsmJSGlobals_h
-
-#include "asmjs/AsmJSModule.h"
-
-namespace js {
-namespace wasm {
-
-enum NeedsBoundsCheck {
-    NO_BOUNDS_CHECK,
-    NEEDS_BOUNDS_CHECK
-};
-
-// Respresents the type of a general asm.js expression.
-class Type
-{
-  public:
-    enum Which {
-        Fixnum = AsmJSNumLit::Fixnum,
-        Signed = AsmJSNumLit::NegativeInt,
-        Unsigned = AsmJSNumLit::BigUnsigned,
-        DoubleLit = AsmJSNumLit::Double,
-        Float = AsmJSNumLit::Float,
-        Int32x4 = AsmJSNumLit::Int32x4,
-        Float32x4 = AsmJSNumLit::Float32x4,
-        Double,
-        MaybeDouble,
-        MaybeFloat,
-        Floatish,
-        Int,
-        Intish,
-        Void
-    };
-
-  private:
-    Which which_;
-
-  public:
-    Type() : which_(Which(-1)) {}
-    static Type Of(const AsmJSNumLit& lit) {
-        MOZ_ASSERT(lit.hasType());
-        Which which = Type::Which(lit.which());
-        MOZ_ASSERT(which >= Fixnum && which <= Float32x4);
-        Type t;
-        t.which_ = which;
-        return t;
-    }
-    MOZ_IMPLICIT Type(Which w) : which_(w) {}
-    Which which() const { return which_; }
-    MOZ_IMPLICIT Type(AsmJSSimdType type) {
-        switch (type) {
-          case AsmJSSimdType_int32x4:
-            which_ = Int32x4;
-            return;
-          case AsmJSSimdType_float32x4:
-            which_ = Float32x4;
-            return;
-        }
-        MOZ_CRASH("unexpected AsmJSSimdType");
-    }
-
-    bool operator==(Type rhs) const { return which_ == rhs.which_; }
-    bool operator!=(Type rhs) const { return which_ != rhs.which_; }
-
-    inline bool operator<=(Type rhs) const {
-        switch (rhs.which_) {
-          case Signed:      return isSigned();
-          case Unsigned:    return isUnsigned();
-          case DoubleLit:   return isDoubleLit();
-          case Double:      return isDouble();
-          case Float:       return isFloat();
-          case Int32x4:     return isInt32x4();
-          case Float32x4:   return isFloat32x4();
-          case MaybeDouble: return isMaybeDouble();
-          case MaybeFloat:  return isMaybeFloat();
-          case Floatish:    return isFloatish();
-          case Int:         return isInt();
-          case Intish:      return isIntish();
-          case Fixnum:      return isFixnum();
-          case Void:        return isVoid();
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected this type");
-    }
-
-    bool isFixnum() const {
-        return which_ == Fixnum;
-    }
-
-    bool isSigned() const {
-        return which_ == Signed || which_ == Fixnum;
-    }
-
-    bool isUnsigned() const {
-        return which_ == Unsigned || which_ == Fixnum;
-    }
-
-    bool isInt() const {
-        return isSigned() || isUnsigned() || which_ == Int;
-    }
-
-    bool isIntish() const {
-        return isInt() || which_ == Intish;
-    }
-
-    bool isDoubleLit() const {
-        return which_ == DoubleLit;
-    }
-
-    bool isDouble() const {
-        return isDoubleLit() || which_ == Double;
-    }
-
-    bool isMaybeDouble() const {
-        return isDouble() || which_ == MaybeDouble;
-    }
-
-    bool isFloat() const {
-        return which_ == Float;
-    }
-
-    bool isMaybeFloat() const {
-        return isFloat() || which_ == MaybeFloat;
-    }
-
-    bool isFloatish() const {
-        return isMaybeFloat() || which_ == Floatish;
-    }
-
-    bool isVoid() const {
-        return which_ == Void;
-    }
-
-    bool isExtern() const {
-        return isDouble() || isSigned();
-    }
-
-    bool isInt32x4() const {
-        return which_ == Int32x4;
-    }
-
-    bool isFloat32x4() const {
-        return which_ == Float32x4;
-    }
-
-    bool isSimd() const {
-        return isInt32x4() || isFloat32x4();
-    }
-
-    bool isVarType() const {
-        return isInt() || isDouble() || isFloat() || isSimd();
-    }
-
-    jit::MIRType toMIRType() const {
-        switch (which_) {
-          case Double:
-          case DoubleLit:
-          case MaybeDouble:
-            return jit::MIRType_Double;
-          case Float:
-          case Floatish:
-          case MaybeFloat:
-            return jit::MIRType_Float32;
-          case Fixnum:
-          case Int:
-          case Signed:
-          case Unsigned:
-          case Intish:
-            return jit::MIRType_Int32;
-          case Int32x4:
-            return jit::MIRType_Int32x4;
-          case Float32x4:
-            return jit::MIRType_Float32x4;
-          case Void:
-            return jit::MIRType_None;
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
-    }
-
-    AsmJSSimdType simdType() const {
-        MOZ_ASSERT(isSimd());
-        switch (which_) {
-          case Int32x4:
-            return AsmJSSimdType_int32x4;
-          case Float32x4:
-            return AsmJSSimdType_float32x4;
-          // Scalar types
-          case Double:
-          case DoubleLit:
-          case MaybeDouble:
-          case Float:
-          case MaybeFloat:
-          case Floatish:
-          case Fixnum:
-          case Int:
-          case Signed:
-          case Unsigned:
-          case Intish:
-          case Void:
-            break;
-        }
-        MOZ_CRASH("not a SIMD Type");
-    }
-
-    const char* toChars() const {
-        switch (which_) {
-          case Double:      return "double";
-          case DoubleLit:   return "doublelit";
-          case MaybeDouble: return "double?";
-          case Float:       return "float";
-          case Floatish:    return "floatish";
-          case MaybeFloat:  return "float?";
-          case Fixnum:      return "fixnum";
-          case Int:         return "int";
-          case Signed:      return "signed";
-          case Unsigned:    return "unsigned";
-          case Intish:      return "intish";
-          case Int32x4:     return "int32x4";
-          case Float32x4:   return "float32x4";
-          case Void:        return "void";
-        }
-        MOZ_CRASH("Invalid Type");
-    }
-};
-
-// Represents the subset of Type that can be used as a variable or
-// argument's type. Note: AsmJSCoercion and VarType are kept separate to
-// make very clear the signed/int distinction: a coercion may explicitly sign
-// an *expression* but, when stored as a variable, this signedness information
-// is explicitly thrown away by the asm.js type system. E.g., in
-//
-//   function f(i) {
-//     i = i | 0;             (1)
-//     if (...)
-//         i = foo() >>> 0;
-//     else
-//         i = bar() | 0;
-//     return i | 0;          (2)
-//   }
-//
-// the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when
-// translated to a VarType, the result is a plain Int since, as shown, it
-// is legal to assign both Signed and Unsigned (or some other Int) values to
-// it. For (2), the AsmJSCoercion is also Signed but, when translated to an
-// RetType, the result is Signed since callers (asm.js and non-asm.js) can
-// rely on the return value being Signed.
-class VarType
-{
-  public:
-    enum Which {
-        Int = Type::Int,
-        Double = Type::Double,
-        Float = Type::Float,
-        Int32x4 = Type::Int32x4,
-        Float32x4 = Type::Float32x4
-    };
-
-  private:
-    Which which_;
-
-  public:
-    VarType()
-      : which_(Which(-1)) {}
-    MOZ_IMPLICIT VarType(Which w)
-      : which_(w) {}
-    MOZ_IMPLICIT VarType(AsmJSCoercion coercion) {
-        switch (coercion) {
-          case AsmJS_ToInt32: which_ = Int; break;
-          case AsmJS_ToNumber: which_ = Double; break;
-          case AsmJS_FRound: which_ = Float; break;
-          case AsmJS_ToInt32x4: which_ = Int32x4; break;
-          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
-        }
-    }
-    static VarType Of(const AsmJSNumLit& lit) {
-        MOZ_ASSERT(lit.hasType());
-        switch (lit.which()) {
-          case AsmJSNumLit::Fixnum:
-          case AsmJSNumLit::NegativeInt:
-          case AsmJSNumLit::BigUnsigned:
-            return Int;
-          case AsmJSNumLit::Double:
-            return Double;
-          case AsmJSNumLit::Float:
-            return Float;
-          case AsmJSNumLit::Int32x4:
-            return Int32x4;
-          case AsmJSNumLit::Float32x4:
-            return Float32x4;
-          case AsmJSNumLit::OutOfRangeInt:
-            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("can't be out of range int");
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected literal type");
-    }
-
-    Which which() const {
-        return which_;
-    }
-    Type toType() const {
-        return Type::Which(which_);
-    }
-    jit::MIRType toMIRType() const {
-        switch(which_) {
-          case Int:       return jit::MIRType_Int32;
-          case Double:    return jit::MIRType_Double;
-          case Float:     return jit::MIRType_Float32;
-          case Int32x4:   return jit::MIRType_Int32x4;
-          case Float32x4: return jit::MIRType_Float32x4;
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
-    }
-    AsmJSCoercion toCoercion() const {
-        switch(which_) {
-          case Int:       return AsmJS_ToInt32;
-          case Double:    return AsmJS_ToNumber;
-          case Float:     return AsmJS_FRound;
-          case Int32x4:   return AsmJS_ToInt32x4;
-          case Float32x4: return AsmJS_ToFloat32x4;
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
-    }
-    static VarType FromCheckedType(Type type) {
-        MOZ_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish() || type.isSimd());
-        if (type.isMaybeDouble())
-            return Double;
-        else if (type.isFloatish())
-            return Float;
-        else if (type.isInt())
-            return Int;
-        else if (type.isInt32x4())
-            return Int32x4;
-        else if (type.isFloat32x4())
-            return Float32x4;
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unknown type in FromCheckedType");
-    }
-    bool operator==(VarType rhs) const { return which_ == rhs.which_; }
-    bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
-};
-
-// Represents the subset of Type that can be used as the return type of a
-// function.
-class RetType
-{
-  public:
-    enum Which {
-        Void      = Type::Void,
-        Signed    = Type::Signed,
-        Double    = Type::Double,
-        Float     = Type::Float,
-        Int32x4   = Type::Int32x4,
-        Float32x4 = Type::Float32x4
-    };
-
-  private:
-    Which which_;
-
-  public:
-    RetType() : which_(Which(-1)) {}
-    MOZ_IMPLICIT RetType(Which w) : which_(w) {}
-    MOZ_IMPLICIT RetType(AsmJSCoercion coercion) {
-        which_ = Which(-1);  // initialize to silence GCC warning
-        switch (coercion) {
-          case AsmJS_ToInt32:     which_ = Signed;    break;
-          case AsmJS_ToNumber:    which_ = Double;    break;
-          case AsmJS_FRound:      which_ = Float;     break;
-          case AsmJS_ToInt32x4:   which_ = Int32x4;   break;
-          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
-        }
-    }
-    Which which() const {
-        return which_;
-    }
-    Type toType() const {
-        return Type::Which(which_);
-    }
-    AsmJSModule::ReturnType toModuleReturnType() const {
-        switch (which_) {
-          case Void:      return AsmJSModule:: Return_Void;
-          case Signed:    return AsmJSModule:: Return_Int32;
-          case Float: // will be converted to a Double
-          case Double:    return AsmJSModule:: Return_Double;
-          case Int32x4:   return AsmJSModule:: Return_Int32x4;
-          case Float32x4: return AsmJSModule:: Return_Float32x4;
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
-    }
-    jit::MIRType toMIRType() const {
-        switch (which_) {
-          case Void:      return jit::MIRType_None;
-          case Signed:    return jit::MIRType_Int32;
-          case Double:    return jit::MIRType_Double;
-          case Float:     return jit::MIRType_Float32;
-          case Int32x4:   return jit::MIRType_Int32x4;
-          case Float32x4: return jit::MIRType_Float32x4;
-        }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
-    }
-    bool operator==(RetType rhs) const { return which_ == rhs.which_; }
-    bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
-};
-
-inline jit::MIRType ToMIRType(jit::MIRType t) { return t; }
-inline jit::MIRType ToMIRType(VarType t) { return t.toMIRType(); }
-
-template <class VecT>
-class ABIArgIter
-{
-    jit::ABIArgGenerator gen_;
-    const VecT& types_;
-    unsigned i_;
-
-    void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); }
-
-  public:
-    explicit ABIArgIter(const VecT& types) : types_(types), i_(0) { settle(); }
-    void operator++(int) { MOZ_ASSERT(!done()); i_++; settle(); }
-    bool done() const { return i_ == types_.length(); }
-
-    jit::ABIArg* operator->() { MOZ_ASSERT(!done()); return &gen_.current(); }
-    jit::ABIArg& operator*() { MOZ_ASSERT(!done()); return gen_.current(); }
-
-    unsigned index() const { MOZ_ASSERT(!done()); return i_; }
-    jit::MIRType mirType() const { MOZ_ASSERT(!done()); return ToMIRType(types_[i_]); }
-    uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); }
-};
-
-typedef Vector<jit::MIRType, 8> MIRTypeVector;
-typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
-
-typedef Vector<VarType, 8, LifoAllocPolicy<Fallible>> VarTypeVector;
-typedef ABIArgIter<VarTypeVector> ABIArgTypeIter;
-
-class Signature
-{
-    VarTypeVector argTypes_;
-    RetType retType_;
-
-  public:
-    explicit Signature(LifoAlloc& alloc)
-      : argTypes_(alloc) {}
-    Signature(LifoAlloc& alloc, RetType retType)
-      : argTypes_(alloc), retType_(retType) {}
-    Signature(VarTypeVector&& argTypes, RetType retType)
-      : argTypes_(Move(argTypes)), retType_(Move(retType)) {}
-    Signature(Signature&& rhs)
-      : argTypes_(Move(rhs.argTypes_)), retType_(Move(rhs.retType_)) {}
-
-    bool copy(const Signature& rhs) {
-        if (!argTypes_.resize(rhs.argTypes_.length()))
-            return false;
-        for (unsigned i = 0; i < argTypes_.length(); i++)
-            argTypes_[i] = rhs.argTypes_[i];
-        retType_ = rhs.retType_;
-        return true;
-    }
-
-    bool appendArg(VarType type) { return argTypes_.append(type); }
-    VarType arg(unsigned i) const { return argTypes_[i]; }
-    const VarTypeVector& args() const { return argTypes_; }
-    VarTypeVector&& extractArgs() { return Move(argTypes_); }
-
-    RetType retType() const { return retType_; }
-};
-
-// Signature that can be only allocated with a LifoAlloc.
-class LifoSignature : public Signature
-{
-    explicit LifoSignature(Signature&& rhs)
-      : Signature(Move(rhs))
-    {}
-
-    LifoSignature(const LifoSignature&) = delete;
-    LifoSignature(const LifoSignature&&) = delete;
-    LifoSignature& operator=(const LifoSignature&) = delete;
-    LifoSignature& operator=(const LifoSignature&&) = delete;
-
-  public:
-    static LifoSignature* new_(LifoAlloc& lifo, Signature&& sig) {
-        void* mem = lifo.alloc(sizeof(LifoSignature));
-        if (!mem)
-            return nullptr;
-        return new (mem) LifoSignature(Move(sig));
-    }
-};
-
-enum class Stmt : uint8_t {
-    Ret,
-
-    Block,
-
-    IfThen,
-    IfElse,
-    Switch,
-
-    While,
-    DoWhile,
-
-    ForInitInc,
-    ForInitNoInc,
-    ForNoInitNoInc,
-    ForNoInitInc,
-
-    Label,
-    Continue,
-    ContinueLabel,
-    Break,
-    BreakLabel,
-
-    CallInternal,
-    CallIndirect,
-    CallImport,
-
-    AtomicsFence,
-
-    // asm.js specific
-    // Expression statements (to be removed in the future)
-    I32Expr,
-    F32Expr,
-    F64Expr,
-    I32X4Expr,
-    F32X4Expr,
-
-    Id,
-    Noop,
-    InterruptCheckHead,
-    InterruptCheckLoop,
-
-    DebugCheckPoint,
-
-    Bad
-};
-
-enum class I32 : uint8_t {
-    // Common opcodes
-    GetLocal,
-    SetLocal,
-    GetGlobal,
-    SetGlobal,
-
-    CallInternal,
-    CallIndirect,
-    CallImport,
-
-    Conditional,
-    Comma,
-
-    Literal,
-
-    // Binary arith opcodes
-    Add,
-    Sub,
-    Mul,
-    SDiv,
-    SMod,
-    UDiv,
-    UMod,
-    Min,
-    Max,
-
-    // Unary arith opcodes
-    Not,
-    Neg,
-
-    // Bitwise opcodes
-    BitOr,
-    BitAnd,
-    BitXor,
-    BitNot,
-
-    Lsh,
-    ArithRsh,
-    LogicRsh,
-
-    // Conversion opcodes
-    FromF32,
-    FromF64,
-
-    // Math builtin opcodes
-    Clz,
-    Abs,
-
-    // Comparison opcodes
-    // Ordering matters (EmitComparison expects signed opcodes to be placed
-    // before unsigned opcodes)
-    EqI32,
-    NeI32,
-    SLtI32,
-    SLeI32,
-    SGtI32,
-    SGeI32,
-    ULtI32,
-    ULeI32,
-    UGtI32,
-    UGeI32,
-
-    EqF32,
-    NeF32,
-    LtF32,
-    LeF32,
-    GtF32,
-    GeF32,
-
-    EqF64,
-    NeF64,
-    LtF64,
-    LeF64,
-    GtF64,
-    GeF64,
-
-    // Heap accesses opcodes
-    SLoad8,
-    SLoad16,
-    SLoad32,
-    ULoad8,
-    ULoad16,
-    ULoad32,
-    Store8,
-    Store16,
-    Store32,
-
-    // Atomics opcodes
-    AtomicsCompareExchange,
-    AtomicsExchange,
-    AtomicsLoad,
-    AtomicsStore,
-    AtomicsBinOp,
-
-    // SIMD opcodes
-    I32X4SignMask,
-    F32X4SignMask,
-
-    I32X4ExtractLane,
-
-    // Specific to AsmJS
-    Id,
-
-    Bad
-};
-
-enum class F32 : uint8_t {
-    // Common opcodes
-    GetLocal,
-    SetLocal,
-    GetGlobal,
</