Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 18 Dec 2015 15:30:53 +0100
changeset 276968 718acb0e060cecd1d630436d303fdacc0e665431
parent 276967 10c3c8d58dfa338f2ec0fc6a3a56da13babe263c (current diff)
parent 276933 c5cb194cc9cb56d742fb3a7a826f0080b0404edc (diff)
child 276969 6b0936bf07f1f4110864b60779e7ae049edc0007
push id16724
push usercbook@mozilla.com
push dateMon, 21 Dec 2015 11:00:52 +0000
treeherderfx-team@3f3f0361567c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone46.0a1
Merge mozilla-central to b2g-inbound
toolkit/themes/windows/global/tree/twisty-clsd-XP.png
toolkit/themes/windows/global/tree/twisty-clsd-hover-rtl.png
toolkit/themes/windows/global/tree/twisty-clsd-hover.png
toolkit/themes/windows/global/tree/twisty-clsd-rtl.png
toolkit/themes/windows/global/tree/twisty-clsd.png
toolkit/themes/windows/global/tree/twisty-open-XP.png
toolkit/themes/windows/global/tree/twisty-open-hover-rtl.png
toolkit/themes/windows/global/tree/twisty-open-hover.png
toolkit/themes/windows/global/tree/twisty-open-rtl.png
toolkit/themes/windows/global/tree/twisty-open.png
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -10,18 +10,18 @@
 "size": 12057960,
 "digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"size": 73029932,
-"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
+"size": 193213220,
+"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5192,35 +5192,40 @@ var TabletModeUpdater = {
     } else {
       document.documentElement.removeAttribute("tabletmode");
     }
     TabsInTitlebar.updateAppearance(true);
   },
 };
 
 var gTabletModePageCounter = {
+  enabled: false,
   inc() {
-    if (!AppConstants.isPlatformAndVersionAtLeast("win", "10.0")) {
+    this.enabled = AppConstants.isPlatformAndVersionAtLeast("win", "10.0");
+    if (!this.enabled) {
       this.inc = () => {};
       return;
     }
     this.inc = this._realInc;
     this.inc();
   },
 
   _desktopCount: 0,
   _tabletCount: 0,
   _realInc() {
     let inTabletMode = document.documentElement.hasAttribute("tabletmode");
     this[inTabletMode ? "_tabletCount" : "_desktopCount"]++;
   },
 
   finish() {
-    Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("tablet", this._tabletCount);
-    Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("desktop", this._desktopCount);
+    if (this.enabled) {
+      let histogram = Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD");
+      histogram.add("tablet", this._tabletCount);
+      histogram.add("desktop", this._desktopCount);
+    }
   },
 };
 
 function displaySecurityInfo()
 {
   BrowserPageInfo(null, "securityTab");
 }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4686,16 +4686,19 @@
           if (this._lastTab)
             this._lastTab.removeAttribute("last-visible-tab");
           this._lastTab = visibleTabs[lastVisible];
           this._lastTab.setAttribute("last-visible-tab", "true");
 
           let hoveredTab = this._hoveredTab;
           if (hoveredTab) {
             hoveredTab._mouseleave();
+          }
+          hoveredTab = this.querySelector("tab:hover");
+          if (hoveredTab) {
             hoveredTab._mouseenter();
           }
         ]]></body>
       </method>
 
       <field name="_blockDblClick">false</field>
 
       <field name="_tabDropIndicator">
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -9,22 +9,22 @@
 {
 "size": 12057960,
 "digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "unpack": true
 },
 {
+"size": 193213220,
+"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
+"algorithm": "sha512",
+"filename": "rustc.tar.xz",
+"unpack": true
+},
+{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
-},
-{
-"size": 73029932,
-"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -5,18 +5,18 @@
 {
 "size": 121389802,
 "digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
-"size": 128301120,
-"digest": "d2d71103a6cec84b150b8f08bfef2682aa713c2d6eadce584f79836ef27ba4380ffb444165d999b79605f18ad165641a7a8cc0e04a201675ad5f655a59adbda9",
+"size": 215952362,
+"digest": "5e9825dbe83b2a157879076da70fc5c989a1638e30d3b14a9901b166db09013c356a9dc4eaf6c16209a1832d9cb1c67ca869e9b9003cab8355a7f03b3dc08775",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -25,16 +25,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
+                                  "resource://gre/modules/TelemetryUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
                                   "resource://gre/modules/Metrics.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
@@ -155,20 +157,16 @@ function loadJSONAsync(file, options) {
     } catch (ex) {
       gLogger.error("Experiments: Could not parse JSON: " + file + " " + ex);
       throw ex;
     }
     return data;
   });
 }
 
-function telemetryEnabled() {
-  return gPrefsTelemetry.get(PREF_TELEMETRY_ENABLED, false);
-}
-
 // Returns a promise that is resolved with the AddonInstall for that URL.
 function addonInstallForURL(url, hash) {
   let deferred = Promise.defer();
   AddonManager.getInstallForURL(url, install => deferred.resolve(install),
                                 "application/x-xpinstall", hash);
   return deferred.promise;
 }
 
@@ -384,17 +382,17 @@ Experiments.Experiments.prototype = {
   get isReady() {
     return !this._shutdown;
   },
 
   init: function () {
     this._shutdown = false;
     configureLogging();
 
-    gExperimentsEnabled = gPrefs.get(PREF_ENABLED, false);
+    gExperimentsEnabled = gPrefs.get(PREF_ENABLED, false) && TelemetryUtils.isTelemetryEnabled;
     this._log.trace("enabled=" + gExperimentsEnabled + ", " + this.enabled);
 
     gPrefs.observe(PREF_LOGGING, configureLogging);
     gPrefs.observe(PREF_MANIFEST_URI, this.updateManifest, this);
     gPrefs.observe(PREF_ENABLED, this._toggleExperimentsEnabled, this);
 
     gPrefsTelemetry.observe(PREF_TELEMETRY_ENABLED, this._telemetryStatusChanged, this);
 
@@ -575,17 +573,17 @@ Experiments.Experiments.prototype = {
   set enabled(enabled) {
     this._log.trace("set enabled(" + enabled + ")");
     gPrefs.set(PREF_ENABLED, enabled);
   },
 
   _toggleExperimentsEnabled: Task.async(function* (enabled) {
     this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
     let wasEnabled = gExperimentsEnabled;
-    gExperimentsEnabled = enabled && telemetryEnabled();
+    gExperimentsEnabled = enabled && TelemetryUtils.isTelemetryEnabled;
 
     if (wasEnabled == gExperimentsEnabled) {
       return;
     }
 
     if (gExperimentsEnabled) {
       yield this.updateManifest();
     } else {
--- a/browser/experiments/test/xpcshell/head.js
+++ b/browser/experiments/test/xpcshell/head.js
@@ -228,8 +228,12 @@ function createAppInfo(optionsIn) {
 function replaceExperiments(experiment, list) {
   Object.defineProperty(experiment, "getExperiments", {
     writable: true,
     value: () => {
       return Promise.resolve(list);
     },
   });
 }
+
+// Experiments require Telemetry to be enabled, and that's not true for debug
+// builds. Let's just enable it here instead of going through each test.
+Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
new file mode 100644
--- /dev/null
+++ b/browser/experiments/test/xpcshell/test_telemetry_disabled.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource:///modules/experiments/Experiments.jsm");
+
+add_test(function test_experiments_activation() {
+  do_get_profile();
+  loadAddonManager();
+
+  Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
+
+  let experiments = Experiments.instance();
+  Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
+
+  // TODO: Test that Experiments are turned back on when bug 1232648 lands.
+
+  run_next_test();
+});
--- a/browser/experiments/test/xpcshell/xpcshell.ini
+++ b/browser/experiments/test/xpcshell/xpcshell.ini
@@ -19,12 +19,13 @@ generated-files =
 [test_activate.js]
 [test_api.js]
 [test_cache.js]
 [test_cacherace.js]
 [test_conditions.js]
 [test_disableExperiments.js]
 [test_fetch.js]
 [test_telemetry.js]
+[test_telemetry_disabled.js]
 [test_healthreport.js]
 [test_previous_provider.js]
 [test_upgrade.js]
 [test_nethang_bug1012924.js]
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -67,17 +67,17 @@ STUB_HOOK = $(NSINSTALL) -D '$(_ABS_DIST
     chmod 0755 '$(_ABS_DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe'; \
     $(NULL)
 endif
 
 SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) ddg
 SEARCHPLUGINS_FILENAMES = $(subst :hidden,,$(SEARCHPLUGINS_NAMES))
 SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
 SEARCHPLUGINS_TARGET := libs searchplugins
-SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(info Missing searchplugin: $(plugin))))
+SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
 # Some locale-specific search plugins may have preprocessor directives, but the
 # default en-US ones do not.
 SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
 PP_TARGETS += SEARCHPLUGINS
 
 list-txt = $(SEARCHPLUGINS_PATH)/list.txt
 GARBAGE += $(list-txt)
 
--- a/browser/themes/windows/pageInfo.css
+++ b/browser/themes/windows/pageInfo.css
@@ -118,27 +118,27 @@ textbox[disabled] {
 /* General Tab */
 groupbox.collapsable caption .caption-icon {
   width: 9px;
   height: 9px;
   background-repeat: no-repeat;
   background-position: center;
   -moz-margin-start: 2px;
   -moz-margin-end: 2px;
-  background-image: url("chrome://global/skin/tree/twisty-open.png");
+  background-image: url("chrome://global/skin/tree/twisty.svg#open");
 }
 
 groupbox.collapsable[closed="true"] {
   border: none;
   margin-bottom: 9px;
   -moz-appearance: none;
 }
 
 groupbox.collapsable[closed="true"] caption .caption-icon {
-  background-image: url("chrome://global/skin/tree/twisty-clsd.png");
+  background-image: url("chrome://global/skin/tree/twisty.svg#clsd");
 }
 
 groupbox tree {
   margin: 0 3px;
   border: none;
 }
 
 #securityBox description {
--- a/build/autoconf/rust.m4
+++ b/build/autoconf/rust.m4
@@ -20,17 +20,17 @@ AC_DEFUN([MOZ_RUST_SUPPORT], [
                       [MOZ_RUST= ])
   if test -z "$RUSTC" -a -n "$MOZ_RUST"; then
     AC_MSG_ERROR([Rust compiler not found.
       To compile rust language sources, you must have 'rustc' in your path.
       See http://www.rust-lang.org/ for more information.])
   fi
   if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \
     "$_RUSTC_MAJOR_VERSION" -lt 1 -o \
-    \( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 4 \); then
+    \( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 5 \); then
     AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old.
       To compile Rust language sources please install at least
-      version 1.4 of the 'rustc' toolchain and make sure it is
+      version 1.5 of the 'rustc' toolchain and make sure it is
       first in your path.
       You can verify this by typing 'rustc --version'.])
   fi
   AC_SUBST(MOZ_RUST)
 ])
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -940,17 +940,17 @@ ifdef ASFILES
 	$(AS) $(ASOUTOPTION)$@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS)
 endif
 
 ifdef MOZ_RUST
 # Assume any system libraries rustc links against are already
 # in the target's LIBS.
 $(RSOBJS):
 	$(REPORT_BUILD)
-	$(RUSTC) $(RUSTFLAGS) --crate-type staticlib -o $(call mk_libname,$<) $(_VPATH_SRCS)
+	$(RUSTC) $(RUSTFLAGS) --crate-type staticlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
 endif
 
 $(SOBJS):
 	$(REPORT_BUILD)
 	$(AS) -o $@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) $(TARGET_LOCAL_INCLUDES) -c $<
 
 $(CPPOBJS):
 	$(REPORT_BUILD)
--- a/configure.in
+++ b/configure.in
@@ -6401,24 +6401,16 @@ if test -z "$TAR"; then
     AC_MSG_ERROR([no tar archiver found in \$PATH])
 fi
 AC_SUBST(TAR)
 
 AC_CHECK_PROGS(WGET, wget, "")
 AC_SUBST(WGET)
 
 dnl ========================================================
-dnl Signing
-dnl ========================================================
-
-if test -n "$MOZ_SIGN_CMD"; then
-    AC_DEFINE(MOZ_SIGNING)
-fi
-
-dnl ========================================================
 dnl Maintenance Service
 dnl ========================================================
 
 MOZ_ARG_ENABLE_BOOL(maintenance-service,
 [  --enable-maintenance-service       Enable building of maintenanceservice],
     MOZ_MAINTENANCE_SERVICE=1,
     MOZ_MAINTENANCE_SERVICE= )
 
--- a/devtools/client/framework/test/browser_toolbox_theme_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_theme_registration.js
@@ -1,113 +1,89 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Test for dynamically registering and unregistering themes
 const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/framework/test/";
 
 var toolbox;
 
-function test()
-{
-  gBrowser.selectedTab = gBrowser.addTab();
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
-
-  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
-    gDevTools.showToolbox(target).then(testRegister);
-  }, true);
-
-  content.location = "data:text/html,test for dynamically registering and unregistering themes";
-}
+add_task(function* themeRegistration() {
+  let tab = yield addTab("data:text/html,test");
+  let target = TargetFactory.forTab(tab);
+  toolbox = yield gDevTools.showToolbox(target);
 
-function testRegister(aToolbox)
-{
-  toolbox = aToolbox
-  gDevTools.once("theme-registered", themeRegistered);
+  let themeId = yield new Promise(resolve => {
+    gDevTools.once("theme-registered", (e, themeId) => {
+      resolve(themeId);
+    });
 
-  gDevTools.registerTheme({
-    id: "test-theme",
-    label: "Test theme",
-    stylesheets: [CHROME_URL + "doc_theme.css"],
-    classList: ["theme-test"],
+    gDevTools.registerTheme({
+      id: "test-theme",
+      label: "Test theme",
+      stylesheets: [CHROME_URL + "doc_theme.css"],
+      classList: ["theme-test"],
+    });
   });
-}
 
-function themeRegistered(event, themeId)
-{
   is(themeId, "test-theme", "theme-registered event handler sent theme id");
 
   ok(gDevTools.getThemeDefinitionMap().has(themeId), "theme added to map");
+});
 
-  // Test that new theme appears in the Options panel
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
-  gDevTools.showToolbox(target, "options").then(() => {
-    let panel = toolbox.getCurrentPanel();
-    let doc = panel.panelWin.frameElement.contentDocument;
-    let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
+add_task(function* themeInOptionsPanel() {
 
-    ok(themeOption, "new theme exists in the Options panel");
+  yield toolbox.selectTool("options");
 
-    // Apply the new theme.
-    applyTheme();
-  });
-}
-
-function applyTheme()
-{
+  let panel = toolbox.getCurrentPanel();
   let panelWin = toolbox.getCurrentPanel().panelWin;
   let doc = panelWin.frameElement.contentDocument;
+  let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
+
+  ok(themeOption, "new theme exists in the Options panel");
+
   let testThemeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
   let lightThemeOption = doc.querySelector("#devtools-theme-box > radio[value=light]");
 
   let color = panelWin.getComputedStyle(testThemeOption).color;
   isnot(color, "rgb(255, 0, 0)", "style unapplied");
 
   // Select test theme.
   testThemeOption.click();
 
+  info("Waiting for theme to finish loading");
+  yield once(panelWin, "theme-switch-complete");
+
   color = panelWin.getComputedStyle(testThemeOption).color;
   is(color, "rgb(255, 0, 0)", "style applied");
 
   // Select light theme
   lightThemeOption.click();
 
+  info("Waiting for theme to finish loading");
+  yield once(panelWin, "theme-switch-complete");
+
   color = panelWin.getComputedStyle(testThemeOption).color;
   isnot(color, "rgb(255, 0, 0)", "style unapplied");
 
   // Select test theme again.
   testThemeOption.click();
+});
 
-  // Then unregister the test theme.
-  testUnregister();
-}
-
-function testUnregister()
-{
+add_task(function* themeUnregistration() {
   gDevTools.unregisterTheme("test-theme");
 
   ok(!gDevTools.getThemeDefinitionMap().has("test-theme"), "theme removed from map");
 
   let panelWin = toolbox.getCurrentPanel().panelWin;
   let doc = panelWin.frameElement.contentDocument;
   let themeBox = doc.querySelector("#devtools-theme-box");
 
   // The default light theme must be selected now.
   is(themeBox.selectedItem, themeBox.querySelector("[value=light]"),
     "theme light must be selected");
-
-  // Make sure the tab-attaching process is done before we destroy the toolbox.
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
-  let actor = target.activeTab.actor;
-  target.client.attachTab(actor, (response) => {
-    cleanup();
-  });
-}
+});
 
-function cleanup()
-{
-  toolbox.destroy().then(function() {
-    toolbox = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  });
-}
+add_task(function* cleanup() {
+  yield toolbox.destroy();
+  toolbox = null;
+});
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -34,34 +34,34 @@ devtools.jar:
     content/layoutview/view.js (layoutview/view.js)
     content/layoutview/view.xhtml (layoutview/view.xhtml)
     content/fontinspector/font-inspector.js (fontinspector/font-inspector.js)
     content/fontinspector/font-inspector.xhtml (fontinspector/font-inspector.xhtml)
     content/fontinspector/font-inspector.css (fontinspector/font-inspector.css)
     content/animationinspector/animation-controller.js (animationinspector/animation-controller.js)
     content/animationinspector/animation-panel.js (animationinspector/animation-panel.js)
     content/animationinspector/animation-inspector.xhtml (animationinspector/animation-inspector.xhtml)
-    content/sourceeditor/codemirror/comment/comment.js (sourceeditor/codemirror/addon/comment/comment.js)
-    content/sourceeditor/codemirror/edit/trailingspace.js (sourceeditor/codemirror/addon/edit/trailingspace.js)
-    content/sourceeditor/codemirror/edit/matchbrackets.js (sourceeditor/codemirror/addon/edit/matchbrackets.js)
-    content/sourceeditor/codemirror/edit/closebrackets.js (sourceeditor/codemirror/addon/edit/closebrackets.js)
-    content/sourceeditor/codemirror/dialog/dialog.js (sourceeditor/codemirror/addon/dialog/dialog.js)
-    content/sourceeditor/codemirror/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css)
-    content/sourceeditor/codemirror/fold/foldcode.js (sourceeditor/codemirror/addon/fold/foldcode.js)
-    content/sourceeditor/codemirror/fold/brace-fold.js (sourceeditor/codemirror/addon/fold/brace-fold.js)
-    content/sourceeditor/codemirror/fold/comment-fold.js (sourceeditor/codemirror/addon/fold/comment-fold.js)
-    content/sourceeditor/codemirror/fold/xml-fold.js (sourceeditor/codemirror/addon/fold/xml-fold.js)
-    content/sourceeditor/codemirror/fold/foldgutter.js (sourceeditor/codemirror/addon/fold/foldgutter.js)
-    content/sourceeditor/codemirror/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js)
-    content/sourceeditor/codemirror/search/search.js (sourceeditor/codemirror/addon/search/search.js)
-    content/sourceeditor/codemirror/search/searchcursor.js (sourceeditor/codemirror/addon/search/searchcursor.js)
-    content/sourceeditor/codemirror/selection/active-line.js (sourceeditor/codemirror/addon/selection/active-line.js)
-    content/sourceeditor/codemirror/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
-    content/sourceeditor/codemirror/codemirror.js (sourceeditor/codemirror/lib/codemirror.js)
-    content/sourceeditor/codemirror/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
+    content/sourceeditor/codemirror/addon/comment/comment.js (sourceeditor/codemirror/addon/comment/comment.js)
+    content/sourceeditor/codemirror/addon/edit/trailingspace.js (sourceeditor/codemirror/addon/edit/trailingspace.js)
+    content/sourceeditor/codemirror/addon/edit/matchbrackets.js (sourceeditor/codemirror/addon/edit/matchbrackets.js)
+    content/sourceeditor/codemirror/addon/edit/closebrackets.js (sourceeditor/codemirror/addon/edit/closebrackets.js)
+    content/sourceeditor/codemirror/addon/dialog/dialog.js (sourceeditor/codemirror/addon/dialog/dialog.js)
+    content/sourceeditor/codemirror/addon/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css)
+    content/sourceeditor/codemirror/addon/fold/foldcode.js (sourceeditor/codemirror/addon/fold/foldcode.js)
+    content/sourceeditor/codemirror/addon/fold/brace-fold.js (sourceeditor/codemirror/addon/fold/brace-fold.js)
+    content/sourceeditor/codemirror/addon/fold/comment-fold.js (sourceeditor/codemirror/addon/fold/comment-fold.js)
+    content/sourceeditor/codemirror/addon/fold/xml-fold.js (sourceeditor/codemirror/addon/fold/xml-fold.js)
+    content/sourceeditor/codemirror/addon/fold/foldgutter.js (sourceeditor/codemirror/addon/fold/foldgutter.js)
+    content/sourceeditor/codemirror/addon/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js)
+    content/sourceeditor/codemirror/addon/search/search.js (sourceeditor/codemirror/addon/search/search.js)
+    content/sourceeditor/codemirror/addon/search/searchcursor.js (sourceeditor/codemirror/addon/search/searchcursor.js)
+    content/sourceeditor/codemirror/addon/selection/active-line.js (sourceeditor/codemirror/addon/selection/active-line.js)
+    content/sourceeditor/codemirror/addon/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
+    content/sourceeditor/codemirror/lib/codemirror.js (sourceeditor/codemirror/lib/codemirror.js)
+    content/sourceeditor/codemirror/lib/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
     content/sourceeditor/codemirror/mode/javascript.js (sourceeditor/codemirror/mode/javascript.js)
     content/sourceeditor/codemirror/mode/xml.js (sourceeditor/codemirror/mode/xml.js)
     content/sourceeditor/codemirror/mode/css.js (sourceeditor/codemirror/mode/css.js)
     content/sourceeditor/codemirror/mode/htmlmixed.js (sourceeditor/codemirror/mode/htmlmixed.js)
     content/sourceeditor/codemirror/mode/clike.js (sourceeditor/codemirror/mode/clike.js)
     content/sourceeditor/codemirror/keymap/emacs.js (sourceeditor/codemirror/keymap/emacs.js)
     content/sourceeditor/codemirror/keymap/sublime.js (sourceeditor/codemirror/keymap/sublime.js)
     content/sourceeditor/codemirror/keymap/vim.js (sourceeditor/codemirror/keymap/vim.js)
--- a/devtools/client/shared/test/browser_theme_switching.js
+++ b/devtools/client/shared/test/browser_theme_switching.js
@@ -2,25 +2,43 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var toolbox;
 
 add_task(function*() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = yield gDevTools.showToolbox(target);
-  let root = toolbox.frame.contentDocument.documentElement;
+  let doc = toolbox.frame.contentDocument;
+  let root = doc.documentElement;
 
   let platform = root.getAttribute("platform");
   let expectedPlatform = getPlatform();
   is(platform, expectedPlatform, ":root[platform] is correct");
 
   let theme = Services.prefs.getCharPref("devtools.theme");
   let className = "theme-" + theme;
-  ok(root.classList.contains(className), ":root has " + className + " class (current theme)");
+  ok(root.classList.contains(className),
+     ":root has " + className + " class (current theme)");
+
+  // Convert the xpath result into an array of strings
+  // like `href="{URL}" type="text/css"`
+  let sheetsIterator = doc.evaluate("processing-instruction('xml-stylesheet')",
+                       doc, null, XPathResult.ANY_TYPE, null);
+  let sheetsInDOM = [];
+  let sheet;
+  while (sheet = sheetsIterator.iterateNext()) {
+    sheetsInDOM.push(sheet.data);
+  }
+
+  let sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
+  info ("Checking for existence of " + sheetsInDOM.length + " sheets");
+  for (let sheet of sheetsFromTheme) {
+    ok(sheetsInDOM.some(s=>s.includes(sheet)), "There is a stylesheet for " + sheet);
+  }
 
   yield toolbox.destroy();
 });
 
 function getPlatform() {
   let {OS} = Services.appinfo;
   if (OS == "WINNT") {
     return "win";
--- a/devtools/client/shared/theme-switching.js
+++ b/devtools/client/shared/theme-switching.js
@@ -1,78 +1,116 @@
 /* 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/. */
 
 (function() {
-  const DEVTOOLS_SKIN_URL = "chrome://devtools/skin/";
+  const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-light.css";
   let documentElement = document.documentElement;
+  let devtoolsStyleSheets = new WeakMap();
 
   function forceStyle() {
     let computedStyle = window.getComputedStyle(documentElement);
     if (!computedStyle) {
       // Null when documentElement is not ready. This method is anyways not
       // required then as scrollbars would be in their state without flushing.
       return;
     }
     let display = computedStyle.display; // Save display value
     documentElement.style.display = "none";
     window.getComputedStyle(documentElement).display; // Flush
     documentElement.style.display = display; // Restore
   }
 
+  /*
+   * Append a new processing instruction and return an object with
+   *  - styleSheet: DOMNode
+   *  - loadPromise: Promise that resolves once the sheets loads or errors
+   */
+  function appendStyleSheet(url) {
+    let styleSheetAttr = `href="${url}" type="text/css"`;
+    let styleSheet = document.createProcessingInstruction(
+      "xml-stylesheet", styleSheetAttr);
+    let loadPromise = new Promise((resolve, reject) => {
+      function onload() {
+        styleSheet.removeEventListener("load", onload);
+        styleSheet.removeEventListener("error", onerror);
+        resolve();
+      }
+      function onerror() {
+        styleSheet.removeEventListener("load", onload);
+        styleSheet.removeEventListener("error", onerror);
+        reject("Failed to load theme file " + url);
+      }
+
+      styleSheet.addEventListener("load", onload);
+      styleSheet.addEventListener("error", onerror);
+    });
+    document.insertBefore(styleSheet, documentElement);
+    return {styleSheet, loadPromise};
+  }
+
+  /*
+   * Notify the window that a theme switch finished so tests can check the DOM
+   */
+  function notifyWindow() {
+    window.dispatchEvent(new CustomEvent("theme-switch-complete", {}));
+  }
+
+  /*
+   * Apply all the sheets from `newTheme` and remove all of the sheets
+   * from `oldTheme`
+   */
   function switchTheme(newTheme, oldTheme) {
     if (newTheme === oldTheme) {
       return;
     }
 
     let oldThemeDef = gDevTools.getThemeDefinition(oldTheme);
 
     // Unload all theme stylesheets related to the old theme.
     if (oldThemeDef) {
-      for (let url of oldThemeDef.stylesheets) {
-        StylesheetUtils.removeSheet(window, url, "author");
+      for (let sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
+        sheet.remove();
       }
     }
 
     // Load all stylesheets associated with the new theme.
     let newThemeDef = gDevTools.getThemeDefinition(newTheme);
 
     // The theme might not be available anymore (e.g. uninstalled)
     // Use the default one.
     if (!newThemeDef) {
       newThemeDef = gDevTools.getThemeDefinition("light");
     }
 
+    // Store the sheets in a WeakMap for access later when the theme gets
+    // unapplied.  It's hard to query for processing instructions so this
+    // is an easy way to access them later without storing a property on
+    // the window
+    devtoolsStyleSheets.set(newThemeDef, []);
+
+    let loadEvents = [];
     for (let url of newThemeDef.stylesheets) {
-      StylesheetUtils.loadSheet(window, url, "author");
+      let {styleSheet,loadPromise} = appendStyleSheet(url);
+      devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
+      loadEvents.push(loadPromise);
     }
 
     // Floating scroll-bars like in OSX
     let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
                  .getService(Ci.nsIAppShellService)
                  .hiddenDOMWindow;
 
     // TODO: extensions might want to customize scrollbar styles too.
     if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
-      let scrollbarsUrl = Services.io.newURI(
-        DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
-
       if (newTheme == "dark") {
-        StylesheetUtils.loadSheet(
-          window,
-          scrollbarsUrl,
-          "agent"
-        );
+        StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
       } else if (oldTheme == "dark") {
-        StylesheetUtils.removeSheet(
-          window,
-          scrollbarsUrl,
-          "agent"
-        );
+        StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
       }
       forceStyle();
     }
 
     if (oldThemeDef) {
       for (let name of oldThemeDef.classList) {
         documentElement.classList.remove(name);
       }
@@ -87,26 +125,27 @@
     }
 
     if (newThemeDef.onApply) {
       newThemeDef.onApply(window, oldTheme);
     }
 
     // Final notification for further theme-switching related logic.
     gDevTools.emit("theme-switched", window, newTheme, oldTheme);
+
+    Promise.all(loadEvents).then(notifyWindow, console.error.bind(console));
   }
 
   function handlePrefChange(event, data) {
     if (data.pref == "devtools.theme") {
       switchTheme(data.newValue, data.oldValue);
     }
   }
 
   const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
   Cu.import("resource://gre/modules/Services.jsm");
   Cu.import("resource://devtools/client/framework/gDevTools.jsm");
   const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
   const StylesheetUtils = require("sdk/stylesheet/utils");
 
   let os;
   let platform = navigator.platform;
   if (platform.startsWith("Win")) {
--- a/devtools/client/sourceeditor/autocomplete.js
+++ b/devtools/client/sourceeditor/autocomplete.js
@@ -3,18 +3,18 @@
  * 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 { Cu } = require("chrome");
 const cssAutoCompleter = require("devtools/client/sourceeditor/css-autocompleter");
 const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
 
 const CM_TERN_SCRIPTS = [
-  "chrome://devtools/content/sourceeditor/codemirror/tern/tern.js",
-  "chrome://devtools/content/sourceeditor/codemirror/hint/show-hint.js"
+  "chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js"
 ];
 
 const autocompleteMap = new WeakMap();
 
 // A simple way to give each popup its own panelId.
 var autocompleteCounter = 0;
 
 /**
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -42,45 +42,45 @@ const L10N = Services.strings.createBund
 const { OS } = Services.appinfo;
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
 // order to initialize a CodeMirror instance.
 
 const CM_STYLES   = [
   "chrome://devtools/skin/common.css",
-  "chrome://devtools/content/sourceeditor/codemirror/codemirror.css",
-  "chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.css",
+  "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css",
   "chrome://devtools/content/sourceeditor/codemirror/mozilla.css"
 ];
 
 const CM_SCRIPTS  = [
   "chrome://devtools/content/shared/theme-switching.js",
-  "chrome://devtools/content/sourceeditor/codemirror/codemirror.js",
-  "chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js",
-  "chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js",
-  "chrome://devtools/content/sourceeditor/codemirror/search/search.js",
-  "chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js",
-  "chrome://devtools/content/sourceeditor/codemirror/edit/closebrackets.js",
-  "chrome://devtools/content/sourceeditor/codemirror/comment/comment.js",
+  "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/search/search.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/edit/closebrackets.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js",
   "chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js",
   "chrome://devtools/content/sourceeditor/codemirror/mode/xml.js",
   "chrome://devtools/content/sourceeditor/codemirror/mode/css.js",
   "chrome://devtools/content/sourceeditor/codemirror/mode/htmlmixed.js",
   "chrome://devtools/content/sourceeditor/codemirror/mode/clike.js",
-  "chrome://devtools/content/sourceeditor/codemirror/selection/active-line.js",
-  "chrome://devtools/content/sourceeditor/codemirror/edit/trailingspace.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/selection/active-line.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/edit/trailingspace.js",
   "chrome://devtools/content/sourceeditor/codemirror/keymap/emacs.js",
   "chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js",
   "chrome://devtools/content/sourceeditor/codemirror/keymap/sublime.js",
-  "chrome://devtools/content/sourceeditor/codemirror/fold/foldcode.js",
-  "chrome://devtools/content/sourceeditor/codemirror/fold/brace-fold.js",
-  "chrome://devtools/content/sourceeditor/codemirror/fold/comment-fold.js",
-  "chrome://devtools/content/sourceeditor/codemirror/fold/xml-fold.js",
-  "chrome://devtools/content/sourceeditor/codemirror/fold/foldgutter.js"
+  "chrome://devtools/content/sourceeditor/codemirror/addon/fold/foldcode.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/fold/brace-fold.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/fold/comment-fold.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/fold/xml-fold.js",
+  "chrome://devtools/content/sourceeditor/codemirror/addon/fold/foldgutter.js"
 ];
 
 const CM_IFRAME   =
   "data:text/html;charset=utf8,<!DOCTYPE html>" +
   "<html dir='ltr'>" +
   "  <head>" +
   "    <style>" +
   "      html, body { height: 100%; }" +
--- a/devtools/client/sourceeditor/test/codemirror/codemirror.html
+++ b/devtools/client/sourceeditor/test/codemirror/codemirror.html
@@ -1,22 +1,22 @@
 <!doctype html>
 <html>
   <head>
     <meta charset="utf-8">
     <title>CodeMirror: Basic Tests</title>
-    <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/codemirror.css">
+    <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css">
     <link rel="stylesheet" href="cm_mode_test.css">
     <!--<link rel="stylesheet" href="../doc/docs.css">-->
 
-    <script src="chrome://devtools/content/sourceeditor/codemirror/codemirror.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/comment/comment.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/emacs.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/sublime.js"></script>
 
     <!--<script src="../addon/mode/overlay.js"></script>
     <script src="../addon/mode/multiplex.js"></script>
     <script src="../mode/xml/xml.js"></script>-->
--- a/devtools/client/sourceeditor/test/codemirror/vimemacs.html
+++ b/devtools/client/sourceeditor/test/codemirror/vimemacs.html
@@ -1,22 +1,22 @@
 <!doctype html>
 <html>
   <head>
     <meta charset="utf-8">
     <title>CodeMirror: VIM/Emacs tests</title>
-    <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/codemirror.css">
+    <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css">
     <link rel="stylesheet" href="cm_mode_test.css">
     <!--<link rel="stylesheet" href="../doc/docs.css">-->
 
-    <script src="chrome://devtools/content/sourceeditor/codemirror/codemirror.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js"></script>
-    <script src="chrome://devtools/content/sourceeditor/codemirror/comment/comment.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js"></script>
+    <script src="chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/sublime.js"></script>
     <script src="chrome://devtools/content/sourceeditor/codemirror/keymap/emacs.js"></script>
 
     <!--<script src="../addon/mode/overlay.js"></script>
     <script src="../addon/mode/multiplex.js"></script>
     <script src="../mode/xml/xml.js"></script>-->
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -285,44 +285,44 @@ body {
 
 /* Animation iterations */
 
 .animation-timeline .animation .iterations {
   position: relative;
   height: 100%;
   box-sizing: border-box;
 
-  --timelime-border-color: var(--theme-body-color);
+  --timeline-border-color: var(--theme-body-color);
   --timeline-background-color: var(--theme-splitter-color);
 
   /* Iterations of the animation are displayed with a repeating linear-gradient
      which size is dynamically changed from JS. The gradient only draws 1px
      borders between each iteration. These borders must have the same color as
      the border of this element */
   background-image:
     linear-gradient(to right,
-                    var(--timelime-border-color) 0,
-                    var(--timelime-border-color) 1px,
+                    var(--timeline-border-color) 0,
+                    var(--timeline-border-color) 1px,
                     transparent 1px,
                     transparent 2px);
   background-repeat: repeat-x;
   background-position: -1px 0;
-  border: 1px solid var(--timelime-border-color);
+  border: 1px solid var(--timeline-border-color);
 
   /* The background color is set independently */
   background-color: var(--timeline-background-color);
 }
 
 .animation-timeline .animation .cssanimation {
-  --timelime-border-color: var(--theme-highlight-lightorange);
+  --timeline-border-color: var(--theme-highlight-lightorange);
   --timeline-background-color: var(--theme-contrast-background);
 }
 
 .animation-timeline .animation .csstransition {
-  --timelime-border-color: var(--theme-highlight-bluegrey);
+  --timeline-border-color: var(--theme-highlight-bluegrey);
   --timeline-background-color: var(--theme-highlight-blue);
 }
 
 .animation-timeline .animation .iterations.infinite {
   border-right-width: 0;
 }
 
 .animation-timeline .animation .iterations.infinite::before,
@@ -364,24 +364,24 @@ body {
 .animation-timeline .animation .delay {
   position: absolute;
   top: 0;
   /* Make sure the delay covers up the animation border */
   transform: translate(-1px, -1px);
   box-sizing: border-box;
   height: calc(100% + 2px);
 
-  border: 1px solid var(--timelime-border-color);
+  border: 1px solid var(--timeline-border-color);
   border-width: 1px 0 1px 1px;
   background-image: repeating-linear-gradient(45deg,
                                               transparent,
                                               transparent 1px,
                                               var(--theme-selection-color) 1px,
                                               var(--theme-selection-color) 4px);
-  background-color: var(--timelime-border-color);
+  background-color: var(--timeline-border-color);
 }
 
 .animation-timeline .animation .delay.negative {
   /* Negative delays are displayed on top of the animation, so they need a
      right border. Whereas normal delays are displayed just before the
      animation, so there's already the animation's left border that serves as
      a separation. */
   border-width: 1px;
--- a/devtools/client/themes/layoutview.css
+++ b/devtools/client/themes/layoutview.css
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/ */
 
 .theme-sidebar {
   box-sizing: border-box;
 }
 
-.theme-sidebar body {
+body.theme-sidebar {
   /* The view will grow bigger as the window gets resized, until 400px */
   max-width: 400px;
   margin: 0px auto;
   padding: 0;
   /* "Contain" the absolutely positioned #main element */
   position: relative;
 }
 
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -189,22 +189,22 @@ const PREF_INPUT_HISTORY_COUNT = "devtoo
  * A WebConsoleFrame instance is an interactive console initialized *per target*
  * that displays console log data as well as provides an interactive terminal to
  * manipulate the target's document content.
  *
  * The WebConsoleFrame is responsible for the actual Web Console UI
  * implementation.
  *
  * @constructor
- * @param object aWebConsoleOwner
+ * @param object webConsoleOwner
  *        The WebConsole owner object.
  */
-function WebConsoleFrame(aWebConsoleOwner)
+function WebConsoleFrame(webConsoleOwner)
 {
-  this.owner = aWebConsoleOwner;
+  this.owner = webConsoleOwner;
   this.hudId = this.owner.hudId;
   this.window = this.owner.iframeWindow;
 
   this._repeatNodes = {};
   this._outputQueue = [];
   this._itemDestroyQueue = [];
   this._pruneCategoriesQueue = {};
   this.filterPrefs = {};
@@ -385,56 +385,56 @@ WebConsoleFrame.prototype = {
   getSaveRequestAndResponseBodies:
   function WCF_getSaveRequestAndResponseBodies() {
     let deferred = promise.defer();
     let toGet = [
       "NetworkMonitor.saveRequestAndResponseBodies"
     ];
 
     // Make sure the web console client connection is established first.
-    this.webConsoleClient.getPreferences(toGet, aResponse => {
-      if (!aResponse.error) {
-        this._saveRequestAndResponseBodies = aResponse.preferences[toGet[0]];
+    this.webConsoleClient.getPreferences(toGet, response => {
+      if (!response.error) {
+        this._saveRequestAndResponseBodies = response.preferences[toGet[0]];
         deferred.resolve(this._saveRequestAndResponseBodies);
       }
       else {
-        deferred.reject(aResponse.error);
+        deferred.reject(response.error);
       }
     });
 
     return deferred.promise;
   },
 
   /**
    * Setter for saving of network request and response bodies.
    *
-   * @param boolean aValue
+   * @param boolean value
    *        The new value you want to set.
    */
   setSaveRequestAndResponseBodies:
-  function WCF_setSaveRequestAndResponseBodies(aValue) {
+  function WCF_setSaveRequestAndResponseBodies(value) {
     if (!this.webConsoleClient) {
       // Don't continue if the webconsole disconnected.
       return promise.resolve(null);
     }
 
     let deferred = promise.defer();
-    let newValue = !!aValue;
+    let newValue = !!value;
     let toSet = {
       "NetworkMonitor.saveRequestAndResponseBodies": newValue,
     };
 
     // Make sure the web console client connection is established first.
-    this.webConsoleClient.setPreferences(toSet, aResponse => {
-      if (!aResponse.error) {
+    this.webConsoleClient.setPreferences(toSet, response => {
+      if (!response.error) {
         this._saveRequestAndResponseBodies = newValue;
-        deferred.resolve(aResponse);
+        deferred.resolve(response);
       }
       else {
-        deferred.reject(aResponse.error);
+        deferred.reject(response.error);
       }
     });
 
     return deferred.promise;
   },
 
   /**
    * Getter for the persistent logging preference.
@@ -490,21 +490,21 @@ WebConsoleFrame.prototype = {
       return this._initDefer.promise;
     }
 
     this._initDefer = promise.defer();
     this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);
 
     this.proxy.connect().then(() => { // on success
       this._initDefer.resolve(this);
-    }, (aReason) => { // on failure
+    }, (reason) => { // on failure
       let node = this.createMessageNode(CATEGORY_JS, SEVERITY_ERROR,
-                                        aReason.error + ": " + aReason.message);
-      this.outputMessage(CATEGORY_JS, node, [aReason]);
-      this._initDefer.reject(aReason);
+                                        reason.error + ": " + reason.message);
+      this.outputMessage(CATEGORY_JS, node, [reason]);
+      this._initDefer.reject(reason);
     });
 
     return this._initDefer.promise;
   },
 
   /**
    * Find the Web Console UI elements and setup event listeners as needed.
    * @private
@@ -555,27 +555,27 @@ WebConsoleFrame.prototype = {
                      .removeAttribute("disabled");
       }
     }
 
     // Update the character width and height needed for the popup offset
     // calculations.
     this._updateCharSize();
 
-    let updateSaveBodiesPrefUI = (aElement) => {
-      this.getSaveRequestAndResponseBodies().then(aValue => {
-        aElement.setAttribute("checked", aValue);
+    let updateSaveBodiesPrefUI = (element) => {
+      this.getSaveRequestAndResponseBodies().then(value => {
+        element.setAttribute("checked", value);
         this.emit("save-bodies-ui-toggled");
       });
     }
 
-    let reverseSaveBodiesPref = ({ target: aElement }) => {
-      this.getSaveRequestAndResponseBodies().then(aValue => {
-        this.setSaveRequestAndResponseBodies(!aValue);
-        aElement.setAttribute("checked", aValue);
+    let reverseSaveBodiesPref = ({ target: element }) => {
+      this.getSaveRequestAndResponseBodies().then(value => {
+        this.setSaveRequestAndResponseBodies(!value);
+        element.setAttribute("checked", value);
         this.emit("save-bodies-pref-reversed");
       });
     }
 
     let saveBodiesDisabled = !this.getFilterState("networkinfo") &&
                              !this.getFilterState("netxhr") &&
                              !this.getFilterState("network");
 
@@ -665,62 +665,62 @@ WebConsoleFrame.prototype = {
         this._filterPrefsPrefix + pref);
     }
   },
 
   /**
    * Attach / detach reflow listeners depending on the checked status
    * of the `CSS > Log` menuitem.
    *
-   * @param function [aCallback=null]
+   * @param function [callback=null]
    *        Optional function to invoke when the listener has been
    *        added/removed.
    */
   _updateReflowActivityListener:
-    function WCF__updateReflowActivityListener(aCallback)
+    function WCF__updateReflowActivityListener(callback)
   {
     if (this.webConsoleClient) {
       let pref = this._filterPrefsPrefix + "csslog";
       if (Services.prefs.getBoolPref(pref)) {
-        this.webConsoleClient.startListeners(["ReflowActivity"], aCallback);
+        this.webConsoleClient.startListeners(["ReflowActivity"], callback);
       } else {
-        this.webConsoleClient.stopListeners(["ReflowActivity"], aCallback);
+        this.webConsoleClient.stopListeners(["ReflowActivity"], callback);
       }
     }
   },
 
   /**
    * Attach / detach server logging listener depending on the filter
    * preferences. If the user isn't interested in the server logs at
    * all the listener is not registered.
    *
-   * @param function [aCallback=null]
+   * @param function [callback=null]
    *        Optional function to invoke when the listener has been
    *        added/removed.
    */
   _updateServerLoggingListener:
-    function WCF__updateServerLoggingListener(aCallback)
+    function WCF__updateServerLoggingListener(callback)
   {
     if (!this.webConsoleClient) {
       return;
     }
 
     let startListener = false;
     let prefs = ["servererror", "serverwarn", "serverinfo", "serverlog"];
     for (let i = 0; i < prefs.length; i++) {
       if (this.filterPrefs[prefs[i]]) {
         startListener = true;
         break;
       }
     }
 
     if (startListener) {
-      this.webConsoleClient.startListeners(["ServerLogging"], aCallback);
+      this.webConsoleClient.startListeners(["ServerLogging"], callback);
     } else {
-      this.webConsoleClient.stopListeners(["ServerLogging"], aCallback);
+      this.webConsoleClient.stopListeners(["ServerLogging"], callback);
     }
   },
 
   /**
    * Sets the events for the filter input field.
    * @private
    */
   _setFilterTextBoxEvents: function WCF__setFilterTextBoxEvents()
@@ -750,35 +750,35 @@ WebConsoleFrame.prototype = {
    *        A descriptor that contains info about the button. Contains "name",
    *        "category", and "prefKey" properties, and optionally a "severities"
    *        property.
    */
   _initFilterButtons: function WCF__initFilterButtons()
   {
     let categories = this.document
                      .querySelectorAll(".webconsole-filter-button[category]");
-    Array.forEach(categories, function(aButton) {
-      aButton.addEventListener("contextmenu", (aEvent) => {
-        aButton.open = true;
+    Array.forEach(categories, function(button) {
+      button.addEventListener("contextmenu", (event) => {
+        button.open = true;
       }, false);
-      aButton.addEventListener("click", this._toggleFilter, false);
+      button.addEventListener("click", this._toggleFilter, false);
 
       let someChecked = false;
-      let severities = aButton.querySelectorAll("menuitem[prefKey]");
-      Array.forEach(severities, function(aMenuItem) {
-        aMenuItem.addEventListener("command", this._toggleFilter, false);
-
-        let prefKey = aMenuItem.getAttribute("prefKey");
+      let severities = button.querySelectorAll("menuitem[prefKey]");
+      Array.forEach(severities, function(menuItem) {
+        menuItem.addEventListener("command", this._toggleFilter, false);
+
+        let prefKey = menuItem.getAttribute("prefKey");
         let checked = this.filterPrefs[prefKey];
-        aMenuItem.setAttribute("checked", checked);
+        menuItem.setAttribute("checked", checked);
         someChecked = someChecked || checked;
       }, this);
 
-      aButton.setAttribute("checked", someChecked);
-      aButton.setAttribute("aria-pressed", someChecked);
+      button.setAttribute("checked", someChecked);
+      button.setAttribute("aria-pressed", someChecked);
     }, this);
 
     if (!this.owner._browserConsole) {
       // The Browser Console displays nsIConsoleMessages which are messages that
       // end up in the JS category, but they are not errors or warnings, they
       // are just log messages. The Web Console does not show such messages.
       let jslog = this.document.querySelector("menuitem[prefKey=jslog]");
       jslog.hidden = true;
@@ -799,30 +799,30 @@ WebConsoleFrame.prototype = {
 
   /**
    * Increase, decrease or reset the font size.
    *
    * @param string size
    *        The size of the font change. Accepted values are "+" and "-".
    *        An unmatched size assumes a font reset.
    */
-  changeFontSize: function WCF_changeFontSize(aSize)
+  changeFontSize: function WCF_changeFontSize(size)
   {
     let fontSize = this.window
                    .getComputedStyle(this.outputNode, null)
                    .getPropertyValue("font-size").replace("px", "");
 
     if (this.outputNode.style.fontSize) {
       fontSize = this.outputNode.style.fontSize.replace("px", "");
     }
 
-    if (aSize == "+" || aSize == "-") {
+    if (size == "+" || size == "-") {
       fontSize = parseInt(fontSize, 10);
 
-      if (aSize == "+") {
+      if (size == "+") {
         fontSize += 1;
       }
       else {
         fontSize -= 1;
       }
 
       if (fontSize < MIN_FONT_SIZE) {
         fontSize = MIN_FONT_SIZE;
@@ -871,32 +871,32 @@ WebConsoleFrame.prototype = {
                              .paddingLeft.replace(/[^0-9.]/g, "") - 4;
   },
 
   /**
    * The event handler that is called whenever a user switches a filter on or
    * off.
    *
    * @private
-   * @param nsIDOMEvent aEvent
+   * @param nsIDOMEvent event
    *        The event that triggered the filter change.
    */
-  _toggleFilter: function WCF__toggleFilter(aEvent)
+  _toggleFilter: function WCF__toggleFilter(event)
   {
-    let target = aEvent.target;
+    let target = event.target;
     let tagName = target.tagName;
     // Prevent toggle if generated from a contextmenu event (right click)
-    let isRightClick = (aEvent.button === 2); // right click is button 2;
-    if (tagName != aEvent.currentTarget.tagName || isRightClick) {
+    let isRightClick = (event.button === 2); // right click is button 2;
+    if (tagName != event.currentTarget.tagName || isRightClick) {
       return;
     }
 
     switch (tagName) {
       case "toolbarbutton": {
-        let originalTarget = aEvent.originalTarget;
+        let originalTarget = event.originalTarget;
         let classes = originalTarget.classList;
 
         if (originalTarget.localName !== "toolbarbutton") {
           // Oddly enough, the click event is sent to the menu button when
           // selecting a menu item with the mouse. Detect this case and bail
           // out.
           break;
         }
@@ -907,17 +907,17 @@ WebConsoleFrame.prototype = {
           // drop-down, so do nothing. (The menu will automatically appear
           // without our intervention.)
           break;
         }
 
         // Toggle on the targeted filter button, and if the user alt clicked,
         // toggle off all other filter buttons and their associated filters.
         let state = target.getAttribute("checked") !== "true";
-        if (aEvent.getModifierState("Alt")) {
+        if (event.getModifierState("Alt")) {
           let buttons = this.document
                         .querySelectorAll(".webconsole-filter-button");
           Array.forEach(buttons, (button) => {
             if (button !== target) {
               button.setAttribute("checked", false);
               button.setAttribute("aria-pressed", false);
               this._setMenuState(button, false);
             }
@@ -980,127 +980,127 @@ WebConsoleFrame.prototype = {
       }
     }
   },
 
   /**
    * Set the menu attributes for a specific toggle button.
    *
    * @private
-   * @param XULElement aTarget
+   * @param XULElement target
    *        Button with drop down items to be toggled.
-   * @param boolean aState
+   * @param boolean state
    *        True if the menu item is being toggled on, and false otherwise.
    */
-  _setMenuState: function WCF__setMenuState(aTarget, aState)
+  _setMenuState: function WCF__setMenuState(target, state)
   {
-    let menuItems = aTarget.querySelectorAll("menuitem");
+    let menuItems = target.querySelectorAll("menuitem");
     Array.forEach(menuItems, (item) => {
-      item.setAttribute("checked", aState);
+      item.setAttribute("checked", state);
       let prefKey = item.getAttribute("prefKey");
-      this.setFilterState(prefKey, aState);
+      this.setFilterState(prefKey, state);
     });
   },
 
   /**
    * Set the filter state for a specific toggle button.
    *
-   * @param string aToggleType
-   * @param boolean aState
+   * @param string toggleType
+   * @param boolean state
    * @returns void
    */
-  setFilterState: function WCF_setFilterState(aToggleType, aState)
+  setFilterState: function WCF_setFilterState(toggleType, state)
   {
-    this.filterPrefs[aToggleType] = aState;
-    this.adjustVisibilityForMessageType(aToggleType, aState);
-
-    Services.prefs.setBoolPref(this._filterPrefsPrefix + aToggleType, aState);
+    this.filterPrefs[toggleType] = state;
+    this.adjustVisibilityForMessageType(toggleType, state);
+
+    Services.prefs.setBoolPref(this._filterPrefsPrefix + toggleType, state);
 
     if (this._updateListenersTimeout) {
       Timers.clearTimeout(this._updateListenersTimeout);
     }
 
     this._updateListenersTimeout = Timers.setTimeout(
       this._onUpdateListeners, 200);
   },
 
   /**
    * Get the filter state for a specific toggle button.
    *
-   * @param string aToggleType
+   * @param string toggleType
    * @returns boolean
    */
-  getFilterState: function WCF_getFilterState(aToggleType)
+  getFilterState: function WCF_getFilterState(toggleType)
   {
-    return this.filterPrefs[aToggleType];
+    return this.filterPrefs[toggleType];
   },
 
   /**
    * Called when a logging filter changes. Allows to stop/start
    * listeners according to the current filter state.
    */
   _onUpdateListeners: function() {
     this._updateReflowActivityListener();
     this._updateServerLoggingListener();
   },
 
   /**
    * Check that the passed string matches the filter arguments.
    *
-   * @param String aString
+   * @param String str
    *        to search for filter words in.
-   * @param String aFilter
+   * @param String filter
    *        is a string containing all of the words to filter on.
    * @returns boolean
    */
-  stringMatchesFilters: function WCF_stringMatchesFilters(aString, aFilter)
+  stringMatchesFilters: function WCF_stringMatchesFilters(str, filter)
   {
-    if (!aFilter || !aString) {
+    if (!filter || !str) {
       return true;
     }
 
-    let searchStr = aString.toLowerCase();
-    let filterStrings = aFilter.toLowerCase().split(/\s+/);
+    let searchStr = str.toLowerCase();
+    let filterStrings = filter.toLowerCase().split(/\s+/);
     return !filterStrings.some(function (f) {
       return searchStr.indexOf(f) == -1;
     });
   },
 
   /**
    * Turns the display of log nodes on and off appropriately to reflect the
-   * adjustment of the message type filter named by @aPrefKey.
+   * adjustment of the message type filter named by @prefKey.
    *
-   * @param string aPrefKey
+   * @param string prefKey
    *        The preference key for the message type being filtered: one of the
    *        values in the MESSAGE_PREFERENCE_KEYS table.
-   * @param boolean aState
-   *        True if the filter named by @aMessageType is being turned on; false
+   * @param boolean state
+   *        True if the filter named by @messageType is being turned on; false
    *        otherwise.
    * @returns void
    */
   adjustVisibilityForMessageType:
-  function WCF_adjustVisibilityForMessageType(aPrefKey, aState)
+  function WCF_adjustVisibilityForMessageType(prefKey, state)
   {
     let outputNode = this.outputNode;
     let doc = this.document;
 
     // Look for message nodes (".message") with the given preference key
     // (filter="error", filter="cssparser", etc.) and add or remove the
     // "filtered-by-type" class, which turns on or off the display.
 
-    let attribute = WORKERTYPES_PREFKEYS.indexOf(aPrefKey) == -1
+    let attribute = WORKERTYPES_PREFKEYS.indexOf(prefKey) == -1
                       ? 'filter' : 'workerType';
 
     let xpath = ".//*[contains(@class, 'message') and " +
-      "@" + attribute + "='" + aPrefKey + "']";
+      "@" + attribute + "='" + prefKey + "']";
     let result = doc.evaluate(xpath, outputNode, null,
       Ci.nsIDOMXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
     for (let i = 0; i < result.snapshotLength; i++) {
       let node = result.snapshotItem(i);
-      if (aState) {
+      if (state) {
         node.classList.remove("filtered-by-type");
       }
       else {
         node.classList.add("filtered-by-type");
       }
     }
   },
 
@@ -1129,277 +1129,277 @@ WebConsoleFrame.prototype = {
       }
     }
   },
 
   /**
    * Applies the user's filters to a newly-created message node via CSS
    * classes.
    *
-   * @param nsIDOMNode aNode
+   * @param nsIDOMNode node
    *        The newly-created message node.
    * @return boolean
    *         True if the message was filtered or false otherwise.
    */
-  filterMessageNode: function WCF_filterMessageNode(aNode)
+  filterMessageNode: function WCF_filterMessageNode(node)
   {
     let isFiltered = false;
 
     // Filter by the message type.
-    let prefKey = MESSAGE_PREFERENCE_KEYS[aNode.category][aNode.severity];
+    let prefKey = MESSAGE_PREFERENCE_KEYS[node.category][node.severity];
     if (prefKey && !this.getFilterState(prefKey)) {
       // The node is filtered by type.
-      aNode.classList.add("filtered-by-type");
+      node.classList.add("filtered-by-type");
       isFiltered = true;
     }
 
     // Filter by worker type
-    if ("workerType" in aNode && !this.getFilterState(aNode.workerType)) {
-      aNode.classList.add("filtered-by-type");
+    if ("workerType" in node && !this.getFilterState(node.workerType)) {
+      node.classList.add("filtered-by-type");
       isFiltered = true;
     }
 
     // Filter on the search string.
     let search = this.filterBox.value;
-    let text = aNode.clipboardText;
+    let text = node.clipboardText;
 
     // if string matches the filter text
     if (!this.stringMatchesFilters(text, search)) {
-      aNode.classList.add("filtered-by-string");
+      node.classList.add("filtered-by-string");
       isFiltered = true;
     }
 
-    if (isFiltered && aNode.classList.contains("inlined-variables-view")) {
-      aNode.classList.add("hidden-message");
+    if (isFiltered && node.classList.contains("inlined-variables-view")) {
+      node.classList.add("hidden-message");
     }
 
     return isFiltered;
   },
 
   /**
    * Merge the attributes of the two nodes that are about to be filtered.
-   * Increment the number of repeats of aOriginal.
+   * Increment the number of repeats of original.
    *
-   * @param nsIDOMNode aOriginal
+   * @param nsIDOMNode original
    *        The Original Node. The one being merged into.
-   * @param nsIDOMNode aFiltered
+   * @param nsIDOMNode filtered
    *        The node being filtered out because it is repeated.
    */
   mergeFilteredMessageNode:
-  function WCF_mergeFilteredMessageNode(aOriginal, aFiltered)
+  function WCF_mergeFilteredMessageNode(original, filtered)
   {
-    let repeatNode = aOriginal.getElementsByClassName("message-repeats")[0];
+    let repeatNode = original.getElementsByClassName("message-repeats")[0];
     if (!repeatNode) {
       return; // no repeat node, return early.
     }
 
     let occurrences = parseInt(repeatNode.getAttribute("value")) + 1;
     repeatNode.setAttribute("value", occurrences);
     repeatNode.textContent = occurrences;
     let str = l10n.getStr("messageRepeats.tooltip2");
     repeatNode.title = PluralForm.get(occurrences, str)
                        .replace("#1", occurrences);
   },
 
   /**
    * Filter the message node from the output if it is a repeat.
    *
    * @private
-   * @param nsIDOMNode aNode
+   * @param nsIDOMNode node
    *        The message node to be filtered or not.
    * @returns nsIDOMNode|null
    *          Returns the duplicate node if the message was filtered, null
    *          otherwise.
    */
-  _filterRepeatedMessage: function WCF__filterRepeatedMessage(aNode)
+  _filterRepeatedMessage: function WCF__filterRepeatedMessage(node)
   {
-    let repeatNode = aNode.getElementsByClassName("message-repeats")[0];
+    let repeatNode = node.getElementsByClassName("message-repeats")[0];
     if (!repeatNode) {
       return null;
     }
 
     let uid = repeatNode._uid;
     let dupeNode = null;
 
-    if (aNode.category == CATEGORY_CSS ||
-        aNode.category == CATEGORY_SECURITY) {
+    if (node.category == CATEGORY_CSS ||
+        node.category == CATEGORY_SECURITY) {
       dupeNode = this._repeatNodes[uid];
       if (!dupeNode) {
-        this._repeatNodes[uid] = aNode;
+        this._repeatNodes[uid] = node;
       }
     }
-    else if ((aNode.category == CATEGORY_WEBDEV ||
-              aNode.category == CATEGORY_JS) &&
-             aNode.category != CATEGORY_NETWORK &&
-             !aNode.classList.contains("inlined-variables-view")) {
+    else if ((node.category == CATEGORY_WEBDEV ||
+              node.category == CATEGORY_JS) &&
+             node.category != CATEGORY_NETWORK &&
+             !node.classList.contains("inlined-variables-view")) {
       let lastMessage = this.outputNode.lastChild;
       if (!lastMessage) {
         return null;
       }
 
       let lastRepeatNode = lastMessage.getElementsByClassName("message-repeats")[0];
       if (lastRepeatNode && lastRepeatNode._uid == uid) {
         dupeNode = lastMessage;
       }
     }
 
     if (dupeNode) {
-      this.mergeFilteredMessageNode(dupeNode, aNode);
+      this.mergeFilteredMessageNode(dupeNode, node);
       return dupeNode;
     }
 
     return null;
   },
 
   /**
    * Display cached messages that may have been collected before the UI is
    * displayed.
    *
-   * @param array aRemoteMessages
+   * @param array remoteMessages
    *        Array of cached messages coming from the remote Web Console
    *        content instance.
    */
-  displayCachedMessages: function WCF_displayCachedMessages(aRemoteMessages)
+  displayCachedMessages: function WCF_displayCachedMessages(remoteMessages)
   {
-    if (!aRemoteMessages.length) {
+    if (!remoteMessages.length) {
       return;
     }
 
-    aRemoteMessages.forEach(function(aMessage) {
-      switch (aMessage._type) {
+    remoteMessages.forEach(function(message) {
+      switch (message._type) {
         case "PageError": {
-          let category = Utils.categoryForScriptError(aMessage);
+          let category = Utils.categoryForScriptError(message);
           this.outputMessage(category, this.reportPageError,
-                             [category, aMessage]);
+                             [category, message]);
           break;
         }
         case "LogMessage":
-          this.handleLogMessage(aMessage);
+          this.handleLogMessage(message);
           break;
         case "ConsoleAPI":
           this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage,
-                             [aMessage]);
+                             [message]);
           break;
         case "NetworkEvent":
-          this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [aMessage]);
+          this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [message]);
           break;
       }
     }, this);
   },
 
   /**
    * Logs a message to the Web Console that originates from the Web Console
    * server.
    *
-   * @param object aMessage
+   * @param object message
    *        The message received from the server.
    * @return nsIDOMElement|null
    *         The message element to display in the Web Console output.
    */
-  logConsoleAPIMessage: function WCF_logConsoleAPIMessage(aMessage)
+  logConsoleAPIMessage: function WCF_logConsoleAPIMessage(message)
   {
     let body = null;
     let clipboardText = null;
-    let sourceURL = aMessage.filename;
-    let sourceLine = aMessage.lineNumber;
-    let level = aMessage.level;
-    let args = aMessage.arguments;
+    let sourceURL = message.filename;
+    let sourceLine = message.lineNumber;
+    let level = message.level;
+    let args = message.arguments;
     let objectActors = new Set();
     let node = null;
 
     // Gather the actor IDs.
-    args.forEach((aValue) => {
-      if (WebConsoleUtils.isActorGrip(aValue)) {
-        objectActors.add(aValue.actor);
+    args.forEach((value) => {
+      if (WebConsoleUtils.isActorGrip(value)) {
+        objectActors.add(value.actor);
       }
     });
 
     switch (level) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "exception":
       case "assert":
       case "debug": {
-        let msg = new Messages.ConsoleGeneric(aMessage);
+        let msg = new Messages.ConsoleGeneric(message);
         node = msg.init(this.output).render().element;
         break;
       }
       case "table": {
-        let msg = new Messages.ConsoleTable(aMessage);
+        let msg = new Messages.ConsoleTable(message);
         node = msg.init(this.output).render().element;
         break;
       }
       case "trace": {
-        let msg = new Messages.ConsoleTrace(aMessage);
+        let msg = new Messages.ConsoleTrace(message);
         node = msg.init(this.output).render().element;
         break;
       }
       case "dir": {
         body = { arguments: args };
         let clipboardArray = [];
-        args.forEach((aValue) => {
-          clipboardArray.push(VariablesView.getString(aValue));
+        args.forEach((value) => {
+          clipboardArray.push(VariablesView.getString(value));
         });
         clipboardText = clipboardArray.join(" ");
         break;
       }
       case "dirxml": {
         // We just alias console.dirxml() with console.log().
-        aMessage.level = "log";
-        return WCF_logConsoleAPIMessage.call(this, aMessage);
+        message.level = "log";
+        return WCF_logConsoleAPIMessage.call(this, message);
       }
       case "group":
       case "groupCollapsed":
-        clipboardText = body = aMessage.groupName;
+        clipboardText = body = message.groupName;
         this.groupDepth++;
         break;
 
       case "groupEnd":
         if (this.groupDepth > 0) {
           this.groupDepth--;
         }
         break;
 
       case "time": {
-        let timer = aMessage.timer;
+        let timer = message.timer;
         if (!timer) {
           return null;
         }
         if (timer.error) {
           Cu.reportError(l10n.getStr(timer.error));
           return null;
         }
         body = l10n.getFormatStr("timerStarted", [timer.name]);
         clipboardText = body;
         break;
       }
 
       case "timeEnd": {
-        let timer = aMessage.timer;
+        let timer = message.timer;
         if (!timer) {
           return null;
         }
         let duration = Math.round(timer.duration * 100) / 100;
         body = l10n.getFormatStr("timeEnd", [timer.name, duration]);
         clipboardText = body;
         break;
       }
 
       case "count": {
-        let counter = aMessage.counter;
+        let counter = message.counter;
         if (!counter) {
           return null;
         }
         if (counter.error) {
           Cu.reportError(l10n.getStr(counter.error));
           return null;
         }
-        let msg = new Messages.ConsoleGeneric(aMessage);
+        let msg = new Messages.ConsoleGeneric(message);
         node = msg.init(this.output).render().element;
         break;
       }
 
       case "timeStamp": {
         // console.timeStamp() doesn't need to display anything.
         return null;
       }
@@ -1426,181 +1426,183 @@ WebConsoleFrame.prototype = {
 
     if (level == "groupEnd") {
       return null; // no need to continue
     }
 
     if (!node) {
       node = this.createMessageNode(CATEGORY_WEBDEV, LEVELS[level], body,
                                     sourceURL, sourceLine, clipboardText,
-                                    level, aMessage.timeStamp);
-      if (aMessage.private) {
+                                    level, message.timeStamp);
+      if (message.private) {
         node.setAttribute("private", true);
       }
     }
 
     if (objectActors.size > 0) {
       node._objectActors = objectActors;
 
       if (!node._messageObject) {
         let repeatNode = node.getElementsByClassName("message-repeats")[0];
         repeatNode._uid += [...objectActors].join("-");
       }
     }
 
-    let workerTypeID = CONSOLE_WORKER_IDS.indexOf(aMessage.workerType);
+    let workerTypeID = CONSOLE_WORKER_IDS.indexOf(message.workerType);
     if (workerTypeID != -1) {
       node.workerType = WORKERTYPES_PREFKEYS[workerTypeID];
       node.setAttribute('workerType', WORKERTYPES_PREFKEYS[workerTypeID]);
     }
 
     return node;
   },
 
   /**
    * Handle ConsoleAPICall objects received from the server. This method outputs
    * the window.console API call.
    *
-   * @param object aMessage
+   * @param object message
    *        The console API message received from the server.
    */
-  handleConsoleAPICall: function WCF_handleConsoleAPICall(aMessage)
+  handleConsoleAPICall: function WCF_handleConsoleAPICall(message)
   {
-    this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [aMessage]);
+    this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [message]);
   },
 
   /**
    * Reports an error in the page source, either JavaScript or CSS.
    *
-   * @param nsIScriptError aScriptError
+   * @param nsIScriptError scriptError
    *        The error message to report.
    * @return nsIDOMElement|undefined
    *         The message element to display in the Web Console output.
    */
-  reportPageError: function WCF_reportPageError(aCategory, aScriptError)
+  reportPageError: function WCF_reportPageError(category, scriptError)
   {
     // Warnings and legacy strict errors become warnings; other types become
     // errors.
     let severity = 'error';
-    if (aScriptError.warning || aScriptError.strict) {
+    if (scriptError.warning || scriptError.strict) {
       severity = 'warning';
-    } else if (aScriptError.info) {
+    } else if (scriptError.info) {
       severity = 'log';
     }
 
-    let category = 'js';
-    switch(aCategory) {
+    switch(category) {
       case CATEGORY_CSS:
         category = 'css';
         break;
       case CATEGORY_SECURITY:
         category = 'security';
         break;
+      default:
+        category = "js";
+        break;
     }
 
     let objectActors = new Set();
 
     // Gather the actor IDs.
     for (let prop of ["errorMessage", "lineText"]) {
-      let grip = aScriptError[prop];
+      let grip = scriptError[prop];
       if (WebConsoleUtils.isActorGrip(grip)) {
         objectActors.add(grip.actor);
       }
     }
 
-    let errorMessage = aScriptError.errorMessage;
+    let errorMessage = scriptError.errorMessage;
     if (errorMessage.type && errorMessage.type == "longString") {
       errorMessage = errorMessage.initial;
     }
 
-    let displayOrigin = aScriptError.sourceName;
+    let displayOrigin = scriptError.sourceName;
 
     // TLS errors are related to the connection and not the resource; therefore
     // it makes sense to only display the protcol, host and port (prePath).
     // This also means messages are grouped for a single origin.
-    if (aScriptError.category && aScriptError.category == "SHA-1 Signature") {
-      let sourceURI = Services.io.newURI(aScriptError.sourceName, null, null).QueryInterface(Ci.nsIURL);
+    if (scriptError.category && scriptError.category == "SHA-1 Signature") {
+      let sourceURI = Services.io.newURI(scriptError.sourceName, null, null).QueryInterface(Ci.nsIURL);
       displayOrigin = sourceURI.prePath;
     }
 
     // Create a new message
     let msg = new Messages.Simple(errorMessage, {
       location: {
         url: displayOrigin,
-        line: aScriptError.lineNumber,
-        column: aScriptError.columnNumber
+        line: scriptError.lineNumber,
+        column: scriptError.columnNumber
       },
-      stack: aScriptError.stacktrace,
+      stack: scriptError.stacktrace,
       category: category,
       severity: severity,
-      timestamp: aScriptError.timeStamp,
-      private: aScriptError.private,
+      timestamp: scriptError.timeStamp,
+      private: scriptError.private,
       filterDuplicates: true
     });
 
     let node = msg.init(this.output).render().element;
 
     // Select the body of the message node that is displayed in the console
     let msgBody = node.getElementsByClassName("message-body")[0];
     // Add the more info link node to messages that belong to certain categories
-    this.addMoreInfoLink(msgBody, aScriptError);
+    this.addMoreInfoLink(msgBody, scriptError);
 
     if (objectActors.size > 0) {
       node._objectActors = objectActors;
     }
 
     return node;
   },
 
   /**
    * Handle PageError objects received from the server. This method outputs the
    * given error.
    *
-   * @param nsIScriptError aPageError
+   * @param nsIScriptError pageError
    *        The error received from the server.
    */
-  handlePageError: function WCF_handlePageError(aPageError)
+  handlePageError: function WCF_handlePageError(pageError)
   {
-    let category = Utils.categoryForScriptError(aPageError);
-    this.outputMessage(category, this.reportPageError, [category, aPageError]);
+    let category = Utils.categoryForScriptError(pageError);
+    this.outputMessage(category, this.reportPageError, [category, pageError]);
   },
 
   /**
    * Handle log messages received from the server. This method outputs the given
    * message.
    *
-   * @param object aPacket
+   * @param object packet
    *        The message packet received from the server.
    */
-  handleLogMessage: function WCF_handleLogMessage(aPacket)
+  handleLogMessage: function WCF_handleLogMessage(packet)
   {
-    if (aPacket.message) {
-      this.outputMessage(CATEGORY_JS, this._reportLogMessage, [aPacket]);
+    if (packet.message) {
+      this.outputMessage(CATEGORY_JS, this._reportLogMessage, [packet]);
     }
   },
 
   /**
    * Display log messages received from the server.
    *
    * @private
-   * @param object aPacket
+   * @param object packet
    *        The message packet received from the server.
    * @return nsIDOMElement
    *         The message element to render for the given log message.
    */
-  _reportLogMessage: function WCF__reportLogMessage(aPacket)
+  _reportLogMessage: function WCF__reportLogMessage(packet)
   {
-    let msg = aPacket.message;
+    let msg = packet.message;
     if (msg.type && msg.type == "longString") {
       msg = msg.initial;
     }
     let node = this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, msg, null,
-                                      null, null, null, aPacket.timeStamp);
-    if (WebConsoleUtils.isActorGrip(aPacket.message)) {
-      node._objectActors = new Set([aPacket.message.actor]);
+                                      null, null, null, packet.timeStamp);
+    if (WebConsoleUtils.isActorGrip(packet.message)) {
+      node._objectActors = new Set([packet.message.actor]);
     }
     return node;
   },
 
   /**
    * Log network event.
    *
    * @param object networkInfo
@@ -1683,52 +1685,52 @@ WebConsoleFrame.prototype = {
     this._updateNetMessage(actorId);
 
     return messageNode;
   },
 
   /**
    * Create a mixed content warning Node.
    *
-   * @param aLinkNode
+   * @param linkNode
    *        Parent to the requested urlNode.
    */
-  makeMixedContentNode: function WCF_makeMixedContentNode(aLinkNode)
+  makeMixedContentNode: function WCF_makeMixedContentNode(linkNode)
   {
     let mixedContentWarning = "[" + l10n.getStr("webConsoleMixedContentWarning") + "]";
 
     // Mixed content warning message links to a Learn More page
     let mixedContentWarningNode = this.document.createElementNS(XHTML_NS, "a");
     mixedContentWarningNode.title = MIXED_CONTENT_LEARN_MORE;
     mixedContentWarningNode.href = MIXED_CONTENT_LEARN_MORE;
     mixedContentWarningNode.className = "learn-more-link";
     mixedContentWarningNode.textContent = mixedContentWarning;
     mixedContentWarningNode.draggable = false;
 
-    aLinkNode.appendChild(mixedContentWarningNode);
-
-    this._addMessageLinkCallback(mixedContentWarningNode, (aEvent) => {
-      aEvent.stopPropagation();
+    linkNode.appendChild(mixedContentWarningNode);
+
+    this._addMessageLinkCallback(mixedContentWarningNode, (event) => {
+      event.stopPropagation();
       this.owner.openLink(MIXED_CONTENT_LEARN_MORE);
     });
   },
 
   /**
    * Adds a more info link node to messages based on the nsIScriptError object
    * that we need to report to the console
    *
-   * @param aNode
+   * @param node
    *        The node to which we will be adding the more info link node
-   * @param aScriptError
+   * @param scriptError
    *        The script error object that we are reporting to the console
    */
-  addMoreInfoLink: function WCF_addMoreInfoLink(aNode, aScriptError)
+  addMoreInfoLink: function WCF_addMoreInfoLink(node, scriptError)
   {
     let url;
-    switch (aScriptError.category) {
+    switch (scriptError.category) {
       case "Insecure Password Field":
         url = INSECURE_PASSWORDS_LEARN_MORE;
         break;
       case "Mixed Content Message":
       case "Mixed Content Blocker":
         url = MIXED_CONTENT_LEARN_MORE;
         break;
       case "Invalid HPKP Headers":
@@ -1743,123 +1745,123 @@ WebConsoleFrame.prototype = {
       case "Tracking Protection":
         url = TRACKING_PROTECTION_LEARN_MORE;
         break;
       default:
         // Unknown category. Return without adding more info node.
         return;
     }
 
-    this.addLearnMoreWarningNode(aNode, url);
+    this.addLearnMoreWarningNode(node, url);
   },
 
   /*
    * Appends a clickable warning node to the node passed
    * as a parameter to the function. When a user clicks on the appended
    * warning node, the browser navigates to the provided url.
    *
-   * @param aNode
+   * @param node
    *        The node to which we will be adding a clickable warning node.
-   * @param aURL
+   * @param url
    *        The url which points to the page where the user can learn more
    *        about security issues associated with the specific message that's
    *        being logged.
    */
   addLearnMoreWarningNode:
-  function WCF_addLearnMoreWarningNode(aNode, aURL)
+  function WCF_addLearnMoreWarningNode(node, url)
   {
     let moreInfoLabel = "[" + l10n.getStr("webConsoleMoreInfoLabel") + "]";
 
     let warningNode = this.document.createElementNS(XHTML_NS, "a");
-    warningNode.title = aURL;
-    warningNode.href = aURL;
+    warningNode.title = url;
+    warningNode.href = url;
     warningNode.draggable = false;
     warningNode.textContent = moreInfoLabel;
     warningNode.className = "learn-more-link";
 
-    this._addMessageLinkCallback(warningNode, (aEvent) => {
-      aEvent.stopPropagation();
-      this.owner.openLink(aURL);
+    this._addMessageLinkCallback(warningNode, (event) => {
+      event.stopPropagation();
+      this.owner.openLink(url);
     });
 
-    aNode.appendChild(warningNode);
+    node.appendChild(warningNode);
   },
 
   /**
    * Log file activity.
    *
-   * @param string aFileURI
+   * @param string fileURI
    *        The file URI that was loaded.
    * @return nsIDOMElement|undefined
    *         The message element to display in the Web Console output.
    */
-  logFileActivity: function WCF_logFileActivity(aFileURI)
+  logFileActivity: function WCF_logFileActivity(fileURI)
   {
     let urlNode = this.document.createElementNS(XHTML_NS, "a");
-    urlNode.setAttribute("title", aFileURI);
+    urlNode.setAttribute("title", fileURI);
     urlNode.className = "url";
-    urlNode.textContent = aFileURI;
+    urlNode.textContent = fileURI;
     urlNode.draggable = false;
-    urlNode.href = aFileURI;
+    urlNode.href = fileURI;
 
     let outputNode = this.createMessageNode(CATEGORY_NETWORK, SEVERITY_LOG,
-                                            urlNode, null, null, aFileURI);
+                                            urlNode, null, null, fileURI);
 
     this._addMessageLinkCallback(urlNode, () => {
-      this.owner.viewSource(aFileURI);
+      this.owner.viewSource(fileURI);
     });
 
     return outputNode;
   },
 
   /**
    * Handle the file activity messages coming from the remote Web Console.
    *
-   * @param string aFileURI
+   * @param string fileURI
    *        The file URI that was requested.
    */
-  handleFileActivity: function WCF_handleFileActivity(aFileURI)
+  handleFileActivity: function WCF_handleFileActivity(fileURI)
   {
-    this.outputMessage(CATEGORY_NETWORK, this.logFileActivity, [aFileURI]);
+    this.outputMessage(CATEGORY_NETWORK, this.logFileActivity, [fileURI]);
   },
 
   /**
    * Handle the reflow activity messages coming from the remote Web Console.
    *
-   * @param object aMessage
+   * @param object msg
    *        An object holding information about a reflow batch.
    */
-  logReflowActivity: function WCF_logReflowActivity(aMessage)
+  logReflowActivity: function WCF_logReflowActivity(message)
   {
-    let {start, end, sourceURL, sourceLine} = aMessage;
+    let {start, end, sourceURL, sourceLine} = message;
     let duration = Math.round((end - start) * 100) / 100;
     let node = this.document.createElementNS(XHTML_NS, "span");
     if (sourceURL) {
       node.textContent = l10n.getFormatStr("reflow.messageWithLink", [duration]);
       let a = this.document.createElementNS(XHTML_NS, "a");
       a.href = "#";
       a.draggable = "false";
       let filename = WebConsoleUtils.abbreviateSourceURL(sourceURL);
-      let functionName = aMessage.functionName || l10n.getStr("stacktrace.anonymousFunction");
+      let functionName = message.functionName || l10n.getStr("stacktrace.anonymousFunction");
       a.textContent = l10n.getFormatStr("reflow.messageLinkText",
                          [functionName, filename, sourceLine]);
       this._addMessageLinkCallback(a, () => {
         this.owner.viewSourceInDebugger(sourceURL, sourceLine);
       });
       node.appendChild(a);
     } else {
       node.textContent = l10n.getFormatStr("reflow.messageWithNoLink", [duration]);
     }
     return this.createMessageNode(CATEGORY_CSS, SEVERITY_LOG, node);
   },
 
 
-  handleReflowActivity: function WCF_handleReflowActivity(aMessage)
+  handleReflowActivity: function WCF_handleReflowActivity(message)
   {
-    this.outputMessage(CATEGORY_CSS, this.logReflowActivity, [aMessage]);
+    this.outputMessage(CATEGORY_CSS, this.logReflowActivity, [message]);
   },
 
   /**
    * Inform user that the window.console API has been replaced by a script
    * in a content page.
    */
   logWarningAboutReplacedAPI: function WCF_logWarningAboutReplacedAPI()
   {
@@ -1906,24 +1908,24 @@ WebConsoleFrame.prototype = {
     }
   },
 
   /**
    * Update an output message to reflect the latest state of a network request,
    * given a network event actor ID.
    *
    * @private
-   * @param string aActorId
+   * @param string actorId
    *        The network event actor ID for which you want to update the message.
    * @return boolean
    *         |true| if the message node was updated, or |false| otherwise.
    */
-  _updateNetMessage: function WCF__updateNetMessage(aActorId)
+  _updateNetMessage: function WCF__updateNetMessage(actorId)
   {
-    let networkInfo = this.webConsoleClient.getNetworkRequest(aActorId);
+    let networkInfo = this.webConsoleClient.getNetworkRequest(actorId);
     if (!networkInfo || !networkInfo.node) {
       return;
     }
 
     let messageNode = networkInfo.node;
     let updates = networkInfo.updates;
     let hasEventTimings = updates.indexOf("eventTimings") > -1;
     let hasResponseStart = updates.indexOf("responseStart") > -1;
@@ -1980,87 +1982,87 @@ WebConsoleFrame.prototype = {
     return toolbox.selectTool("netmonitor").then(panel => {
       return panel.panelWin.NetMonitorController.inspectRequest(requestId);
     });
   },
 
   /**
    * Handler for page location changes.
    *
-   * @param string aURI
+   * @param string uri
    *        New page location.
-   * @param string aTitle
+   * @param string title
    *        New page title.
    */
-  onLocationChange: function WCF_onLocationChange(aURI, aTitle)
+  onLocationChange: function WCF_onLocationChange(uri, title)
   {
-    this.contentLocation = aURI;
+    this.contentLocation = uri;
     if (this.owner.onLocationChange) {
-      this.owner.onLocationChange(aURI, aTitle);
+      this.owner.onLocationChange(uri, title);
     }
   },
 
   /**
    * Handler for the tabNavigated notification.
    *
-   * @param string aEvent
+   * @param string event
    *        Event name.
-   * @param object aPacket
+   * @param object packet
    *        Notification packet received from the server.
    */
-  handleTabNavigated: function WCF_handleTabNavigated(aEvent, aPacket)
+  handleTabNavigated: function WCF_handleTabNavigated(event, packet)
   {
-    if (aEvent == "will-navigate") {
+    if (event == "will-navigate") {
       if (this.persistLog) {
-        let marker = new Messages.NavigationMarker(aPacket, Date.now());
+        let marker = new Messages.NavigationMarker(packet, Date.now());
         this.output.addMessage(marker);
       }
       else {
         this.jsterm.clearOutput();
       }
     }
 
-    if (aPacket.url) {
-      this.onLocationChange(aPacket.url, aPacket.title);
-    }
-
-    if (aEvent == "navigate" && !aPacket.nativeConsoleAPI) {
+    if (packet.url) {
+      this.onLocationChange(packet.url, packet.title);
+    }
+
+    if (event == "navigate" && !packet.nativeConsoleAPI) {
       this.logWarningAboutReplacedAPI();
     }
   },
 
   /**
    * Output a message node. This filters a node appropriately, then sends it to
    * the output, regrouping and pruning output as necessary.
    *
    * Note: this call is async - the given message node may not be displayed when
    * you call this method.
    *
-   * @param integer aCategory
+   * @param integer category
    *        The category of the message you want to output. See the CATEGORY_*
    *        constants.
-   * @param function|nsIDOMElement aMethodOrNode
+   * @param function|nsIDOMElement methodOrNode
    *        The method that creates the message element to send to the output or
    *        the actual element. If a method is given it will be bound to the HUD
-   *        object and the arguments will be |aArguments|.
-   * @param array [aArguments]
+   *        object and the arguments will be |args|.
+   * @param array [args]
    *        If a method is given to output the message element then the method
    *        will be invoked with the list of arguments given here. The last
    *        object in this array should be the packet received from the
    *        back end.
    */
-  outputMessage: function WCF_outputMessage(aCategory, aMethodOrNode, aArguments)
+  outputMessage: function WCF_outputMessage(category, methodOrNode, args)
   {
     if (!this._outputQueue.length) {
       // If the queue is empty we consider that now was the last output flush.
       // This avoid an immediate output flush when the timer executes.
       this._lastOutputFlush = Date.now();
     }
 
-    this._outputQueue.push([aCategory, aMethodOrNode, aArguments]);
+    this._outputQueue.push([category, methodOrNode, args]);
 
     this._initOutputTimer();
   },
 
   /**
    * Try to flush the output message queue. This takes the messages in the
    * output queue and displays them. Outputting stops at MESSAGES_IN_INTERVAL.
    * Further output is queued to happen later - see OUTPUT_INTERVAL.
@@ -2141,18 +2143,18 @@ WebConsoleFrame.prototype = {
     if (shouldPrune || !this._outputQueue.length) {
       // Only bother measuring the scrollHeight if not scrolled to bottom,
       // since the oldScrollHeight will not be used if it is.
       if (!scrolledToBottom) {
         oldScrollHeight = scrollNode.scrollHeight;
       }
 
       let categories = Object.keys(this._pruneCategoriesQueue);
-      categories.forEach(function _pruneOutput(aCategory) {
-        removedNodes += this.pruneOutputIfNecessary(aCategory);
+      categories.forEach(function _pruneOutput(category) {
+        removedNodes += this.pruneOutputIfNecessary(category);
       }, this);
       this._pruneCategoriesQueue = {};
     }
 
     let isInputOutput = lastVisibleNode &&
                         (lastVisibleNode.category == CATEGORY_INPUT ||
                          lastVisibleNode.category == CATEGORY_OUTPUT);
 
@@ -2206,31 +2208,31 @@ WebConsoleFrame.prototype = {
                                        OUTPUT_INTERVAL,
                                        Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   /**
    * Output a message from the queue.
    *
    * @private
-   * @param nsISupportsString aHudIdSupportsString
+   * @param nsISupportsString hudIdSupportsString
    *        The HUD ID as an nsISupportsString.
-   * @param array aItem
+   * @param array item
    *        An item from the output queue - this item represents a message.
    * @return object
    *         An object that holds the following properties:
    *         - node: the DOM element of the message.
    *         - isRepeated: the DOM element of the original message, if this is
    *         a repeated message, otherwise null.
    *         - visible: boolean that tells if the message is visible.
    */
   _outputMessageFromQueue:
-  function WCF__outputMessageFromQueue(aHudIdSupportsString, aItem)
+  function WCF__outputMessageFromQueue(hudIdSupportsString, item)
   {
-    let [category, methodOrNode, args] = aItem;
+    let [category, methodOrNode, args] = item;
 
     // The last object in the args array should be message
     // object or response packet received from the server.
     let message = (args && args.length) ? args[args.length-1] : null;
 
     let node = typeof methodOrNode == "function" ?
                methodOrNode.apply(this, args || []) :
                methodOrNode;
@@ -2249,17 +2251,17 @@ WebConsoleFrame.prototype = {
 
     let visible = !isRepeated && !isFiltered;
     if (!isRepeated) {
       this.outputNode.insertBefore(node,
                                    afterNode ? afterNode.nextSibling : null);
       this._pruneCategoriesQueue[node.category] = true;
 
       let nodeID = node.getAttribute("id");
-      Services.obs.notifyObservers(aHudIdSupportsString,
+      Services.obs.notifyObservers(hudIdSupportsString,
                                    "web-console-message-created", nodeID);
 
     }
 
     if (node._onOutput) {
       node._onOutput();
       delete node._onOutput;
     }
@@ -2277,22 +2279,22 @@ WebConsoleFrame.prototype = {
    * that will be removed at the end of the queue anyway.
    * @private
    */
   _pruneOutputQueue: function WCF__pruneOutputQueue()
   {
     let nodes = {};
 
     // Group the messages per category.
-    this._outputQueue.forEach(function(aItem, aIndex) {
-      let [category] = aItem;
+    this._outputQueue.forEach(function(item, index) {
+      let [category] = item;
       if (!(category in nodes)) {
         nodes[category] = [];
       }
-      nodes[category].push(aIndex);
+      nodes[category].push(index);
     }, this);
 
     let pruned = 0;
 
     // Loop through the categories we found and prune if needed.
     for (let category in nodes) {
       let limit = Utils.logLimitForCategory(category);
       let indexes = nodes[category];
@@ -2309,25 +2311,25 @@ WebConsoleFrame.prototype = {
     return pruned;
   },
 
   /**
    * Destroy an item that was once in the outputQueue but isn't needed
    * after all.
    *
    * @private
-   * @param array aItem
+   * @param array item
    *        The item you want to destroy.  Does not remove it from the output
    *        queue.
    */
-  _destroyItem: function WCF__destroyItem(aItem)
+  _destroyItem: function WCF__destroyItem(item)
   {
     // TODO: handle object releasing in a more elegant way once all console
     // messages use the new API - bug 778766.
-    let [category, methodOrNode, args] = aItem;
+    let [category, methodOrNode, args] = item;
     if (typeof methodOrNode != "function" && methodOrNode._objectActors) {
       for (let actor of methodOrNode._objectActors) {
         this._releaseObject(actor);
       }
       methodOrNode._objectActors.clear();
     }
 
     if (methodOrNode == this.output._flushMessageQueue &&
@@ -2353,19 +2355,19 @@ WebConsoleFrame.prototype = {
       }
       if (connectionId && this.webConsoleClient.hasNetworkRequest(connectionId)) {
         this.webConsoleClient.removeNetworkRequest(connectionId);
         this._releaseObject(connectionId);
       }
     }
     else if (category == CATEGORY_WEBDEV &&
              methodOrNode == this.logConsoleAPIMessage) {
-      args[0].arguments.forEach((aValue) => {
-        if (WebConsoleUtils.isActorGrip(aValue)) {
-          this._releaseObject(aValue.actor);
+      args[0].arguments.forEach((value) => {
+        if (WebConsoleUtils.isActorGrip(value)) {
+          this._releaseObject(value.actor);
         }
       });
     }
     else if (category == CATEGORY_JS &&
              methodOrNode == this.reportPageError) {
       let pageError = args[1];
       for (let prop of ["errorMessage", "lineText"]) {
         let grip = pageError[prop];
@@ -2378,110 +2380,110 @@ WebConsoleFrame.prototype = {
              methodOrNode == this._reportLogMessage) {
       if (WebConsoleUtils.isActorGrip(args[0].message)) {
         this._releaseObject(args[0].message.actor);
       }
     }
   },
 
   /**
-   * Ensures that the number of message nodes of type aCategory don't exceed that
+   * Ensures that the number of message nodes of type category don't exceed that
    * category's line limit by removing old messages as needed.
    *
-   * @param integer aCategory
+   * @param integer category
    *        The category of message nodes to prune if needed.
    * @return number
    *         The number of removed nodes.
    */
-  pruneOutputIfNecessary: function WCF_pruneOutputIfNecessary(aCategory)
+  pruneOutputIfNecessary: function WCF_pruneOutputIfNecessary(category)
   {
-    let logLimit = Utils.logLimitForCategory(aCategory);
+    let logLimit = Utils.logLimitForCategory(category);
     let messageNodes = this.outputNode.querySelectorAll(".message[category=" +
-                       CATEGORY_CLASS_FRAGMENTS[aCategory] + "]");
+                       CATEGORY_CLASS_FRAGMENTS[category] + "]");
     let n = Math.max(0, messageNodes.length - logLimit);
     [...messageNodes].slice(0, n).forEach(this.removeOutputMessage, this);
     return n;
   },
 
   /**
    * Remove a given message from the output.
    *
-   * @param nsIDOMNode aNode
+   * @param nsIDOMNode node
    *        The message node you want to remove.
    */
-  removeOutputMessage: function WCF_removeOutputMessage(aNode)
+  removeOutputMessage: function WCF_removeOutputMessage(node)
   {
-    if (aNode._messageObject) {
-      aNode._messageObject.destroy();
-    }
-
-    if (aNode._objectActors) {
-      for (let actor of aNode._objectActors) {
+    if (node._messageObject) {
+      node._messageObject.destroy();
+    }
+
+    if (node._objectActors) {
+      for (let actor of node._objectActors) {
         this._releaseObject(actor);
       }
-      aNode._objectActors.clear();
-    }
-
-    if (aNode.category == CATEGORY_CSS ||
-        aNode.category == CATEGORY_SECURITY) {
-      let repeatNode = aNode.getElementsByClassName("message-repeats")[0];
+      node._objectActors.clear();
+    }
+
+    if (node.category == CATEGORY_CSS ||
+        node.category == CATEGORY_SECURITY) {
+      let repeatNode = node.getElementsByClassName("message-repeats")[0];
       if (repeatNode && repeatNode._uid) {
         delete this._repeatNodes[repeatNode._uid];
       }
     }
-    else if (aNode._connectionId &&
-             aNode.category == CATEGORY_NETWORK) {
-      this.webConsoleClient.removeNetworkRequest(aNode._connectionId);
-      this._releaseObject(aNode._connectionId);
-    }
-    else if (aNode.classList.contains("inlined-variables-view")) {
-      let view = aNode._variablesView;
+    else if (node._connectionId &&
+             node.category == CATEGORY_NETWORK) {
+      this.webConsoleClient.removeNetworkRequest(node._connectionId);
+      this._releaseObject(node._connectionId);
+    }
+    else if (node.classList.contains("inlined-variables-view")) {
+      let view = node._variablesView;
       if (view) {
         view.controller.releaseActors();
       }
-      aNode._variablesView = null;
-    }
-
-    aNode.remove();
+      node._variablesView = null;
+    }
+
+    node.remove();
   },
 
   /**
    * Given a category and message body, creates a DOM node to represent an
    * incoming message. The timestamp is automatically added.
    *
-   * @param number aCategory
+   * @param number category
    *        The category of the message: one of the CATEGORY_* constants.
-   * @param number aSeverity
+   * @param number severity
    *        The severity of the message: one of the SEVERITY_* constants;
-   * @param string|nsIDOMNode aBody
+   * @param string|nsIDOMNode body
    *        The body of the message, either a simple string or a DOM node.
-   * @param string aSourceURL [optional]
+   * @param string sourceURL [optional]
    *        The URL of the source file that emitted the error.
-   * @param number aSourceLine [optional]
+   * @param number sourceLine [optional]
    *        The line number on which the error occurred. If zero or omitted,
    *        there is no line number associated with this message.
-   * @param string aClipboardText [optional]
+   * @param string clipboardText [optional]
    *        The text that should be copied to the clipboard when this node is
-   *        copied. If omitted, defaults to the body text. If `aBody` is not
+   *        copied. If omitted, defaults to the body text. If `body` is not
    *        a string, then the clipboard text must be supplied.
-   * @param number aLevel [optional]
+   * @param number level [optional]
    *        The level of the console API message.
-   * @param number aTimeStamp [optional]
+   * @param number timestamp [optional]
    *        The timestamp to use for this message node. If omitted, the current
    *        date and time is used.
    * @return nsIDOMNode
    *         The message node: a DIV ready to be inserted into the Web Console
    *         output node.
    */
   createMessageNode:
-  function WCF_createMessageNode(aCategory, aSeverity, aBody, aSourceURL,
-                                 aSourceLine, aClipboardText, aLevel, aTimeStamp)
+  function WCF_createMessageNode(category, severity, body, sourceURL,
+                                 sourceLine, clipboardText, level, timestamp)
   {
-    if (typeof aBody != "string" && aClipboardText == null && aBody.innerText) {
-      aClipboardText = aBody.innerText;
+    if (typeof body != "string" && clipboardText == null && body.innerText) {
+      clipboardText = body.innerText;
     }
 
     let indentNode = this.document.createElementNS(XHTML_NS, "span");
     indentNode.className = "indent";
 
     // Apply the current group by indenting appropriately.
     let indent = this.groupDepth * GROUP_INDENT;
     indentNode.style.width = indent + "px";
@@ -2492,93 +2494,93 @@ WebConsoleFrame.prototype = {
     let iconContainer = this.document.createElementNS(XHTML_NS, "span");
     iconContainer.className = "icon";
 
     // Create the message body, which contains the actual text of the message.
     let bodyNode = this.document.createElementNS(XHTML_NS, "span");
     bodyNode.className = "message-body-wrapper message-body devtools-monospace";
 
     // Store the body text, since it is needed later for the variables view.
-    let body = aBody;
+    let storedBody = body;
     // If a string was supplied for the body, turn it into a DOM node and an
     // associated clipboard string now.
-    aClipboardText = aClipboardText ||
-                     (aBody + (aSourceURL ? " @ " + aSourceURL : "") +
-                              (aSourceLine ? ":" + aSourceLine : ""));
-
-    let timestamp = aTimeStamp || Date.now();
+    clipboardText = clipboardText ||
+                     (body + (sourceURL ? " @ " + sourceURL : "") +
+                              (sourceLine ? ":" + sourceLine : ""));
+
+    timestamp = timestamp || Date.now();
 
     // Create the containing node and append all its elements to it.
     let node = this.document.createElementNS(XHTML_NS, "div");
     node.id = "console-msg-" + gSequenceId();
     node.className = "message";
-    node.clipboardText = aClipboardText;
+    node.clipboardText = clipboardText;
     node.timestamp = timestamp;
-    this.setMessageType(node, aCategory, aSeverity);
-
-    if (aBody instanceof Ci.nsIDOMNode) {
-      bodyNode.appendChild(aBody);
+    this.setMessageType(node, category, severity);
+
+    if (body instanceof Ci.nsIDOMNode) {
+      bodyNode.appendChild(body);
     }
     else {
       let str = undefined;
-      if (aLevel == "dir") {
-        str = VariablesView.getString(aBody.arguments[0]);
+      if (level == "dir") {
+        str = VariablesView.getString(body.arguments[0]);
       }
       else {
-        str = aBody;
+        str = body;
       }
 
       if (str !== undefined) {
-        aBody = this.document.createTextNode(str);
-        bodyNode.appendChild(aBody);
+        body = this.document.createTextNode(str);
+        bodyNode.appendChild(body);
       }
     }
 
     // Add the message repeats node only when needed.
     let repeatNode = null;
-    if (aCategory != CATEGORY_INPUT &&
-        aCategory != CATEGORY_OUTPUT &&
-        aCategory != CATEGORY_NETWORK &&
-        !(aCategory == CATEGORY_CSS && aSeverity == SEVERITY_LOG)) {
+    if (category != CATEGORY_INPUT &&
+        category != CATEGORY_OUTPUT &&
+        category != CATEGORY_NETWORK &&
+        !(category == CATEGORY_CSS && severity == SEVERITY_LOG)) {
       repeatNode = this.document.createElementNS(XHTML_NS, "span");
       repeatNode.setAttribute("value", "1");
       repeatNode.className = "message-repeats";
       repeatNode.textContent = 1;
-      repeatNode._uid = [bodyNode.textContent, aCategory, aSeverity, aLevel,
-                         aSourceURL, aSourceLine].join(":");
+      repeatNode._uid = [bodyNode.textContent, category, severity, level,
+                         sourceURL, sourceLine].join(":");
     }
 
     // Create the timestamp.
     let timestampNode = this.document.createElementNS(XHTML_NS, "span");
     timestampNode.className = "timestamp devtools-monospace";
 
     let timestampString = l10n.timestampString(timestamp);
     timestampNode.textContent = timestampString + " ";
 
     // Create the source location (e.g. www.example.com:6) that sits on the
     // right side of the message, if applicable.
     let locationNode;
-    if (aSourceURL && IGNORED_SOURCE_URLS.indexOf(aSourceURL) == -1) {
-      locationNode = this.createLocationNode({url: aSourceURL,
-                                              line: aSourceLine});
+    if (sourceURL && IGNORED_SOURCE_URLS.indexOf(sourceURL) == -1) {
+      locationNode = this.createLocationNode({url: sourceURL,
+                                              line: sourceLine});
     }
 
     node.appendChild(timestampNode);
     node.appendChild(indentNode);
     node.appendChild(iconContainer);
 
     // Display the variables view after the message node.
-    if (aLevel == "dir") {
+    if (level == "dir") {
       let options = {
-        objectActor: body.arguments[0],
+        objectActor: storedBody.arguments[0],
         targetElement: bodyNode,
         hideFilterInput: true,
       };
-      this.jsterm.openVariablesView(options).then((aView) => {
-        node._variablesView = aView;
+      this.jsterm.openVariablesView(options).then((view) => {
+        node._variablesView = view;
         if (node.classList.contains("hidden-message")) {
           node.classList.remove("hidden-message");
         }
       });
 
       node.classList.add("inlined-variables-view");
     }
 
@@ -2595,24 +2597,24 @@ WebConsoleFrame.prototype = {
   },
 
   /**
    * Creates the anchor that displays the textual location of an incoming
    * message.
    *
    * @param object aLocation
    *        An object containing url, line and column number of the message source (destructured).
-   * @param string aTarget [optional]
+   * @param string target [optional]
    *        Tells which tool to open the link with, on click. Supported tools:
    *        jsdebugger, styleeditor, scratchpad.
    * @return nsIDOMNode
    *         The new anchor element, ready to be added to the message node.
    */
   createLocationNode:
-  function WCF_createLocationNode({url, line, column}, aTarget)
+  function WCF_createLocationNode({url, line, column}, target)
   {
     if (!url) {
       url = "";
     }
     let locationNode = this.document.createElementNS(XHTML_NS, "a");
     let filenameNode = this.document.createElementNS(XHTML_NS, "span");
 
     // Create the text, which consists of an abbreviated version of the URL
@@ -2632,18 +2634,18 @@ WebConsoleFrame.prototype = {
     }
 
     filenameNode.className = "filename";
     filenameNode.textContent = " " + (filename || l10n.getStr("unknownLocation"));
     locationNode.appendChild(filenameNode);
 
     locationNode.href = isScratchpad || !fullURL ? "#" : fullURL;
     locationNode.draggable = false;
-    if (aTarget) {
-      locationNode.target = aTarget;
+    if (target) {
+      locationNode.target = target;
     }
     locationNode.setAttribute("title", url);
     locationNode.className = "message-location theme-link devtools-monospace";
 
     // Make the location clickable.
     let onClick = () => {
       let target = locationNode.target;
       if (target == "scratchpad" || isScratchpad) {
@@ -2677,210 +2679,210 @@ WebConsoleFrame.prototype = {
     }
 
     return locationNode;
   },
 
   /**
    * Adjusts the category and severity of the given message.
    *
-   * @param nsIDOMNode aMessageNode
+   * @param nsIDOMNode messageNode
    *        The message node to alter.
-   * @param number aCategory
+   * @param number category
    *        The category for the message; one of the CATEGORY_ constants.
-   * @param number aSeverity
+   * @param number severity
    *        The severity for the message; one of the SEVERITY_ constants.
    * @return void
    */
   setMessageType:
-  function WCF_setMessageType(aMessageNode, aCategory, aSeverity)
+  function WCF_setMessageType(messageNode, category, severity)
   {
-    aMessageNode.category = aCategory;
-    aMessageNode.severity = aSeverity;
-    aMessageNode.setAttribute("category", CATEGORY_CLASS_FRAGMENTS[aCategory]);
-    aMessageNode.setAttribute("severity", SEVERITY_CLASS_FRAGMENTS[aSeverity]);
-    aMessageNode.setAttribute("filter", MESSAGE_PREFERENCE_KEYS[aCategory][aSeverity]);
+    messageNode.category = category;
+    messageNode.severity = severity;
+    messageNode.setAttribute("category", CATEGORY_CLASS_FRAGMENTS[category]);
+    messageNode.setAttribute("severity", SEVERITY_CLASS_FRAGMENTS[severity]);
+    messageNode.setAttribute("filter", MESSAGE_PREFERENCE_KEYS[category][severity]);
   },
 
   /**
    * Add the mouse event handlers needed to make a link.
    *
    * @private
-   * @param nsIDOMNode aNode
+   * @param nsIDOMNode node
    *        The node for which you want to add the event handlers.
-   * @param function aCallback
+   * @param function callback
    *        The function you want to invoke on click.
    */
-  _addMessageLinkCallback: function WCF__addMessageLinkCallback(aNode, aCallback)
+  _addMessageLinkCallback: function WCF__addMessageLinkCallback(node, callback)
   {
-    aNode.addEventListener("mousedown", (aEvent) => {
+    node.addEventListener("mousedown", (event) => {
       this._mousedown = true;
-      this._startX = aEvent.clientX;
-      this._startY = aEvent.clientY;
+      this._startX = event.clientX;
+      this._startY = event.clientY;
     }, false);
 
-    aNode.addEventListener("click", (aEvent) => {
+    node.addEventListener("click", (event) => {
       let mousedown = this._mousedown;
       this._mousedown = false;
 
-      aEvent.preventDefault();
+      event.preventDefault();
 
       // Do not allow middle/right-click or 2+ clicks.
-      if (aEvent.detail != 1 || aEvent.button != 0) {
+      if (event.detail != 1 || event.button != 0) {
         return;
       }
 
       // If this event started with a mousedown event and it ends at a different
       // location, we consider this text selection.
       if (mousedown &&
-          (this._startX != aEvent.clientX) &&
-          (this._startY != aEvent.clientY))
+          (this._startX != event.clientX) &&
+          (this._startY != event.clientY))
       {
         this._startX = this._startY = undefined;
         return;
       }
 
       this._startX = this._startY = undefined;
 
-      aCallback.call(this, aEvent);
+      callback.call(this, event);
     }, false);
   },
 
-  _addFocusCallback: function WCF__addFocusCallback(aNode, aCallback)
+  _addFocusCallback: function WCF__addFocusCallback(node, callback)
   {
-    aNode.addEventListener("mousedown", (aEvent) => {
+    node.addEventListener("mousedown", (event) => {
       this._mousedown = true;
-      this._startX = aEvent.clientX;
-      this._startY = aEvent.clientY;
+      this._startX = event.clientX;
+      this._startY = event.clientY;
     }, false);
 
-    aNode.addEventListener("click", (aEvent) => {
+    node.addEventListener("click", (event) => {
       let mousedown = this._mousedown;
       this._mousedown = false;
 
       // Do not allow middle/right-click or 2+ clicks.
-      if (aEvent.detail != 1 || aEvent.button != 0) {
+      if (event.detail != 1 || event.button != 0) {
         return;
       }
 
       // If this event started with a mousedown event and it ends at a different
       // location, we consider this text selection.
       // Add a fuzz modifier of two pixels in any direction to account for sloppy
       // clicking.
       if (mousedown &&
-          (Math.abs(aEvent.clientX - this._startX) >= 2) &&
-          (Math.abs(aEvent.clientY - this._startY) >= 1))
+          (Math.abs(event.clientX - this._startX) >= 2) &&
+          (Math.abs(event.clientY - this._startY) >= 1))
       {
         this._startX = this._startY = undefined;
         return;
       }
 
       this._startX = this._startY = undefined;
 
-      aCallback.call(this, aEvent);
+      callback.call(this, event);
     }, false);
   },
 
   /**
    * Handler for the pref-changed event coming from the toolbox.
    * Currently this function only handles the timestamps preferences.
    *
    * @private
-   * @param object aEvent
+   * @param object event
    *        This parameter is a string that holds the event name
    *        pref-changed in this case.
-   * @param object aData
+   * @param object data
    *        This is the pref-changed data object.
   */
-  _onToolboxPrefChanged: function WCF__onToolboxPrefChanged(aEvent, aData)
+  _onToolboxPrefChanged: function WCF__onToolboxPrefChanged(event, data)
   {
-    if (aData.pref == PREF_MESSAGE_TIMESTAMP) {
-      if (aData.newValue) {
+    if (data.pref == PREF_MESSAGE_TIMESTAMP) {
+      if (data.newValue) {
         this.outputNode.classList.remove("hideTimestamps");
       }
       else {
         this.outputNode.classList.add("hideTimestamps");
       }
     }
   },
 
   /**
    * Copies the selected items to the system clipboard.
    *
-   * @param object aOptions
+   * @param object options
    *        - linkOnly:
    *        An optional flag to copy only URL without timestamp and
    *        other meta-information. Default is false.
    *        - contextmenu:
    *        An optional flag to copy the last clicked item which brought
    *        up the context menu if nothing is selected. Default is false.
    */
-  copySelectedItems: function WCF_copySelectedItems(aOptions)
+  copySelectedItems: function WCF_copySelectedItems(options)
   {
-    aOptions = aOptions || { linkOnly: false, contextmenu: false };
+    options = options || { linkOnly: false, contextmenu: false };
 
     // Gather up the selected items and concatenate their clipboard text.
     let strings = [];
 
     let children = this.output.getSelectedMessages();
-    if (!children.length && aOptions.contextmenu) {
+    if (!children.length && options.contextmenu) {
       children = [this._contextMenuHandler.lastClickedMessage];
     }
 
     for (let item of children) {
       // Ensure the selected item hasn't been filtered by type or string.
       if (!item.classList.contains("filtered-by-type") &&
           !item.classList.contains("filtered-by-string")) {
         let timestampString = l10n.timestampString(item.timestamp);
-        if (aOptions.linkOnly) {
+        if (options.linkOnly) {
           strings.push(item.url);
         }
         else {
           strings.push(item.clipboardText);
         }
       }
     }
 
     clipboardHelper.copyString(strings.join("\n"));
   },
 
   /**
    * Object properties provider. This function gives you the properties of the
    * remote object you want.
    *
-   * @param string aActor
+   * @param string actor
    *        The object actor ID from which you want the properties.
-   * @param function aCallback
+   * @param function callback
    *        Function you want invoked once the properties are received.
    */
   objectPropertiesProvider:
-  function WCF_objectPropertiesProvider(aActor, aCallback)
+  function WCF_objectPropertiesProvider(actor, callback)
   {
-    this.webConsoleClient.inspectObjectProperties(aActor,
-      function(aResponse) {
-        if (aResponse.error) {
+    this.webConsoleClient.inspectObjectProperties(actor,
+      function(response) {
+        if (response.error) {
           Cu.reportError("Failed to retrieve the object properties from the " +
-                         "server. Error: " + aResponse.error);
+                         "server. Error: " + response.error);
           return;
         }
-        aCallback(aResponse.properties);
+        callback(response.properties);
       });
   },
 
   /**
    * Release an actor.
    *
    * @private
-   * @param string aActor
+   * @param string actor
    *        The actor ID you want to release.
    */
-  _releaseObject: function WCF__releaseObject(aActor)
+  _releaseObject: function WCF__releaseObject(actor)
   {
     if (this.proxy) {
-      this.proxy.releaseActor(aActor);
+      this.proxy.releaseActor(actor);
     }
   },
 
   /**
    * Open the selected item's URL in a new tab.
    */
   openSelectedItemInTab: function WCF_openSelectedItemInTab()
   {
@@ -2959,53 +2961,53 @@ WebConsoleFrame.prototype = {
     return this._destroyer.promise;
   },
 };
 
 
 /**
  * @see VariablesView.simpleValueEvalMacro
  */
-function simpleValueEvalMacro(aItem, aCurrentString)
+function simpleValueEvalMacro(item, currentString)
 {
-  return VariablesView.simpleValueEvalMacro(aItem, aCurrentString, "_self");
+  return VariablesView.simpleValueEvalMacro(item, currentString, "_self");
 };
 
 
 /**
  * @see VariablesView.overrideValueEvalMacro
  */
-function overrideValueEvalMacro(aItem, aCurrentString)
+function overrideValueEvalMacro(item, currentString)
 {
-  return VariablesView.overrideValueEvalMacro(aItem, aCurrentString, "_self");
+  return VariablesView.overrideValueEvalMacro(item, currentString, "_self");
 };
 
 
 /**
  * @see VariablesView.getterOrSetterEvalMacro
  */
-function getterOrSetterEvalMacro(aItem, aCurrentString)
+function getterOrSetterEvalMacro(item, currentString)
 {
-  return VariablesView.getterOrSetterEvalMacro(aItem, aCurrentString, "_self");
+  return VariablesView.getterOrSetterEvalMacro(item, currentString, "_self");
 }
 
 
 
 /**
  * Create a JSTerminal (a JavaScript command line). This is attached to an
  * existing HeadsUpDisplay (a Web Console instance). This code is responsible
  * with handling command line input, code evaluation and result output.
  *
  * @constructor
- * @param object aWebConsoleFrame
+ * @param object webConsoleFrame
  *        The WebConsoleFrame object that owns this JSTerm instance.
  */
-function JSTerm(aWebConsoleFrame)
+function JSTerm(webConsoleFrame)
 {
-  this.hud = aWebConsoleFrame;
+  this.hud = webConsoleFrame;
   this.hudId = this.hud.hudId;
   this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);
 
   this.lastCompletion = { value: null };
   this._loadHistory();
 
   this._objectActorsInVariablesViews = new Map();
 
@@ -3236,55 +3238,55 @@ JSTerm.prototype = {
       inputNode.focus();
     }
   },
 
   /**
    * The JavaScript evaluation response handler.
    *
    * @private
-   * @param object [aAfterMessage]
+   * @param object [afterMessage]
    *        Optional message after which the evaluation result will be
    *        inserted.
-   * @param function [aCallback]
+   * @param function [callback]
    *        Optional function to invoke when the evaluation result is added to
    *        the output.
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
   _executeResultCallback:
-  function JST__executeResultCallback(aAfterMessage, aCallback, aResponse)
+  function JST__executeResultCallback(afterMessage, callback, response)
   {
     if (!this.hud) {
       return;
     }
-    if (aResponse.error) {
-      Cu.reportError("Evaluation error " + aResponse.error + ": " +
-                     aResponse.message);
+    if (response.error) {
+      Cu.reportError("Evaluation error " + response.error + ": " +
+                     response.message);
       return;
     }
-    let errorMessage = aResponse.exceptionMessage;
-    let result = aResponse.result;
-    let helperResult = aResponse.helperResult;
+    let errorMessage = response.exceptionMessage;
+    let result = response.result;
+    let helperResult = response.helperResult;
     let helperHasRawOutput = !!(helperResult || {}).rawOutput;
 
     if (helperResult && helperResult.type) {
       switch (helperResult.type) {
         case "clearOutput":
           this.clearOutput();
           break;
         case "clearHistory":
           this.clearHistory();
           break;
         case "inspectObject":
-          if (aAfterMessage) {
-            if (!aAfterMessage._objectActors) {
-              aAfterMessage._objectActors = new Set();
+          if (afterMessage) {
+            if (!afterMessage._objectActors) {
+              afterMessage._objectActors = new Set();
             }
-            aAfterMessage._objectActors.add(helperResult.object.actor);
+            afterMessage._objectActors.add(helperResult.object.actor);
           }
           this.openVariablesView({
             label: VariablesView.getString(helperResult.object, { concise: true }),
             objectActor: helperResult.object,
           });
           break;
         case "error":
           try {
@@ -3302,120 +3304,120 @@ JSTerm.prototype = {
           break;
       }
     }
 
     // Hide undefined results coming from JSTerm helper functions.
     if (!errorMessage && result && typeof result == "object" &&
         result.type == "undefined" &&
         helperResult && !helperHasRawOutput) {
-      aCallback && aCallback();
+      callback && callback();
       return;
     }
 
-    let msg = new Messages.JavaScriptEvalOutput(aResponse, errorMessage);
+    let msg = new Messages.JavaScriptEvalOutput(response, errorMessage);
     this.hud.output.addMessage(msg);
 
-    if (aCallback) {
+    if (callback) {
       let oldFlushCallback = this.hud._flushCallback;
       this.hud._flushCallback = () => {
-        aCallback(msg.element);
+        callback(msg.element);
         if (oldFlushCallback) {
           oldFlushCallback();
           this.hud._flushCallback = oldFlushCallback;
           return true;
         }
 
         return false;
       };
     }
 
-    msg._afterMessage = aAfterMessage;
+    msg._afterMessage = afterMessage;
     msg._objectActors = new Set();
 
-    if (WebConsoleUtils.isActorGrip(aResponse.exception)) {
-      msg._objectActors.add(aResponse.exception.actor);
+    if (WebConsoleUtils.isActorGrip(response.exception)) {
+      msg._objectActors.add(response.exception.actor);
     }
 
     if (WebConsoleUtils.isActorGrip(result)) {
       msg._objectActors.add(result.actor);
     }
   },
 
   /**
    * Execute a string. Execution happens asynchronously in the content process.
    *
-   * @param string [aExecuteString]
+   * @param string [executeString]
    *        The string you want to execute. If this is not provided, the current
    *        user input is used - taken from |this.inputNode.value|.
-   * @param function [aCallback]
+   * @param function [callback]
    *        Optional function to invoke when the result is displayed.
    *        This is deprecated - please use the promise return value instead.
    * @returns Promise
    *          Resolves with the message once the result is displayed.
    */
-  execute: function JST_execute(aExecuteString, aCallback)
+  execute: function JST_execute(executeString, callback)
   {
     let deferred = promise.defer();
-    let callback = function(msg) {
+    let resultCallback = function(msg) {
       deferred.resolve(msg);
-      if (aCallback) {
-        aCallback(msg);
+      if (callback) {
+        callback(msg);
       }
     }
 
     // attempt to execute the content of the inputNode
-    aExecuteString = aExecuteString || this.inputNode.value;
-    if (!aExecuteString) {
+    executeString = executeString || this.inputNode.value;
+    if (!executeString) {
       return;
     }
 
     let selectedNodeActor = null;
     let inspectorSelection = this.hud.owner.getInspectorSelection();
     if (inspectorSelection && inspectorSelection.nodeFront) {
       selectedNodeActor = inspectorSelection.nodeFront.actorID;
     }
 
-    let message = new Messages.Simple(aExecuteString, {
+    let message = new Messages.Simple(executeString, {
       category: "input",
       severity: "log",
     });
     this.hud.output.addMessage(message);
-    let onResult = this._executeResultCallback.bind(this, message, callback);
+    let onResult = this._executeResultCallback.bind(this, message, resultCallback);
 
     let options = {
       frame: this.SELECTED_FRAME,
       selectedNodeActor: selectedNodeActor,
     };
 
-    this.requestEvaluation(aExecuteString, options).then(onResult, onResult);
+    this.requestEvaluation(executeString, options).then(onResult, onResult);
 
     // Append a new value in the history of executed code, or overwrite the most
     // recent entry. The most recent entry may contain the last edited input
     // value that was not evaluated yet.
-    this.history[this.historyIndex++] = aExecuteString;
+    this.history[this.historyIndex++] = executeString;
     this.historyPlaceHolder = this.history.length;
 
     if (this.history.length > this.inputHistoryCount) {
       this.history.splice(0, this.history.length - this.inputHistoryCount);
       this.historyIndex = this.historyPlaceHolder = this.history.length;
     }
     this.storeHistory();
     WebConsoleUtils.usageCount++;
     this.setInputValue("");
     this.clearCompletion();
     return deferred.promise;
   },
 
   /**
    * Request a JavaScript string evaluation from the server.
    *
-   * @param string aString
+   * @param string str
    *        String to execute.
-   * @param object [aOptions]
+   * @param object [options]
    *        Options for evaluation:
    *        - bindObjectActor: tells the ObjectActor ID for which you want to do
    *        the evaluation. The Debugger.Object of the OA will be bound to
    *        |_self| during evaluation, such that it's usable in the string you
    *        execute.
    *        - frame: tells the stackframe depth to evaluate the string in. If
    *        the jsdebugger is paused, you can pick the stackframe to be used for
    *        evaluation. Use |this.SELECTED_FRAME| to always pick the
@@ -3424,75 +3426,75 @@ JSTerm.prototype = {
    *        global content window.
    *        - selectedNodeActor: tells the NodeActor ID of the current selection in
    *        the Inspector, if such a selection exists. This is used by helper
    *        functions that can evaluate on the current selection.
    * @return object
    *         A promise object that is resolved when the server response is
    *         received.
    */
-  requestEvaluation: function JST_requestEvaluation(aString, aOptions = {})
+  requestEvaluation: function JST_requestEvaluation(str, options = {})
   {
     let deferred = promise.defer();
 
-    function onResult(aResponse) {
-      if (!aResponse.error) {
-        deferred.resolve(aResponse);
+    function onResult(response) {
+      if (!response.error) {
+        deferred.resolve(response);
       }
       else {
-        deferred.reject(aResponse);
+        deferred.reject(response);
       }
     }
 
     let frameActor = null;
-    if ("frame" in aOptions) {
-      frameActor = this.getFrameActor(aOptions.frame);
+    if ("frame" in options) {
+      frameActor = this.getFrameActor(options.frame);
     }
 
     let evalOptions = {
-      bindObjectActor: aOptions.bindObjectActor,
+      bindObjectActor: options.bindObjectActor,
       frameActor: frameActor,
-      selectedNodeActor: aOptions.selectedNodeActor,
-      selectedObjectActor: aOptions.selectedObjectActor,
+      selectedNodeActor: options.selectedNodeActor,
+      selectedObjectActor: options.selectedObjectActor,
     };
 
-    this.webConsoleClient.evaluateJSAsync(aString, onResult, evalOptions);
+    this.webConsoleClient.evaluateJSAsync(str, onResult, evalOptions);
     return deferred.promise;
   },
 
   /**
    * Retrieve the FrameActor ID given a frame depth.
    *
-   * @param number aFrame
+   * @param number frame
    *        Frame depth.
    * @return string|null
    *         The FrameActor ID for the given frame depth.
    */
-  getFrameActor: function JST_getFrameActor(aFrame)
+  getFrameActor: function JST_getFrameActor(frame)
   {
     let state = this.hud.owner.getDebuggerFrames();
     if (!state) {
       return null;
     }
 
     let grip;
-    if (aFrame == this.SELECTED_FRAME) {
+    if (frame == this.SELECTED_FRAME) {
       grip = state.frames[state.selected];
     }
     else {
-      grip = state.frames[aFrame];
+      grip = state.frames[frame];
     }
 
     return grip ? grip.actor : null;
   },
 
   /**
    * Opens a new variables view that allows the inspection of the given object.
    *
-   * @param object aOptions
+   * @param object options
    *        Options for the variables view:
    *        - objectActor: grip of the ObjectActor you want to show in the
    *        variables view.
    *        - rawObject: the raw object you want to show in the variables view.
    *        - label: label to display in the variables view for inspected
    *        object.
    *        - hideFilterInput: optional boolean, |true| if you want to hide the
    *        variables view filter input.
@@ -3500,60 +3502,60 @@ JSTerm.prototype = {
    *        to. An iframe element is used as a container for the view. If this
    *        option is not used, then the variables view opens in the sidebar.
    *        - autofocus: optional boolean, |true| if you want to give focus to
    *        the variables view window after open, |false| otherwise.
    * @return object
    *         A promise object that is resolved when the variables view has
    *         opened. The new variables view instance is given to the callbacks.
    */
-  openVariablesView: function JST_openVariablesView(aOptions)
+  openVariablesView: function JST_openVariablesView(options)
   {
-    let onContainerReady = (aWindow) => {
-      let container = aWindow.document.querySelector("#variables");
+    let onContainerReady = (window) => {
+      let container = window.document.querySelector("#variables");
       let view = this._variablesView;
-      if (!view || aOptions.targetElement) {
+      if (!view || options.targetElement) {
         let viewOptions = {
           container: container,
-          hideFilterInput: aOptions.hideFilterInput,
+          hideFilterInput: options.hideFilterInput,
         };
         view = this._createVariablesView(viewOptions);
-        if (!aOptions.targetElement) {
+        if (!options.targetElement) {
           this._variablesView = view;
-          aWindow.addEventListener("keypress", this._onKeypressInVariablesView);
+          window.addEventListener("keypress", this._onKeypressInVariablesView);
         }
       }
-      aOptions.view = view;
-      this._updateVariablesView(aOptions);
-
-      if (!aOptions.targetElement && aOptions.autofocus) {
-        aWindow.focus();
+      options.view = view;
+      this._updateVariablesView(options);
+
+      if (!options.targetElement && options.autofocus) {
+        window.focus();
       }
 
-      this.emit("variablesview-open", view, aOptions);
+      this.emit("variablesview-open", view, options);
       return view;
     };
 
     let openPromise;
-    if (aOptions.targetElement) {
+    if (options.targetElement) {
       let deferred = promise.defer();
       openPromise = deferred.promise;
-      let document = aOptions.targetElement.ownerDocument;
+      let document = options.targetElement.ownerDocument;
       let iframe = document.createElementNS(XHTML_NS, "iframe");
 
-      iframe.addEventListener("load", function onIframeLoad(aEvent) {
+      iframe.addEventListener("load", function onIframeLoad(event) {
         iframe.removeEventListener("load", onIframeLoad, true);
         iframe.style.visibility = "visible";
         deferred.resolve(iframe.contentWindow);
       }, true);
 
       iframe.flex = 1;
       iframe.style.visibility = "hidden";
       iframe.setAttribute("src", VARIABLES_VIEW_URL);
-      aOptions.targetElement.appendChild(iframe);
+      options.targetElement.appendChild(iframe);
     }
     else {
       if (!this.sidebar) {
         this._createSidebar();
       }
       openPromise = this._addVariablesViewSidebarTab();
     }
 
@@ -3607,299 +3609,299 @@ JSTerm.prototype = {
     return deferred.promise;
   },
 
   /**
    * The keypress event handler for the Variables View sidebar. Currently this
    * is used for removing the sidebar when Escape is pressed.
    *
    * @private
-   * @param nsIDOMEvent aEvent
+   * @param nsIDOMEvent event
    *        The keypress DOM event object.
    */
-  _onKeypressInVariablesView: function JST__onKeypressInVariablesView(aEvent)
+  _onKeypressInVariablesView: function JST__onKeypressInVariablesView(event)
   {
-    let tag = aEvent.target.nodeName;
-    if (aEvent.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE || aEvent.shiftKey ||
-        aEvent.altKey || aEvent.ctrlKey || aEvent.metaKey ||
+    let tag = event.target.nodeName;
+    if (event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE || event.shiftKey ||
+        event.altKey || event.ctrlKey || event.metaKey ||
         ["input", "textarea", "select", "textbox"].indexOf(tag) > -1) {
         return;
     }
 
     this._sidebarDestroy();
     this.inputNode.focus();
-    aEvent.stopPropagation();
+    event.stopPropagation();
   },
 
   /**
    * Create a variables view instance.
    *
    * @private
-   * @param object aOptions
+   * @param object options
    *        Options for the new Variables View instance:
    *        - container: the DOM element where the variables view is inserted.
    *        - hideFilterInput: boolean, if true the variables filter input is
    *        hidden.
    * @return object
    *         The new Variables View instance.
    */
-  _createVariablesView: function JST__createVariablesView(aOptions)
+  _createVariablesView: function JST__createVariablesView(options)
   {
-    let view = new VariablesView(aOptions.container);
+    let view = new VariablesView(options.container);
     view.toolbox = gDevTools.getToolbox(this.hud.owner.target);
     view.searchPlaceholder = l10n.getStr("propertiesFilterPlaceholder");
     view.emptyText = l10n.getStr("emptyPropertiesList");
-    view.searchEnabled = !aOptions.hideFilterInput;
+    view.searchEnabled = !options.hideFilterInput;
     view.lazyEmpty = this._lazyVariablesView;
 
     VariablesViewController.attach(view, {
-      getEnvironmentClient: aGrip => {
-        return new EnvironmentClient(this.hud.proxy.client, aGrip);
+      getEnvironmentClient: grip => {
+        return new EnvironmentClient(this.hud.proxy.client, grip);
       },
-      getObjectClient: aGrip => {
-        return new ObjectClient(this.hud.proxy.client, aGrip);
+      getObjectClient: grip => {
+        return new ObjectClient(this.hud.proxy.client, grip);
       },
-      getLongStringClient: aGrip => {
-        return this.webConsoleClient.longString(aGrip);
+      getLongStringClient: grip => {
+        return this.webConsoleClient.longString(grip);
       },
-      releaseActor: aActor => {
-        this.hud._releaseObject(aActor);
+      releaseActor: actor => {
+        this.hud._releaseObject(actor);
       },
       simpleValueEvalMacro: simpleValueEvalMacro,
       overrideValueEvalMacro: overrideValueEvalMacro,
       getterOrSetterEvalMacro: getterOrSetterEvalMacro,
     });
 
     // Relay events from the VariablesView.
-    view.on("fetched", (aEvent, aType, aVar) => {
-      this.emit("variablesview-fetched", aVar);
+    view.on("fetched", (event, type, variableObject) => {
+      this.emit("variablesview-fetched", variableObject);
     });
 
     return view;
   },
 
   /**
    * Update the variables view.
    *
    * @private
-   * @param object aOptions
+   * @param object options
    *        Options for updating the variables view:
    *        - view: the view you want to update.
    *        - objectActor: the grip of the new ObjectActor you want to show in
    *        the view.
    *        - rawObject: the new raw object you want to show.
    *        - label: the new label for the inspected object.
    */
-  _updateVariablesView: function JST__updateVariablesView(aOptions)
+  _updateVariablesView: function JST__updateVariablesView(options)
   {
-    let view = aOptions.view;
+    let view = options.view;
     view.empty();
 
     // We need to avoid pruning the object inspection starting point.
     // That one is pruned when the console message is removed.
-    view.controller.releaseActors(aActor => {
-      return view._consoleLastObjectActor != aActor;
+    view.controller.releaseActors(actor => {
+      return view._consoleLastObjectActor != actor;
     });
 
-    if (aOptions.objectActor &&
+    if (options.objectActor &&
         (!this.hud.owner._browserConsole ||
          Services.prefs.getBoolPref("devtools.chrome.enabled"))) {
       // Make sure eval works in the correct context.
-      view.eval = this._variablesViewEvaluate.bind(this, aOptions);
-      view.switch = this._variablesViewSwitch.bind(this, aOptions);
-      view.delete = this._variablesViewDelete.bind(this, aOptions);
+      view.eval = this._variablesViewEvaluate.bind(this, options);
+      view.switch = this._variablesViewSwitch.bind(this, options);
+      view.delete = this._variablesViewDelete.bind(this, options);
     }
     else {
       view.eval = null;
       view.switch = null;
       view.delete = null;
     }
 
-    let { variable, expanded } = view.controller.setSingleVariable(aOptions);
+    let { variable, expanded } = view.controller.setSingleVariable(options);
     variable.evaluationMacro = simpleValueEvalMacro;
 
-    if (aOptions.objectActor) {
-      view._consoleLastObjectActor = aOptions.objectActor.actor;
-    }
-    else if (aOptions.rawObject) {
+    if (options.objectActor) {
+      view._consoleLastObjectActor = options.objectActor.actor;
+    }
+    else if (options.rawObject) {
       view._consoleLastObjectActor = null;
     }
     else {
       throw new Error("Variables View cannot open without giving it an object " +
                       "display.");
     }
 
     expanded.then(() => {
-      this.emit("variablesview-updated", view, aOptions);
+      this.emit("variablesview-updated", view, options);
     });
   },
 
   /**
    * The evaluation function used by the variables view when editing a property
    * value.
    *
    * @private
-   * @param object aOptions
+   * @param object options
    *        The options used for |this._updateVariablesView()|.
-   * @param object aVar
+   * @param object variableObject
    *        The Variable object instance for the edited property.
-   * @param string aValue
+   * @param string value
    *        The value the edited property was changed to.
    */
   _variablesViewEvaluate:
-  function JST__variablesViewEvaluate(aOptions, aVar, aValue)
+  function JST__variablesViewEvaluate(options, variableObject, value)
   {
-    let updater = this._updateVariablesView.bind(this, aOptions);
+    let updater = this._updateVariablesView.bind(this, options);
     let onEval = this._silentEvalCallback.bind(this, updater);
-    let string = aVar.evaluationMacro(aVar, aValue);
+    let string = variableObject.evaluationMacro(variableObject, value);
 
     let evalOptions = {
       frame: this.SELECTED_FRAME,
-      bindObjectActor: aOptions.objectActor.actor,
+      bindObjectActor: options.objectActor.actor,
     };
 
     this.requestEvaluation(string, evalOptions).then(onEval, onEval);
   },
 
   /**
    * The property deletion function used by the variables view when a property
    * is deleted.
    *
    * @private
-   * @param object aOptions
+   * @param object options
    *        The options used for |this._updateVariablesView()|.
-   * @param object aVar
+   * @param object variableObject
    *        The Variable object instance for the deleted property.
    */
-  _variablesViewDelete: function JST__variablesViewDelete(aOptions, aVar)
+  _variablesViewDelete: function JST__variablesViewDelete(options, variableObject)
   {
     let onEval = this._silentEvalCallback.bind(this, null);
 
     let evalOptions = {
       frame: this.SELECTED_FRAME,
-      bindObjectActor: aOptions.objectActor.actor,
+      bindObjectActor: options.objectActor.actor,
     };
 
-    this.requestEvaluation("delete _self" + aVar.symbolicName, evalOptions)
+    this.requestEvaluation("delete _self" + variableObject.symbolicName, evalOptions)
         .then(onEval, onEval);
   },
 
   /**
    * The property rename function used by the variables view when a property
    * is renamed.
    *
    * @private
-   * @param object aOptions
+   * @param object options
    *        The options used for |this._updateVariablesView()|.
-   * @param object aVar
+   * @param object variableObject
    *        The Variable object instance for the renamed property.
-   * @param string aNewName
+   * @param string newName
    *        The new name for the property.
    */
   _variablesViewSwitch:
-  function JST__variablesViewSwitch(aOptions, aVar, aNewName)
+  function JST__variablesViewSwitch(options, variableObject, newName)
   {
-    let updater = this._updateVariablesView.bind(this, aOptions);
+    let updater = this._updateVariablesView.bind(this, options);
     let onEval = this._silentEvalCallback.bind(this, updater);
 
     let evalOptions = {
       frame: this.SELECTED_FRAME,
-      bindObjectActor: aOptions.objectActor.actor,
+      bindObjectActor: options.objectActor.actor,
     };
 
-    let newSymbolicName = aVar.ownerView.symbolicName + '["' + aNewName + '"]';
-    if (newSymbolicName == aVar.symbolicName) {
+    let newSymbolicName = variableObject.ownerView.symbolicName + '["' + newName + '"]';
+    if (newSymbolicName == variableObject.symbolicName) {
       return;
     }
 
-    let code = "_self" + newSymbolicName + " = _self" + aVar.symbolicName + ";" +
-               "delete _self" + aVar.symbolicName;
+    let code = "_self" + newSymbolicName + " = _self" + variableObject.symbolicName + ";" +
+               "delete _self" + variableObject.symbolicName;
 
     this.requestEvaluation(code, evalOptions).then(onEval, onEval);
   },
 
   /**
    * A noop callback for JavaScript evaluation. This method releases any
    * result ObjectActors that come from the server for evaluation requests. This
    * is used for editing, renaming and deleting properties in the variables
    * view.
    *
    * Exceptions are displayed in the output.
    *
    * @private
-   * @param function aCallback
+   * @param function callback
    *        Function to invoke once the response is received.
-   * @param object aResponse
+   * @param object response
    *        The response packet received from the server.
    */
-  _silentEvalCallback: function JST__silentEvalCallback(aCallback, aResponse)
+  _silentEvalCallback: function JST__silentEvalCallback(callback, response)
   {
-    if (aResponse.error) {
-      Cu.reportError("Web Console evaluation failed. " + aResponse.error + ":" +
-                     aResponse.message);
-
-      aCallback && aCallback(aResponse);
+    if (response.error) {
+      Cu.reportError("Web Console evaluation failed. " + response.error + ":" +
+                     response.message);
+
+      callback && callback(response);
       return;
     }
 
-    if (aResponse.exceptionMessage) {
-      let message = new Messages.Simple(aResponse.exceptionMessage, {
+    if (response.exceptionMessage) {
+      let message = new Messages.Simple(response.exceptionMessage, {
         category: "output",
         severity: "error",
-        timestamp: aResponse.timestamp,
+        timestamp: response.timestamp,
       });
       this.hud.output.addMessage(message);
       message._objectActors = new Set();
-      if (WebConsoleUtils.isActorGrip(aResponse.exception)) {
-        message._objectActors.add(aResponse.exception.actor);
+      if (WebConsoleUtils.isActorGrip(response.exception)) {
+        message._objectActors.add(response.exception.actor);
       }
     }
 
-    let helper = aResponse.helperResult || { type: null };
+    let helper = response.helperResult || { type: null };
     let helperGrip = null;
     if (helper.type == "inspectObject") {
       helperGrip = helper.object;
     }
 
-    let grips = [aResponse.result, helperGrip];
+    let grips = [response.result, helperGrip];
     for (let grip of grips) {
       if (WebConsoleUtils.isActorGrip(grip)) {
         this.hud._releaseObject(grip.actor);
       }
     }
 
-    aCallback && aCallback(aResponse);
+    callback && callback(response);
   },
 
 
   /**
    * Clear the Web Console output.
    *
    * This method emits the "messages-cleared" notification.
    *
-   * @param boolean aClearStorage
+   * @param boolean clearStorage
    *        True if you want to clear the console messages storage associated to
    *        this Web Console.
    */
-  clearOutput: function JST_clearOutput(aClearStorage)
+  clearOutput: function JST_clearOutput(clearStorage)
   {
     let hud = this.hud;
     let outputNode = hud.outputNode;
     let node;
     while ((node = outputNode.firstChild)) {
       hud.removeOutputMessage(node);
     }
 
     hud.groupDepth = 0;
     hud._outputQueue.forEach(hud._destroyItem, hud);
     hud._outputQueue = [];
     this.webConsoleClient.clearNetworkRequests();
     hud._repeatNodes = {};
 
-    if (aClearStorage) {
+    if (clearStorage) {
       this.webConsoleClient.clearMessagesCache();
     }
 
     this._sidebarDestroy();
 
     this.emit("messages-cleared");
   },
 
@@ -3937,24 +3939,24 @@ JSTerm.prototype = {
     }
   },
 
   /**
    * Sets the value of the input field (command line), and resizes the field to
    * fit its contents. This method is preferred over setting "inputNode.value"
    * directly, because it correctly resizes the field.
    *
-   * @param string aNewValue
+   * @param string newValue
    *        The new value to set.
    * @returns void
    */
-  setInputValue: function JST_setInputValue(aNewValue)
+  setInputValue: function JST_setInputValue(newValue)
   {
-    this.inputNode.value = aNewValue;
-    this.lastInputValue = aNewValue;
+    this.inputNode.value = newValue;
+    this.lastInputValue = newValue;
     this.completeNode.value = "";
     this.resizeInput();
     this._inputChanged = true;
     this.emit("set-input-value");
   },
 
   /**
    * The inputNode "input" and "keyup" event handler.
@@ -3980,25 +3982,25 @@ JSTerm.prototype = {
       this.clearCompletion();
     }
   },
 
   /**
    * The inputNode "keypress" event handler.
    *
    * @private
-   * @param nsIDOMEvent aEvent
+   * @param nsIDOMEvent event
    */
-  _keyPress: function JST__keyPress(aEvent)
+  _keyPress: function JST__keyPress(event)
   {
     let inputNode = this.inputNode;
     let inputUpdated = false;
 
-    if (aEvent.ctrlKey) {
-      switch (aEvent.charCode) {
+    if (event.ctrlKey) {
+      switch (event.charCode) {
         case 101:
           // control-e
           if (Services.appinfo.OS == "WINNT") {
             break;
           }
           let lineEndPos = inputNode.value.length;
           if (this.hasMultilineInput()) {
             // find index of closest newline >= cursor
@@ -4006,117 +4008,117 @@ JSTerm.prototype = {
               if (inputNode.value.charAt(i) == "\r" ||
                   inputNode.value.charAt(i) == "\n") {
                 lineEndPos = i;
                 break;
               }
             }
           }
           inputNode.setSelectionRange(lineEndPos, lineEndPos);
-          aEvent.preventDefault();
+          event.preventDefault();
           this.clearCompletion();
           break;
 
         case 110:
           // Control-N differs from down arrow: it ignores autocomplete state.
           // Note that we preserve the default 'down' navigation within
           // multiline text.
           if (Services.appinfo.OS == "Darwin" &&
               this.canCaretGoNext() &&
               this.historyPeruse(HISTORY_FORWARD)) {
-            aEvent.preventDefault();
+            event.preventDefault();
             // Ctrl-N is also used to focus the Network category button on MacOSX.
             // The preventDefault() call doesn't prevent the focus from moving
             // away from the input.
             inputNode.focus();
           }
           this.clearCompletion();
           break;
 
         case 112:
           // Control-P differs from up arrow: it ignores autocomplete state.
           // Note that we preserve the default 'up' navigation within
           // multiline text.
           if (Services.appinfo.OS == "Darwin" &&
               this.canCaretGoPrevious() &&
               this.historyPeruse(HISTORY_BACK)) {
-            aEvent.preventDefault();
+            event.preventDefault();
             // Ctrl-P may also be used to focus some category button on MacOSX.
             // The preventDefault() call doesn't prevent the focus from moving
             // away from the input.
             inputNode.focus();
           }
           this.clearCompletion();
           break;
         default:
           break;
       }
       return;
     }
-    else if (aEvent.shiftKey &&
-        aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
+    else if (event.shiftKey &&
+        event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
       // shift return
       // TODO: expand the inputNode height by one line
       return;
     }
 
-    switch (aEvent.keyCode) {
+    switch (event.keyCode) {
       case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE:
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
-          aEvent.preventDefault();
-          aEvent.stopPropagation();
+          event.preventDefault();
+          event.stopPropagation();
         }
         else if (this.sidebar) {
           this._sidebarDestroy();
-          aEvent.preventDefault();
-          aEvent.stopPropagation();
+          event.preventDefault();
+          event.stopPropagation();
         }
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
         if (this._autocompletePopupNavigated &&
             this.autocompletePopup.isOpen &&
             this.autocompletePopup.selectedIndex > -1) {
           this.acceptProposedCompletion();
         }
         else {
           this.execute();
           this._inputChanged = false;
         }
-        aEvent.preventDefault();
+        event.preventDefault();
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_UP:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_BACKWARD);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         }
         else if (this.canCaretGoPrevious()) {
           inputUpdated = this.historyPeruse(HISTORY_BACK);
         }
         if (inputUpdated) {
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_FORWARD);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         }
         else if (this.canCaretGoNext()) {
           inputUpdated = this.historyPeruse(HISTORY_FORWARD);
         }
         if (inputUpdated) {
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEUP);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
@@ -4124,53 +4126,53 @@ JSTerm.prototype = {
         }
         else {
           this.hud.outputNode.parentNode.scrollTop =
             Math.max(0,
               this.hud.outputNode.parentNode.scrollTop -
               this.hud.outputNode.parentNode.clientHeight
             );
         }
-        aEvent.preventDefault();
+        event.preventDefault();
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEDOWN);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         }
         else {
           this.hud.outputNode.parentNode.scrollTop =
             Math.min(this.hud.outputNode.parentNode.scrollHeight,
               this.hud.outputNode.parentNode.scrollTop +
               this.hud.outputNode.parentNode.clientHeight
             );
         }
-        aEvent.preventDefault();
+        event.preventDefault();
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex = 0;
-          aEvent.preventDefault();
+          event.preventDefault();
         } else if (this.inputNode.value.length <= 0) {
           this.hud.outputNode.parentNode.scrollTop = 0;
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_END:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex = this.autocompletePopup.itemCount - 1;
-          aEvent.preventDefault();
+          event.preventDefault();
         } else if (this.inputNode.value.length <= 0) {
           this.hud.outputNode.parentNode.scrollTop = this.hud.outputNode.parentNode.scrollHeight;
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         break;
 
       case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
         if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
           this.clearCompletion();
         }
         break;
@@ -4182,33 +4184,33 @@ JSTerm.prototype = {
                              this.inputNode.value.length;
         let haveSuggestion = this.autocompletePopup.isOpen ||
                              this.lastCompletion.value;
         let useCompletion = cursorAtTheEnd || this._autocompletePopupNavigated;
         if (haveSuggestion && useCompletion &&
             this.complete(this.COMPLETE_HINT_ONLY) &&
             this.lastCompletion.value &&
             this.acceptProposedCompletion()) {
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
         }
         break;
       }
       case Ci.nsIDOMKeyEvent.DOM_VK_TAB:
         // Generate a completion and accept the first proposed value.
         if (this.complete(this.COMPLETE_HINT_ONLY) &&
             this.lastCompletion &&
             this.acceptProposedCompletion()) {
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         else if (this._inputChanged) {
           this.updateCompleteNode(l10n.getStr("Autocomplete.blank"));
-          aEvent.preventDefault();
+          event.preventDefault();
         }
         break;
       default:
         break;
     }
   },
 
   /**
@@ -4218,47 +4220,47 @@ JSTerm.prototype = {
   _focusEventHandler: function JST__focusEventHandler()
   {
     this._inputChanged = false;
   },
 
   /**
    * Go up/down the history stack of input values.
    *
-   * @param number aDirection
+   * @param number direction
    *        History navigation direction: HISTORY_BACK or HISTORY_FORWARD.
    *
    * @returns boolean
    *          True if the input value changed, false otherwise.
    */
-  historyPeruse: function JST_historyPeruse(aDirection)
+  historyPeruse: function JST_historyPeruse(direction)
   {
     if (!this.history.length) {
       return false;
     }
 
     // Up Arrow key
-    if (aDirection == HISTORY_BACK) {
+    if (direction == HISTORY_BACK) {
       if (this.historyPlaceHolder <= 0) {
         return false;
       }
       let inputVal = this.history[--this.historyPlaceHolder];
 
       // Save the current input value as the latest entry in history, only if
       // the user is already at the last entry.
       // Note: this code does not store changes to items that are already in
       // history.
       if (this.historyPlaceHolder+1 == this.historyIndex) {
         this.history[this.historyIndex] = this.inputNode.value || "";
       }
 
       this.setInputValue(inputVal);
     }
     // Down Arrow key
-    else if (aDirection == HISTORY_FORWARD) {
+    else if (direction == HISTORY_FORWARD) {
       if (this.historyPlaceHolder >= (this.history.length-1)) {
         return false;
       }
 
       let inputVal = this.history[++this.historyPlaceHolder];
       this.setInputValue(inputVal);
     }
     else {
@@ -4321,17 +4323,17 @@ JSTerm.prototype = {
            node.selectionStart == 0 && !multiline;
   },
 
   /**
    * Completes the current typed text in the inputNode. Completion is performed
    * only if the selection/cursor is at the end of the string. If no completion
    * is found, the current inputNode value and cursor/selection stay.
    *
-   * @param int aType possible values are
+   * @param int type possible values are
    *    - this.COMPLETE_FORWARD: If there is more than one possible completion
    *          and the input value stayed the same compared to the last time this
    *          function was called, then the next completion of all possible
    *          completions is used. If the value changed, then the first possible
    *          completion is used and the selection is set from the current
    *          cursor position to the end of the completed text.
    *          If there is only one possible completion, then this completion
    *          value is used and the cursor is put at the end of the completion.
@@ -4343,87 +4345,87 @@ JSTerm.prototype = {
    *    - this.COMPLETE_PAGEDOWN: Scroll down one page if available or select the
    *          last item.
    *    - this.COMPLETE_HINT_ONLY: If there is more than one possible
    *          completion and the input value stayed the same compared to the
    *          last time this function was called, then the same completion is
    *          used again. If there is only one possible completion, then
    *          the inputNode.value is set to this value and the selection is set
    *          from the current cursor position to the end of the completed text.
-   * @param function aCallback
+   * @param function callback
    *        Optional function invoked when the autocomplete properties are
    *        updated.
    * @returns boolean true if there existed a completion for the current input,
    *          or false otherwise.
    */
-  complete: function JSTF_complete(aType, aCallback)
+  complete: function JSTF_complete(type, callback)
   {
     let inputNode = this.inputNode;
     let inputValue = inputNode.value;
     let frameActor = this.getFrameActor(this.SELECTED_FRAME);
 
     // If the inputNode has no value, then don't try to complete on it.
     if (!inputValue) {
       this.clearCompletion();
-      aCallback && aCallback(this);
+      callback && callback(this);
       this.emit("autocomplete-updated");
       return false;
     }
 
     // Only complete if the selection is empty.
     if (inputNode.selectionStart != inputNode.selectionEnd) {
       this.clearCompletion();
-      aCallback && aCallback(this);
+      callback && callback(this);
       this.emit("autocomplete-updated");
       return false;
     }
 
     // Update the completion results.
     if (this.lastCompletion.value != inputValue || frameActor != this._lastFrameActorId) {
-      this._updateCompletionResult(aType, aCallback);
+      this._updateCompletionResult(type, callback);
       return false;
     }
 
     let popup = this.autocompletePopup;
     let accepted = false;
 
-    if (aType != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) {
+    if (type != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) {
       this.acceptProposedCompletion();
       accepted = true;
     }
-    else if (aType == this.COMPLETE_BACKWARD) {
+    else if (type == this.COMPLETE_BACKWARD) {
       popup.selectPreviousItem();
     }
-    else if (aType == this.COMPLETE_FORWARD) {
+    else if (type == this.COMPLETE_FORWARD) {
       popup.selectNextItem();
     }
-    else if (aType == this.COMPLETE_PAGEUP) {
+    else if (type == this.COMPLETE_PAGEUP) {
       popup.selectPreviousPageItem();
     }
-    else if (aType == this.COMPLETE_PAGEDOWN) {
+    else if (type == this.COMPLETE_PAGEDOWN) {
       popup.selectNextPageItem();
     }
 
-    aCallback && aCallback(this);
+    callback && callback(this);
     this.emit("autocomplete-updated");
     return accepted || popup.itemCount > 0;
   },
 
   /**
    * Update the completion result. This operation is performed asynchronously by
    * fetching updated results from the content process.
    *
    * @private
-   * @param int aType
+   * @param int type
    *        Completion type. See this.complete() for details.
-   * @param function [aCallback]
+   * @param function [callback]
    *        Optional, function to invoke when completion results are received.
    */
   _updateCompletionResult:
-  function JST__updateCompletionResult(aType, aCallback)
+  function JST__updateCompletionResult(type, callback)
   {
     let frameActor = this.getFrameActor(this.SELECTED_FRAME);
     if (this.lastCompletion.value == this.inputNode.value && frameActor == this._lastFrameActorId) {
       return;
     }
 
     let requestId = gSequenceId();
     let cursor = this.inputNode.selectionStart;
@@ -4452,81 +4454,81 @@ JSTerm.prototype = {
       }
 
       let newList = cache.sort().filter(function(l) {
         return l.startsWith(filterBy);
       });
 
       this.lastCompletion = {
         requestId: null,
-        completionType: aType,
+        completionType: type,
         value: null,
       };
 
       let response = { matches: newList, matchProp: filterBy };
-      this._receiveAutocompleteProperties(null, aCallback, response);
+      this._receiveAutocompleteProperties(null, callback, response);
       return;
     }
 
     this._lastFrameActorId = frameActor;
 
     this.lastCompletion = {
       requestId: requestId,
-      completionType: aType,
+      completionType: type,
       value: null,
     };
 
-    let callback = this._receiveAutocompleteProperties.bind(this, requestId,
-                                                            aCallback);
-
-    this.webConsoleClient.autocomplete(input, cursor, callback, frameActor);
+    let autocompleteCallback = this._receiveAutocompleteProperties.bind(this, requestId,
+                                                            callback);
+
+    this.webConsoleClient.autocomplete(input, cursor, autocompleteCallback, frameActor);
   },
 
   /**
    * Handler for the autocompletion results. This method takes
    * the completion result received from the server and updates the UI
    * accordingly.
    *
-   * @param number aRequestId
+   * @param number requestId
    *        Request ID.
-   * @param function [aCallback=null]
+   * @param function [callback=null]
    *        Optional, function to invoke when the completion result is received.
-   * @param object aMessage
+   * @param object message
    *        The JSON message which holds the completion results received from
    *        the content process.
    */
   _receiveAutocompleteProperties:
-  function JST__receiveAutocompleteProperties(aRequestId, aCallback, aMessage)
+  function JST__receiveAutocompleteProperties(requestId, callback, message)
   {
     let inputNode = this.inputNode;
     let inputValue = inputNode.value;
     if (this.lastCompletion.value == inputValue ||
-        aRequestId != this.lastCompletion.requestId) {
+        requestId != this.lastCompletion.requestId) {
       return;
     }
     // Cache whatever came from the server if the last char is alphanumeric or '.'
     let cursor = inputNode.selectionStart;
     let inputUntilCursor = inputValue.substring(0, cursor);
 
-    if (aRequestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
-      this._autocompleteCache = aMessage.matches;
+    if (requestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
+      this._autocompleteCache = message.matches;
       this._autocompleteQuery = inputUntilCursor;
     }
 
-    let matches = aMessage.matches;
-    let lastPart = aMessage.matchProp;
+    let matches = message.matches;
+    let lastPart = message.matchProp;
     if (!matches.length) {
       this.clearCompletion();
-      aCallback && aCallback(this);
+      callback && callback(this);
       this.emit("autocomplete-updated");
       return;
     }
 
-    let items = matches.reverse().map(function(aMatch) {
-      return { preLabel: lastPart, label: aMatch };
+    let items = matches.reverse().map(function(match) {
+      return { preLabel: lastPart, label: match };
     });
 
     let popup = this.autocompletePopup;
     popup.setItems(items);
 
     let completionType = this.lastCompletion.completionType;
     this.lastCompletion = {
       value: inputValue,
@@ -4556,17 +4558,17 @@ JSTerm.prototype = {
     }
     else if (completionType == this.COMPLETE_BACKWARD) {
       popup.selectPreviousItem();
     }
     else if (completionType == this.COMPLETE_FORWARD) {
       popup.selectNextItem();
     }
 
-    aCallback && aCallback(this);
+    callback && callback(this);
     this.emit("autocomplete-updated");
   },
 
   onAutocompleteSelect: function JSTF_onAutocompleteSelect()
   {
     // Render the suggestion only if the cursor is at the end of the input.
     if (this.inputNode.selectionStart != this.inputNode.value.length) {
       return;
@@ -4624,24 +4626,24 @@ JSTerm.prototype = {
     this.clearCompletion();
 
     return updated;
   },
 
   /**
    * Update the node that displays the currently selected autocomplete proposal.
    *
-   * @param string aSuffix
+   * @param string suffix
    *        The proposed suffix for the inputNode value.
    */
-  updateCompleteNode: function JSTF_updateCompleteNode(aSuffix)
+  updateCompleteNode: function JSTF_updateCompleteNode(suffix)
   {
     // completion prefix = input, with non-control chars replaced by spaces
-    let prefix = aSuffix ? this.inputNode.value.replace(/[\S]/g, " ") : "";
-    this.completeNode.value = prefix + aSuffix;
+    let prefix = suffix ? this.inputNode.value.replace(/[\S]/g, " ") : "";
+    this.completeNode.value = prefix + suffix;
   },
 
 
   /**
    * Destroy the sidebar.
    * @private
    */
   _sidebarDestroy: function JST__sidebarDestroy()
@@ -4697,54 +4699,54 @@ JSTerm.prototype = {
 
 /**
  * Utils: a collection of globally used functions.
  */
 var Utils = {
   /**
    * Scrolls a node so that it's visible in its containing element.
    *
-   * @param nsIDOMNode aNode
+   * @param nsIDOMNode node
    *        The node to make visible.
    * @returns void
    */
-  scrollToVisible: function Utils_scrollToVisible(aNode)
+  scrollToVisible: function Utils_scrollToVisible(node)
   {
-    aNode.scrollIntoView(false);
+    node.scrollIntoView(false);
   },
 
   /**
    * Check if the given output node is scrolled to the bottom.
    *
-   * @param nsIDOMNode aOutputNode
+   * @param nsIDOMNode outputNode
    * @return boolean
    *         True if the output node is scrolled to the bottom, or false
    *         otherwise.
    */
-  isOutputScrolledToBottom: function Utils_isOutputScrolledToBottom(aOutputNode)
+  isOutputScrolledToBottom: function Utils_isOutputScrolledToBottom(outputNode)
   {
-    let lastNodeHeight = aOutputNode.lastChild ?
-                         aOutputNode.lastChild.clientHeight : 0;
-    let scrollNode = aOutputNode.parentNode;
+    let lastNodeHeight = outputNode.lastChild ?
+                         outputNode.lastChild.clientHeight : 0;
+    let scrollNode = outputNode.parentNode;
     return scrollNode.scrollTop + scrollNode.clientHeight >=
            scrollNode.scrollHeight - lastNodeHeight / 2;
   },
 
   /**
    * Determine the category of a given nsIScriptError.
    *
-   * @param nsIScriptError aScriptError
+   * @param nsIScriptError scriptError
    *        The script error you want to determine the category for.
    * @return CATEGORY_JS|CATEGORY_CSS|CATEGORY_SECURITY
    *         Depending on the script error CATEGORY_JS, CATEGORY_CSS, or
    *         CATEGORY_SECURITY can be returned.
    */
-  categoryForScriptError: function Utils_categoryForScriptError(aScriptError)
+  categoryForScriptError: function Utils_categoryForScriptError(scriptError)
   {
-    let category = aScriptError.category;
+    let category = scriptError.category;
 
     if (/^(?:CSS|Layout)\b/.test(category)) {
       return CATEGORY_CSS;
     }
 
     switch (category) {
       case "Mixed Content Blocker":
       case "Mixed Content Message":
@@ -4763,28 +4765,28 @@ var Utils = {
       default:
         return CATEGORY_JS;
     }
   },
 
   /**
    * Retrieve the limit of messages for a specific category.
    *
-   * @param number aCategory
+   * @param number category
    *        The category of messages you want to retrieve the limit for. See the
    *        CATEGORY_* constants.
    * @return number
    *         The number of messages allowed for the specific category.
    */
-  logLimitForCategory: function Utils_logLimitForCategory(aCategory)
+  logLimitForCategory: function Utils_logLimitForCategory(category)
   {
     let logLimit = DEFAULT_LOG_LIMIT;
 
     try {
-      let prefName = CATEGORY_CLASS_FRAGMENTS[aCategory];
+      let prefName = CATEGORY_CLASS_FRAGMENTS[category];
       logLimit = Services.prefs.getIntPref("devtools.hud.loglimit." + prefName);
       logLimit = Math.max(logLimit, 1);
     }
     catch (e) { }
 
     return logLimit;
   },
 };
@@ -4792,19 +4794,19 @@ var Utils = {
 ///////////////////////////////////////////////////////////////////////////////
 // CommandController
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * A controller (an instance of nsIController) that makes editing actions
  * behave appropriately in the context of the Web Console.
  */
-function CommandController(aWebConsole)
+function CommandController(webConsole)
 {
-  this.owner = aWebConsole;
+  this.owner = webConsole;
 }
 
 CommandController.prototype = {
   /**
    * Selects all the text in the HUD output.
    */
   selectAll: function CommandController_selectAll()
   {
@@ -4827,27 +4829,27 @@ CommandController.prototype = {
   /**
    * Copies the last clicked message.
    */
   copyLastClicked: function CommandController_copy()
   {
     this.owner.copySelectedItems({ linkOnly: false, contextmenu: true });
   },
 
-  supportsCommand: function CommandController_supportsCommand(aCommand)
+  supportsCommand: function CommandController_supportsCommand(command)
   {
     if (!this.owner || !this.owner.output) {
       return false;
     }
-    return this.isCommandEnabled(aCommand);
+    return this.isCommandEnabled(command);
   },
 
-  isCommandEnabled: function CommandController_isCommandEnabled(aCommand)
+  isCommandEnabled: function CommandController_isCommandEnabled(command)
   {
-    switch (aCommand) {
+    switch (command) {
       case "consoleCmd_openURL":
       case "consoleCmd_copyURL": {
         // Only enable URL-related actions if node is Net Activity.
         let selectedItem = this.owner.output.getSelectedMessages(1)[0] ||
                            this.owner._contextMenuHandler.lastClickedMessage;
         return selectedItem && "url" in selectedItem;
       }
       case "cmd_copy": {
@@ -4864,19 +4866,19 @@ CommandController.prototype = {
       case "cmd_fontSizeReduce":
       case "cmd_fontSizeReset":
       case "cmd_close":
         return this.owner.owner._browserConsole;
     }
     return false;
   },
 
-  doCommand: function CommandController_doCommand(aCommand)
+  doCommand: function CommandController_doCommand(command)
   {
-    switch (aCommand) {
+    switch (command) {
       case "consoleCmd_openURL":
         this.openURL();
         break;
       case "consoleCmd_copyURL":
         this.copyURL();
         break;
       case "consoleCmd_clearOutput":
         this.owner.jsterm.clearOutput(true);
@@ -4910,25 +4912,25 @@ CommandController.prototype = {
 // Web Console connection proxy
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * The WebConsoleConnectionProxy handles the connection between the Web Console
  * and the application we connect to through the remote debug protocol.
  *
  * @constructor
- * @param object aWebConsole
- *        The Web Console instance that owns this connection proxy.
- * @param RemoteTarget aTarget
+ * @param object webConsoleFrame
+ *        The WebConsoleFrame object that owns this connection proxy.
+ * @param RemoteTarget target
  *        The target that the console will connect to.
  */
-function WebConsoleConnectionProxy(aWebConsole, aTarget)
+function WebConsoleConnectionProxy(webConsoleFrame, target)
 {
-  this.owner = aWebConsole;
-  this.target = aTarget;
+  this.webConsoleFrame = webConsoleFrame;
+  this.target = target;
 
   this._onPageError = this._onPageError.bind(this);
   this._onLogMessage = this._onLogMessage.bind(this);
   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   this._onFileActivity = this._onFileActivity.bind(this);
   this._onReflowActivity = this._onReflowActivity.bind(this);
@@ -4937,22 +4939,22 @@ function WebConsoleConnectionProxy(aWebC
   this._onAttachConsole = this._onAttachConsole.bind(this);
   this._onCachedMessages = this._onCachedMessages.bind(this);
   this._connectionTimeout = this._connectionTimeout.bind(this);
   this._onLastPrivateContextExited = this._onLastPrivateContextExited.bind(this);
 }
 
 WebConsoleConnectionProxy.prototype = {
   /**
-   * The owning Web Console instance.
+   * The owning Web Console Frame instance.
    *
    * @see WebConsoleFrame
    * @type object
    */
-  owner: null,
+  webConsoleFrame: null,
 
   /**
    * The target that the console connects to.
    * @type RemoteTarget
    */
   target: null,
 
   /**
@@ -5045,17 +5047,17 @@ WebConsoleConnectionProxy.prototype = {
       client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited);
     }
     this.target.on("will-navigate", this._onTabNavigated);
     this.target.on("navigate", this._onTabNavigated);
 
     this._consoleActor = this.target.form.consoleActor;
     if (this.target.isTabActor) {
       let tab = this.target.form;
-      this.owner.onLocationChange(tab.url, tab.title);
+      this.webConsoleFrame.onLocationChange(tab.url, tab.title);
     }
     this._attachConsole();
 
     return connPromise;
   },
 
   /**
    * Connection timeout handler.
@@ -5082,143 +5084,143 @@ WebConsoleConnectionProxy.prototype = {
     this.client.attachConsole(this._consoleActor, listeners,
                               this._onAttachConsole);
   },
 
   /**
    * The "attachConsole" response handler.
    *
    * @private
-   * @param object aResponse
+   * @param object response
    *        The JSON response object received from the server.
-   * @param object aWebConsoleClient
+   * @param object webConsoleClient
    *        The WebConsoleClient instance for the attached console, for the
    *        specific tab we work with.
    */
-  _onAttachConsole: function WCCP__onAttachConsole(aResponse, aWebConsoleClient)
+  _onAttachConsole: function WCCP__onAttachConsole(response, webConsoleClient)
   {
-    if (aResponse.error) {
-      Cu.reportError("attachConsole failed: " + aResponse.error + " " +
-                     aResponse.message);
-      this._connectDefer.reject(aResponse);
+    if (response.error) {
+      Cu.reportError("attachConsole failed: " + response.error + " " +
+                     response.message);
+      this._connectDefer.reject(response);
       return;
     }
 
-    this.webConsoleClient = aWebConsoleClient;
-
-    this._hasNativeConsoleAPI = aResponse.nativeConsoleAPI;
+    this.webConsoleClient = webConsoleClient;
+
+    this._hasNativeConsoleAPI = response.nativeConsoleAPI;
     this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
     this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
 
     let msgs = ["PageError", "ConsoleAPI"];
     this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
 
-    this.owner._onUpdateListeners();
+    this.webConsoleFrame._onUpdateListeners();
   },
 
   /**
    * The "cachedMessages" response handler.
    *
    * @private
-   * @param object aResponse
+   * @param object response
    *        The JSON response object received from the server.
    */
-  _onCachedMessages: function WCCP__onCachedMessages(aResponse)
+  _onCachedMessages: function WCCP__onCachedMessages(response)
   {
-    if (aResponse.error) {
-      Cu.reportError("Web Console getCachedMessages error: " + aResponse.error +
-                     " " + aResponse.message);
-      this._connectDefer.reject(aResponse);
+    if (response.error) {
+      Cu.reportError("Web Console getCachedMessages error: " + response.error +
+                     " " + response.message);
+      this._connectDefer.reject(response);
       return;
     }
 
     if (!this._connectTimer) {
       // This happens if the promise is rejected (eg. a timeout), but the
       // connection attempt is successful, nonetheless.
       Cu.reportError("Web Console getCachedMessages error: invalid state.");
     }
 
-    let messages = aResponse.messages.concat(...this.webConsoleClient.getNetworkEvents());
+    let messages = response.messages.concat(...this.webConsoleClient.getNetworkEvents());
     messages.sort((a, b) => a.timeStamp - b.timeStamp);
 
-    this.owner.displayCachedMessages(messages);
+    this.webConsoleFrame.displayCachedMessages(messages);
 
     if (!this._hasNativeConsoleAPI) {
-      this.owner.logWarningAboutReplacedAPI();
+      this.webConsoleFrame.logWarningAboutReplacedAPI();
     }
 
     this.connected = true;
     this._connectDefer.resolve(this);
   },
 
   /**
    * The "pageError" message type handler. We redirect any page errors to the UI
    * for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onPageError: function WCCP__onPageError(aType, aPacket)
+  _onPageError: function WCCP__onPageError(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handlePageError(aPacket.pageError);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handlePageError(packet.pageError);
     }
   },
 
   /**
    * The "logMessage" message type handler. We redirect any message to the UI
    * for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onLogMessage: function WCCP__onLogMessage(aType, aPacket)
+  _onLogMessage: function WCCP__onLogMessage(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleLogMessage(aPacket);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handleLogMessage(packet);
     }
   },
 
   /**
    * The "consoleAPICall" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onConsoleAPICall: function WCCP__onConsoleAPICall(aType, aPacket)
+  _onConsoleAPICall: function WCCP__onConsoleAPICall(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleConsoleAPICall(aPacket.message);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handleConsoleAPICall(packet.message);
     }
   },
 
   /**
    * The "networkEvent" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param string type
    *        Message type.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEvent: function(type, networkInfo)
   {
-    if (this.owner) {
-      this.owner.handleNetworkEvent(networkInfo);
+    if (this.webConsoleFrame) {
+      this.webConsoleFrame.handleNetworkEvent(networkInfo);
     }
   },
 
   /**
    * The "networkEventUpdate" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
@@ -5226,109 +5228,109 @@ WebConsoleConnectionProxy.prototype = {
    *        Message type.
    * @param object packet
    *        The message received from the server.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEventUpdate: function(type, { packet, networkInfo })
   {
-    if (this.owner) {
-      this.owner.handleNetworkEventUpdate(networkInfo, packet);
+    if (this.webConsoleFrame) {
+      this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
     }
   },
 
   /**
    * The "fileActivity" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onFileActivity: function WCCP__onFileActivity(aType, aPacket)
+  _onFileActivity: function WCCP__onFileActivity(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleFileActivity(aPacket.uri);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handleFileActivity(packet.uri);
     }
   },
 
-  _onReflowActivity: function WCCP__onReflowActivity(aType, aPacket)
+  _onReflowActivity: function WCCP__onReflowActivity(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleReflowActivity(aPacket);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handleReflowActivity(packet);
     }
   },
 
   /**
    * The "serverLogCall" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onServerLogCall: function WCCP__onServerLogCall(aType, aPacket)
+  _onServerLogCall: function WCCP__onServerLogCall(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleConsoleAPICall(aPacket.message);
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.handleConsoleAPICall(packet.message);
     }
   },
 
   /**
    * The "lastPrivateContextExited" message type handler. When this message is
    * received the Web Console UI is cleared.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
   _onLastPrivateContextExited:
-  function WCCP__onLastPrivateContextExited(aType, aPacket)
+  function WCCP__onLastPrivateContextExited(type, packet)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.jsterm.clearPrivateMessages();
+    if (this.webConsoleFrame && packet.from == this._consoleActor) {
+      this.webConsoleFrame.jsterm.clearPrivateMessages();
     }
   },
 
   /**
    * The "will-navigate" and "navigate" event handlers. We redirect any message
    * to the UI for displaying.
    *
    * @private
-   * @param string aEvent
+   * @param string event
    *        Event type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
    */
-  _onTabNavigated: function WCCP__onTabNavigated(aEvent, aPacket)
+  _onTabNavigated: function WCCP__onTabNavigated(event, packet)
   {
-    if (!this.owner) {
+    if (!this.webConsoleFrame) {
       return;
     }
 
-    this.owner.handleTabNavigated(aEvent, aPacket);
+    this.webConsoleFrame.handleTabNavigated(event, packet);
   },
 
   /**
    * Release an object actor.
    *
-   * @param string aActor
+   * @param string actor
    *        The actor ID to send the request to.
    */
-  releaseActor: function WCCP_releaseActor(aActor)
+  releaseActor: function WCCP_releaseActor(actor)
   {
     if (this.client) {
-      this.client.release(aActor);
+      this.client.release(actor);
     }
   },
 
   /**
    * Disconnect the Web Console from the remote server.
    *
    * @return object
    *         A promise object that is resolved when disconnect completes.
@@ -5357,17 +5359,17 @@ WebConsoleConnectionProxy.prototype = {
     this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
     this.target.off("will-navigate", this._onTabNavigated);
     this.target.off("navigate", this._onTabNavigated);
 
     this.client = null;
     this.webConsoleClient = null;
     this.target = null;
     this.connected = false;
-    this.owner = null;
+    this.webConsoleFrame = null;
     this._disconnecter.resolve(null);
 
     return this._disconnecter.promise;
   },
 };
 
 function gSequenceId()
 {
@@ -5378,58 +5380,58 @@ gSequenceId.n = 0;
 ///////////////////////////////////////////////////////////////////////////////
 // Context Menu
 ///////////////////////////////////////////////////////////////////////////////
 
 /*
  * ConsoleContextMenu this used to handle the visibility of context menu items.
  *
  * @constructor
- * @param object aOwner
+ * @param object owner
  *        The WebConsoleFrame instance that owns this object.
  */
-function ConsoleContextMenu(aOwner)
+function ConsoleContextMenu(owner)
 {
-  this.owner = aOwner;
+  this.owner = owner;
   this.popup = this.owner.document.getElementById("output-contextmenu");
   this.build = this.build.bind(this);
   this.popup.addEventListener("popupshowing", this.build);
 }
 
 ConsoleContextMenu.prototype = {
   lastClickedMessage: null,
 
   /*
    * Handle to show/hide context menu item.
    */
-  build: function CCM_build(aEvent)
+  build: function CCM_build(event)
   {
-    let metadata = this.getSelectionMetadata(aEvent.rangeParent);
+    let metadata = this.getSelectionMetadata(event.rangeParent);
     for (let element of this.popup.children) {
       element.hidden = this.shouldHideMenuItem(element, metadata);
     }
   },
 
   /*
    * Get selection information from the view.
    *
-   * @param nsIDOMElement aClickElement
+   * @param nsIDOMElement clickElement
    *        The DOM element the user clicked on.
    * @return object
    *         Selection metadata.
    */
-  getSelectionMetadata: function CCM_getSelectionMetadata(aClickElement)
+  getSelectionMetadata: function CCM_getSelectionMetadata(clickElement)
   {
     let metadata = {
       selectionType: "",
       selection: new Set(),
     };
     let selectedItems = this.owner.output.getSelectedMessages();
     if (!selectedItems.length) {
-      let clickedItem = this.owner.output.getMessageForElement(aClickElement);
+      let clickedItem = this.owner.output.getMessageForElement(clickElement);
       if (clickedItem) {
         this.lastClickedMessage = clickedItem;
         selectedItems = [clickedItem];
       }
     }
 
     metadata.selectionType = selectedItems.length > 1 ? "multiple" : "single";
 
@@ -5455,36 +5457,36 @@ ConsoleContextMenu.prototype = {
     }
 
     return metadata;
   },
 
   /*
    * Determine if an item should be hidden.
    *
-   * @param nsIDOMElement aMenuItem
-   * @param object aMetadata
+   * @param nsIDOMElement menuItem
+   * @param object metadata
    * @return boolean
    *         Whether the given item should be hidden or not.
    */
-  shouldHideMenuItem: function CCM_shouldHideMenuItem(aMenuItem, aMetadata)
+  shouldHideMenuItem: function CCM_shouldHideMenuItem(menuItem, metadata)
   {
-    let selectionType = aMenuItem.getAttribute("selectiontype");
-    if (selectionType && !aMetadata.selectionType == selectionType) {
+    let selectionType = menuItem.getAttribute("selectiontype");
+    if (selectionType && !metadata.selectionType == selectionType) {
       return true;
     }
 
-    let selection = aMenuItem.getAttribute("selection");
+    let selection = menuItem.getAttribute("selection");
     if (!selection) {
       return false;
     }
 
     let shouldHide = true;
     let itemData = selection.split("|");
-    for (let type of aMetadata.selection) {
+    for (let type of metadata.selection) {
       // check whether this menu item should show or not.
       if (itemData.indexOf(type) !== -1) {
         shouldHide = false;
         break;
       }
     }
 
     return shouldHide;
--- a/devtools/shared/heapsnapshot/DominatorTree.cpp
+++ b/devtools/shared/heapsnapshot/DominatorTree.cpp
@@ -1,42 +1,28 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/devtools/DominatorTree.h"
-#include "js/Debug.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/dom/DominatorTreeBinding.h"
 
 namespace mozilla {
 namespace devtools {
 
-static MallocSizeOf
-getCurrentThreadDebuggerMallocSizeOf()
-{
-  auto ccrt = CycleCollectedJSRuntime::Get();
-  MOZ_ASSERT(ccrt);
-  auto rt = ccrt->Runtime();
-  MOZ_ASSERT(rt);
-  auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
-  MOZ_ASSERT(mallocSizeOf);
-  return mallocSizeOf;
-}
-
 dom::Nullable<uint64_t>
 DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv)
 {
   JS::ubi::Node::Id id(aNodeId);
   auto node = mHeapSnapshot->getNodeById(id);
   if (node.isNothing())
     return dom::Nullable<uint64_t>();
 
-  auto mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
+  auto mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
   JS::ubi::Node::Size size = 0;
   if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return dom::Nullable<uint64_t>();
   }
 
   MOZ_ASSERT(size != 0,
              "The node should not have been unknown since we got it from the heap snapshot.");
@@ -78,17 +64,17 @@ DominatorTree::GetImmediatelyDominated(u
   MOZ_ASSERT(aOutResult.IsNull());
 
   JS::ubi::Node::Id id(aNodeId);
   Maybe<JS::ubi::Node> node = mHeapSnapshot->getNodeById(id);
   if (node.isNothing())
     return;
 
   // Get all immediately dominated nodes and their retained sizes.
-  MallocSizeOf mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
+  MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
   Maybe<JS::ubi::DominatorTree::DominatedSetRange> range = mDominatorTree.getDominatedSet(*node);
   MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot.");
   size_t length = range->length();
   nsTArray<NodeAndRetainedSize> dominatedNodes(length);
   for (const JS::ubi::Node& dominatedNode : *range) {
     JS::ubi::Node::Size retainedSize = 0;
     if (NS_WARN_IF(!mDominatorTree.getRetainedSize(dominatedNode, mallocSizeOf, retainedSize))) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -50,16 +50,28 @@ using namespace dom;
 
 using ::google::protobuf::io::ArrayInputStream;
 using ::google::protobuf::io::CodedInputStream;
 using ::google::protobuf::io::GzipInputStream;
 using ::google::protobuf::io::ZeroCopyInputStream;
 
 using JS::ubi::AtomOrTwoByteChars;
 
+MallocSizeOf
+GetCurrentThreadDebuggerMallocSizeOf()
+{
+  auto ccrt = CycleCollectedJSRuntime::Get();
+  MOZ_ASSERT(ccrt);
+  auto rt = ccrt->Runtime();
+  MOZ_ASSERT(rt);
+  auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
+  MOZ_ASSERT(mallocSizeOf);
+  return mallocSizeOf;
+}
+
 /*** Cycle Collection Boilerplate *****************************************************************/
 
 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)
@@ -477,17 +489,17 @@ HeapSnapshot::TakeCensus(JSContext* cx, 
   }
 
   JS::ubi::RootedCount rootCount(cx, rootType->makeCount());
   if (NS_WARN_IF(!rootCount)) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
-  JS::ubi::CensusHandler handler(census, rootCount);
+  JS::ubi::CensusHandler handler(census, rootCount, GetCurrentThreadDebuggerMallocSizeOf());
 
   {
     JS::AutoCheckCannotGC nogc;
 
     JS::ubi::CensusTraversal traversal(JS_GetRuntime(cx), handler, nogc);
     if (NS_WARN_IF(!traversal.init())) {
       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
@@ -499,17 +511,17 @@ HeapSnapshot::TakeCensus(JSContext* cx, 
     }
 
     if (NS_WARN_IF(!traversal.traverse())) {
       rv.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
   }
 
-  if (NS_WARN_IF(!handler.report(rval))) {
+  if (NS_WARN_IF(!handler.report(cx, rval))) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 }
 
 already_AddRefed<DominatorTree>
 HeapSnapshot::ComputeDominatorTree(ErrorResult& rv)
 {
--- a/devtools/shared/heapsnapshot/HeapSnapshot.h
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.h
@@ -216,12 +216,15 @@ WriteHeapGraph(JSContext* cx,
                JS::AutoCheckCannotGC& noGC)
 {
   uint32_t ignoreNodeCount;
   uint32_t ignoreEdgeCount;
   return WriteHeapGraph(cx, node, writer, wantNames, zones, noGC,
                         ignoreNodeCount, ignoreEdgeCount);
 }
 
+// Get the mozilla::MallocSizeOf for the current thread's JSRuntime.
+MallocSizeOf GetCurrentThreadDebuggerMallocSizeOf();
+
 } // namespace devtools
 } // namespace mozilla
 
 #endif // mozilla_devtools_HeapSnapshot__
--- a/dom/base/FileReader.cpp
+++ b/dom/base/FileReader.cpp
@@ -1,44 +1,44 @@
 /* -*- 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 "FileReader.h"
 
-#include "nsContentCID.h"
-#include "nsContentUtils.h"
-#include "nsDOMClassInfoID.h"
-#include "nsError.h"
-#include "nsIFile.h"
-#include "nsNetCID.h"
-#include "nsNetUtil.h"
+#include "nsIEventTarget.h"
+#include "nsIGlobalObject.h"
+#include "nsITimer.h"
+#include "nsITransport.h"
+#include "nsIStreamTransportService.h"
 
-#include "nsXPCOM.h"
-#include "nsIDOMEventListener.h"
-#include "nsJSEnvironment.h"
-#include "nsCycleCollectionParticipant.h"
 #include "mozilla/Base64.h"
+#include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileReaderBinding.h"
 #include "mozilla/dom/ProgressEvent.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMJSUtils.h"
+#include "nsError.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
 #include "xpcpublic.h"
-#include "nsDOMJSUtils.h"
 
-#include "jsfriendapi.h"
-
-#include "nsITransport.h"
-#include "nsIStreamTransportService.h"
+#include "WorkerPrivate.h"
+#include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
+using namespace workers;
+
 #define ABORT_STR "abort"
 #define LOAD_STR "load"
 #define LOADSTART_STR "loadstart"
 #define LOADEND_STR "loadend"
 #define ERROR_STR "error"
 #define PROGRESS_STR "progress"
 
 const uint64_t kUnknownSize = uint64_t(-1);
@@ -51,17 +51,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader,
                                                 DOMEventTargetHelper)
-  tmp->mResultArrayBuffer = nullptr;
+  tmp->Shutdown();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FileReader,
                                                DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
@@ -71,54 +71,81 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
 
+class MOZ_RAII FileReaderDecreaseBusyCounter
+{
+  RefPtr<FileReader> mFileReader;
+public:
+  explicit FileReaderDecreaseBusyCounter(FileReader* aFileReader)
+    : mFileReader(aFileReader)
+  {}
+
+  ~FileReaderDecreaseBusyCounter()
+  {
+    mFileReader->DecreaseBusyCounter();
+  }
+};
+
 void
 FileReader::RootResultArrayBuffer()
 {
   mozilla::HoldJSObjects(this);
 }
 
 //FileReader constructors/initializers
 
-FileReader::FileReader(nsPIDOMWindow* aWindow)
+FileReader::FileReader(nsPIDOMWindow* aWindow,
+                       WorkerPrivate* aWorkerPrivate)
   : DOMEventTargetHelper(aWindow)
   , mFileData(nullptr)
   , mDataLen(0)
   , mDataFormat(FILE_AS_BINARY)
   , mResultArrayBuffer(nullptr)
   , mProgressEventWasDelayed(false)
   , mTimerIsActive(false)
   , mReadyState(EMPTY)
   , mTotal(0)
   , mTransferred(0)
+  , mTarget(do_GetCurrentThread())
+  , mBusyCount(0)
+  , mWorkerPrivate(aWorkerPrivate)
 {
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate && !aWindow);
+  MOZ_ASSERT_IF(NS_IsMainThread(), !mWorkerPrivate);
   SetDOMStringToNull(mResult);
 }
 
 FileReader::~FileReader()
 {
-  FreeFileData();
-  mResultArrayBuffer = nullptr;
+  Shutdown();
   DropJSObjects(this);
 }
 
 /* static */ already_AddRefed<FileReader>
 FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
   // The owner can be null when this object is used by chrome code.
   nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
-  RefPtr<FileReader> fileReader = new FileReader(owner);
+  WorkerPrivate* workerPrivate = nullptr;
 
-  if (!owner && nsContentUtils::IsCallerChrome()) {
+  if (!NS_IsMainThread()) {
+    JSContext* cx = aGlobal.Context();
+    workerPrivate = GetWorkerPrivateFromContext(cx);
+    MOZ_ASSERT(workerPrivate);
+  }
+
+  RefPtr<FileReader> fileReader = new FileReader(owner, workerPrivate);
+
+  if (!owner && nsContentUtils::ThreadsafeIsCallerChrome()) {
     // Instead of grabbing some random global from the context stack,
     // let's use the default one (junk scope) for now.
     // We should move away from this Init...
     fileReader->BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
   }
 
   return fileReader.forget();
 }
@@ -210,17 +237,27 @@ FileReader::DoOnLoadEnd(nsresult aStatus
 
   aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
   aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
 
   nsresult rv = NS_OK;
   switch (mDataFormat) {
     case FILE_AS_ARRAYBUFFER: {
       AutoJSAPI jsapi;
-      if (NS_WARN_IF(!jsapi.Init(DOMEventTargetHelper::GetParentObject()))) {
+      nsCOMPtr<nsIGlobalObject> globalObject;
+
+      if (NS_IsMainThread()) {
+        globalObject = do_QueryInterface(GetParentObject());
+      } else {
+        MOZ_ASSERT(mWorkerPrivate);
+        MOZ_ASSERT(mBusyCount);
+        globalObject = mWorkerPrivate->GlobalScope();
+      }
+
+      if (!globalObject || !jsapi.Init(globalObject)) {
         FreeFileData();
         return NS_ERROR_FAILURE;
       }
 
       RootResultArrayBuffer();
       mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData);
       if (!mResultArrayBuffer) {
         JS_ClearPendingException(jsapi.cx());
@@ -251,48 +288,68 @@ FileReader::DoOnLoadEnd(nsresult aStatus
   mResult.SetIsVoid(false);
 
   FreeFileData();
 
   return rv;
 }
 
 nsresult
-FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
+FileReader::DoAsyncWait()
 {
-  MOZ_ASSERT(aStream);
+  nsresult rv = IncreaseBusyCounter();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mAsyncStream->AsyncWait(this,
+                               /* aFlags*/ 0,
+                               /* aRequestedCount */ 0,
+                               mTarget);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    DecreaseBusyCounter();
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+FileReader::DoReadData(uint64_t aCount)
+{
+  MOZ_ASSERT(mAsyncStream);
 
   if (mDataFormat == FILE_AS_BINARY) {
     //Continuously update our binary string as data comes in
     uint32_t oldLen = mResult.Length();
     NS_ASSERTION(mResult.Length() == mDataLen, "unexpected mResult length");
     if (uint64_t(oldLen) + aCount > UINT32_MAX)
       return NS_ERROR_OUT_OF_MEMORY;
     char16_t *buf = nullptr;
     mResult.GetMutableData(&buf, oldLen + aCount, fallible);
     NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
 
     uint32_t bytesRead = 0;
-    aStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
-                          &bytesRead);
+    mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
+                               &bytesRead);
     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   }
   else {
     //Update memory buffer to reflect the contents of the file
     if (mDataLen + aCount > UINT32_MAX) {
       // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
       return NS_ERROR_OUT_OF_MEMORY;
     }
     if (mDataFormat != FILE_AS_ARRAYBUFFER) {
       mFileData = (char *) realloc(mFileData, mDataLen + aCount);
       NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
     }
 
     uint32_t bytesRead = 0;
-    aStream->Read(mFileData + mDataLen, aCount, &bytesRead);
+    mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   }
 
   mDataLen += aCount;
   return NS_OK;
 }
 
 // Helper methods
@@ -356,20 +413,17 @@ FileReader::ReadFileContent(Blob& aBlob,
   mAsyncStream = do_QueryInterface(wrapper);
   MOZ_ASSERT(mAsyncStream);
 
   mTotal = mBlob->GetSize(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  aRv = mAsyncStream->AsyncWait(this,
-                                /* aFlags*/ 0,
-                                /* aRequestedCount */ 0,
-                                NS_GetCurrentThread());
+  aRv = DoAsyncWait();
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   //FileReader should be in loading state here
   mReadyState = LOADING;
   DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
 
@@ -462,16 +516,17 @@ FileReader::StartProgressEventTimer()
   if (!mProgressNotifier) {
     mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
   }
 
   if (mProgressNotifier) {
     mProgressEventWasDelayed = false;
     mTimerIsActive = true;
     mProgressNotifier->Cancel();
+    mProgressNotifier->SetTarget(mTarget);
     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
                                         nsITimer::TYPE_ONE_SHOT);
   }
 }
 
 void
 FileReader::ClearProgressEventTimer()
 {
@@ -545,28 +600,30 @@ FileReader::Notify(nsITimer* aTimer)
 // InputStreamCallback
 NS_IMETHODIMP
 FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
   if (mReadyState != LOADING || aStream != mAsyncStream) {
     return NS_OK;
   }
 
+  // We use this class to decrease the busy counter at the end of this method.
+  // In theory we can do it immediatelly but, for debugging reasons, we want to
+  // be 100% sure we have a feature when OnLoadEnd() is called.
+  FileReaderDecreaseBusyCounter RAII(this);
+
   uint64_t aCount;
   nsresult rv = aStream->Available(&aCount);
 
   if (NS_SUCCEEDED(rv) && aCount) {
-    rv = DoReadData(aStream, aCount);
+    rv = DoReadData(aCount);
   }
 
   if (NS_SUCCEEDED(rv)) {
-    rv = aStream->AsyncWait(this,
-                            /* aFlags*/ 0,
-                            /* aRequestedCount */ 0,
-                            NS_GetCurrentThread());
+    rv = DoAsyncWait();
   }
 
   if (NS_FAILED(rv) || !aCount) {
     if (rv == NS_BASE_STREAM_CLOSED) {
       rv = NS_OK;
     }
     return OnLoadEnd(rv);
   }
@@ -638,10 +695,61 @@ FileReader::Abort(ErrorResult& aRv)
   //Clean up memory buffer
   FreeFileData();
 
   // Dispatch the events
   DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
   DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
 }
 
+nsresult
+FileReader::IncreaseBusyCounter()
+{
+  if (mWorkerPrivate && mBusyCount++ == 0 &&
+      !mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void
+FileReader::DecreaseBusyCounter()
+{
+  MOZ_ASSERT_IF(mWorkerPrivate, mBusyCount);
+  if (mWorkerPrivate && --mBusyCount == 0) {
+    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+  }
+}
+
+bool
+FileReader::Notify(JSContext* aCx, Status aStatus)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (aStatus > Running) {
+    Shutdown();
+  }
+
+  return true;
+}
+
+void
+FileReader::Shutdown()
+{
+  FreeFileData();
+  mResultArrayBuffer = nullptr;
+
+  if (mAsyncStream) {
+    mAsyncStream->Close();
+    mAsyncStream = nullptr;
+  }
+
+  if (mWorkerPrivate && mBusyCount != 0) {
+    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+    mWorkerPrivate = nullptr;
+    mBusyCount = 0;
+  }
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/FileReader.h
+++ b/dom/base/FileReader.h
@@ -4,57 +4,67 @@
  * 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_dom_FileReader_h
 #define mozilla_dom_FileReader_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/dom/DOMError.h"
 
-#include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
-#include "nsIStreamListener.h"
-#include "nsISupportsUtils.h"
 #include "nsIInterfaceRequestor.h"
-#include "nsITimer.h"
-#include "nsJSUtils.h"
+#include "nsCOMPtr.h"
 #include "nsString.h"
-#include "nsTArray.h"
 #include "nsWeakReference.h"
-#include "prtime.h"
+#include "WorkerFeature.h"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
+class nsITimer;
+class nsIEventTarget;
+
 namespace mozilla {
 namespace dom {
 
 class Blob;
+class DOMError;
+
+namespace workers {
+class WorkerPrivate;
+}
 
 extern const uint64_t kUnknownSize;
 
+class FileReaderDecreaseBusyCounter;
+
 class FileReader final : public DOMEventTargetHelper,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
                          public nsIInputStreamCallback,
-                         public nsITimerCallback
+                         public nsITimerCallback,
+                         public workers::WorkerFeature
 {
+  friend class FileReaderDecreaseBusyCounter;
+
 public:
-  explicit FileReader(nsPIDOMWindow* aWindow);
+  FileReader(nsPIDOMWindow* aWindow,
+             workers::WorkerPrivate* aWorkerPrivate);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINTERFACEREQUESTOR
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader, DOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader,
+                                                         DOMEventTargetHelper)
 
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
   static already_AddRefed<FileReader>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
   void ReadAsArrayBuffer(JSContext* aCx, Blob& aBlob, ErrorResult& aRv)
   {
     ReadFileContent(aBlob, EmptyString(), FILE_AS_ARRAYBUFFER, aRv);
   }
@@ -91,16 +101,19 @@ public:
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(loadend)
 
   void ReadAsBinaryString(Blob& aBlob, ErrorResult& aRv)
   {
     ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
   }
 
+  // WorkerFeature
+  bool Notify(JSContext* aCx, workers::Status) override;
+
 private:
   virtual ~FileReader();
 
   // This must be in sync with dom/webidl/FileReader.webidl
   enum eReadyState {
     EMPTY = 0,
     LOADING = 1,
     DONE = 2
@@ -126,28 +139,34 @@ private:
 
   nsresult OnLoadEnd(nsresult aStatus);
 
   void StartProgressEventTimer();
   void ClearProgressEventTimer();
   void DispatchError(nsresult rv, nsAString& finalEvent);
   nsresult DispatchProgressEvent(const nsAString& aType);
 
-  nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount);
+  nsresult DoAsyncWait();
+  nsresult DoReadData(uint64_t aCount);
 
   nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
                        nsAString& aTerminationEvent);
 
   void FreeFileData()
   {
     free(mFileData);
     mFileData = nullptr;
     mDataLen = 0;
   }
 
+  nsresult IncreaseBusyCounter();
+  void DecreaseBusyCounter();
+
+  void Shutdown();
+
   char *mFileData;
   RefPtr<Blob> mBlob;
   nsCString mCharset;
   uint32_t mDataLen;
 
   eDataFormat mDataFormat;
 
   nsString mResult;
@@ -161,14 +180,21 @@ private:
   nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
 
   RefPtr<DOMError> mError;
 
   eReadyState mReadyState;
 
   uint64_t mTotal;
   uint64_t mTransferred;
+
+  nsCOMPtr<nsIEventTarget> mTarget;
+
+  uint64_t mBusyCount;
+
+  // Kept alive with a WorkerFeature.
+  workers::WorkerPrivate* mWorkerPrivate;
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_FileReader_h
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -1,24 +1,28 @@
 /* -*- 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 "ImageEncoder.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/unused.h"
 #include "gfxUtils.h"
+#include "nsIThreadPool.h"
 #include "nsNetUtil.h"
+#include "nsXPCOMCIDInternal.h"
+#include "WorkerPrivate.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
 // This class should be placed inside GetBRGADataSourceSurfaceSync(). However,
 // due to B2G ICS uses old complier (C++98/03) which forbids local class as
@@ -64,76 +68,79 @@ private:
 // main thread.
 already_AddRefed<DataSourceSurface>
 GetBRGADataSourceSurfaceSync(already_AddRefed<layers::Image> aImage)
 {
   RefPtr<SurfaceHelper> helper = new SurfaceHelper(Move(aImage));
   return helper->GetDataSurfaceSafe();
 }
 
-class EncodingCompleteEvent : public nsRunnable
+class EncodingCompleteEvent : public nsCancelableRunnable
 {
   virtual ~EncodingCompleteEvent() {}
 
 public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  EncodingCompleteEvent(nsIThread* aEncoderThread,
-                        EncodeCompleteCallback* aEncodeCompleteCallback)
+  explicit EncodingCompleteEvent(EncodeCompleteCallback* aEncodeCompleteCallback)
     : mImgSize(0)
     , mType()
     , mImgData(nullptr)
-    , mEncoderThread(aEncoderThread)
     , mEncodeCompleteCallback(aEncodeCompleteCallback)
     , mFailed(false)
-  {}
+  {
+    if (!NS_IsMainThread() && workers::GetCurrentThreadWorkerPrivate()) {
+      mCreationThread = NS_GetCurrentThread();
+    } else {
+      NS_GetMainThread(getter_AddRefs(mCreationThread));
+    }
+  }
 
   NS_IMETHOD Run() override
   {
     nsresult rv = NS_OK;
-    MOZ_ASSERT(NS_IsMainThread());
 
     if (!mFailed) {
       // The correct parentObject has to be set by the mEncodeCompleteCallback.
       RefPtr<Blob> blob =
         Blob::CreateMemoryBlob(nullptr, mImgData, mImgSize, mType);
       MOZ_ASSERT(blob);
 
       rv = mEncodeCompleteCallback->ReceiveBlob(blob.forget());
     }
 
     mEncodeCompleteCallback = nullptr;
 
-    mEncoderThread->Shutdown();
     return rv;
   }
 
   void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
   {
     mImgData = aImgData;
     mImgSize = aImgSize;
     mType = aType;
   }
 
   void SetFailed()
   {
     mFailed = true;
   }
 
+  nsIThread* GetCreationThread()
+  {
+    return mCreationThread;
+  }
+
 private:
   uint64_t mImgSize;
   nsAutoString mType;
   void* mImgData;
-  nsCOMPtr<nsIThread> mEncoderThread;
+  nsCOMPtr<nsIThread> mCreationThread;
   RefPtr<EncodeCompleteCallback> mEncodeCompleteCallback;
   bool mFailed;
 };
 
-NS_IMPL_ISUPPORTS_INHERITED0(EncodingCompleteEvent, nsRunnable);
-
 class EncodingRunnable : public nsRunnable
 {
   virtual ~EncodingRunnable() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   EncodingRunnable(const nsAString& aType,
@@ -202,17 +209,18 @@ public:
     void* imgData = nullptr;
 
     nsresult rv = ProcessImageData(&imgSize, &imgData);
     if (NS_FAILED(rv)) {
       mEncodingCompleteEvent->SetFailed();
     } else {
       mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
     }
-    rv = NS_DispatchToMainThread(mEncodingCompleteEvent);
+    rv = mEncodingCompleteEvent->GetCreationThread()->
+      Dispatch(mEncodingCompleteEvent, nsIThread::DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       // Better to leak than to crash.
       Unused << mEncodingCompleteEvent.forget();
       return rv;
     }
 
     return rv;
   }
@@ -226,16 +234,18 @@ private:
   RefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
   int32_t mFormat;
   const nsIntSize mSize;
   bool mUsingCustomOptions;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(EncodingRunnable, nsRunnable);
 
+StaticRefPtr<nsIThreadPool> ImageEncoder::sThreadPool;
+
 /* static */
 nsresult
 ImageEncoder::ExtractData(nsAString& aType,
                           const nsAString& aOptions,
                           const nsIntSize aSize,
                           nsICanvasRenderingContextInternal* aContext,
                           layers::AsyncCanvasRenderer* aRenderer,
                           nsIInputStream** aStream)
@@ -257,34 +267,35 @@ ImageEncoder::ExtractDataFromLayersImage
                                               layers::Image* aImage,
                                               EncodeCompleteCallback* aEncodeCallback)
 {
   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   if (!encoder) {
     return NS_IMAGELIB_ERROR_NO_ENCODER;
   }
 
-  nsCOMPtr<nsIThread> encoderThread;
-  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = EnsureThreadPool();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   RefPtr<EncodingCompleteEvent> completeEvent =
-    new EncodingCompleteEvent(encoderThread, aEncodeCallback);
+    new EncodingCompleteEvent(aEncodeCallback);
 
   nsIntSize size(aImage->GetSize().width, aImage->GetSize().height);
   nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
                                                      aOptions,
                                                      nullptr,
                                                      aImage,
                                                      encoder,
                                                      completeEvent,
                                                      imgIEncoder::INPUT_FORMAT_HOSTARGB,
                                                      size,
                                                      aUsingCustomOptions);
-  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
+  return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
 /* static */
 nsresult
 ImageEncoder::ExtractDataAsync(nsAString& aType,
                                const nsAString& aOptions,
                                bool aUsingCustomOptions,
                                UniquePtr<uint8_t[]> aImageBuffer,
@@ -292,33 +303,34 @@ ImageEncoder::ExtractDataAsync(nsAString
                                const nsIntSize aSize,
                                EncodeCompleteCallback* aEncodeCallback)
 {
   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   if (!encoder) {
     return NS_IMAGELIB_ERROR_NO_ENCODER;
   }
 
-  nsCOMPtr<nsIThread> encoderThread;
-  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = EnsureThreadPool();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   RefPtr<EncodingCompleteEvent> completeEvent =
-    new EncodingCompleteEvent(encoderThread, aEncodeCallback);
+    new EncodingCompleteEvent(aEncodeCallback);
 
   nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
                                                      aOptions,
                                                      Move(aImageBuffer),
                                                      nullptr,
                                                      encoder,
                                                      completeEvent,
                                                      aFormat,
                                                      aSize,
                                                      aUsingCustomOptions);
-  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
+  return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
 /*static*/ nsresult
 ImageEncoder::GetInputStream(int32_t aWidth,
                              int32_t aHeight,
                              uint8_t* aImageBuffer,
                              int32_t aFormat,
                              imgIEncoder* aEncoder,
@@ -474,10 +486,53 @@ ImageEncoder::GetImageEncoder(nsAString&
     aType.AssignLiteral("image/png");
     nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
     encoder = do_CreateInstance(PNGEncoderCID.get());
   }
 
   return encoder.forget();
 }
 
+/* static */
+nsresult
+ImageEncoder::EnsureThreadPool()
+{
+  if (!sThreadPool) {
+    nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+    sThreadPool = threadPool;
+    if (!NS_IsMainThread()) {
+      NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
+        ClearOnShutdown(&sThreadPool);
+      }));
+    } else {
+      ClearOnShutdown(&sThreadPool);
+    }
+
+    const uint32_t kThreadLimit = 2;
+    const uint32_t kIdleThreadLimit = 1;
+    const uint32_t kIdleThreadTimeoutMs = 30000;
+
+    nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = sThreadPool->SetThreadLimit(kThreadLimit);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = sThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = sThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ImageEncoder.h
+++ b/dom/base/ImageEncoder.h
@@ -11,16 +11,17 @@
 #include "nsError.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/UniquePtr.h"
 #include "nsLayoutUtils.h"
 #include "nsSize.h"
 
 class nsICanvasRenderingContextInternal;
+class nsIThreadPool;
 
 namespace mozilla {
 
 namespace layers {
 class AsyncCanvasRenderer;
 class Image;
 } // namespace layers
 
@@ -102,16 +103,21 @@ private:
 
   // Creates and returns an encoder instance of the type specified in aType.
   // aType may change to "image/png" if no instance of the original type could
   // be created and we had to fall back to a PNG encoder. A null return value
   // should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
   // undefined in this case.
   static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
 
+  static nsresult EnsureThreadPool();
+
+  // Thread pool for dispatching EncodingRunnable.
+  static StaticRefPtr<nsIThreadPool> sThreadPool;
+
   friend class EncodingRunnable;
 };
 
 /**
  *  The callback interface of ExtractDataAsync and ExtractDataFromLayersImageAsync.
  *  ReceiveBlob() is called on main thread when encoding is complete.
  */
 class EncodeCompleteCallback
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/OffscreenCanvas.h"
 #include "mozilla/dom/OffscreenCanvasBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "MultipartBlobImpl.h"
 #include "nsFormData.h"
 #include "nsIRemoteBlob.h"
 #include "nsQueryObject.h"
 
@@ -301,17 +302,17 @@ StructuredCloneHolder::Read(nsISupports*
   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 
   // If we are tranferring something, we cannot call 'Read()' more than once.
   if (mSupportsTransferring) {
     mBlobImplArray.Clear();
-    mClonedImages.Clear();
+    mClonedSurfaces.Clear();
     Clear();
   }
 }
 
 void
 StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
                                       JSContext* aCx,
                                       uint64_t* aBuffer,
@@ -971,17 +972,17 @@ StructuredCloneHolder::CustomReadHandler
     MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
                mSupportedContext == SameProcessDifferentThread);
 
     // Get the current global object.
     // This can be null.
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     // aIndex is the index of the cloned image.
     return ImageBitmap::ReadStructuredClone(aCx, aReader,
-                                            parent, GetImages(), aIndex);
+                                            parent, GetSurfaces(), aIndex);
    }
 
   return ReadFullySerializableObjects(aCx, aReader, aTag);
 }
 
 bool
 StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
                                           JSStructuredCloneWriter* aWriter,
@@ -1016,17 +1017,17 @@ StructuredCloneHolder::CustomWriteHandle
   }
 
   // See if this is an ImageBitmap object.
   if (mSupportedContext == SameProcessSameThread ||
       mSupportedContext == SameProcessDifferentThread) {
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
-                                               GetImages(),
+                                               GetSurfaces(),
                                                imageBitmap);
     }
   }
 
   return WriteFullySerializableObjects(aCx, aWriter, aObj);
 }
 
 bool
@@ -1067,29 +1068,50 @@ StructuredCloneHolder::CustomReadTransfe
   }
 
   if (aTag == SCTAG_DOM_CANVAS) {
     MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
                mSupportedContext == SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
-    RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
+    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
+    RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
     if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
       JS_ClearPendingException(aCx);
       return false;
     }
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
+  if (aTag == SCTAG_DOM_IMAGEBITMAP) {
+    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
+               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(aContent);
+    ImageBitmapCloneData* data =
+      static_cast<ImageBitmapCloneData*>(aContent);
+    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
+    RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
+    delete data;
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
+      JS_ClearPendingException(aCx);
+      return false;
+    }
+
+    aReturnObject.set(&value.toObject());
+    return true;
+  }
+
   return false;
 }
 
 bool
 StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
                                                   JS::Handle<JSObject*> aObj,
                                                   uint32_t* aTag,
                                                   JS::TransferableOwnership* aOwnership,
@@ -1128,16 +1150,31 @@ StructuredCloneHolder::CustomWriteTransf
         *aTag = SCTAG_DOM_CANVAS;
         *aOwnership = JS::SCTAG_TMO_CUSTOM;
         *aContent = canvas->ToCloneData();
         MOZ_ASSERT(*aContent);
         canvas->SetNeutered();
 
         return true;
       }
+
+      ImageBitmap* bitmap = nullptr;
+      rv = UNWRAP_OBJECT(ImageBitmap, aObj, bitmap);
+      if (NS_SUCCEEDED(rv)) {
+        MOZ_ASSERT(bitmap);
+
+        *aExtraData = 0;
+        *aTag = SCTAG_DOM_IMAGEBITMAP;
+        *aOwnership = JS::SCTAG_TMO_CUSTOM;
+        *aContent = bitmap->ToCloneData();
+        MOZ_ASSERT(*aContent);
+        bitmap->Close();
+
+        return true;
+      }
     }
   }
 
   return false;
 }
 
 void
 StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
@@ -1158,12 +1195,22 @@ StructuredCloneHolder::CustomFreeTransfe
     MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
                mSupportedContext == SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
     delete data;
     return;
   }
+
+  if (aTag == SCTAG_DOM_IMAGEBITMAP) {
+    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
+               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(aContent);
+    ImageBitmapCloneData* data =
+      static_cast<ImageBitmapCloneData*>(aContent);
+    delete data;
+    return;
+  }
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -17,16 +17,20 @@
 #endif
 
 namespace mozilla {
 class ErrorResult;
 namespace layers {
 class Image;
 }
 
+namespace gfx {
+class DataSourceSurface;
+}
+
 namespace dom {
 
 class StructuredCloneHolderBase
 {
 public:
   StructuredCloneHolderBase();
   virtual ~StructuredCloneHolderBase();
 
@@ -176,17 +180,17 @@ public:
   // You should free this buffer with StructuredCloneHolder::FreeBuffer().
   void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
                              ErrorResult& aRv);
 
   // Call this method to know if this object is keeping some DOM object alive.
   bool HasClonedDOMObjects() const
   {
     return !mBlobImplArray.IsEmpty() ||
-           !mClonedImages.IsEmpty();
+           !mClonedSurfaces.IsEmpty();
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
@@ -207,19 +211,19 @@ public:
   }
 
   nsTArray<MessagePortIdentifier>& PortIdentifiers()
   {
     MOZ_ASSERT(mSupportsTransferring);
     return mPortIdentifiers;
   }
 
-  nsTArray<RefPtr<layers::Image>>& GetImages()
+  nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces()
   {
-    return mClonedImages;
+    return mClonedSurfaces;
   }
 
   // Implementations of the virtual methods to allow cloning of objects which
   // JS engine itself doesn't clone.
 
   virtual JSObject* CustomReadHandler(JSContext* aCx,
                                       JSStructuredCloneReader* aReader,
                                       uint32_t aTag,
@@ -286,20 +290,20 @@ protected:
   bool mSupportsCloning;
   bool mSupportsTransferring;
   ContextSupport mSupportedContext;
 
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
   // This is used for sharing the backend of ImageBitmaps.
-  // The layers::Image object must be thread-safely reference-counted.
-  // The layers::Image object will not be written ever via any ImageBitmap
+  // The DataSourceSurface object must be thread-safely reference-counted.
+  // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
-  nsTArray<RefPtr<layers::Image>> mClonedImages;
+  nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
 
   // This raw pointer is only set within ::Read() and is unset by the end.
   nsISupports* MOZ_NON_OWNING_REF mParent;
 
   // This array contains the ports once we've finished the reading. It's
   // generated from the mPortIdentifiers array.
   nsTArray<RefPtr<MessagePort>> mTransferredPorts;
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6770,87 +6770,47 @@ nsContentUtils::HaveEqualPrincipals(nsID
   if (!aDoc1 || !aDoc2) {
     return false;
   }
   bool principalsEqual = false;
   aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
   return principalsEqual;
 }
 
-static void
-CheckForWindowedPlugins(nsISupports* aSupports, void* aResult)
-{
-  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
-  if (!content || !content->IsInDoc()) {
-    return;
-  }
-  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(content));
-  if (!olc) {
-    return;
-  }
-  RefPtr<nsNPAPIPluginInstance> plugin;
-  olc->GetPluginInstance(getter_AddRefs(plugin));
-  if (!plugin) {
-    return;
-  }
-  bool isWindowless = false;
-  nsresult res = plugin->IsWindowless(&isWindowless);
-  if (NS_SUCCEEDED(res) && !isWindowless) {
-    *static_cast<bool*>(aResult) = true;
-  }
-}
-
-static bool
-DocTreeContainsWindowedPlugins(nsIDocument* aDoc, void* aResult)
-{
-  if (!nsContentUtils::IsChromeDoc(aDoc)) {
-    aDoc->EnumerateActivityObservers(CheckForWindowedPlugins, aResult);
-  }
-  if (*static_cast<bool*>(aResult)) {
-    // Return false to stop iteration, we found a windowed plugin.
-    return false;
-  }
-  aDoc->EnumerateSubDocuments(DocTreeContainsWindowedPlugins, aResult);
-  // Return false to stop iteration if we found a windowed plugin in
-  // the sub documents.
-  return !*static_cast<bool*>(aResult);
-}
-
-/* static */
-bool
-nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc)
-{
-#ifdef XP_MACOSX
-  // We control dispatch to all mac plugins.
-  return false;
-#endif
-  bool result = false;
-  
-  // Find the top of the document's branch, the child of the chrome document.
-  nsIDocument* doc = aDoc;
-  nsIDocument* parent = nullptr;
-  while (doc && (parent = doc->GetParentDocument()) && !IsChromeDoc(parent)) {
-    doc = parent;
-  }
-
-  DocTreeContainsWindowedPlugins(doc, &result);
-  return result;
-}
-
 /* static */
 bool
 nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
 {
 #ifdef XP_MACOSX
   // We control dispatch to all mac plugins.
   return false;
+#else
+  if (!aContent || !aContent->IsInDoc()) {
+    return false;
+  }
+
+  nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aContent);
+  if (!olc) {
+    return false;
+  }
+
+  RefPtr<nsNPAPIPluginInstance> plugin;
+  olc->GetPluginInstance(getter_AddRefs(plugin));
+  if (!plugin) {
+    return false;
+  }
+
+  bool isWindowless = false;
+  nsresult res = plugin->IsWindowless(&isWindowless);
+  if (NS_FAILED(res)) {
+    return false;
+  }
+
+  return !isWindowless;
 #endif
-  bool result = false;
-  CheckForWindowedPlugins(aContent, &result);
-  return result;
 }
 
 /* static */
 void
 nsContentUtils::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
                                                    nsIContent* aDest,
                                                    int32_t aOldChildCount)
 {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2055,24 +2055,16 @@ public:
    * be used to "fingerprint" and track the user across websites.
    */
   static bool ResistFingerprinting()
   {
     return sPrivacyResistFingerprinting;
   }
 
   /**
-   * Returns true if the doc tree branch which contains aDoc contains any
-   * plugins which we don't control event dispatch for, i.e. do any plugins
-   * in the same tab as this document receive key events outside of our
-   * control? This always returns false on MacOSX.
-   */
-  static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
-
-  /**
    * Return true if this doc is controlled by a ServiceWorker.
    */
   static bool IsControlledByServiceWorker(nsIDocument* aDocument);
 
   /**
    * Fire mutation events for changes caused by parsing directly into a
    * context node.
    *
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7663,17 +7663,16 @@ nsGlobalWindow::Open(const nsAString& aU
   nsCOMPtr<nsIDOMWindow> window;
   nsresult rv = OpenInternal(aUrl, aName, aOptions,
                              false,          // aDialog
                              false,          // aContentModal
                              true,           // aCalledNoScript
                              false,          // aDoJSFixups
                              true,           // aNavigate
                              nullptr, nullptr,  // No args
-                             GetPrincipal(),    // aCalleePrincipal
                              nullptr,           // aJSCallerContext
                              getter_AddRefs(window));
   if (NS_SUCCEEDED(rv) && window) {
     return CallQueryInterface(window, _retval);
   }
   return rv;
 }
 
@@ -7684,17 +7683,16 @@ nsGlobalWindow::OpenJS(const nsAString& 
   MOZ_ASSERT(IsOuterWindow());
   return OpenInternal(aUrl, aName, aOptions,
                       false,          // aDialog
                       false,          // aContentModal
                       false,          // aCalledNoScript
                       true,           // aDoJSFixups
                       true,           // aNavigate
                       nullptr, nullptr,  // No args
-                      GetPrincipal(),    // aCalleePrincipal
                       nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
                       _retval);
 }
 
 // like Open, but attaches to the new window any extra parameters past
 // [features] as a JS property named "arguments"
 nsresult
 nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
@@ -7704,17 +7702,16 @@ nsGlobalWindow::OpenDialog(const nsAStri
   MOZ_ASSERT(IsOuterWindow());
   return OpenInternal(aUrl, aName, aOptions,
                       true,                    // aDialog
                       false,                   // aContentModal
                       true,                    // aCalledNoScript
                       false,                   // aDoJSFixups
                       true,                    // aNavigate
                       nullptr, aExtraArgument,    // Arguments
-                      GetPrincipal(),             // aCalleePrincipal
                       nullptr,                    // aJSCallerContext
                       _retval);
 }
 
 // Like Open, but passes aNavigate=false.
 /* virtual */ nsresult
 nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
                                const nsAString& aName,
@@ -7724,17 +7721,16 @@ nsGlobalWindow::OpenNoNavigate(const nsA
   MOZ_ASSERT(IsOuterWindow());
   return OpenInternal(aUrl, aName, aOptions,
                       false,          // aDialog
                       false,          // aContentModal
                       true,           // aCalledNoScript
                       false,          // aDoJSFixups
                       false,          // aNavigate
                       nullptr, nullptr,  // No args
-                      GetPrincipal(),    // aCalleePrincipal
                       nullptr,           // aJSCallerContext
                       _retval);
 
 }
 
 already_AddRefed<nsIDOMWindow>
 nsGlobalWindow::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
                                 const nsAString& aName, const nsAString& aOptions,
@@ -7754,17 +7750,16 @@ nsGlobalWindow::OpenDialogOuter(JSContex
   nsCOMPtr<nsIDOMWindow> dialog;
   aError = OpenInternal(aUrl, aName, aOptions,
                         true,             // aDialog
                         false,            // aContentModal
                         false,            // aCalledNoScript
                         false,            // aDoJSFixups
                         true,                // aNavigate
                         argvArray, nullptr,  // Arguments
-                        GetPrincipal(),      // aCalleePrincipal
                         aCx,                 // aJSCallerContext
                         getter_AddRefs(dialog));
   return dialog.forget();
 }
 
 already_AddRefed<nsIDOMWindow>
 nsGlobalWindow::OpenDialog(JSContext* aCx, const nsAString& aUrl,
                            const nsAString& aName, const nsAString& aOptions,
@@ -8834,17 +8829,16 @@ nsGlobalWindow::ShowModalDialogOuter(con
   nsContentUtils::SetMicroTaskLevel(0);
   aError = OpenInternal(aUrl, EmptyString(), options,
                         false,          // aDialog
                         true,           // aContentModal
                         true,           // aCalledNoScript
                         true,           // aDoJSFixups
                         true,           // aNavigate
                         nullptr, argHolder, // args
-                        GetPrincipal(),     // aCalleePrincipal
                         nullptr,            // aJSCallerContext
                         getter_AddRefs(dlgWin));
   nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
   LeaveModalState();
   if (aError.Failed()) {
     return nullptr;
   }
 
@@ -11265,17 +11259,16 @@ public:
 
 nsresult
 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
                              const nsAString& aOptions, bool aDialog,
                              bool aContentModal, bool aCalledNoScript,
                              bool aDoJSFixups, bool aNavigate,
                              nsIArray *argv,
                              nsISupports *aExtraArgument,
-                             nsIPrincipal *aCalleePrincipal,
                              JSContext *aJSCallerContext,
                              nsIDOMWindow **aReturn)
 {
   MOZ_ASSERT(IsOuterWindow());
 
 #ifdef DEBUG
   uint32_t argc = 0;
   if (argv)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1387,17 +1387,16 @@ private:
                                     const nsAString& aOptions,
                                     bool aDialog,
                                     bool aContentModal,
                                     bool aCalledNoScript,
                                     bool aDoJSFixups,
                                     bool aNavigate,
                                     nsIArray *argv,
                                     nsISupports *aExtraArgument,
-                                    nsIPrincipal *aCalleePrincipal,
                                     JSContext *aJSCallerContext,
                                     nsIDOMWindow **aReturn);
 
 public:
   // Timeout Functions
   // Language agnostic timeout function (all args passed).
   // |interval| is in milliseconds.
   nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5214,29 +5214,27 @@ CanvasRenderingContext2D::GetImageDataAr
     if (!readback || !readback->Map(DataSourceSurface::READ, &rawData)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   IntRect dstWriteRect = srcReadRect;
   dstWriteRect.MoveBy(-aX, -aY);
 
-  uint8_t* src;
-  uint32_t srcStride;
-
-  if (readback) {
-    srcStride = rawData.mStride;
-    src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
-  }
-
   JS::AutoCheckCannotGC nogc;
   bool isShared;
   uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
   MOZ_ASSERT(!isShared);        // Should not happen, data was created above
-  if (!readback) {
+
+  uint8_t* src;
+  uint32_t srcStride;
+  if (readback) {
+    srcStride = rawData.mStride;
+    src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
+  } else {
     src = data;
     srcStride = aWidth * 4;
   }
 
   uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
 
   if (mOpaque) {
     for (int32_t j = 0; j < dstWriteRect.height; ++j) {
@@ -5574,19 +5572,19 @@ CanvasRenderingContext2D::GetBufferProvi
     return nullptr;
   }
 
   mBufferProvider = new PersistentBufferProviderBasic(mTarget);
 
   return mBufferProvider;
 }
 
-already_AddRefed<CanvasLayer>
+already_AddRefed<Layer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                         CanvasLayer *aOldLayer,
+                                         Layer *aOldLayer,
                                          LayerManager *aManager)
 {
   if (mOpaque || mIsSkiaGL) {
     // If we're opaque then make sure we have a surface so we paint black
     // instead of transparent.
     // If we're using SkiaGL, then SkiaGLTex() below needs the target to
     // be accessible.
     EnsureTarget();
@@ -5619,18 +5617,20 @@ CanvasRenderingContext2D::GetCanvasLayer
       data.mFrontbufferGLTex = skiaGLTex;
       PersistentBufferProvider *provider = GetBufferProvider(aManager);
       data.mBufferProvider = provider;
     } else {
       PersistentBufferProvider *provider = GetBufferProvider(aManager);
       data.mBufferProvider = provider;
     }
 
-    if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
-      RefPtr<CanvasLayer> ret = aOldLayer;
+    if (userData &&
+        userData->IsForContext(this) &&
+        static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
+      RefPtr<Layer> ret = aOldLayer;
       return ret.forget();
     }
   }
 
   RefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
   if (!canvasLayer) {
     NS_WARNING("CreateCanvasLayer returned null!");
     // No DidTransactionCallback will be received, so mark the context clean
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -454,19 +454,19 @@ public:
     }
     return mTarget->Snapshot();
   }
 
   NS_IMETHOD SetIsOpaque(bool isOpaque) override;
   bool GetIsOpaque() override { return mOpaque; }
   NS_IMETHOD Reset() override;
   mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
-  already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                               CanvasLayer *aOldLayer,
-                                               LayerManager *aManager) override;
+  already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                         Layer *aOldLayer,
+                                         LayerManager *aManager) override;
   virtual bool ShouldForceInactiveLayer(LayerManager *aManager) override;
   void MarkContextClean() override;
   void MarkContextCleanForFrameCapture() override;
   bool IsContextCleanForFrameCapture() override;
   NS_IMETHOD SetIsIPC(bool isIPC) override;
   // this rect is in canvas device space
   void Redraw(const mozilla::gfx::Rect &r);
   NS_IMETHOD Redraw(const gfxRect &r) override { Redraw(ToRect(r)); return NS_OK; }
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CanvasRenderingContextHelper.h"
+#include "ImageBitmapRenderingContext.h"
 #include "ImageEncoder.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtr.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
@@ -21,46 +22,16 @@ namespace dom {
 void
 CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
                                      nsIGlobalObject* aGlobal,
                                      FileCallback& aCallback,
                                      const nsAString& aType,
                                      JS::Handle<JS::Value> aParams,
                                      ErrorResult& aRv)
 {
-  nsAutoString type;
-  nsContentUtils::ASCIIToLower(aType, type);
-
-  nsAutoString params;
-  bool usingCustomParseOptions;
-  aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  if (mCurrentContext) {
-    // We disallow canvases of width or height zero, and set them to 1, so
-    // we will have a discrepancy with the sizes of the canvas and the context.
-    // That discrepancy is OK, the rest are not.
-    nsIntSize elementSize = GetWidthHeight();
-    if ((elementSize.width != mCurrentContext->GetWidth() &&
-         (elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
-        (elementSize.height != mCurrentContext->GetHeight() &&
-         (elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-  }
-
-  UniquePtr<uint8_t[]> imageBuffer;
-  int32_t format = 0;
-  if (mCurrentContext) {
-    imageBuffer = mCurrentContext->GetImageBuffer(&format);
-  }
-
   // Encoder callback when encoding is complete.
   class EncodeCallback : public EncodeCompleteCallback
   {
   public:
     EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
       : mGlobal(aGlobal)
       , mFileCallback(aCallback) {}
 
@@ -92,16 +63,59 @@ CanvasRenderingContextHelper::ToBlob(JSC
 
     nsCOMPtr<nsIGlobalObject> mGlobal;
     RefPtr<FileCallback> mFileCallback;
   };
 
   RefPtr<EncodeCompleteCallback> callback =
     new EncodeCallback(aGlobal, &aCallback);
 
+  ToBlob(aCx, aGlobal, callback, aType, aParams, aRv);
+}
+
+void
+CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
+                                     nsIGlobalObject* aGlobal,
+                                     EncodeCompleteCallback* aCallback,
+                                     const nsAString& aType,
+                                     JS::Handle<JS::Value> aParams,
+                                     ErrorResult& aRv)
+{
+  nsAutoString type;
+  nsContentUtils::ASCIIToLower(aType, type);
+
+  nsAutoString params;
+  bool usingCustomParseOptions;
+  aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  if (mCurrentContext) {
+    // We disallow canvases of width or height zero, and set them to 1, so
+    // we will have a discrepancy with the sizes of the canvas and the context.
+    // That discrepancy is OK, the rest are not.
+    nsIntSize elementSize = GetWidthHeight();
+    if ((elementSize.width != mCurrentContext->GetWidth() &&
+         (elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
+        (elementSize.height != mCurrentContext->GetHeight() &&
+         (elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+  }
+
+  UniquePtr<uint8_t[]> imageBuffer;
+  int32_t format = 0;
+  if (mCurrentContext) {
+    imageBuffer = mCurrentContext->GetImageBuffer(&format);
+  }
+
+  RefPtr<EncodeCompleteCallback> callback = aCallback;
+
   aRv = ImageEncoder::ExtractDataAsync(type,
                                        params,
                                        usingCustomParseOptions,
                                        Move(imageBuffer),
                                        format,
                                        GetWidthHeight(),
                                        callback);
 }
@@ -133,16 +147,21 @@ CanvasRenderingContextHelper::CreateCont
   case CanvasContextType::WebGL2:
     Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
 
     ret = WebGL2Context::Create();
     if (!ret)
       return nullptr;
 
     break;
+
+  case CanvasContextType::ImageBitmap:
+    ret = new ImageBitmapRenderingContext();
+
+    break;
   }
   MOZ_ASSERT(ret);
 
   return ret.forget();
 }
 
 already_AddRefed<nsISupports>
 CanvasRenderingContextHelper::GetContext(JSContext* aCx,
--- a/dom/canvas/CanvasRenderingContextHelper.h
+++ b/dom/canvas/CanvasRenderingContextHelper.h
@@ -13,23 +13,25 @@ class nsICanvasRenderingContextInternal;
 class nsIGlobalObject;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
+class EncodeCompleteCallback;
 class FileCallback;
 
 enum class CanvasContextType : uint8_t {
   NoContext,
   Canvas2D,
   WebGL1,
-  WebGL2
+  WebGL2,
+  ImageBitmap
 };
 
 /**
  * Povides common RenderingContext functionality used by both OffscreenCanvas
  * and HTMLCanvasElement.
  */
 class CanvasRenderingContextHelper
 {
@@ -52,16 +54,20 @@ protected:
                                const JS::Value& aEncoderOptions,
                                nsAString& outParams,
                                bool* const outCustomParseOptions);
 
   void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback,
               const nsAString& aType, JS::Handle<JS::Value> aParams,
               ErrorResult& aRv);
 
+  void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback,
+              const nsAString& aType, JS::Handle<JS::Value> aParams,
+              ErrorResult& aRv);
+
   virtual already_AddRefed<nsICanvasRenderingContextInternal>
   CreateContext(CanvasContextType aContextType);
 
   virtual nsIntSize GetWidthHeight() = 0;
 
   CanvasContextType mCurrentContextType;
   nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
 };
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -56,16 +56,21 @@ GetCanvasContextType(const nsAString& st
 
   if (WebGL2Context::IsSupported()) {
     if (str.EqualsLiteral("webgl2")) {
       *out_type = dom::CanvasContextType::WebGL2;
       return true;
     }
   }
 
+  if (str.EqualsLiteral("bitmaprenderer")) {
+    *out_type = dom::CanvasContextType::ImageBitmap;
+    return true;
+  }
+
   return false;
 }
 
 /**
  * This security check utility might be called from an source that never taints
  * others. For example, while painting a CanvasPattern, which is created from an
  * ImageBitmap, onto a canvas. In this case, the caller could set the CORSUsed
  * true in order to pass this check and leave the aPrincipal to be a nullptr
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ImageBitmap.h"
+
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "imgTools.h"
 #include "libyuv.h"
@@ -140,25 +141,24 @@ CropAndCopyDataSourceSurface(DataSourceS
       dstBufferPtr += dstMap.GetStride();
     }
   }
 
   return dstDataSurface.forget();
 }
 
 /*
- * Encapsulate the given _aSurface_ into a layers::CairoImage.
+ * Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
  */
 static already_AddRefed<layers::Image>
 CreateImageFromSurface(SourceSurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
-  RefPtr<layers::CairoImage> image =
-    new layers::CairoImage(aSurface->GetSize(), aSurface);
-
+  RefPtr<layers::SourceSurfaceImage> image =
+    new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
   return image.forget();
 }
 
 /*
  * CreateImageFromRawData(), CreateSurfaceFromRawData() and
  * CreateImageFromRawDataInMainThreadSyncTask are helpers for
  * create-from-ImageData case
  */
@@ -245,23 +245,23 @@ CreateImageFromRawData(const gfx::IntSiz
     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
+ * This class is used to create a layers::SourceSurfaceImage from raw data in the main
  * thread. While creating an ImageBitmap from an ImageData, we need to create
  * a SouceSurface from the ImageData's raw data and then set the SourceSurface
- * into a layers::CairoImage. However, the layers::CairoImage asserts the
+ * into a layers::SourceSurfaceImage. However, the layers::SourceSurfaceImage asserts the
  * setting operation in the main thread, so if we are going to create an
  * ImageBitmap from an ImageData off the main thread, we post an event to the
- * main thread to create a layers::CairoImage from an ImageData's raw data.
+ * main thread to create a layers::SourceSurfaceImage from an ImageData's raw data.
  */
 class CreateImageFromRawDataInMainThreadSyncTask final :
   public WorkerMainThreadRunnable
 {
 public:
   CreateImageFromRawDataInMainThreadSyncTask(uint8_t* aBuffer,
                                              uint32_t aBufferLength,
                                              uint32_t aStride,
@@ -404,26 +404,38 @@ ImageBitmap::~ImageBitmap()
 
 JSObject*
 ImageBitmap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ImageBitmapBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
+ImageBitmap::Close()
+{
+  mData = nullptr;
+  mSurface = nullptr;
+  mPictureRect.SetEmpty();
+}
+
+void
 ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
 {
   mPictureRect = FixUpNegativeDimension(aRect, aRv);
 }
 
 already_AddRefed<SourceSurface>
 ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
 {
   MOZ_ASSERT(aTarget);
 
+  if (!mData) {
+    return nullptr;
+  }
+
   if (!mSurface) {
     mSurface = mData->GetAsSourceSurface();
   }
 
   if (!mSurface) {
     return nullptr;
   }
 
@@ -488,16 +500,78 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
   // to, under the assumption it'll likely be drawn again to that target.
   // This call should be a no-op for already-optimized surfaces
   mSurface = target->OptimizeSourceSurface(mSurface);
 
   RefPtr<gfx::SourceSurface> surface(mSurface);
   return surface.forget();
 }
 
+already_AddRefed<layers::Image>
+ImageBitmap::TransferAsImage()
+{
+  RefPtr<layers::Image> image = mData;
+  Close();
+  return image.forget();
+}
+
+ImageBitmapCloneData*
+ImageBitmap::ToCloneData()
+{
+  ImageBitmapCloneData* result = new ImageBitmapCloneData();
+  result->mPictureRect = mPictureRect;
+  RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
+  result->mSurface = surface->GetDataSurface();
+  MOZ_ASSERT(result->mSurface);
+
+  return result;
+}
+
+/* static */ already_AddRefed<ImageBitmap>
+ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
+                                 ImageBitmapCloneData* aData)
+{
+  RefPtr<layers::Image> data =
+    CreateImageFromSurface(aData->mSurface);
+
+  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+  ErrorResult rv;
+  ret->SetPictureRect(aData->mPictureRect, rv);
+  return ret.forget();
+}
+
+/* static */ already_AddRefed<ImageBitmap>
+ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
+                                       OffscreenCanvas& aOffscreenCanvas,
+                                       ErrorResult& aRv)
+{
+  // Check origin-clean.
+  if (aOffscreenCanvas.IsWriteOnly()) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  nsLayoutUtils::SurfaceFromElementResult res =
+    nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas,
+                                              nsLayoutUtils::SFE_WANT_FIRST_FRAME);
+
+  RefPtr<SourceSurface> surface = res.GetSourceSurface();
+
+  if (NS_WARN_IF(!surface)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return nullptr;
+  }
+
+  RefPtr<layers::Image> data =
+    CreateImageFromSurface(surface);
+
+  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+  return ret.forget();
+}
+
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Check if the image element is completely available or not.
   if (!aImageEl.Complete()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
@@ -1149,17 +1223,17 @@ ImageBitmap::Create(nsIGlobalObject* aGl
 
   return promise.forget();
 }
 
 /*static*/ JSObject*
 ImageBitmap::ReadStructuredClone(JSContext* aCx,
                                  JSStructuredCloneReader* aReader,
                                  nsIGlobalObject* aParent,
-                                 const nsTArray<RefPtr<layers::Image>>& aClonedImages,
+                                 const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
                                  uint32_t aIndex)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
   // aParent might be null.
 
   uint32_t picRectX_;
   uint32_t picRectY_;
@@ -1172,28 +1246,29 @@ ImageBitmap::ReadStructuredClone(JSConte
   }
 
   int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
   int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
   int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
   int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
 
   // Create a new ImageBitmap.
-  MOZ_ASSERT(!aClonedImages.IsEmpty());
-  MOZ_ASSERT(aIndex < aClonedImages.Length());
+  MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
+  MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
 
   // RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
   // called because the static analysis thinks dereferencing XPCOM objects
   // can GC (because in some cases it can!), and a return statement with a
   // JSObject* type means that JSObject* is on the stack as a raw pointer
   // while destructors are running.
   JS::Rooted<JS::Value> value(aCx);
   {
+    RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
     RefPtr<ImageBitmap> imageBitmap =
-      new ImageBitmap(aParent, aClonedImages[aIndex]);
+      new ImageBitmap(aParent, img);
 
     ErrorResult error;
     imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
                                         picRectWidth, picRectHeight), error);
     if (NS_WARN_IF(error.Failed())) {
       error.SuppressException();
       return nullptr;
     }
@@ -1203,35 +1278,51 @@ ImageBitmap::ReadStructuredClone(JSConte
     }
   }
 
   return &(value.toObject());
 }
 
 /*static*/ bool
 ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
-                                  nsTArray<RefPtr<layers::Image>>& aClonedImages,
+                                  nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
                                   ImageBitmap* aImageBitmap)
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aImageBitmap);
 
   const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
   const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
   const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
   const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
 
-  // Indexing the cloned images and send the index to the receiver.
-  uint32_t index = aClonedImages.Length();
+  // Indexing the cloned surfaces and send the index to the receiver.
+  uint32_t index = aClonedSurfaces.Length();
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight))) {
     return false;
   }
 
-  aClonedImages.AppendElement(aImageBitmap->mData);
-
+  RefPtr<SourceSurface> surface =
+    aImageBitmap->mData->GetAsSourceSurface();
+  RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
+  RefPtr<DataSourceSurface> dstDataSurface;
+  {
+    // DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
+    // won't Unmap after exiting function. So instead calling GetStride()
+    // directly, using ScopedMap to get stride.
+    DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
+    dstDataSurface =
+      Factory::CreateDataSourceSurfaceWithStride(snapshot->GetSize(),
+                                                 snapshot->GetFormat(),
+                                                 map.GetStride(),
+                                                 true);
+  }
+  MOZ_ASSERT(dstDataSurface);
+  Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
+  aClonedSurfaces.AppendElement(dstDataSurface);
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -20,24 +20,26 @@ struct JSStructuredCloneWriter;
 
 class nsIGlobalObject;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace gfx {
+class DataSourceSurface;
 class SourceSurface;
 }
 
 namespace layers {
 class Image;
 }
 
 namespace dom {
+class OffscreenCanvas;
 
 namespace workers {
 class WorkerStructuredCloneClosure;
 }
 
 class CanvasRenderingContext2D;
 class File;
 class HTMLCanvasElement;
@@ -45,16 +47,22 @@ class HTMLImageElement;
 class HTMLVideoElement;
 class ImageData;
 class Promise;
 class PostMessageEvent; // For StructuredClone between windows.
 class CreateImageBitmapFromBlob;
 class CreateImageBitmapFromBlobTask;
 class CreateImageBitmapFromBlobWorkerTask;
 
+struct ImageBitmapCloneData final
+{
+  RefPtr<gfx::DataSourceSurface> mSurface;
+  gfx::IntRect mPictureRect;
+};
+
 /*
  * ImageBitmap is an opaque handler to several kinds of image-like objects from
  * HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
  * CanvasRenderingContext2D and Image Blob.
  *
  * An ImageBitmap could be painted to a canvas element.
  *
  * Generally, an ImageBitmap only keeps a reference to its source object's
@@ -78,37 +86,57 @@ public:
     return mPictureRect.Width();
   }
 
   uint32_t Height() const
   {
     return mPictureRect.Height();
   }
 
+  void Close();
+
   /*
    * The PrepareForDrawTarget() might return null if the mPictureRect does not
    * intersect with the size of mData.
    */
   already_AddRefed<gfx::SourceSurface>
   PrepareForDrawTarget(gfx::DrawTarget* aTarget);
 
+  /*
+   * Transfer ownership of buffer to caller. So this function call
+   * Close() implicitly.
+   */
+  already_AddRefed<layers::Image>
+  TransferAsImage();
+
+  ImageBitmapCloneData*
+  ToCloneData();
+
+  static already_AddRefed<ImageBitmap>
+  CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData);
+
+  static already_AddRefed<ImageBitmap>
+  CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
+                            OffscreenCanvas& aOffscreenCanvas,
+                            ErrorResult& aRv);
+
   static already_AddRefed<Promise>
   Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
          const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
   static JSObject*
   ReadStructuredClone(JSContext* aCx,
                       JSStructuredCloneReader* aReader,
                       nsIGlobalObject* aParent,
-                      const nsTArray<RefPtr<layers::Image>>& aClonedImages,
+                      const nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
                       uint32_t aIndex);
 
   static bool
   WriteStructuredClone(JSStructuredCloneWriter* aWriter,
-                       nsTArray<RefPtr<layers::Image>>& aClonedImages,
+                       nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
                        ImageBitmap* aImageBitmap);
 
   friend CreateImageBitmapFromBlob;
   friend CreateImageBitmapFromBlobTask;
   friend CreateImageBitmapFromBlobWorkerTask;
 
 protected:
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBitmapRenderingContext.h"
+#include "mozilla/dom/ImageBitmapRenderingContextBinding.h"
+#include "ImageContainer.h"
+#include "ImageLayers.h"
+
+namespace mozilla {
+namespace dom {
+
+ImageBitmapRenderingContext::ImageBitmapRenderingContext()
+  : mWidth(0)
+  , mHeight(0)
+{
+}
+
+ImageBitmapRenderingContext::~ImageBitmapRenderingContext()
+{
+  RemovePostRefreshObserver();
+}
+
+JSObject*
+ImageBitmapRenderingContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<layers::Image>
+ImageBitmapRenderingContext::ClipToIntrinsicSize()
+{
+  if (!mImage) {
+    return nullptr;
+  }
+
+  // If image is larger than canvas intrinsic size, clip it to the intrinsic size.
+  RefPtr<gfx::SourceSurface> surface;
+  RefPtr<layers::Image> result;
+  if (mWidth < mImage->GetSize().width ||
+      mHeight < mImage->GetSize().height) {
+    surface = MatchWithIntrinsicSize();
+  } else {
+    surface = mImage->GetAsSourceSurface();
+  }
+  result = new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
+  return result.forget();
+}
+
+void
+ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap)
+{
+  Reset();
+  mImage = aImageBitmap.TransferAsImage();
+
+  if (!mImage) {
+    return;
+  }
+
+  Redraw(gfxRect(0, 0, mWidth, mHeight));
+}
+
+int32_t
+ImageBitmapRenderingContext::GetWidth() const
+{
+  return mWidth;
+}
+
+int32_t
+ImageBitmapRenderingContext::GetHeight() const
+{
+  return mHeight;
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
+{
+  mWidth = aWidth;
+  mHeight = aHeight;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::InitializeWithSurface(nsIDocShell* aDocShell,
+                                                   gfxASurface* aSurface,
+                                                   int32_t aWidth,
+                                                   int32_t aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+already_AddRefed<DataSourceSurface>
+ImageBitmapRenderingContext::MatchWithIntrinsicSize()
+{
+  RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
+  RefPtr<DataSourceSurface> temp =
+    Factory::CreateDataSourceSurface(IntSize(mWidth, mHeight), surface->GetFormat());
+  if (!temp) {
+    return nullptr;
+  }
+
+  DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
+  if (!map.IsMapped()) {
+    return nullptr;
+  }
+
+  RefPtr<DrawTarget> dt =
+    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                     map.GetData(),
+                                     temp->GetSize(),
+                                     map.GetStride(),
+                                     temp->GetFormat());
+  if (!dt) {
+    return nullptr;
+  }
+
+
+  dt->ClearRect(Rect(0, 0, mWidth, mHeight));
+  dt->CopySurface(surface,
+                  IntRect(0, 0, surface->GetSize().width,
+                                surface->GetSize().height),
+                  IntPoint(0, 0));
+
+  return temp.forget();
+}
+
+mozilla::UniquePtr<uint8_t[]>
+ImageBitmapRenderingContext::GetImageBuffer(int32_t* aFormat)
+{
+  *aFormat = 0;
+
+  if (!mImage) {
+    return nullptr;
+  }
+
+  RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
+  RefPtr<DataSourceSurface> data = surface->GetDataSurface();
+  if (!data) {
+    return nullptr;
+  }
+
+  if (data->GetSize() != IntSize(mWidth, mHeight)) {
+    data = MatchWithIntrinsicSize();
+  }
+
+  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+  return SurfaceToPackedBGRA(data);
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
+                                            const char16_t* aEncoderOptions,
+                                            nsIInputStream** aStream)
+{
+  nsCString enccid("@mozilla.org/image/encoder;2?type=");
+  enccid += aMimeType;
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
+  if (!encoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  int32_t format = 0;
+  UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
+  if (!imageBuffer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
+                                      encoder, aEncoderOptions, aStream);
+}
+
+already_AddRefed<mozilla::gfx::SourceSurface>
+ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
+{
+  if (!mImage) {
+    return nullptr;
+  }
+
+  if (aPremultAlpha) {
+    *aPremultAlpha = true;
+  }
+
+  RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
+  if (surface->GetSize() != IntSize(mWidth, mHeight)) {
+    return MatchWithIntrinsicSize();
+  }
+
+  return surface.forget();
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
+{
+  return NS_OK;
+}
+
+bool
+ImageBitmapRenderingContext::GetIsOpaque()
+{
+  return false;
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::Reset()
+{
+  if (mCanvasElement) {
+    mCanvasElement->InvalidateCanvas();
+  }
+
+  mImage = nullptr;
+
+  return NS_OK;
+}
+
+already_AddRefed<Layer>
+ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                            Layer* aOldLayer,
+                                            LayerManager* aManager)
+{
+  if (!mImage) {
+    // No DidTransactionCallback will be received, so mark the context clean
+    // now so future invalidations will be dispatched.
+    MarkContextClean();
+    return nullptr;
+  }
+
+  RefPtr<ImageLayer> imageLayer;
+
+  if (aOldLayer) {
+    imageLayer = static_cast<ImageLayer*>(aOldLayer);
+  } else {
+    imageLayer = aManager->CreateImageLayer();
+  }
+
+  RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
+  if (!imageContainer) {
+    imageContainer = aManager->CreateImageContainer();
+    imageLayer->SetContainer(imageContainer);
+  }
+
+  nsAutoTArray<ImageContainer::NonOwningImage, 1> imageList;
+  RefPtr<layers::Image> image = ClipToIntrinsicSize();
+  imageList.AppendElement(ImageContainer::NonOwningImage(image));
+  imageContainer->SetCurrentImages(imageList);
+
+  return imageLayer.forget();
+}
+
+void
+ImageBitmapRenderingContext::MarkContextClean()
+{
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty)
+{
+  if (!mCanvasElement) {
+    return NS_OK;
+  }
+
+  mozilla::gfx::Rect rect = ToRect(aDirty);
+  mCanvasElement->InvalidateCanvasContent(&rect);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC)
+{
+  return NS_OK;
+}
+
+void
+ImageBitmapRenderingContext::DidRefresh()
+{
+}
+
+void
+ImageBitmapRenderingContext::MarkContextCleanForFrameCapture()
+{
+}
+
+bool
+ImageBitmapRenderingContext::IsContextCleanForFrameCapture()
+{
+  return true;
+}
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
+  mCanvasElement,
+  mOffscreenCanvas)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapRenderingContext.h
@@ -0,0 +1,97 @@
+/* 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 ImageBitmapRenderingContext_h
+#define ImageBitmapRenderingContext_h
+
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+class SourceSurface;
+}
+
+namespace layers {
+class Image;
+class ImageContainer;
+}
+
+namespace dom {
+
+/**
+ * The purpose of ImageBitmapRenderingContext is to provide a faster and efficient
+ * way to display ImageBitmap. Simply call TransferImageBitmap() then we'll transfer
+ * the surface of ImageBitmap to this context and then to use it to display.
+ *
+ * See more details in spec: https://wiki.whatwg.org/wiki/OffscreenCanvas
+ */
+class ImageBitmapRenderingContext final :
+  public nsICanvasRenderingContextInternal,
+  public nsWrapperCache
+{
+  virtual ~ImageBitmapRenderingContext();
+
+public:
+  ImageBitmapRenderingContext();
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  // nsISupports interface + CC
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
+
+  void TransferImageBitmap(ImageBitmap& aImageBitmap);
+
+  // nsICanvasRenderingContextInternal
+  virtual int32_t GetWidth() const override;
+  virtual int32_t GetHeight() const override;
+
+  NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
+
+  NS_IMETHOD InitializeWithSurface(nsIDocShell* aDocShell,
+                                   gfxASurface* aSurface,
+                                   int32_t aWidth,
+                                   int32_t aHeight) override;
+
+  virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
+  NS_IMETHOD GetInputStream(const char* aMimeType,
+                            const char16_t* aEncoderOptions,
+                            nsIInputStream** aStream) override;
+
+  virtual already_AddRefed<mozilla::gfx::SourceSurface>
+  GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
+
+  NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
+  virtual bool GetIsOpaque() override;
+  NS_IMETHOD Reset() override;
+  virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                                 Layer* aOldLayer,
+                                                 LayerManager* aManager) override;
+  virtual void MarkContextClean() override;
+
+  NS_IMETHOD Redraw(const gfxRect& aDirty) override;
+  NS_IMETHOD SetIsIPC(bool aIsIPC) override;
+
+  virtual void DidRefresh() override;
+
+  virtual void MarkContextCleanForFrameCapture() override;
+  virtual bool IsContextCleanForFrameCapture() override;
+
+protected:
+  already_AddRefed<gfx::DataSourceSurface> MatchWithIntrinsicSize();
+  already_AddRefed<layers::Image> ClipToIntrinsicSize();
+  int32_t mWidth;
+  int32_t mHeight;
+
+  RefPtr<layers::Image> mImage;
+};
+
+}
+}
+
+#endif /* ImageBitmapRenderingContext_h */
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -3,88 +3,102 @@
 /* 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 "OffscreenCanvas.h"
 
 #include "mozilla/dom/OffscreenCanvasBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerScope.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/Telemetry.h"
 #include "CanvasRenderingContext2D.h"
 #include "CanvasUtils.h"
 #include "GLScreenBuffer.h"
 #include "WebGL1Context.h"
 #include "WebGL2Context.h"
 
 namespace mozilla {
 namespace dom {
 
 OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
                                                    uint32_t aWidth, uint32_t aHeight,
                                                    layers::LayersBackend aCompositorBackend,
-                                                   bool aNeutered)
+                                                   bool aNeutered, bool aIsWriteOnly)
   : mRenderer(aRenderer)
   , mWidth(aWidth)
   , mHeight(aHeight)
   , mCompositorBackendType(aCompositorBackend)
   , mNeutered(aNeutered)
+  , mIsWriteOnly(aIsWriteOnly)
 {
 }
 
 OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
 {
 }
 
-OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
+OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal,
+                                 uint32_t aWidth,
                                  uint32_t aHeight,
                                  layers::LayersBackend aCompositorBackend,
                                  layers::AsyncCanvasRenderer* aRenderer)
-  : mAttrDirty(false)
+  : DOMEventTargetHelper(aGlobal)
+  , mAttrDirty(false)
   , mNeutered(false)
+  , mIsWriteOnly(false)
   , mWidth(aWidth)
   , mHeight(aHeight)
   , mCompositorBackendType(aCompositorBackend)
   , mCanvasClient(nullptr)
   , mCanvasRenderer(aRenderer)
 {}
 
 OffscreenCanvas::~OffscreenCanvas()
 {
   ClearResources();
 }
 
-OffscreenCanvas*
-OffscreenCanvas::GetParentObject() const
-{
-  return nullptr;
-}
-
 JSObject*
 OffscreenCanvas::WrapObject(JSContext* aCx,
                             JS::Handle<JSObject*> aGivenProto)
 {
   return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
 }
 
+/* static */ already_AddRefed<OffscreenCanvas>
+OffscreenCanvas::Constructor(const GlobalObject& aGlobal,
+                             uint32_t aWidth,
+                             uint32_t aHeight,
+                             ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  RefPtr<OffscreenCanvas> offscreenCanvas =
+    new OffscreenCanvas(global, aWidth, aHeight,
+                        layers::LayersBackend::LAYERS_NONE, nullptr);
+  return offscreenCanvas.forget();
+}
+
 void
 OffscreenCanvas::ClearResources()
 {
   if (mCanvasClient) {
     mCanvasClient->Clear();
     ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
     mCanvasClient = nullptr;
 
     if (mCanvasRenderer) {
       nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
       MOZ_RELEASE_ASSERT(activeThread);
-      MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
+      bool current;
+      activeThread->IsOnCurrentThread(&current);
+      MOZ_RELEASE_ASSERT(current);
       mCanvasRenderer->SetCanvasClient(nullptr);
       mCanvasRenderer->mContext = nullptr;
       mCanvasRenderer->mGLContext = nullptr;
       mCanvasRenderer->ResetActiveThread();
     }
   }
 }
 
@@ -102,55 +116,59 @@ OffscreenCanvas::GetContext(JSContext* a
   // We only support WebGL in workers for now
   CanvasContextType contextType;
   if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return nullptr;
   }
 
   if (!(contextType == CanvasContextType::WebGL1 ||
-        contextType == CanvasContextType::WebGL2))
+        contextType == CanvasContextType::WebGL2 ||
+        contextType == CanvasContextType::ImageBitmap))
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return nullptr;
   }
 
   already_AddRefed<nsISupports> result =
     CanvasRenderingContextHelper::GetContext(aCx,
                                              aContextId,
                                              aContextOptions,
                                              aRv);
 
   if (!mCurrentContext) {
     return nullptr;
   }
 
   if (mCanvasRenderer) {
-    WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
-    gl::GLContext* gl = webGL->GL();
-    mCanvasRenderer->mContext = mCurrentContext;
-    mCanvasRenderer->SetActiveThread();
-    mCanvasRenderer->mGLContext = gl;
-    mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
+    if (contextType == CanvasContextType::WebGL1 ||
+        contextType == CanvasContextType::WebGL2) {
+      WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
+      gl::GLContext* gl = webGL->GL();
+      mCanvasRenderer->mContext = mCurrentContext;
+      mCanvasRenderer->SetActiveThread();
+      mCanvasRenderer->mGLContext = gl;
+      mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
 
-    if (ImageBridgeChild::IsCreated()) {
-      TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
-      mCanvasClient = ImageBridgeChild::GetSingleton()->
-        CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
-      mCanvasRenderer->SetCanvasClient(mCanvasClient);
+      if (ImageBridgeChild::IsCreated()) {
+        TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+        mCanvasClient = ImageBridgeChild::GetSingleton()->
+          CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
+        mCanvasRenderer->SetCanvasClient(mCanvasClient);
 
-      gl::GLScreenBuffer* screen = gl->Screen();
-      gl::SurfaceCaps caps = screen->mCaps;
-      auto forwarder = mCanvasClient->GetForwarder();
+        gl::GLScreenBuffer* screen = gl->Screen();
+        gl::SurfaceCaps caps = screen->mCaps;
+        auto forwarder = mCanvasClient->GetForwarder();
 
-      UniquePtr<gl::SurfaceFactory> factory =
-        gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
+        UniquePtr<gl::SurfaceFactory> factory =
+          gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
 
-      if (factory)
-        screen->Morph(Move(factory));
+        if (factory)
+          screen->Morph(Move(factory));
+      }
     }
   }
 
   return result;
 }
 
 already_AddRefed<nsICanvasRenderingContextInternal>
 OffscreenCanvas::CreateContext(CanvasContextType aContextType)
@@ -160,16 +178,22 @@ OffscreenCanvas::CreateContext(CanvasCon
 
   ret->SetOffscreenCanvas(this);
   return ret.forget();
 }
 
 void
 OffscreenCanvas::CommitFrameToCompositor()
 {
+  if (!mCanvasRenderer) {
+    // This offscreen canvas doesn't associate to any HTML canvas element.
+    // So, just bail out.
+    return;
+  }
+
   // The attributes has changed, we have to notify main
   // thread to change canvas size.
   if (mAttrDirty) {
     if (mCanvasRenderer) {
       mCanvasRenderer->SetWidth(mWidth);
       mCanvasRenderer->SetHeight(mHeight);
       mCanvasRenderer->NotifyElementAboutAttributesChanged();
     }
@@ -186,25 +210,131 @@ OffscreenCanvas::CommitFrameToCompositor
       UpdateAsyncCanvasRenderer(mCanvasRenderer);
   }
 }
 
 OffscreenCanvasCloneData*
 OffscreenCanvas::ToCloneData()
 {
   return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
-                                      mCompositorBackendType, mNeutered);
+                                      mCompositorBackendType, mNeutered, mIsWriteOnly);
+}
+
+already_AddRefed<ImageBitmap>
+OffscreenCanvas::TransferToImageBitmap()
+{
+  ErrorResult rv;
+  RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(GetGlobalObject(), *this, rv);
+
+  // Clear the content.
+  if ((mCurrentContextType == CanvasContextType::WebGL1 ||
+       mCurrentContextType == CanvasContextType::WebGL2))
+  {
+    WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
+    webGL->ClearScreen();
+  }
+
+  return result.forget();
+}
+
+already_AddRefed<Promise>
+OffscreenCanvas::ToBlob(JSContext* aCx,
+                        const nsAString& aType,
+                        JS::Handle<JS::Value> aParams,
+                        ErrorResult& aRv)
+{
+  // do a trust check if this is a write-only canvas
+  if (mIsWriteOnly) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = GetGlobalObject();
+
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  // Encoder callback when encoding is complete.
+  class EncodeCallback : public EncodeCompleteCallback
+  {
+  public:
+    EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
+      : mGlobal(aGlobal)
+      , mPromise(aPromise) {}
+
+    // This is called on main thread.
+    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
+    {
+      RefPtr<Blob> blob = aBlob;
+
+      ErrorResult rv;
+      uint64_t size = blob->GetSize(rv);
+      if (rv.Failed()) {
+        rv.SuppressException();
+      } else {
+        AutoJSAPI jsapi;
+        if (jsapi.Init(mGlobal)) {
+          JS_updateMallocCounter(jsapi.cx(), size);
+        }
+      }
+
+      if (mPromise) {
+        RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
+        mPromise->MaybeResolve(newBlob);
+      }
+
+      mGlobal = nullptr;
+      mPromise = nullptr;
+
+      return rv.StealNSResult();
+    }
+
+    nsCOMPtr<nsIGlobalObject> mGlobal;
+    RefPtr<Promise> mPromise;
+  };
+
+  RefPtr<EncodeCompleteCallback> callback =
+    new EncodeCallback(global, promise);
+
+  CanvasRenderingContextHelper::ToBlob(aCx, global,
+                                       callback, aType, aParams, aRv);
+
+  return promise.forget();
+}
+
+already_AddRefed<gfx::SourceSurface>
+OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha)
+{
+  if (!mCurrentContext) {
+    return nullptr;
+  }
+
+  return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
+}
+
+nsCOMPtr<nsIGlobalObject>
+OffscreenCanvas::GetGlobalObject()
+{
+  if (NS_IsMainThread()) {
+    return GetParentObject();
+  }
+
+  dom::workers::WorkerPrivate* workerPrivate =
+    dom::workers::GetCurrentThreadWorkerPrivate();
+  return workerPrivate->GlobalScope();
 }
 
 /* static */ already_AddRefed<OffscreenCanvas>
-OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
+OffscreenCanvas::CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData)
 {
   MOZ_ASSERT(aData);
   RefPtr<OffscreenCanvas> wc =
-    new OffscreenCanvas(aData->mWidth, aData->mHeight,
+    new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
                         aData->mCompositorBackendType, aData->mRenderer);
   if (aData->mNeutered) {
     wc->SetNeutered();
   }
   return wc.forget();
 }
 
 /* static */ bool
--- a/dom/canvas/OffscreenCanvas.h
+++ b/dom/canvas/OffscreenCanvas.h
@@ -20,53 +20,63 @@ namespace mozilla {
 class ErrorResult;
 
 namespace layers {
 class AsyncCanvasRenderer;
 class CanvasClient;
 } // namespace layers
 
 namespace dom {
+class Blob;
+class ImageBitmap;
 
 // This is helper class for transferring OffscreenCanvas to worker thread.
 // Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
 // Canvas to worker thread directly. Thus, we create this helper class and
 // store necessary data in it then pass it to worker thread.
 struct OffscreenCanvasCloneData final
 {
   OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
                            uint32_t aWidth, uint32_t aHeight,
                            layers::LayersBackend aCompositorBackend,
-                           bool aNeutered);
+                           bool aNeutered, bool aIsWriteOnly);
   ~OffscreenCanvasCloneData();
 
   RefPtr<layers::AsyncCanvasRenderer> mRenderer;
   uint32_t mWidth;
   uint32_t mHeight;
   layers::LayersBackend mCompositorBackendType;
   bool mNeutered;
+  bool mIsWriteOnly;
 };
 
 class OffscreenCanvas final : public DOMEventTargetHelper
                             , public CanvasRenderingContextHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
 
-  OffscreenCanvas(uint32_t aWidth,
+  OffscreenCanvas(nsIGlobalObject* aGlobal,
+                  uint32_t aWidth,
                   uint32_t aHeight,
                   layers::LayersBackend aCompositorBackend,
                   layers::AsyncCanvasRenderer* aRenderer);
 
-  OffscreenCanvas* GetParentObject() const;
+  nsCOMPtr<nsIGlobalObject> GetParentObject() const { return GetOwnerGlobal(); }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
+  static already_AddRefed<OffscreenCanvas>
+  Constructor(const GlobalObject& aGlobal,
+              uint32_t aWidth,
+              uint32_t aHeight,
+              ErrorResult& aRv);
+
   void ClearResources();
 
   uint32_t Width() const
   {
     return mWidth;
   }
 
   uint32_t Height() const
@@ -95,23 +105,34 @@ public:
     }
 
     if (mHeight != aHeight) {
       mHeight = aHeight;
       CanvasAttrChanged();
     }
   }
 
+  already_AddRefed<ImageBitmap>
+  TransferToImageBitmap();
+
+  already_AddRefed<Promise>
+  ToBlob(JSContext* aCx,
+         const nsAString& aType,
+         JS::Handle<JS::Value> aParams,
+         ErrorResult& aRv);
+
   nsICanvasRenderingContextInternal* GetContext() const
   {
     return mCurrentContext;
   }
 
+  already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
+
   static already_AddRefed<OffscreenCanvas>
-  CreateFromCloneData(OffscreenCanvasCloneData* aData);
+  CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
 
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
 
   // Return true on main-thread, and return gfx.offscreencanvas.enabled
   // on worker thread.
   static bool PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj);
 
   OffscreenCanvasCloneData* ToCloneData();
@@ -142,33 +163,46 @@ public:
     mNeutered = true;
   }
 
   bool IsNeutered() const
   {
     return mNeutered;
   }
 
+  void SetWriteOnly()
+  {
+    mIsWriteOnly = true;
+  }
+
+  bool IsWriteOnly() const
+  {
+    return mIsWriteOnly;
+  }
+
   layers::LayersBackend GetCompositorBackendType() const
   {
     return mCompositorBackendType;
   }
 
 private:
   ~OffscreenCanvas();
 
+  nsCOMPtr<nsIGlobalObject> GetGlobalObject();
+
   void CanvasAttrChanged()
   {
     mAttrDirty = true;
     ErrorResult dummy;
     UpdateContext(nullptr, JS::NullHandleValue, dummy);
   }
 
   bool mAttrDirty;
   bool mNeutered;
+  bool mIsWriteOnly;
 
   uint32_t mWidth;
   uint32_t mHeight;
 
   layers::LayersBackend mCompositorBackendType;
 
   layers::CanvasClient* mCanvasClient;
   RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL2Context.h"
 
+#include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "WebGLBuffer.h"
 #include "WebGLFormats.h"
 #include "WebGLTransformFeedback.h"
 
 namespace mozilla {
 
 WebGL2Context::WebGL2Context()
@@ -32,17 +32,17 @@ UniquePtr<webgl::FormatUsageAuthority>
 WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const
 {
     return webgl::FormatUsageAuthority::CreateForWebGL2(gl);
 }
 
 /*static*/ bool
 WebGL2Context::IsSupported()
 {
-    return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
+    return gfxPrefs::WebGL2Enabled();
 }
 
 /*static*/ WebGL2Context*
 WebGL2Context::Create()
 {
     return new WebGL2Context();
 }
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1126,27 +1126,27 @@ public:
 
         webgl->UpdateLastUseIndex();
     }
 
 private:
     RefPtr<HTMLCanvasElement> mCanvas;
 };
 
-already_AddRefed<layers::CanvasLayer>
+already_AddRefed<layers::Layer>
 WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
-                             CanvasLayer* oldLayer,
+                             Layer* oldLayer,
                              LayerManager* manager)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!mResetLayer && oldLayer &&
         oldLayer->HasUserData(&gWebGLLayerUserData)) {
-        RefPtr<layers::CanvasLayer> ret = oldLayer;
+        RefPtr<layers::Layer> ret = oldLayer;
         return ret.forget();
     }
 
     RefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
     if (!canvasLayer) {
         NS_WARNING("CreateCanvasLayer returned null!");
         return nullptr;
     }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -326,18 +326,18 @@ public:
      */
     WebGLTexture*
     ActiveBoundTextureForTexImageTarget(const TexImageTarget texImgTarget) const
     {
         const TexTarget texTarget = TexImageTargetToTexTarget(texImgTarget);
         return ActiveBoundTextureForTarget(texTarget);
     }
 
-    already_AddRefed<CanvasLayer>
-    GetCanvasLayer(nsDisplayListBuilder* builder, CanvasLayer* oldLayer,
+    already_AddRefed<Layer>
+    GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer,
                    LayerManager* manager) override;
 
     // Note that 'clean' here refers to its invalidation state, not the
     // contents of the buffer.
     void MarkContextClean() override { mInvalidated = false; }
 
     void MarkContextCleanForFrameCapture() override { mCapturedFrameInvalidated = false; }
 
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -534,21 +534,24 @@ FormatUsageAuthority::CreateForWebGL1(gl
         usage->isRenderable = isRenderable;
         usage->isFilterable = isFilterable;
     };
 
     // GLES 2.0.25, p117, Table 4.5
     // RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments"
     //                              render filter
     //                              able   able
-    fnSet(EffectiveFormat::RGBA8  , true , true);
-    fnSet(EffectiveFormat::RGBA4  , true , true);
-    fnSet(EffectiveFormat::RGB5_A1, true , true);
-    fnSet(EffectiveFormat::RGB8   , false, true);
-    fnSet(EffectiveFormat::RGB565 , true , true);
+    fnSet(EffectiveFormat::RGBA8  , true, true);
+    fnSet(EffectiveFormat::RGBA4  , true, true);
+    fnSet(EffectiveFormat::RGB5_A1, true, true);
+    fnSet(EffectiveFormat::RGB565 , true, true);
+
+    // RGB8 is not guaranteed to be renderable, but we should allow it for web-compat.
+    // Min-capability mode should mark this as non-renderable.
+    fnSet(EffectiveFormat::RGB8, true, true);
 
     fnSet(EffectiveFormat::Luminance8Alpha8, false, true);
     fnSet(EffectiveFormat::Luminance8      , false, true);
     fnSet(EffectiveFormat::Alpha8          , false, true);
 
     fnSet(EffectiveFormat::DEPTH_COMPONENT16, true, false);
     fnSet(EffectiveFormat::STENCIL_INDEX8   , true, false);
 
--- a/dom/canvas/moz.build
+++ b/dom/canvas/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/.
 
 TEST_DIRS += ['compiledtest']
 
 # Number changes to this file to avoid bug 1081323 (clobber after changing a manifest):
-# 1
+# 2
 
 MOCHITEST_MANIFESTS += [
     'test/crossorigin/mochitest.ini',
     'test/mochitest-subsuite-webgl.ini',
     'test/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
@@ -29,32 +29,34 @@ EXPORTS.mozilla.ipc += [
 EXPORTS.mozilla.dom += [
     'CanvasGradient.h',
     'CanvasPath.h',
     'CanvasPattern.h',
     'CanvasRenderingContext2D.h',
     'CanvasRenderingContextHelper.h',
     'CanvasUtils.h',
     'ImageBitmap.h',
+    'ImageBitmapRenderingContext.h',
     'ImageBitmapSource.h',
     'ImageData.h',
     'OffscreenCanvas.h',
     'TextMetrics.h',
     'WebGLVertexArrayObject.h',
 ]
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
+    'ImageBitmapRenderingContext.cpp',
     'ImageData.cpp',
     'OffscreenCanvas.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
     'MurmurHash3.cpp',
     'TexUnpackBlob.cpp',
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -21,29 +21,31 @@
   { 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
 
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
+class Layer;
 class LayerManager;
 } // namespace layers
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 } // namespace mozilla
 
 class nsICanvasRenderingContextInternal :
   public nsISupports,
   public nsAPostRefreshObserver
 {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
+  typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
   void SetCanvasElement(mozilla::dom::HTMLCanvasElement* parentCanvas)
   {
     RemovePostRefreshObserver();
     mCanvasElement = parentCanvas;
@@ -124,19 +126,19 @@ public:
   virtual bool GetIsOpaque() = 0;
 
   // Invalidate this context and release any held resources, in preperation
   // for possibly reinitializing with SetDimensions/InitializeWithSurface.
   NS_IMETHOD Reset() = 0;
 
   // Return the CanvasLayer for this context, creating
   // one for the given layer manager if not available.
-  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* builder,
-                                                       CanvasLayer *oldLayer,
-                                                       LayerManager *manager) = 0;
+  virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder,
+                                                 Layer *oldLayer,
+                                                 LayerManager *manager) = 0;
 
   // Return true if the canvas should be forced to be "inactive" to ensure
   // it can be drawn to the screen even if it's too large to be blitted by
   // an accelerated CanvasLayer.
   virtual bool ShouldForceInactiveLayer(LayerManager *manager) { return false; }
 
   virtual void MarkContextClean() = 0;
 
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -213,16 +213,17 @@ skip-if = toolkit == 'cocoa'
 [test_2d.path.arc.shape.3.html]
 skip-if = toolkit != 'cocoa'
 [test_2d.path.rect.selfintersect.html]
 skip-if = toolkit != 'cocoa'
 # This test is bogus according to the spec; see bug 407107
 [test_2d.path.rect.zero.6.html]
 disabled = bug 407107
 [test_2d.strokeRect.zero.5.html]
+[test_bitmaprenderer.html]
 [test_bug232227.html]
 [test_bug613794.html]
 [test_bug753758.html]
 [test_bug764125.html]
 [test_bug856472.html]
 [test_bug866575.html]
 skip-if = (toolkit == 'gonk' && debug) #bug 1045153
 [test_bug902651.html]
@@ -241,37 +242,43 @@ skip-if = os == "android" || appname == 
 support-files = captureStream_common.js
 [test_drawImageIncomplete.html]
 [test_drawImage_document_domain.html]
 [test_drawImage_edge_cases.html]
 [test_drawWindow.html]
 support-files = file_drawWindow_source.html file_drawWindow_common.js
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
 [test_imagebitmap.html]
+[test_imagebitmap_close.html]
 [test_imagebitmap_cropping.html]
 [test_imagebitmap_on_worker.html]
 [test_imagebitmap_structuredclone.html]
 [test_imagebitmap_structuredclone_iframe.html]
 [test_imagebitmap_structuredclone_window.html]
+[test_imagebitmap_transfer.html]
 [test_ImageData_ctor.html]
 [test_isPointInStroke.html]
 [test_mozDashOffset.html]
 [test_mozGetAsFile.html]
 [test_strokeText_throw.html]
 [test_toBlob.html]
 [test_toDataURL_alpha.html]
 [test_toDataURL_lowercase_ascii.html]
 [test_toDataURL_parameters.html]
 [test_windingRuleUndefined.html]
 [test_2d.fillText.gradient.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
 [test_2d_composite_canvaspattern_setTransform.html]
 [test_createPattern_broken.html]
 [test_setlinedash.html]
 [test_filter.html]
+[test_offscreencanvas_toblob.html]
+tags = offscreencanvas
+[test_offscreencanvas_toimagebitmap.html]
+tags = offscreencanvas
 [test_offscreencanvas_basic_webgl.html]
 tags = offscreencanvas
 [test_offscreencanvas_dynamic_fallback.html]
 tags = offscreencanvas
 [test_offscreencanvas_sharedworker.html]
 tags = offscreencanvas
 [test_offscreencanvas_serviceworker.html]
 tags = offscreencanvas
--- a/dom/canvas/test/offscreencanvas.js
+++ b/dom/canvas/test/offscreencanvas.js
@@ -1,32 +1,54 @@
 /* WebWorker for test_offscreencanvas_*.html */
+(function(){
+
 var port = null;
 
+function isInWorker() {
+  try {
+    return !(self instanceof Window);
+  } catch (e) {
+    return true;
+  }
+}
+
+function postMessageGeneral(data) {
+  if (isInWorker()) {
+    if (port) {
+      port.postMessage(data);
+    } else {
+      postMessage(data);
+    }
+  } else {
+    postMessage(data, "*");
+  }
+}
+
 function ok(expect, msg) {
-  if (port) {
-    port.postMessage({type: "test", result: !!expect, name: msg});
-  } else {
-    postMessage({type: "test", result: !!expect, name: msg});
-  }
+  postMessageGeneral({type: "test", result: !!expect, name: msg});
 }
 
 function finish() {
-  if (port) {
-    port.postMessage({type: "finish"});
-  } else {
-    postMessage({type: "finish"});
-  }
+  postMessageGeneral({type: "finish"});
 }
 
 function drawCount(count) {
+  postMessageGeneral({type: "draw", count: count});
+}
+
+function sendBlob(blob) {
+  postMessageGeneral({type: "blob", blob: blob});
+}
+
+function sendImageBitmap(img) {
   if (port) {
-    port.postMessage({type: "draw", count: count});
+    port.postMessage({type: "imagebitmap", bitmap: img});
   } else {
-    postMessage({type: "draw", count: count});
+    postMessage({type: "imagebitmap", bitmap: img});
   }
 }
 
 //--------------------------------------------------------------------
 // WebGL Drawing Functions
 //--------------------------------------------------------------------
 function createDrawFunc(canvas) {
   var gl;
@@ -135,37 +157,42 @@ function createDrawFunc(canvas) {
 
   gl.useProgram(program);
   gl.enableVertexAttribArray(program.position);
   gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
 
   // Start drawing
   checkGLError('after setup');
 
-  return function(prefix) {
+  return function(prefix, needCommitFrame) {
     if (prefix) {
       prefix = "[" + prefix + "] ";
     } else {
       prefix = "";
     }
 
     gl.viewport(0, 0, canvas.width, canvas.height);
 
     preDraw(prefix);
     gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
     postDraw(prefix);
-    gl.commit();
+    if (needCommitFrame) {
+      gl.commit();
+    }
     checkGLError(prefix);
   };
 }
 
 /* entry point */
 function entryFunction(testStr, subtests, offscreenCanvas) {
   var test = testStr;
   var canvas = offscreenCanvas;
+  if (test == "webgl_imagebitmap") {
+    canvas = new OffscreenCanvas(64, 64);
+  }
 
   if (test != "subworker") {
     ok(canvas, "Canvas successfully transfered to worker");
     ok(canvas.getContext, "Canvas has getContext");
 
     ok(canvas.width == 64, "OffscreenCanvas width should be 64");
     ok(canvas.height == 64, "OffscreenCanvas height should be 64");
   }
@@ -185,61 +212,88 @@ function entryFunction(testStr, subtests
     var count = 0;
     var iid = setInterval(function() {
       if (count++ > 20) {
         clearInterval(iid);
         ok(true, "Worker is done");
         finish();
         return;
       }
-      draw("loop " +count);
+      draw("loop " +count, true);
     }, 0);
   }
   //------------------------------------------------------------------------
   // Test dynamic fallback
   //------------------------------------------------------------------------
   else if (test == "webgl_fallback") {
     draw = createDrawFunc(canvas);
     if (!draw) {
       return;
     }
 
     var count = 0;
     var iid = setInterval(function() {
       ++count;
-      draw("loop " + count);
+      draw("loop " + count, true);
       drawCount(count);
     }, 0);
   }
   //------------------------------------------------------------------------
+  // Test toBlob
+  //------------------------------------------------------------------------
+  else if (test == "webgl_toblob") {
+    draw = createDrawFunc(canvas);
+    if (!draw) {
+      return;
+    }
+
+    draw("", false);
+    canvas.toBlob().then(function(blob) {
+      sendBlob(blob);
+    });
+  }
+  //------------------------------------------------------------------------
+  // Test toImageBitmap
+  //------------------------------------------------------------------------
+  else if (test == "webgl_imagebitmap") {
+    draw = createDrawFunc(canvas);
+    if (!draw) {
+      return;
+    }
+
+    draw("", false);
+    var imgBitmap = canvas.transferToImageBitmap();
+    sendImageBitmap(imgBitmap);
+  }
+  //------------------------------------------------------------------------
   // Canvas Size Change from Worker
   //------------------------------------------------------------------------
   else if (test == "webgl_changesize") {
     draw = createDrawFunc(canvas);
     if (!draw) {
       finish();
       return;
     }
 
-    draw("64x64");
+    draw("64x64", true);
 
     setTimeout(function() {
       canvas.width = 128;
       canvas.height = 128;
-      draw("Increased to 128x128");
+      draw("Increased to 128x128", true);
 
       setTimeout(function() {
         canvas.width = 32;
         canvas.width = 32;
-        draw("Decreased to 32x32");
+        draw("Decreased to 32x32", true);
 
         setTimeout(function() {
           canvas.width = 64;
           canvas.height = 64;
-          draw("Increased to 64x64");
+          draw("Increased to 64x64", true);
 
           ok(true, "Worker is done");
           finish();
         }, 0);
       }, 0);
     }, 0);
   }
   //------------------------------------------------------------------------
@@ -292,8 +346,14 @@ onconnect = function(evt) {
   port = evt.ports[0];
 
   port.addEventListener('message', function(evt) {
     entryFunction(evt.data.test, evt.data.subtests, evt.data.canvas);
   });
 
   port.start();
 };
+
+if (!isInWorker()) {
+  window.entryFunction = entryFunction;
+}
+
+})();
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_bitmaprenderer.html
@@ -0,0 +1,163 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL in OffscreenCanvas</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script type="text/js-worker">
+function ok(expect, msg) {
+  postMessage({"type": "status", status: !!expect, msg: msg});
+}
+
+onmessage = function(event) {
+  var bitmap = event.data.bitmap;
+  ok(!!bitmap, "Get the ImageBitmap from the main script.");
+
+  var offscreenCanvas = new OffscreenCanvas(64, 64);
+  var ctx = offscreenCanvas.getContext('bitmaprenderer');
+  ok(!!ctx, "Get bitmaprenderer context on worker.");
+
+  ctx.transferImageBitmap(bitmap);
+  var resultBitmap = offscreenCanvas.transferToImageBitmap();
+  postMessage({"type": "bitmap", bitmap: resultBitmap}, [resultBitmap]);
+}
+</script>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function createCanvas(width, height) {
+  var htmlCanvas = document.createElement('canvas');
+  htmlCanvas.width = width;
+  htmlCanvas.height = height;
+  document.body.appendChild(htmlCanvas);
+  return htmlCanvas;
+}
+
+function runTest(canvasWidth, canvasHeight, nextTest) {
+  var canvas1 = createCanvas(canvasWidth, canvasHeight);
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+
+  var canvasRef = createCanvas(90, 90);
+  var ctx = canvasRef.getContext("2d");
+  // Clear with black transparent first
+  ctx.fillStyle = "rgba(0, 0, 0, 0)";
+  ctx.fillRect(0, 0, 90, 90);
+
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+
+  createImageBitmap(canvas1).then(function(bmp) {
+    document.body.removeChild(canvas1);
+
+    var canvas2 = createCanvas(90, 90);
+    var ctx2 = canvas2.getContext("bitmaprenderer");
+    ctx2.transferImageBitmap(bmp);
+
+    ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
+
+    // Exam render result
+    canvasRef.style.display = "none";
+    canvas2.style.display = "block";
+    var snapshot = snapshotWindow(window);
+
+    canvasRef.style.display = "block";
+    canvas2.style.display = "none";
+    var snapshotRef = snapshotWindow(window);
+
+    var results = compareSnapshots(snapshot, snapshotRef, true);
+    ok(results[0], "Screenshots should be the same");
+
+    document.body.removeChild(canvasRef);
+    document.body.removeChild(canvas2);
+
+    nextTest();
+  });
+}
+
+function scaleTest() {
+  var canvas1 = createCanvas(64, 64);
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  var canvas2 = createCanvas(64, 64);
+  var ctx2 = canvas2.getContext("2d");
+  ctx2.fillStyle = "#00FF00";
+  ctx2.fillRect(0, 0, 64, 64);
+
+  var p1 = createImageBitmap(canvas1);
+  var p2 = createImageBitmap(canvas2);
+  Promise.all([p1, p2]).then(function(bitmaps) {
+    document.body.removeChild(canvas1);
+    document.body.removeChild(canvas2);
+
+    // Create a large canvas then shrink.
+    var canvas3 = createCanvas(128, 128);
+    var ctx3 = canvas3.getContext("bitmaprenderer");
+    ctx3.transferImageBitmap(bitmaps[0]);
+    var snapshotLargeRef = snapshotWindow(window);
+
+    canvas3.width = 32;
+    canvas3.height = 32;
+    var snapshotSmall = snapshotWindow(window);
+    document.body.removeChild(canvas3);
+
+    // Create a small canvas then grow.
+    var canvas4 = createCanvas(32, 32);
+    var ctx4 = canvas4.getContext("bitmaprenderer");
+    ctx4.transferImageBitmap(bitmaps[1]);
+    var snapshotSmallRef = snapshotWindow(window);
+
+    canvas4.width = 128;
+    canvas4.height = 128;
+    var snapshotLarge = snapshotWindow(window);
+    document.body.removeChild(canvas4);
+
+    var resultsLarge = compareSnapshots(snapshotLarge, snapshotLargeRef, true);
+    ok(resultsLarge[0], "Screenshots should be the same");
+
+    var resultsSmall = compareSnapshots(snapshotSmall, snapshotSmallRef, true);
+    ok(resultsSmall[0], "Screenshots should be the same");
+    runTestOnWorker();
+  });
+}
+
+function runTestOnWorker() {
+  var canvas1 = createCanvas(64, 64);
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
+
+  var worker = new Worker(window.URL.createObjectURL(blob));
+
+  createImageBitmap(canvas1).then(function(bmp) {
+    worker.postMessage({bitmap: bmp}, [bmp]);
+    worker.onmessage = function(event) {
+      if (event.data.type == "status") {
+        ok(event.data.status, event.data.msg);
+      } else if (event.data.type == "bitmap") {
+        var canvas2 = createCanvas(64, 64);
+        var ctx2 = canvas2.getContext('bitmaprenderer');
+        ctx2.transferImageBitmap(event.data.bitmap);
+        ok(canvas1.toDataURL() == canvas2.toDataURL(), 'toDataURL should be the same');
+        SimpleTest.finish();
+      }
+    }
+  });
+}
+
+SpecialPowers.pushPrefEnv({'set': [
+  ['gfx.offscreencanvas.enabled', true],
+]}, runTest.bind(this, 64, 64, runTest.bind(this, 128, 128, scaleTest)));
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_close.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL in OffscreenCanvas</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script type="text/js-worker">
+function ok(expect, msg) {
+  postMessage({"type": "status", status: !!expect, msg: msg});
+}
+
+onmessage = function(event) {
+  var bitmap = event.data.bitmap;
+  ok(!!bitmap, "Get the ImageBitmap from the main script.");
+  bitmap.close();
+  ok(bitmap.width == 0 && bitmap.height == 0, "After close(), width and height should return 0");
+  postMessage({"type": "finish"});
+}
+</script>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function createCanvas() {
+  var htmlCanvas = document.createElement('canvas');
+  htmlCanvas.width = 64;
+  htmlCanvas.height = 64;
+  document.body.appendChild(htmlCanvas);
+  return htmlCanvas;
+}
+
+function runTest() {
+  var canvas1 = createCanvas();
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  var canvasRef = createCanvas();
+  var ctx = canvasRef.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  createImageBitmap(canvas1).then(function(bmp) {
+    var canvas2 = createCanvas();
+    var ctx2 = canvas2.getContext("2d");
+    ctx2.drawImage(bmp, 0, 0);
+
+    ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
+    document.body.removeChild(canvas2);
+
+    bmp.close();
+    ok(bmp.width == 0 && bmp.height == 0, "After close(), width and height should return 0");
+    var canvas2 = createCanvas();
+    var ctx2 = canvas2.getContext("2d");
+    var beforeDrawImageDataURL = canvas2.toDataURL();
+    ctx2.drawImage(bmp, 0, 0);
+    var afterDrawImageDataURL = canvas2.toDataURL();
+    ok(beforeDrawImageDataURL == afterDrawImageDataURL,
+       "Drawing operations with a closed ImageBitmap should do nothing.");
+    runTestOnWorker();
+  });
+}
+
+function runTestOnWorker() {
+  var canvas1 = createCanvas();
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
+
+  var worker = new Worker(window.URL.createObjectURL(blob));
+
+  createImageBitmap(canvas1).then(function(bmp) {
+    worker.postMessage({bitmap: bmp}, [bmp]);
+    worker.onmessage = function(event) {
+      if (event.data.type == "status") {
+        ok(event.data.status, event.data.msg);
+      } else if (event.data.type == "finish") {
+        SimpleTest.finish();
+      }
+    }
+  });
+}
+
+runTest();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_transfer.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap : Transfer</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<script type="text/javascript">
+
+var gImage1;
+var gImage2;
+var gImageBitmap1;
+var gImageBitmap2;
+
+function isPixel(ctx1, ctx2, x, y) {
+  var pixel1 = ctx1.getImageData(x, y, 1, 1);
+  var pixel2 = ctx2.getImageData(x, y, 1, 1);
+  ok(pixel1.data[0] == pixel2.data[0] &&
+     pixel1.data[1] == pixel2.data[1] &&
+     pixel1.data[2] == pixel2.data[2] &&
+     pixel1.data[3] == pixel2.data[3],
+    "Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] +  ")");
+}
+
+function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  canvas1.width  = imageElement.naturalWidth;
+  canvas1.height = imageElement.naturalHeight;
+  canvas2.width  = imageElement.naturalWidth;
+  canvas2.height = imageElement.naturalHeight;
+
+  var ctx1 = canvas1.getContext('2d');
+  var ctx2 = canvas2.getContext('2d');
+
+  ctx1.drawImage(imageElement, 0, 0);
+  ctx2.drawImage(imageBitmap, 0, 0);
+
+  document.body.appendChild(canvas1);
+  document.body.appendChild(canvas2);
+
+  for (var t = 0; t < 20; ++t) {
+    // check one random pixel
+    var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
+    var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
+    isPixel(ctx1, ctx2, randomX, randomY);
+  }
+}
+
+var worker = new Worker("imagebitmap_structuredclone.js");
+worker.onmessage = function(event) {
+
+  if (event.data.type == "status") {
+    ok(event.data.status, event.data.msg);
+  } else if (event.data.type == "finish") {
+    SimpleTest.finish();
+  } else if (event.data.type == "bitmap1") {
+    compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
+  } else if (event.data.type == "bitmap2") {
+    compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
+  }
+}
+
+function prepareTwoImageBitmap() {
+  gImage1 = document.createElement('img');
+  gImage2 = document.createElement('img');
+  gImage1.src = "image_rgrg-256x256.png";
+  gImage2.src = "image_yellow.png";
+
+  var p1 = new Promise(function(resolve, reject) {
+    gImage1.onload = function() {
+      var promise = createImageBitmap(gImage1);
+      promise.then(function(bitmap) {
+        gImageBitmap1 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  var p2 = new Promise(function(resolve, reject) {
+    gImage2.onload = function() {
+      var promise = createImageBitmap(gImage2);
+      promise.then(function(bitmap) {
+        gImageBitmap2 = bitmap;
+        resolve(true);
+      });
+    }
+  });
+
+  return Promise.all([p1, p2]);
+}
+
+function runTests() {
+  ok(worker, "Worker created successfully.");
+
+  prepareTwoImageBitmap().then(function(){
+    worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2},
+                       [gImageBitmap1, gImageBitmap2]);
+
+    ok(gImageBitmap1.width == 0 && gImageBitmap1.height == 0,
+       "After transfer, ImageBitmap become neutered");
+    ok(gImageBitmap2.width == 0 && gImageBitmap2.height == 0,
+       "After transfer, ImageBitmap become neutered");
+  });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_offscreencanvas_toblob.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL in OffscreenCanvas</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="offscreencanvas.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<canvas id="c" width="64" height="64"></canvas>
+<canvas id="c-mt" width="64" height="64"></canvas>
+<canvas id="c-ref" width="64" height="64"></canvas>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function testBlob(blob, callback) {
+  // testing toBlob
+  // Fill c-ref with green color.
+  var c = document.getElementById("c-ref");
+  var ctx = c.getContext("2d");
+  ctx.rect(0, 0, 64, 64);
+  ctx.fillStyle = "#00FF00";
+  ctx.fill();
+  var reader = new FileReader();
+  reader.onload = function(e) {
+    ok(c.toDataURL() == e.target.result, "toBlob should return a 64x64 green square");
+    callback();
+  };
+  reader.readAsDataURL(blob);
+}
+
+function runTestOnMainThread() {
+  var htmlCanvas = document.getElementById("c-mt");
+  ok(htmlCanvas, "Should have HTML canvas element");
+
+  window.onmessage = function(evt) {
+    var msg = evt.data || {};
+    if (msg.type == "test") {
+      ok(msg.result, msg.name);
+    }
+    if (msg.type == "blob") {
+      testBlob(msg.blob, SimpleTest.finish);
+    }
+  }
+
+  ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
+
+  var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
+  ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
+
+  entryFunction('webgl_toblob', '', offscreenCanvas);
+}
+
+function runTest() {
+
+  var htmlCanvas = document.getElementById("c");
+  var worker = new Worker("offscreencanvas.js");
+
+  ok(htmlCanvas, "Should have HTML canvas element");
+  ok(worker, "Web worker successfully created");
+
+  worker.onmessage = function(evt) {
+    var msg = evt.data || {};
+    if (msg.type == "test") {
+      ok(msg.result, msg.name);
+    }
+    if (msg.type == "blob") {
+      testBlob(msg.blob, function() {
+        worker.terminate();
+        runTestOnMainThread();
+      });
+    }
+  }
+
+  ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
+
+  var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
+  ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
+
+  worker.postMessage({test: 'webgl_toblob', canvas: offscreenCanvas}, [offscreenCanvas]);
+}
+
+SpecialPowers.pushPrefEnv({'set': [
+  ['gfx.offscreencanvas.enabled', true],
+  ['webgl.force-enabled', true],
+]}, runTest);
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_offscreencanvas_toimagebitmap.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL in OffscreenCanvas</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<canvas id="c" width="64" height="64"></canvas>
+<canvas id="c2" width="64" height="64"></canvas>
+<canvas id="c-ref" width="64" height="64"></canvas>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+
+  var worker = new Worker("offscreencanvas.js");
+
+  ok(worker, "Web worker successfully created");
+
+  worker.onmessage = function(evt) {
+    var msg = evt.data || {};
+    if (msg.type == "test") {
+      ok(msg.result, msg.name);
+    }
+    if (msg.type == "imagebitmap") {
+      // testing toBlob
+      // Fill c-ref with green color.
+      var c = document.getElementById("c-ref");
+      var ctx = c.getContext("2d");
+      ctx.rect(0, 0, 64, 64);
+      ctx.fillStyle = "#00FF00";
+      ctx.fill();
+
+      var htmlCanvas = document.getElementById("c");
+      var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
+      bitmapRenderer.transferImageBitmap(msg.bitmap);
+
+      ok(c.toDataURL() == htmlCanvas.toDataURL(),
+         "imagebitmap should return a 64x64 green square");
+
+      // The ownership of msg.bitmap should be transferred to canvas "c" when
+      // we called transferImageBitmap. So we test if the ownership is actually
+      // transferred here.
+      var htmlCanvas = document.getElementById("c2");
+      var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
+      bitmapRenderer.transferImageBitmap(msg.bitmap);
+
+      SimpleTest.doesThrow(
+        function() { c2.toDataURL(); },
+        "ImageBitmap has been transferred, toDataURL will throw.");
+
+      worker.terminate();
+      SimpleTest.finish();
+    }
+  }
+
+  worker.postMessage({test: 'webgl_imagebitmap'});
+}
+
+SpecialPowers.pushPrefEnv({'set': [
+  ['gfx.offscreencanvas.enabled', true],
+  ['webgl.force-enabled', true],
+]}, runTest);
+
+</script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -8,17 +8,16 @@ support-files =
 
 [webgl-mochitest/test_backbuffer_channels.html]
 fail-if = (os == 'b2g')
 [webgl-mochitest/test_depth_readpixels.html]
 [webgl-mochitest/test_capture.html]
 support-files = captureStream_common.js
 [webgl-mochitest/test_cubemap_must_be_square.html]
 [webgl-mochitest/test_depth_tex_lazy_clear.html]
-skip-if = 1
 [webgl-mochitest/test_draw.html]
 [webgl-mochitest/test_fb_param.html]
 [webgl-mochitest/test_fb_param_crash.html]
 [webgl-mochitest/test_hidden_alpha.html]
 skip-if = (os == 'b2g') || buildapp == 'mulet' # Mulet - bug 1093639 (crashes in libLLVM-3.0.so)
 [webgl-mochitest/test_implicit_color_buffer_float.html]
 [webgl-mochitest/test_highp_fs.html]
 [webgl-mochitest/test_no_arr_points.html]
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -369,17 +369,18 @@ HTMLCanvasElement::~HTMLCanvasElement()
 
   if (mAsyncCanvasRenderer) {
     mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
                                    mCurrentContext, mPrintCallback,
-                                   mPrintState, mOriginalCanvas)
+                                   mPrintState, mOriginalCanvas,
+                                   mOffscreenCanvas)
 
 NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement)
   NS_INTERFACE_TABLE_INHERITED(HTMLCanvasElement, nsIDOMHTMLCanvasElement)
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
 
@@ -774,20 +775,27 @@ HTMLCanvasElement::TransferControlToOffs
   }
 
   if (!mOffscreenCanvas) {
     nsIntSize sz = GetWidthHeight();
     RefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
     renderer->SetWidth(sz.width);
     renderer->SetHeight(sz.height);
 
-    mOffscreenCanvas = new OffscreenCanvas(sz.width,
+    nsCOMPtr<nsIGlobalObject> global =
+      do_QueryInterface(OwnerDoc()->GetInnerWindow());
+    mOffscreenCanvas = new OffscreenCanvas(global,
+                                           sz.width,
                                            sz.height,
                                            GetCompositorBackendType(),
                                            renderer);
+    if (mWriteOnly) {
+      mOffscreenCanvas->SetWriteOnly();
+    }
+
     if (!mContextObserver) {
       mContextObserver = new HTMLCanvasElementObserver(this);
     }
   } else {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
   return mOffscreenCanvas;
@@ -1033,35 +1041,35 @@ HTMLCanvasElement::GetIsOpaque()
 }
 
 bool
 HTMLCanvasElement::GetOpaqueAttr()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
 }
 
-already_AddRefed<CanvasLayer>
+already_AddRefed<Layer>
 HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                  CanvasLayer *aOldLayer,
+                                  Layer *aOldLayer,
                                   LayerManager *aManager)
 {
   // The address of sOffscreenCanvasLayerUserDataDummy is used as the user
   // data key for retained LayerManagers managed by FrameLayerBuilder.
   // We don't much care about what value in it, so just assign a dummy
   // value for it.
   static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
 
   if (mCurrentContext) {
     return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
   }
 
   if (mOffscreenCanvas) {
     if (!mResetLayer &&
         aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
-      RefPtr<CanvasLayer> ret = aOldLayer;
+      RefPtr<Layer> ret = aOldLayer;
       return ret.forget();
     }
 
     RefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
     if (!layer) {
       NS_WARNING("CreateCanvasLayer failed!");
       return nullptr;
     }
@@ -1168,17 +1176,17 @@ HTMLCanvasElement::IsFrameCaptureRequest
   }
   return false;
 }
 
 void
 HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface)
 {
   RefPtr<SourceSurface> surface = aSurface;
-  RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), surface);
+  RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), surface);
 
   // Loop backwards to allow removing elements in the loop.
   for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {
     WeakPtr<FrameCaptureListener> listener = mRequestedFrameListeners[i];
     if (!listener) {
       // listener was destroyed. Remove it from the list.
       mRequestedFrameListeners.RemoveElementAt(i);
       continue;
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -26,16 +26,17 @@ class nsITimerCallback;
 namespace mozilla {
 
 class WebGLContext;
 
 namespace layers {
 class AsyncCanvasRenderer;
 class CanvasLayer;
 class Image;
+class Layer;
 class LayerManager;
 } // namespace layers
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace dom {
 class CanvasCaptureMediaStream;
@@ -115,16 +116,17 @@ class HTMLCanvasElement final : public n
 {
   enum {
     DEFAULT_CANVAS_WIDTH = 300,
     DEFAULT_CANVAS_HEIGHT = 150
   };
 
   typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
   typedef layers::CanvasLayer CanvasLayer;
+  typedef layers::Layer Layer;
   typedef layers::LayerManager LayerManager;
 
 public:
   explicit HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLCanvasElement, canvas)
 
   // nsISupports
@@ -303,19 +305,19 @@ public:
   nsresult CopyInnerTo(mozilla::dom::Element* aDest);
 
   virtual nsresult PreHandleEvent(mozilla::EventChainPreVisitor& aVisitor) override;
 
   /*
    * Helpers called by various users of Canvas
    */
 
-  already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                               CanvasLayer *aOldLayer,
-                                               LayerManager *aManager);
+  already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                         Layer *aOldLayer,
+                                         LayerManager *aManager);
   // Should return true if the canvas layer should always be marked inactive.
   // We should return true here if we can't do accelerated compositing with
   // a non-BasicCanvasLayer.
   bool ShouldForceInactiveLayer(LayerManager *aManager);
 
   // Call this whenever we need future changes to the canvas
   // to trigger fresh invalidation requests. This needs to be called
   // whenever we render the canvas contents to the screen, or whenever we
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -991,17 +991,17 @@ HTMLImageElement::PictureSourceSrcsetCha
     mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
 
   if (aSourceNode == currentSrc) {
     // We're currently using this node as our responsive selector
     // source.
     mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
   }
 
-  if (!mInDocResponsiveContent) {
+  if (!mInDocResponsiveContent && IsInComposedDoc()) {
     nsIDocument* doc = GetOurOwnerDoc();
     if (doc) {
       doc->AddResponsiveContent(this);
       mInDocResponsiveContent = true;
     }
   }
 
   // This always triggers the image update steps per the spec, even if
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -290,30 +290,35 @@ HTMLInputElement::nsFilePickerShownCallb
 {
 }
 
 NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
 
 NS_IMETHODIMP
 UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
 {
-  nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-  NS_ENSURE_STATE(localFile);
-
-  if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR ||
-      !mResult) {
-    // Default to "desktop" directory for each platform
-    nsCOMPtr<nsIFile> homeDir;
-    NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
-    localFile = do_QueryInterface(homeDir);
-  } else {
-    nsAutoString prefStr;
-    nsCOMPtr<nsIVariant> pref;
-    mResult->GetValue(getter_AddRefs(pref));
-    pref->GetAsAString(prefStr);
+  nsCOMPtr<nsIFile> localFile;
+  nsAutoString prefStr;
+
+  if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
+    prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
+    if (prefStr.IsEmpty()) {
+      // If no custom directory was set through the pref, default to
+      // "desktop" directory for each platform.
+      NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile));
+    }
+  }
+
+  if (!localFile) {
+    if (prefStr.IsEmpty() && mResult) {
+      nsCOMPtr<nsIVariant> pref;
+      mResult->GetValue(getter_AddRefs(pref));
+      pref->GetAsAString(prefStr);
+    }
+    localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
     localFile->InitWithPath(prefStr);
   }
 
   mFilePicker->SetDisplayDirectory(localFile);
   mFilePicker->Open(mFpCallback);
   return NS_OK;
 }
 
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -595,8 +595,10 @@ support-files = file_bug871161-1.html fi
 [test_window_open_close.html]
 skip-if = buildapp == 'b2g' # bug 1129014
 [test_img_complete.html]
 [test_viewport_resize.html]
 [test_extapp.html]
 [test_image_clone_load.html]
 [test_bug1203668.html]
 [test_bug1166138.html]
+[test_filepicker_default_directory.html]
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_filepicker_default_directory.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1194893
+-->
+<head>
+  <title>Test for filepicker default directory</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1194893">Mozilla Bug 1194893</a>
+<div id="content">
+  <input type="file" id="f">
+</div>
+<pre id="text">
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+const { Cc: Cc, Ci: Ci } = SpecialPowers;
+
+// Platform-independent directory names are #define'd in xpcom/io/nsDirectoryServiceDefs.h
+var defaultUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
+                            .getService(Ci.nsIDirectoryService)
+                            .QueryInterface(Ci.nsIProperties)
+                            .get("Desk", Ci.nsIFile);
+
+// When we want to test an upload directory other than the default, we need to
+// get a valid directory in a platform-independent way. Since NS_OS_DESKTOP_DIR
+// may fallback to NS_OS_HOME_DIR, let's use NS_OS_TMP_DIR.
+var customUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
+                            .getService(Ci.nsIDirectoryService)
+                            .QueryInterface(Ci.nsIProperties)
+                            .get("TmpD", Ci.nsIFile);
+
+// Useful for debugging
+//info("defaultUploadDirectory" + defaultUploadDirectory.path);
+//info("customUploadDirectory" + customUploadDirectory.path);
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+// need to show the MockFilePicker so .displayDirectory gets set
+var f = document.getElementById("f");
+f.focus();
+
+var testIndex = 0;
+var tests = [
+  ["", defaultUploadDirectory.path],
+  [customUploadDirectory.path, customUploadDirectory.path]
+]
+
+MockFilePicker.showCallback = function(filepicker) {
+  info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path);
+
+  is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path,
+  tests[testIndex][1]);
+
+  if (++testIndex == tests.length) {
+    MockFilePicker.cleanup();
+    SimpleTest.finish();
+  } else {
+    launchNextTest();
+  }
+}
+
+function launchNextTest() {
+  SpecialPowers.pushPrefEnv(
+    { 'set': [
+      ['dom.input.fallbackUploadDir', tests[testIndex][0]],
+    ]},
+  function () {
+    f.click();
+  });
+}
+
+launchNextTest();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -39,17 +39,17 @@ StructuredCloneData::Copy(const Structur
       SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
                                                     aData.DataLength());
     NS_ENSURE_TRUE(mSharedData, false);
   }
 
   MOZ_ASSERT(BlobImpls().IsEmpty());
   BlobImpls().AppendElements(aData.BlobImpls());
 
-  MOZ_ASSERT(GetImages().IsEmpty());
+  MOZ_ASSERT(GetSurfaces().IsEmpty());
 
   return true;
 }
 
 void
 StructuredCloneData::Read(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aValue,
                           ErrorResult &aRv)
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -785,16 +785,17 @@ MediaDecoder::Play()
 nsresult
 MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(!mShuttingDown, NS_ERROR_FAILURE);
 
   UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
 
+  MOZ_ASSERT(!mIsDormant, "should be out of dormant by now");
   MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
 
   int64_t timeUsecs = 0;
   nsresult rv = SecondsToUsecs(aTime, timeUsecs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLogicalPosition = aTime;
   mWasEndedWhenEnteredDormant = false;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -777,16 +777,27 @@ MediaDecoderStateMachine::OnNotDecoded(M
              [self] (MediaData::Type aType) -> void {
                self->WaitRequestRef(aType).Complete();
                self->DispatchDecodeTasksIfNeeded();
              },
              [self] (WaitForDataRejectValue aRejection) -> void {
                self->WaitRequestRef(aRejection.mType).Complete();
              }));
 
+    // We are out of data to decode and will enter buffering mode soon.
+    // We want to play the frames we have already decoded, so we stop pre-rolling
+    // and ensure that loadeddata is fired as required.
+    if (isAudio) {
+      StopPrerollingAudio();
+    } else {
+      StopPrerollingVideo();
+    }
+    if (mState == DECODER_STATE_BUFFERING || mState == DECODER_STATE_DECODING) {
+        MaybeFinishDecodeFirstFrame();
+    }
     return;
   }
 
   if (aReason == MediaDecoderReader::CANCELED) {
     DispatchDecodeTasksIfNeeded();
     return;
   }
 
@@ -1476,17 +1487,17 @@ MediaDecoderStateMachine::Seek(SeekTarge
 
   // We need to be able to seek both at a transport level and at a media level
   // to seek.
   if (!mMediaSeekable) {
     DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
     return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
   }
 
-  NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
+  MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA,
                "We should have got duration already");
 
   if (mState < DECODER_STATE_DECODING ||
       (IsDecodingFirstFrame() && !mReader->ForceZeroStartTime())) {
     DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
     mQueuedSeek.RejectIfExists(__func__);
     mQueuedSeek.mTarget = aTarget;
     return mQueuedSeek.mPromise.Ensure(__func__);
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaEventSource_h_
 #define MediaEventSource_h_
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/IndexSequence.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -541,16 +541,19 @@ MediaFormatReader::OnDemuxFailed(TrackTy
   switch (aFailure) {
     case DemuxerFailureReason::END_OF_STREAM:
       NotifyEndOfStream(aTrack);
       break;
     case DemuxerFailureReason::DEMUXER_ERROR:
       NotifyError(aTrack);
       break;
     case DemuxerFailureReason::WAITING_FOR_DATA:
+      if (!decoder.mWaitingForData) {
+        decoder.mNeedDraining = true;
+      }
       NotifyWaitingForData(aTrack);
       break;
     case DemuxerFailureReason::CANCELED:
     case DemuxerFailureReason::SHUTDOWN:
       if (decoder.HasPromise()) {
         decoder.RejectPromise(CANCELED, __func__);
       }
       break;
@@ -686,16 +689,19 @@ MediaFormatReader::NotifyError(TrackType
 }
 
 void
 MediaFormatReader::NotifyWaitingForData(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
   decoder.mWaitingForData = true;
+  if (decoder.mTimeThreshold) {
+    decoder.mTimeThreshold.ref().mWaiting = true;
+  }
   ScheduleUpdate(aTrack);
 }
 
 void
 MediaFormatReader::NotifyEndOfStream(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
@@ -755,28 +761,42 @@ bool
 MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
 
   if (!decoder.mReceivedNewData) {
     return false;
   }
-  decoder.mReceivedNewData = false;
-  decoder.mWaitingForData = false;
-  bool hasLastEnd;
-  media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
+
   // Update our cached TimeRange.
   decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
-  if (decoder.mTimeRanges.Length() &&
-      (!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) {
-    // New data was added after our previous end, we can clear the EOS flag.
-    decoder.mDemuxEOS = false;
+
+  if (decoder.mDrainComplete || decoder.mDraining) {
+    // We do not want to clear mWaitingForData or mDemuxEOS while
+    // a drain is in progress in order to properly complete the operation.
+    return false;
   }
 
+  bool hasLastEnd;
+  media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
+  if (hasLastEnd) {
+    if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) {
+      // New data was added after our previous end, we can clear the EOS flag.
+      decoder.mDemuxEOS = false;
+    }
+    decoder.mLastTimeRangesEnd = Some(lastEnd);
+  }
+
+  decoder.mReceivedNewData = false;
+  if (decoder.mTimeThreshold) {
+    decoder.mTimeThreshold.ref().mWaiting = false;
+  }
+  decoder.mWaitingForData = false;
+
   if (decoder.mError) {
     return false;
   }
   if (decoder.HasWaitingPromise()) {
     MOZ_ASSERT(!decoder.HasPromise());
     LOG("We have new data. Resolving WaitingPromise");
     decoder.mWaitingPromise.Resolve(decoder.mType, __func__);
     return true;
@@ -803,16 +823,18 @@ MediaFormatReader::RequestDemuxSamples(T
 
   if (!decoder.mQueuedSamples.IsEmpty()) {
     // No need to demux new samples.
     return;
   }
 
   if (decoder.mDemuxEOS) {
     // Nothing left to demux.
+    // We do not want to attempt to demux while in waiting for data mode
+    // as it would retrigger an unecessary drain.
     return;
   }
 
   LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack));
   if (aTrack == TrackInfo::kVideoTrack) {
     DoDemuxVideo();
   } else {
     DoDemuxAudio();
@@ -878,57 +900,31 @@ MediaFormatReader::HandleDemuxedSamples(
         return;
       }
 
       LOG("%s stream id has changed from:%d to:%d, recreating decoder.",
           TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
           info->GetID());
       decoder.mInfo = info;
       decoder.mLastStreamSourceID = info->GetID();
+      decoder.mNextStreamSourceID.reset();
       // Flush will clear our array of queued samples. So make a copy now.
       nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
       Flush(aTrack);
       decoder.mDecoder->Shutdown();
       decoder.mDecoder = nullptr;
       if (sample->mKeyframe) {
         decoder.mQueuedSamples.AppendElements(Move(samples));
         NotifyDecodingRequested(aTrack);
       } else {
-        MOZ_ASSERT(decoder.mTimeThreshold.isNothing());
+        SeekTarget seekTarget =
+          decoder.mTimeThreshold.refOr(SeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false));
         LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
-            sample->mTime);
-        decoder.mTimeThreshold = Some(TimeUnit::FromMicroseconds(sample->mTime));
-        RefPtr<MediaFormatReader> self = this;
-        decoder.ResetDemuxer();
-        decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref())
-                   ->Then(OwnerThread(), __func__,
-                          [self, aTrack] (media::TimeUnit aTime) {
-                            auto& decoder = self->GetDecoderData(aTrack);
-                            decoder.mSeekRequest.Complete();
-                            self->NotifyDecodingRequested(aTrack);
-                          },
-                          [self, aTrack] (DemuxerFailureReason aResult) {
-                            auto& decoder = self->GetDecoderData(aTrack);
-                            decoder.mSeekRequest.Complete();
-                            switch (aResult) {
-                              case DemuxerFailureReason::WAITING_FOR_DATA:
-                                self->NotifyWaitingForData(aTrack);
-                                break;
-                              case DemuxerFailureReason::END_OF_STREAM:
-                                self->NotifyEndOfStream(aTrack);
-                                break;
-                              case DemuxerFailureReason::CANCELED:
-                              case DemuxerFailureReason::SHUTDOWN:
-                                break;
-                              default:
-                                self->NotifyError(aTrack);
-                                break;
-                            }
-                            decoder.mTimeThreshold.reset();
-                          }));
+            seekTarget.mTime.ToMicroseconds());
+        InternalSeek(aTrack, seekTarget);
       }
       return;
     }
 
     LOGV("Input:%lld (dts:%lld kf:%d)",
          sample->mTime, sample->mTimecode, sample->mKeyframe);
     decoder.mOutputRequested = true;
     decoder.mNumSamplesInput++;
@@ -953,16 +949,52 @@ MediaFormatReader::HandleDemuxedSamples(
     samplesPending = true;
   }
 
   // We have serviced the decoder's request for more data.
   decoder.mInputExhausted = false;
 }
 
 void
+MediaFormatReader::InternalSeek(TrackType aTrack, const SeekTarget& aTarget)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  auto& decoder = GetDecoderData(aTrack);
+  decoder.mTimeThreshold = Some(aTarget);
+  RefPtr<MediaFormatReader> self = this;
+  decoder.ResetDemuxer();
+  decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime)
+             ->Then(OwnerThread(), __func__,
+                    [self, aTrack] (media::TimeUnit aTime) {
+                      auto& decoder = self->GetDecoderData(aTrack);
+                      decoder.mSeekRequest.Complete();
+                      self->NotifyDecodingRequested(aTrack);
+                    },
+                    [self, aTrack] (DemuxerFailureReason aResult) {
+                      auto& decoder = self->GetDecoderData(aTrack);
+                      decoder.mSeekRequest.Complete();
+                      switch (aResult) {
+                        case DemuxerFailureReason::WAITING_FOR_DATA:
+                          self->NotifyWaitingForData(aTrack);
+                          break;
+                        case DemuxerFailureReason::END_OF_STREAM:
+                          self->NotifyEndOfStream(aTrack);
+                          break;
+                        case DemuxerFailureReason::CANCELED:
+                        case DemuxerFailureReason::SHUTDOWN:
+                          break;
+                        default:
+                          self->NotifyError(aTrack);
+                          break;
+                      }
+                      decoder.mTimeThreshold.reset();
+                    }));
+}
+
+void
 MediaFormatReader::DrainDecoder(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   auto& decoder = GetDecoderData(aTrack);
   if (!decoder.mNeedDraining || decoder.mDraining) {
     return;
   }
@@ -987,111 +1019,131 @@ MediaFormatReader::Update(TrackType aTra
   MOZ_ASSERT(OnTaskQueue());
 
   if (mShutdown) {
     return;
   }
 
   LOGV("Processing update for %s", TrackTypeToStr(aTrack));
 
-  bool needInput = false;
   bool needOutput = false;
   auto& decoder = GetDecoderData(aTrack);
   decoder.mUpdateScheduled = false;
 
   if (!mInitDone) {
     return;
   }
 
   if (UpdateReceivedNewData(aTrack)) {
     LOGV("Nothing more to do");
     return;
   }
 
-  if (!decoder.HasPromise() && decoder.mWaitingForData) {
-    // Nothing more we can do at present.
-    LOGV("Still waiting for data.");
-    return;
-  }
-
   // Record number of frames decoded and parsed. Automatically update the
   // stats counters using the AutoNotifyDecoded stack-based class.
   AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
 
-  if (aTrack == TrackInfo::kVideoTrack) {
-    uint64_t delta =
-      decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
-    a.mDecoded = static_cast<uint32_t>(delta);
-    mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
+  // Drop any frames found prior our internal seek target.
+  while (decoder.mTimeThreshold && decoder.mOutput.Length()) {
+    RefPtr<MediaData>& output = decoder.mOutput[0];
+    SeekTarget target = decoder.mTimeThreshold.ref();
+    media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime);
+    if (time >= target.mTime) {
+      // We have reached our internal seek target.
+      decoder.mTimeThreshold.reset();
+    }
+    if (time < target.mTime || target.mDropTarget) {
+      LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
+           TrackTypeToStr(aTrack),
+           media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
+           target.mTime.ToSeconds(),
+           output->mKeyframe);
+      decoder.mOutput.RemoveElementAt(0);
+    }
   }
 
   if (decoder.HasPromise()) {
     needOutput = true;
-    if (!decoder.mOutput.IsEmpty()) {
+    if (decoder.mOutput.Length()) {
       // We have a decoded sample ready to be returned.
       if (aTrack == TrackType::kVideoTrack) {
+        uint64_t delta =
+          decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
+        a.mDecoded = static_cast<uint32_t>(delta);
+        mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
         nsCString error;
         mVideo.mIsHardwareAccelerated =
           mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
       }
-      while (decoder.mOutput.Length()) {
-        RefPtr<MediaData> output = decoder.mOutput[0];
-        decoder.mOutput.RemoveElementAt(0);
-        decoder.mSizeOfQueue -= 1;
-        if (decoder.mTimeThreshold.isNothing() ||
-            media::TimeUnit::FromMicroseconds(output->mTime) >= decoder.mTimeThreshold.ref()) {
-          ReturnOutput(output, aTrack);
-          decoder.mTimeThreshold.reset();
-          break;
-        } else {
-          LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)",
-               media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
-               decoder.mTimeThreshold.ref().ToSeconds(),
-               output->mKeyframe);
-        }
-      }
+      RefPtr<MediaData> output = decoder.mOutput[0];
+      decoder.mOutput.RemoveElementAt(0);
+      decoder.mSizeOfQueue -= 1;
+      decoder.mLastSampleTime =
+        Some(media::TimeUnit::FromMicroseconds(output->mTime));
+      ReturnOutput(output, aTrack);
+    } else if (decoder.mError) {
+      LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
+      decoder.RejectPromise(DECODE_ERROR, __func__);
+      return;
     } else if (decoder.mDrainComplete) {
+      bool wasDraining = decoder.mDraining;
       decoder.mDrainComplete = false;
       decoder.mDraining = false;
-      if (decoder.mError) {
-        LOG("Decoding Error");
-        decoder.RejectPromise(DECODE_ERROR, __func__);
-        return;
-      } else if (decoder.mDemuxEOS) {
+      if (decoder.mDemuxEOS) {
+        LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
         decoder.RejectPromise(END_OF_STREAM, __func__);
+      } else if (decoder.mWaitingForData) {
+        if (wasDraining && decoder.mLastSampleTime &&
+            !decoder.mNextStreamSourceID) {
+          // We have completed draining the decoder following WaitingForData.
+          // Set up the internal seek machinery to be able to resume from the
+          // last sample decoded.
+          LOG("Seeking to last sample time: %lld",
+              decoder.mLastSampleTime.ref().ToMicroseconds());
+          InternalSeek(aTrack, SeekTarget(decoder.mLastSampleTime.ref(), true));
+        }
+        LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
+        decoder.RejectPromise(WAITING_FOR_DATA, __func__);
       }
-    } else if (decoder.mError) {
-      decoder.RejectPromise(DECODE_ERROR, __func__);
-      return;
-    } else if (decoder.mWaitingForData) {
-      LOG("Waiting For Data");
-      decoder.RejectPromise(WAITING_FOR_DATA, __func__);
-      return;
+      // Now that draining has completed, we check if we have received
+      // new data again as the result may now be different from the earlier
+      // run.
+      if (UpdateReceivedNewData(aTrack)) {
+        LOGV("Nothing more to do");
+        return;
+      }
     }
   }
 
   if (decoder.mNeedDraining) {
     DrainDecoder(aTrack);
     return;
   }
 
-  if (!NeedInput(decoder)) {
+  bool needInput = NeedInput(decoder);
+
+  LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u",
+       TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
+       decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
+       uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
+       decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID);
+
+  if (decoder.mWaitingForData &&
+      (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) {
+    // Nothing more we can do at present.
+    LOGV("Still waiting for data.");
+    return;
+  }
+
+  if (!needInput) {
     LOGV("No need for additional input (pending:%u)",
          uint32_t(decoder.mOutput.Length()));
     return;
   }
 
-  needInput = true;
-
-  LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u ahead:%d sid:%u",
-       TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
-       decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
-       uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
-       !decoder.HasPromise(), decoder.mLastStreamSourceID);
-
   // Demux samples if we don't have some.
   RequestDemuxSamples(aTrack);
 
   HandleDemuxedSamples(aTrack, a);
 }
 
 void
 MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack)
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -122,16 +122,32 @@ private:
   // Called when new samples need to be demuxed.
   void RequestDemuxSamples(TrackType aTrack);
   // Handle demuxed samples by the input behavior.
   void HandleDemuxedSamples(TrackType aTrack,
                             AbstractMediaDecoder::AutoNotifyDecoded& aA);
   // Decode any pending already demuxed samples.
   bool DecodeDemuxedSamples(TrackType aTrack,
                             MediaRawData* aSample);
+
+  struct SeekTarget {
+    SeekTarget(const media::TimeUnit& aTime, bool aDropTarget)
+      : mTime(aTime)
+      , mDropTarget(aDropTarget)
+      , mWaiting(false)
+    {}
+
+    media::TimeUnit mTime;
+    bool mDropTarget;
+    bool mWaiting;
+  };
+  // Perform an internal seek to aTime. If aDropTarget is true then
+  // the first sample past the target will be dropped.
+  void InternalSeek(TrackType aTrack, const SeekTarget& aTarget);
+
   // Drain the current decoder.
   void DrainDecoder(TrackType aTrack);
   void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
   void NotifyInputExhausted(TrackType aTrack);
   void NotifyDrainComplete(TrackType aTrack);
   void NotifyError(TrackType aTrack);
   void NotifyWaitingForData(TrackType aTrack);
   void NotifyEndOfStream(TrackType aTrack);
@@ -256,18 +272,21 @@ private:
     bool mDecodingRequested;
     bool mOutputRequested;
     bool mInputExhausted;
     bool mError;
     bool mNeedDraining;
     bool mDraining;
     bool mDrainComplete;
     // If set, all decoded samples prior mTimeThreshold will be dropped.
-    // Used for internal seeking when a change of stream is detected.
-    Maybe<media::TimeUnit> mTimeThreshold;
+    // Used for internal seeking when a change of stream is detected or when
+    // encountering data discontinuity.
+    Maybe<SeekTarget> mTimeThreshold;
+    // Time of last sample returned.
+    Maybe<media::TimeUnit> mLastSampleTime;
 
     // Decoded samples returned my mDecoder awaiting being returned to
     // state machine upon request.
     nsTArray<RefPtr<MediaData>> mOutput;
     uint64_t mNumSamplesInput;
     uint64_t mNumSamplesOutput;
     uint64_t mNumSamplesOutputTotal;
 
@@ -295,32 +314,34 @@ private:
       mQueuedSamples.Clear();
       mDecodingRequested = false;
       mOutputRequested = false;
       mInputExhausted = false;
       mNeedDraining = false;
       mDraining = false;
       mDrainComplete = false;
       mTimeThreshold.reset();
+      mLastSampleTime.reset();
       mOutput.Clear();
       mNumSamplesInput = 0;
       mNumSamplesOutput = 0;
       mSizeOfQueue = 0;
       mNextStreamSourceID.reset();
     }
 
     // Used by the MDSM for logging purposes.
     Atomic<size_t> mSizeOfQueue;
     // Used by the MDSM to determine if video decoding is hardware accelerated.
     // This value is updated after a frame is successfully decoded.
     Atomic<bool> mIsHardwareAccelerated;
     // Sample format monitoring.
     uint32_t mLastStreamSourceID;
     Maybe<uint32_t> mNextStreamSourceID;
     media::TimeIntervals mTimeRanges;
+    Maybe<media::TimeUnit> mLastTimeRangesEnd;
     RefPtr<SharedTrackInfo> mInfo;
   };
 
   template<typename PromiseType>
   struct DecoderDataWithPromise : public DecoderData {
     DecoderDataWithPromise(MediaFormatReader* aOwner,
                            MediaData::Type aType,
                            uint32_t aDecodeAhead) :
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -475,17 +475,20 @@ DecodedStream::CreateData(MozPromiseHold
       // properly on the main thread.
       if (mData) {
         DecodedStreamData* data = mData.release();
         RefPtr<DecodedStream> self = mThis.forget();
         nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
           self->mOutputStreamManager.Disconnect();
           delete data;
         });
-        AbstractThread::MainThread()->Dispatch(r.forget());
+        // We are in tail dispatching phase. Don't call
+        // AbstractThread::MainThread()->Dispatch() to avoid reentrant
+        // AutoTaskDispatcher.
+        NS_DispatchToMainThread(r.forget());
       }
     }
     RefPtr<DecodedStream> mThis;
     Method mMethod;
     UniquePtr<DecodedStreamData> mData;
   };
 
   // Post a message to ensure |mData| is only updated on the worker thread.
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "MediaQueue.h"
 #include "VideoSink.h"
 
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 #define VSINK_LOG(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
     ("VideoSink=%p " msg, this, ##__VA_ARGS__))
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -290,17 +290,17 @@ MediaSourceDemuxer::GetMozDebugReaderDat
 
 MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
                                                  TrackInfo::TrackType aType,
                                                  TrackBuffersManager* aManager)
   : mParent(aParent)
   , mManager(aManager)
   , mType(aType)
   , mMonitor("MediaSourceTrackDemuxer")
-  , mLastSeek(Some(TimeUnit()))
+  , mReset(true)
 {
 }
 
 UniquePtr<TrackInfo>
 MediaSourceTrackDemuxer::GetInfo() const
 {
   return mParent->GetTrackInfo(mType)->Clone();
 }
@@ -323,17 +323,18 @@ MediaSourceTrackDemuxer::GetSamples(int3
 
 void
 MediaSourceTrackDemuxer::Reset()
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
   RefPtr<MediaSourceTrackDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
-      self->mLastSeek = Some(TimeUnit());
+      self->mNextSample.reset();
+      self->mReset = true;
       self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
       {
         MonitorAutoLock mon(self->mMonitor);
         self->mNextRandomAccessPoint =
           self->mManager->GetNextRandomAccessPoint(self->mType);
       }
     });
   mParent->GetTaskQueue()->Dispatch(task.forget());
@@ -375,59 +376,68 @@ MediaSourceTrackDemuxer::BreakCycles()
 
 RefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
 {
   TimeIntervals buffered = mManager->Buffered(mType);
   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
 
   if (!buffered.Contains(aTime)) {
-    mLastSeek = Some(aTime);
     // We don't have the data to seek to.
     return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
                                         __func__);
   }
   TimeUnit seekTime =
     mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
+  bool error;
+  RefPtr<MediaRawData> sample =
+    mManager->GetSample(mType,
+                        media::TimeUnit(),
+                        error);
+  MOZ_ASSERT(!error && sample);
+  mNextSample = Some(sample);
+  mReset = false;
   {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
   }
-  mLastSeek = Some(aTime);
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
 MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
 {
-  if (mLastSeek) {
+  if (mReset) {
     // If a seek (or reset) was recently performed, we ensure that the data
     // we are about to retrieve is still available.
     TimeIntervals buffered = mManager->Buffered(mType);
     buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
 
-    if (!buffered.Contains(mLastSeek.ref())) {
+    if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) {
       return SamplesPromise::CreateAndReject(
         mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
                               DemuxerFailureReason::WAITING_FOR_DATA, __func__);
     }
-    mLastSeek.reset();
+    mReset = false;
   }
-  bool error;
-  RefPtr<MediaRawData> sample =
-    mManager->GetSample(mType,
-                        MediaSourceDemuxer::EOS_FUZZ,
-                        error);
-  if (!sample) {
-    if (error) {
-      return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
+  bool error = false;
+  RefPtr<MediaRawData> sample;
+  if (mNextSample) {
+    sample = mNextSample.ref();
+    mNextSample.reset();
+  } else {
+    sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error);
+    if (!sample) {
+      if (error) {
+        return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
+      }
+      return SamplesPromise::CreateAndReject(
+        mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
+                              DemuxerFailureReason::WAITING_FOR_DATA, __func__);
     }
-    return SamplesPromise::CreateAndReject(
-      mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
-                            DemuxerFailureReason::WAITING_FOR_DATA, __func__);
   }
   RefPtr<SamplesHolder> samples = new SamplesHolder;
   samples->mSamples.AppendElement(sample);
   if (mNextRandomAccessPoint.ToMicroseconds() <= sample->mTime) {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
   }
   return SamplesPromise::CreateAndResolve(samples, __func__);
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -124,14 +124,17 @@ private:
   media::TimeUnit GetNextRandomAccessPoint();
 
   RefPtr<MediaSourceDemuxer> mParent;
   RefPtr<TrackBuffersManager> mManager;
   TrackInfo::TrackType mType;
   // Monitor protecting members below accessed from multiple threads.
   Monitor mMonitor;
   media::TimeUnit mNextRandomAccessPoint;
-  Maybe<media::TimeUnit> mLastSeek;
+  Maybe<RefPtr<MediaRawData>> mNextSample;
+  // Set to true following a reset. Ensure that the next sample demuxed
+  // is available at position 0.
+  bool mReset;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -35,28 +35,30 @@ support-files =
   bipbop/bipbop13.m4s^headers^ bipbop/bipbop_video13.m4s^headers^
 
 [test_BufferedSeek.html]
 [test_BufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_BufferingWait.html]
 skip-if = toolkit == 'android' #timeout android bug 1199531
 [test_BufferingWait_mp4.html]
-skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_DrainOnMissingData_mp4.html]
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_EndOfStream.html]
 skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
 [test_EndOfStream_mp4.html]
 skip-if = (toolkit == 'android' || buildapp == 'mulet') || ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_DurationUpdated.html]
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_FrameSelection.html]
 [test_HaveMetadataUnbufferedSeek.html]
 [test_HaveMetadataUnbufferedSeek_mp4.html]
-skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_LoadedDataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_LoadedMetadataFired.html]
 [test_LoadedMetadataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource.html]
 [test_MediaSource_memory_reporting.html]
 [test_MediaSource_mp4.html]
@@ -93,16 +95,16 @@ skip-if = ((os == "win" && os_version ==
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SplitAppend.html]
 [test_SplitAppend_mp4.html]<