merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 18 Nov 2014 17:25:29 +0100
changeset 240546 084441e904d1225f15d5f641cc0efb82d4ead4cd
parent 240439 7913c9392c5f2660facc4069f3614a001a0792af (current diff)
parent 240545 ab85b334225452d907b69cb88d3cc5352ff8b530 (diff)
child 240570 c43ca53cd42bf6b4a5488304271955d0852ba07b
child 240579 a8a9d42356c8bdf21f91432d28a0999b59a44057
child 240622 d1469442b5f79bbb5d86d99b951af8987aef0050
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
084441e904d1 / 36.0a1 / 20141118144012 / files
nightly linux64
084441e904d1 / 36.0a1 / 20141118144012 / files
nightly mac
084441e904d1 / 36.0a1 / 20141118144012 / files
nightly win32
084441e904d1 / 36.0a1 / 20141118144012 / files
nightly win64
084441e904d1 / 36.0a1 / 20141118144012 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/installer/package-manifest.in
dom/webidl/moz.build
netwerk/protocol/http/SpdyPush3.cpp
netwerk/protocol/http/SpdyPush3.h
netwerk/protocol/http/SpdySession3.cpp
netwerk/protocol/http/SpdySession3.h
netwerk/protocol/http/SpdyStream3.cpp
netwerk/protocol/http/SpdyStream3.h
security/manager/ssl/tests/unit/test_ev_certs/cert8.db
security/manager/ssl/tests/unit/test_ev_certs/int-ev-valid.p12
security/manager/ssl/tests/unit/test_ev_certs/int-non-ev-root.p12
security/manager/ssl/tests/unit/test_ev_certs/key3.db
security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.p12
security/manager/ssl/tests/unit/test_ev_certs/secmod.db
testing/xpcshell/node-spdy/lib/spdy/parser.js
testing/xpcshell/node-spdy/lib/spdy/protocol/generic.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v2/dictionary.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v2/framer.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v2/index.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v2/protocol.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v3/dictionary.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v3/framer.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v3/index.js
testing/xpcshell/node-spdy/lib/spdy/protocol/v3/protocol.js
testing/xpcshell/node-spdy/test/unit/framer-test.js
testing/xpcshell/node-spdy/test/unit/parser-test.js
testing/xpcshell/node-spdy/test/unit/server-test.js
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -446,16 +446,28 @@ AndroidPresenter.prototype.announce =
 
 AndroidPresenter.prototype.liveRegion =
   function AndroidPresenter_liveRegion(aContext, aIsPolite,
     aIsHide, aModifiedText) {
     return this.announce(
       UtteranceGenerator.genForLiveRegion(aContext, aIsHide, aModifiedText));
   };
 
+AndroidPresenter.prototype.noMove =
+  function AndroidPresenter_noMove(aMoveMethod) {
+    return {
+      type: this.type,
+      details: [
+      { eventType: this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED,
+        exitView: aMoveMethod,
+        text: ['']
+      }]
+    };
+  };
+
 /**
  * A B2G presenter for Gaia.
  */
 function B2GPresenter() {}
 
 B2GPresenter.prototype = Object.create(Presenter.prototype);
 
 B2GPresenter.prototype.type = 'B2G';
--- a/accessible/tests/mochitest/textattrs/test_general.html
+++ b/accessible/tests/mochitest/textattrs/test_general.html
@@ -704,22 +704,22 @@
     <span style="font-family: monospace;">text</span>text
     <span style="font-family: serif;">text</span>text
     <span style="font-family: BodoniThatDoesntExist;">text</span>text
     <span style="font-family: Comic Sans MS, cursive;">text</span>text
     <span style="font-family: sans-serif, fantasy;">text</span>text
   </p>
 
   <p id="area17">
-    <span style="-moz-text-decoration-line: underline;">underline
-    </span><span style="text-decoration: underline; -moz-text-decoration-color: blue;">blue
-    </span><span style="text-decoration: underline; -moz-text-decoration-style: dotted;">dotted
-    </span><span style="-moz-text-decoration-line: line-through;">linethrough
-    </span><span style="text-decoration: line-through; -moz-text-decoration-color: blue;">blue
-    </span><span style="text-decoration: line-through; -moz-text-decoration-style: wavy;">wavy
+    <span style="text-decoration-line: underline;">underline
+    </span><span style="text-decoration: underline; text-decoration-color: blue;">blue
+    </span><span style="text-decoration: underline; text-decoration-style: dotted;">dotted
+    </span><span style="text-decoration-line: line-through;">linethrough
+    </span><span style="text-decoration: line-through; text-decoration-color: blue;">blue
+    </span><span style="text-decoration: line-through; text-decoration-style: wavy;">wavy
     </span>
   </p>
 
   <ul>
     <li id="area18" class="gencontent">item</li>
   </ul>
 
   <p id="area19">uncolored
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/debug
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/debug
@@ -5,17 +5,16 @@ mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/ob
 ac_add_options --enable-application=b2g
 ac_add_options --enable-b2g-camera
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
-ac_add_options --disable-elf-hack
 ac_add_options --enable-debug-symbols
 ac_add_options --enable-debug
 #. "$topsrcdir/build/mozconfig.cache"
 ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/nightly
@@ -6,17 +6,16 @@ ac_add_options --enable-application=b2g
 ac_add_options --enable-b2g-camera
 ac_add_options --enable-updater
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
 export GONK_PRODUCT=generic
 ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-"
-ac_add_options --disable-elf-hack
 ac_add_options --enable-debug-symbols
 # ac_add_options --enable-profiling
 #. "$topsrcdir/build/mozconfig.cache"
 ENABLE_MARIONETTE=1
 
 # Enable dump() from JS.
 export CXXFLAGS="-DMOZ_ENABLE_JS_DUMP -include $topsrcdir/gonk-toolchain/gonk-misc/Unicode.h -include $topsrcdir/gonk-toolchain/system/vold/ResponseCode.h"
 
--- a/b2g/config/mozconfigs/linux32_gecko/debug
+++ b/b2g/config/mozconfigs/linux32_gecko/debug
@@ -24,17 +24,16 @@ export MOZ_TELEMETRY_REPORTING=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
-ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/linux32_gecko/nightly
+++ b/b2g/config/mozconfigs/linux32_gecko/nightly
@@ -22,17 +22,16 @@ export MOZ_TELEMETRY_REPORTING=1
 # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
-ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
 
 # Build simulator xpi and phone tweaks for b2g-desktop
--- a/b2g/config/mozconfigs/linux64_gecko/debug
+++ b/b2g/config/mozconfigs/linux64_gecko/debug
@@ -24,17 +24,16 @@ export MOZ_TELEMETRY_REPORTING=1
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
-ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
 
 . "$topsrcdir/b2g/config/mozconfigs/common.override"
--- a/b2g/config/mozconfigs/linux64_gecko/nightly
+++ b/b2g/config/mozconfigs/linux64_gecko/nightly
@@ -22,17 +22,16 @@ export MOZ_TELEMETRY_REPORTING=1
 # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
 
 # Use sccache
 no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
-ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
 
 # Build simulator xpi and phone tweaks for b2g-desktop
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -78,19 +78,16 @@ endif
 ifneq (,$(filter rtsp,$(NECKO_PROTOCOLS)))
 DEFINES += -DMOZ_RTSP
 endif
 
 ifdef GKMEDIAS_SHARED_LIBRARY
 DEFINES += -DGKMEDIAS_SHARED_LIBRARY
 endif
 
-ifdef MOZ_REPLACE_MALLOC
-DEFINES += -DMOZ_REPLACE_MALLOC
-endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
 
 ifdef MOZ_WIDGET_GTK
 DEFINES += -DMOZ_GTK=1
 ifdef MOZ_ENABLE_GTK3
 DEFINES += -DMOZ_GTK3=1
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -16,16 +16,17 @@ skip-if = os == "mac" # Intermittent fai
 [browser_newtab_bug752841.js]
 [browser_newtab_bug765628.js]
 [browser_newtab_bug876313.js]
 [browser_newtab_bug991111.js]
 [browser_newtab_bug991210.js]
 [browser_newtab_bug998387.js]
 [browser_newtab_disable.js]
 [browser_newtab_drag_drop.js]
+skip-if = os == "win" && debug # bug 1097056; test fails in --run-by-dir mode on win8 x64 debug
 [browser_newtab_drag_drop_ext.js]
 [browser_newtab_drop_preview.js]
 [browser_newtab_enhanced.js]
 [browser_newtab_focus.js]
 [browser_newtab_intro.js]
 [browser_newtab_perwindow_private_browsing.js]
 [browser_newtab_reportLinkAction.js]
 [browser_newtab_reflow_load.js]
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -31,9 +31,9 @@ skip-if = e10s # Bug ?????? - some issue
 skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
 [browser_healthreport.js]
 [browser_private_search_perwindowpb.js]
 skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
 [browser_yahoo.js]
 [browser_yahoo_behavior.js]
 skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
 [browser_abouthome_behavior.js]
-skip-if = e10s # Bug ???????
+skip-if = e10s || true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
--- a/browser/components/tabview/test/browser.ini
+++ b/browser/components/tabview/test/browser.ini
@@ -14,17 +14,17 @@ support-files =
 [browser_tabview_alltabs.js]
 [browser_tabview_apptabs.js]
 [browser_tabview_bug580412.js]
 [browser_tabview_bug586553.js]
 [browser_tabview_bug587043.js]
 [browser_tabview_bug587231.js]
 skip-if = buildapp == 'mulet'
 [browser_tabview_bug587276.js]
-skip-if = e10s # Bug 1091200
+skip-if = e10s || true # Bug 1091200, bug 1096285
 [browser_tabview_bug587351.js]
 [browser_tabview_bug587503.js]
 [browser_tabview_bug587990.js]
 [browser_tabview_bug588265.js]
 [browser_tabview_bug589324.js]
 skip-if = e10s # Bug 1086190
 [browser_tabview_bug590606.js]
 [browser_tabview_bug591706.js]
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -1,13 +1,12 @@
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
-ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,14 +1,13 @@
 no_tooltool=1
 no_sccache=1
 
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
-ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -1,13 +1,12 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
-ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,14 +1,13 @@
 no_tooltool=1
 no_sccache=1
 
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
-ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -29,27 +29,25 @@ whitelist['nightly']['linux32'] += [
     'CC="ccache $REAL_CC"',
     'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
     'ac_add_options --with-ccache=/usr/bin/ccache',
     '. "$topsrcdir/build/mozconfig.cache"',
     'export MOZILLA_OFFICIAL=1',
     'export MOZ_TELEMETRY_REPORTING=1',
     "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
     'STRIP_FLAGS="--strip-debug"',
-    'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
 ]
 
 whitelist['nightly']['linux64'] += [
     'export MOZILLA_OFFICIAL=1',
     'export MOZ_TELEMETRY_REPORTING=1',
     "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
     'STRIP_FLAGS="--strip-debug"',
     'ac_add_options --with-ccache=/usr/bin/ccache',
     '. "$topsrcdir/build/mozconfig.cache"',
-    'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
 ]
 
 whitelist['nightly']['macosx-universal'] += [
     'if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then',
     'ac_add_options --with-macbundlename-prefix=Firefox',
     'fi',
     'mk_add_options MOZ_MAKE_FLAGS="-j12"',
     'ac_add_options --with-ccache',
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -349,53 +349,53 @@ skip-if = e10s
 skip-if = e10s
 [browser_dbg_scripts-switching-01.js]
 skip-if = e10s
 [browser_dbg_scripts-switching-02.js]
 skip-if = e10s
 [browser_dbg_scripts-switching-03.js]
 skip-if = e10s
 [browser_dbg_search-autofill-identifier.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-basic-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-basic-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-basic-03.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-basic-04.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-global-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-global-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-global-03.js]
-skip-if = e10s
+skip-if = e10s # Bug 1093535
 [browser_dbg_search-global-04.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-global-05.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-global-06.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-popup-jank.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-sources-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-sources-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-sources-03.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_search-symbols.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_searchbox-help-popup-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_searchbox-help-popup-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_searchbox-parse.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_source-maps-01.js]
 skip-if = e10s && debug
 [browser_dbg_source-maps-02.js]
 skip-if = e10s && debug
 [browser_dbg_source-maps-03.js]
 skip-if = e10s && debug
 [browser_dbg_source-maps-04.js]
 skip-if = e10s # Bug 1093535
--- a/browser/devtools/debugger/test/browser_dbg_search-autofill-identifier.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-autofill-identifier.js
@@ -5,17 +5,17 @@
  * Tests that Debugger Search uses the identifier under cursor if nothing is
  * selected or manually passed and searching using certain operators.
  */
 "use strict";
 
 function test() {
   const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
 
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let Source = 'code_function-search-01.js';
     let Debugger = aPanel.panelWin;
     let Editor = Debugger.DebuggerView.editor;
     let Filtering = Debugger.DebuggerView.Filtering;
 
     function doSearch(aOperator) {
       Editor.dropSelection();
       Filtering._doSearch(aOperator);
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-01.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests basic search functionality (find token and jump to line).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gFiltering, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFiltering = gDebugger.DebuggerView.Filtering;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, ".html").then(performTest);
@@ -304,16 +303,15 @@ function performTest() {
     "The search field has the right initial value (2).");
 
 
   closeDebuggerAndFinish(gPanel);
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gFiltering = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-02.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests basic file search functionality.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(performSimpleSearch)
       .then(() => verifySourceAndCaret("-01.js", 1, 1, [1, 1]))
@@ -28,17 +27,17 @@ function test() {
       .then(() => verifySourceAndCaret("-01.js", 2, 48, [96, 100]))
       .then(combineWithTokenColonSearch)
       .then(() => verifySourceAndCaret("-01.js", 2, 11, [56, 63]))
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function performSimpleSearch() {
   let finished = promise.all([
     ensureSourceIs(gPanel, "-02.js"),
     ensureCaretAt(gPanel, 1),
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FILE_SEARCH_MATCH_FOUND),
@@ -111,14 +110,13 @@ function verifySourceAndCaret(aUrl, aLin
   ok(isCaretPos(gPanel, aLine, aColumn),
     "The current caret position appears to be correct.");
   ok(isEditorSel(gPanel, aSelection),
     "The current editor selection appears to be correct.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-03.js
@@ -3,23 +3,22 @@
 
 /**
  * Tests that searches which cause a popup to be shown properly handle the
  * ESCAPE key.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(performFileSearch)
       .then(escapeAndHide)
@@ -32,17 +31,17 @@ function test() {
       .then(performGlobalSearch)
       .then(escapeAndClear)
       .then(() => verifySourceAndCaret("-01.js", 4, 10))
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function performFileSearch() {
   let finished = promise.all([
     ensureSourceIs(gPanel, "-02.js"),
     ensureCaretAt(gPanel, 1),
     once(gDebugger, "popupshown"),
@@ -107,14 +106,13 @@ function verifySourceAndCaret(aUrl, aLin
   ok(gSources.selectedItem.value.contains(aUrl),
     "The selected item's value appears to be correct.");
   ok(isCaretPos(gPanel, aLine, aColumn),
     "The current caret position appears to be correct.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-04.js
@@ -3,23 +3,22 @@
 
 /**
  * Tests that the selection is dropped for line and token searches, after
  * pressing backspace enough times.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(testLineSearch)
@@ -118,15 +117,14 @@ function testTokenSearch() {
   is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (2.5).");
   is(gSearchBox.value, "",
     "The searchbox should have been cleared.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-01.js
@@ -3,40 +3,39 @@
 
 /**
  * Tests basic functionality of global search (lowercase + upper case, expected
  * UI behavior, number of results found etc.)
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(secondSearch)
       .then(clearSearch)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function firstSearch() {
   let deferred = promise.defer();
 
   is(gSearchView.itemCount, 0,
     "The global search pane shouldn't have any entries yet.");
@@ -259,16 +258,15 @@ function clearSearch() {
   is(gSearchView.widget._parent.hidden, true,
     "The global search pane shouldn't be visible after clearing.");
   is(gSearchView._splitter.hidden, true,
     "The global search pane splitter shouldn't be visible after clearing.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-02.js
@@ -3,23 +3,22 @@
 
 /**
  * Tests if the global search results switch back and forth, and wrap around
  * when switching between them.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
@@ -29,17 +28,17 @@ function test() {
       .then(doWrapAroundJump)
       .then(doBackwardsWrapAroundJump)
       .then(testSearchTokenEmpty)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function firstSearch() {
   let deferred = promise.defer();
 
   is(gSearchView.itemCount, 0,
     "The global search pane shouldn't have any entries yet.");
@@ -203,16 +202,15 @@ function testSearchTokenEmpty() {
   is(gSearchView.widget._parent.hidden, true,
     "The global search pane shouldn't be visible after clearing.");
   is(gSearchView._splitter.hidden, true,
     "The global search pane splitter shouldn't be visible after clearing.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-03.js
@@ -3,39 +3,38 @@
 
 /**
  * Tests if the global search results are cleared on location changes, and
  * the expected UI behaviors are triggered.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(performTest)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function firstSearch() {
   let deferred = promise.defer();
 
   is(gSearchView.itemCount, 0,
     "The global search pane shouldn't have any entries yet.");
@@ -91,16 +90,15 @@ function performTest() {
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-04.js
@@ -3,39 +3,38 @@
 
 /**
  * Tests if the global search results trigger MatchFound and NoMatchFound events
  * properly, and triggers the expected UI behavior.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(secondSearch)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function firstSearch() {
   let deferred = promise.defer();
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
@@ -79,16 +78,15 @@ function secondSearch() {
 
   typeText(gSearchBox, "/");
 
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-05.js
@@ -4,23 +4,22 @@
 /**
  * Tests if the global search results are expanded/collapsed on click, and
  * clicking matches makes the source editor shows the correct source and
  * makes a selection based on the match.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
@@ -28,17 +27,17 @@ function test() {
       .then(testExpandCollapse)
       .then(testClickLineToJump)
       .then(testClickMatchToJump)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function doSearch() {
   let deferred = promise.defer();
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
@@ -141,16 +140,15 @@ function testClickMatchToJump() {
 
   EventUtils.sendMouseEvent({ type: "click" }, lastMatch);
 
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-06.js
@@ -3,23 +3,22 @@
 
 /**
  * Tests if the global search results are hidden when they're supposed to
  * (after a focus lost, or when ESCAPE is pressed).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchView, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
@@ -27,17 +26,17 @@ function test() {
       .then(testFocusLost)
       .then(doSearch)
       .then(testEscape)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function doSearch() {
   let deferred = promise.defer();
 
   is(gSearchView.itemCount, 0,
     "The global search pane shouldn't have any entries yet.");
@@ -106,16 +105,15 @@ function testEscape() {
   is(gSearchView.widget._parent.hidden, true,
     "The global search pane shouldn't be visible after clearing.");
   is(gSearchView._splitter.hidden, true,
     "The global search pane splitter shouldn't be visible after clearing.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-popup-jank.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-popup-jank.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that sources aren't selected by default when finding a match.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_editor-mode.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     gDebugger.DebuggerView.FilteredSources._autoSelectFirstItem = false;
     gDebugger.DebuggerView.FilteredFunctions._autoSelectFirstItem = false;
 
     waitForSourceShown(gPanel, "-01.js")
@@ -106,13 +105,12 @@ function pressKey(aKey) {
 function pressKeyToHide(aKey) {
   let finished = waitForResultsHidden();
   EventUtils.sendKey(aKey, gDebugger);
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-sources-01.js
@@ -2,26 +2,25 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests basic functionality of sources filtering (file search).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources, gSearchView, gSearchBox;
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(3);
 
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.FilteredSources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(bogusSearch)
@@ -221,15 +220,14 @@ function verifyContents(aArgs) {
   is(gSearchView.itemCount, aArgs.itemCount,
     "No sources should be displayed in the sources container after a bogus search.");
   is(gSearchView.hidden, aArgs.hidden,
     "No sources should be displayed in the sources container after a bogus search.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-sources-02.js
@@ -2,26 +2,25 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests more complex functionality of sources filtering (file search).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_editor-mode.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources, gSourceUtils, gSearchView, gSearchBox;
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(3);
 
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSourceUtils = gDebugger.SourceUtils;
     gSearchView = gDebugger.DebuggerView.FilteredSources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
@@ -263,16 +262,15 @@ function verifyContents(aMatches) {
       "The filtered sources view should have the correct source labels.");
     ok(gSearchView.widget._parent.querySelector(".results-panel-item-label-below[value=\"" + trimmedLocation + "\"]"),
       "The filtered sources view should have the correct source locations.");
   }
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
   gSourceUtils = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-sources-03.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that while searching for files, the sources list remains unchanged.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_editor-mode.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources, gSearchBox;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(superGenericSearch)
       .then(verifySourcesPane)
@@ -87,14 +86,13 @@ function verifySourcesPane() {
   ok(gSources.getItemForAttachment(e => e.label == "code_test-editor-mode"),
     "The second source's label should be correct.");
   ok(gSources.getItemForAttachment(e => e.label == "doc_editor-mode.html"),
     "The third source's label should be correct.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-symbols.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-symbols.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the function searching works properly.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSources, gSearchBox, gFilteredFunctions;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gFilteredFunctions = gDebugger.DebuggerView.FilteredFunctions;
 
     waitForSourceShown(gPanel, "-01.js")
@@ -454,16 +453,15 @@ function saveSearch() {
 
 function writeInfo() {
   info("Current source url:\n" + gSources.selectedValue);
   info("Debugger editor text:\n" + gEditor.getText());
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gSearchBox = null;
   gFilteredFunctions = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
@@ -3,37 +3,36 @@
 
 /**
  * Make sure that the searchbox popup is displayed when focusing the searchbox,
  * and hidden when the user starts typing.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSearchBox, gSearchBoxPanel;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(showPopup)
       .then(hidePopup)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function showPopup() {
   is(gSearchBoxPanel.state, "closed",
     "The search box panel shouldn't be visible yet.");
 
   let finished = once(gSearchBoxPanel, "popupshown");
@@ -47,14 +46,13 @@ function hidePopup() {
 
   let finished = once(gSearchBoxPanel, "popuphidden");
   setText(gSearchBox, "#");
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSearchBox = null;
   gSearchBoxPanel = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
@@ -3,23 +3,22 @@
 
 /**
  * Make sure that the searchbox popup isn't displayed when there's some text
  * already present.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gEditor, gSearchBox, gSearchBoxPanel;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel;
 
     once(gSearchBoxPanel, "popupshown").then(() => {
       ok(false, "Damn it, this shouldn't have happened.");
@@ -29,17 +28,17 @@ function test() {
       .then(tryShowPopup)
       .then(focusEditor)
       .then(testFocusLost)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.firstCall();
+    callInTab(gTab, "firstCall");
   });
 }
 
 function tryShowPopup() {
   setText(gSearchBox, "#call()");
   ok(isCaretPos(gPanel, 4, 22),
     "The editor caret position appears to be correct.");
   ok(isEditorSel(gPanel, [125, 131]),
@@ -72,15 +71,14 @@ function testFocusLost() {
     "The editor selected text appears to be correct after gaining focus.");
 
   is(gSearchBoxPanel.state, "closed",
     "The search box panel should still not be visible.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSearchBox = null;
   gSearchBoxPanel = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-parse.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-parse.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that text entered in the debugger's searchbox is properly parsed.
  */
 
 function test() {
-  initDebugger("about:blank").then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger("about:blank").then(([aTab,, aPanel]) => {
     let filterView = aPanel.panelWin.DebuggerView.Filtering;
     let searchbox = aPanel.panelWin.DebuggerView.Filtering._searchbox;
 
     setText(searchbox, "");
     is(filterView.searchData.toSource(), '["", [""]]',
       "The searchbox data wasn't parsed correctly (1).");
 
     setText(searchbox, "#token");
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -125,19 +125,16 @@ DEFINES += -DAB=$(AB)
 
 DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
 ifdef MOZ_NATIVE_ICU
 DEFINES += -DMOZ_NATIVE_ICU
 endif
 ifdef MOZ_SHARED_ICU
 DEFINES += -DMOZ_SHARED_ICU
 endif
-ifdef MOZ_REPLACE_MALLOC
-DEFINES += -DMOZ_REPLACE_MALLOC
-endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
--- a/browser/themes/shared/devtools/dark-theme.css
+++ b/browser/themes/shared/devtools/dark-theme.css
@@ -98,17 +98,17 @@
 }
 
 .CodeMirror-Tern-completion-object:before {
   background-color: #3689b2;
 }
 
 .cm-s-mozilla .cm-unused-line {
   text-decoration: line-through;
-  -moz-text-decoration-color: #5f88b0;
+  text-decoration-color: #5f88b0;
 }
 
 .cm-s-mozilla .cm-executed-line {
   background-color: #133c26;
 }
 
 .theme-fg-color3,
 .cm-s-mozilla .cm-builtin,
--- a/browser/themes/shared/devtools/light-theme.css
+++ b/browser/themes/shared/devtools/light-theme.css
@@ -71,17 +71,17 @@
 }
 
 .theme-separator { /* grey */
   border-color: #cddae5;
 }
 
 .cm-s-mozilla .cm-unused-line {
   text-decoration: line-through;
-  -moz-text-decoration-color: #5f88b0;
+  text-decoration-color: #5f88b0;
 }
 
 .cm-s-mozilla .cm-executed-line {
   background-color: #fcfffc;
 }
 
 .theme-fg-color1,
 .cm-s-mozilla .cm-number,
--- a/browser/themes/shared/devtools/ruleview.css
+++ b/browser/themes/shared/devtools/ruleview.css
@@ -193,17 +193,17 @@
   }
 }
 
 .ruleview-overridden {
   text-decoration: line-through;
 }
 
 .theme-light .ruleview-overridden {
-  -moz-text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
+  text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
   padding: 0;
 }
 
 .ruleview-property {
--- a/configure.in
+++ b/configure.in
@@ -7156,16 +7156,17 @@ if test "$NS_TRACE_MALLOC" -a "$MOZ_REPL
     AC_MSG_ERROR([--enable-trace-malloc and --enable-replace-malloc are conflicting options])
 fi
 
 if test -n "$MOZ_REPLACE_MALLOC" -a -z "$MOZ_MEMORY"; then
     dnl We don't want to enable jemalloc unconditionally because it may be a
     dnl deliberate choice not to enable it (bug 702250, for instance)
     AC_MSG_ERROR([--enable-replace-malloc requires --enable-jemalloc])
 elif test -n "$MOZ_REPLACE_MALLOC"; then
+    AC_DEFINE(MOZ_REPLACE_MALLOC)
     MOZ_NATIVE_JEMALLOC=
 
     dnl Replace-malloc Mac linkage quirks
     if test -n "$MACOSX_DEPLOYMENT_TARGET"; then
         AC_CACHE_CHECK([how to do weak dynamic linking],
             ac_cv_weak_dynamic_linking,
             [echo 'extern void foo() __attribute__((weak_import));int bar() { if (foo) foo(); return 0; }' > conftest.c
              if AC_TRY_COMMAND([${CC-cc} -o conftest${DLL_SUFFIX} $CFLAGS -dynamiclib $LDFLAGS -Wl,-U,_foo conftest.c $LIBS 1>&5]) &&
@@ -7378,27 +7379,17 @@ MOZ_ARG_ENABLE_BOOL(install-strip,
 dnl ========================================================
 dnl = --disable-elf-hack
 dnl ========================================================
 
 USE_ELF_HACK=1
 MOZ_ARG_DISABLE_BOOL(elf-hack,
 [  --disable-elf-hack      Disable elf hacks],
     [USE_ELF_HACK=],
-    [USE_ELF_HACK=F # Force enable elf-hack])
-
-# Disable elf hack for profiling because the built in profiler
-# doesn't read the segments properly with elf hack. This is
-# temporary and should be fixed soon in the profiler.
-if test "$MOZ_PROFILING" = 1; then
-  if test "$USE_ELF_HACK" = F; then
-    AC_ERROR([--enable-elf-hack is not compatible with --enable-profiling])
-  fi
-  USE_ELF_HACK=
-fi
+    [USE_ELF_HACK=1])
 
 # Only enable elfhack where supported
 if test "$USE_ELF_HACK" = 1; then
     case "${HOST_OS_ARCH},${OS_ARCH}" in
     Linux,Linux)
         case "${CPU_ARCH}" in
         arm | x86 | x86_64)
             USE_ELF_HACK=1
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -57,17 +57,16 @@ namespace dom {
 EventSource::EventSource(nsPIDOMWindow* aOwnerWindow) :
   DOMEventTargetHelper(aOwnerWindow),
   mStatus(PARSE_STATE_OFF),
   mFrozen(false),
   mErrorLoadOnRedirect(false),
   mGoingToDispatchAllMessages(false),
   mWithCredentials(false),
   mWaitingForOnStopRequest(false),
-  mInterrupted(false),
   mLastConvertionResult(NS_OK),
   mReadyState(CONNECTING),
   mScriptLine(0),
   mInnerWindowID(0)
 {
 }
 
 EventSource::~EventSource()
@@ -336,39 +335,40 @@ EventSource::OnStartRequest(nsIRequest *
                             nsISupports *ctxt)
 {
   nsresult rv = CheckHealthOfRequestCallback(aRequest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool requestSucceeded;
-  rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString contentType;
-  rv = httpChannel->GetContentType(contentType);
+  nsresult status;
+  rv = aRequest->GetStatus(&status);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsresult status;
-  aRequest->GetStatus(&status);
-
-  if (NS_FAILED(status) || !requestSucceeded ||
-      !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
-    DispatchFailConnection();
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_FAILED(status)) {
+    // EventSource::OnStopRequest will evaluate if it shall either reestablish
+    // or fail the connection
+    return NS_ERROR_ABORT;
   }
 
   uint32_t httpStatus;
   rv = httpChannel->GetResponseStatus(&httpStatus);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (httpStatus != 200) {
-    mInterrupted = true;
+    DispatchFailConnection();
+    return NS_ERROR_ABORT;
+  }
+
+  nsAutoCString contentType;
+  rv = httpChannel->GetContentType(contentType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
     DispatchFailConnection();
     return NS_ERROR_ABORT;
   }
 
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &EventSource::AnnounceConnection);
   NS_ENSURE_STATE(event);
 
@@ -449,40 +449,48 @@ EventSource::OnStopRequest(nsIRequest *a
                            nsresult aStatusCode)
 {
   mWaitingForOnStopRequest = false;
 
   if (mReadyState == CLOSED) {
     return NS_ERROR_ABORT;
   }
 
-  if (NS_FAILED(aStatusCode)) {
+  // "Network errors that prevents the connection from being established in the
+  //  first place (e.g. DNS errors), must cause the user agent to asynchronously
+  //  reestablish the connection.
+  //
+  //  (...) the cancelation of the fetch algorithm by the user agent (e.g. in
+  //  response to window.stop() or the user canceling the network connection
+  //  manually) must cause the user agent to fail the connection.
+
+  if (NS_FAILED(aStatusCode) &&
+      aStatusCode != NS_ERROR_CONNECTION_REFUSED &&
+      aStatusCode != NS_ERROR_NET_TIMEOUT &&
+      aStatusCode != NS_ERROR_NET_RESET &&
+      aStatusCode != NS_ERROR_NET_INTERRUPT &&
+      aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
+      aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) {
     DispatchFailConnection();
-    return aStatusCode;
+    return NS_ERROR_ABORT;
   }
 
-  nsresult rv;
-  nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest);
-  if (NS_SUCCEEDED(healthOfRequestResult) &&
-      mLastConvertionResult == NS_PARTIAL_MORE_INPUT) {
-    // we had an incomplete UTF8 char at the end of the stream
-    rv = ParseCharacter(REPLACEMENT_CHAR);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  nsresult rv = CheckHealthOfRequestCallback(aRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   ClearFields();
 
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &EventSource::ReestablishConnection);
   NS_ENSURE_STATE(event);
 
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return healthOfRequestResult;
+  return NS_OK;
 }
 
 /**
  * Simple helper class that just forwards the redirect callback back
  * to the EventSource.
  */
 class AsyncVerifyRedirectCallbackFwr MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
 {
@@ -864,21 +872,16 @@ EventSource::ResetConnection()
 
 void
 EventSource::ReestablishConnection()
 {
   if (mReadyState == CLOSED) {
     return;
   }
 
-  if (mReadyState != OPEN) {
-    NS_WARNING("Unexpected mReadyState!!!");
-    return;
-  }
-
   nsresult rv = ResetConnection();
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to reset the connection!!!");
     return;
   }
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
@@ -989,17 +992,17 @@ EventSource::ConsoleError()
 {
   nsAutoCString targetSpec;
   nsresult rv = mSrc->GetSpec(targetSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
   const char16_t *formatStrings[] = { specUTF16.get() };
 
-  if (mReadyState == CONNECTING && !mInterrupted) {
+  if (mReadyState == CONNECTING) {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                              MOZ_UTF16("connectionFailure"),
                              formatStrings, ArrayLength(formatStrings));
   } else {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                              MOZ_UTF16("netInterrupt"),
                              formatStrings, ArrayLength(formatStrings));
   }
--- a/dom/base/EventSource.h
+++ b/dom/base/EventSource.h
@@ -216,17 +216,16 @@ protected:
   };
   ParserStatus mStatus;
 
   bool mFrozen;
   bool mErrorLoadOnRedirect;
   bool mGoingToDispatchAllMessages;
   bool mWithCredentials;
   bool mWaitingForOnStopRequest;
-  bool mInterrupted;
 
   // used while reading the input streams
   nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
   nsresult mLastConvertionResult;
   nsString mLastFieldName;
   nsString mLastFieldValue;
 
   nsCOMPtr<nsILoadGroup> mLoadGroup;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14089,18 +14089,22 @@ nsGlobalWindow::GetSidebar(OwningExterna
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 #endif
 }
 
 void
 nsGlobalWindow::ClearDocumentDependentSlots(JSContext* aCx)
 {
   MOZ_ASSERT(IsInnerWindow());
-  WindowBinding::ClearCachedDocumentValue(aCx, this);
-  WindowBinding::ClearCachedPerformanceValue(aCx, this);
+
+  // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
+  if (!WindowBinding::ClearCachedDocumentValue(aCx, this) ||
+      !WindowBinding::ClearCachedPerformanceValue(aCx, this)) {
+    MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
+  }
 }
 
 /* static */
 JSObject*
 nsGlobalWindow::CreateNamedPropertiesObject(JSContext *aCx,
                                             JS::Handle<JSObject*> aProto)
 {
   return WindowNamedPropertiesHandler::Create(aCx, aProto);
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -177,17 +177,17 @@ nsImageLoadingContent::Notify(imgIReques
           = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
         nsIDocument *doc = GetOurOwnerDoc();
         doc->AddBlockedTrackingNode(thisNode);
       }
     }
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopRequest(aRequest, status);
+    return OnLoadComplete(aRequest, status);
   }
 
   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
     if (mFireEventsOnDecode) {
       mFireEventsOnDecode = false;
 
       uint32_t reqStatus;
       aRequest->GetImageStatus(&reqStatus);
@@ -200,18 +200,17 @@ nsImageLoadingContent::Notify(imgIReques
 
     UpdateImageState(true);
   }
 
   return NS_OK;
 }
 
 nsresult
-nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
-                                     nsresult aStatus)
+nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
 {
   uint32_t oldStatus;
   aRequest->GetImageStatus(&oldStatus);
 
   //XXXjdm This occurs when we have a pending request created, then another
   //       pending request replaces it before the first one is finished.
   //       This begs the question of what the correct behaviour is; we used
   //       to not have to care because we ran this code in OnStopDecode which
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -204,17 +204,17 @@ protected:
    */
   virtual mozilla::CORSMode GetCORSMode();
 
   // Subclasses are *required* to call BindToTree/UnbindFromTree.
   void BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                   nsIContent* aBindingParent, bool aCompileEventHandlers);
   void UnbindFromTree(bool aDeep, bool aNullParent);
 
-  nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus);
+  nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
   void OnUnlockedDraw();
   nsresult OnImageIsAnimated(imgIRequest *aRequest);
 
   // The nsContentPolicyType we would use for this ImageLoadType
   static nsContentPolicyType PolicyTypeForLoad(ImageLoadType aImageLoadType);
 
 private:
   /**
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2407,24 +2407,16 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
     nsCycleCollector_dispatchDeferredDeletion();
   }
 
   if (sPrevGCSliceCallback)
     (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
 }
 
 void
-nsJSContext::ReportPendingException()
-{
-  if (mIsInitialized) {
-    nsJSUtils::ReportPendingException(mContext);
-  }
-}
-
-void
 nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
 {
   mWindowProxy = aWindowProxy;
 }
 
 JSObject*
 nsJSContext::GetWindowProxy()
 {
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -141,21 +141,16 @@ protected:
 
   // Helper to convert xpcom datatypes to jsvals.
   nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
                                    JS::Handle<JSObject*> aScope,
                                    JS::AutoValueVector &aArgsOut);
 
   nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv);
 
-  // Report the pending exception on our mContext, if any.  This
-  // function will set aside the frame chain on mContext before
-  // reporting.
-  void ReportPendingException();
-
 private:
   void DestroyJSContext();
 
   nsrefcnt GetCCRefcnt();
 
   JSContext *mContext;
   JS::Heap<JSObject*> mWindowProxy;
 
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -23,16 +23,17 @@
 #include "nsReadableUtils.h"
 #include "nsGkAtoms.h"
 #include "nsComponentManagerUtils.h"
 #include "nsLayoutStatics.h"
 #include "nsBindingManager.h"
 #include "nsHashKeys.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsNameSpaceManager.h"
+#include "nsDocument.h"
 
 using namespace mozilla;
 using mozilla::dom::NodeInfo;
 
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gNodeInfoManagerLeakPRLog;
@@ -148,30 +149,43 @@ nsNodeInfoManager::~nsNodeInfoManager()
 
   nsLayoutStatics::Release();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
-  if (tmp->mDocument &&
-      nsCCUncollectableMarker::InGeneration(cb,
-                                            tmp->mDocument->GetMarkedCCGeneration())) {
-    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
-  }
   if (tmp->mNonDocumentNodeInfos) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager)
+  if (tmp->mDocument) {
+    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkip(tmp->mDocument, aRemovingAllowed);
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager)
+  if (tmp->mDocument) {
+    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipInCC(tmp->mDocument);
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager)
+  if (tmp->mDocument) {
+    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipThis(tmp->mDocument);
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 nsresult
 nsNodeInfoManager::Init(nsIDocument *aDocument)
 {
   NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY);
 
   NS_PRECONDITION(!mPrincipal,
                   "Being inited when we already have a principal?");
   nsresult rv;
--- a/dom/base/nsNodeInfoManager.h
+++ b/dom/base/nsNodeInfoManager.h
@@ -35,17 +35,17 @@ class NodeInfo;
 class nsNodeInfoManager MOZ_FINAL
 {
 private:
   ~nsNodeInfoManager();
 
 public:
   nsNodeInfoManager();
 
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsNodeInfoManager)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_NATIVE_CLASS(nsNodeInfoManager)
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsNodeInfoManager)
 
   /**
    * Initialize the nodeinfo manager with a document.
    */
   nsresult Init(nsIDocument *aDocument);
 
--- a/dom/base/test/test_bug338583.html
+++ b/dom/base/test/test_bug338583.html
@@ -348,39 +348,39 @@ https://bugzilla.mozilla.org/show_bug.cg
       ok(gEventSourceObj3_g.hits['fn_onmessage'] == 0, "Test 3.g failed");
       gEventSourceObj3_g.close();
       setTestHasFinished(test_id);
     }, parseInt(1500*stress_factor));
   }
 
   function fnMessageListenerTest3h(e) {
     fnMessageListenerTest3h.msg_ok = (fnMessageListenerTest3h.msg_ok && e.data == "ok");
-    fnMessageListenerTest3h.id_ok = (fnMessageListenerTest3h.msg_ok && e.lastEventId == "");
+    fnMessageListenerTest3h.id_ok = (fnMessageListenerTest3h.id_ok && e.lastEventId == "");
   }
 
   function doTest3_h(test_id) {
     gEventSourceObj3_h = new EventSource("badMessageEvent2.eventsource");
 
-    gEventSourceObj3_h.addEventListener('message event', fnMessageListenerTest3h, true);
+    gEventSourceObj3_h.addEventListener('message', fnMessageListenerTest3h, true);
     fnMessageListenerTest3h.msg_ok = true;
     fnMessageListenerTest3h.id_ok = true;
 
     gEventSourceObj3_h.onmessage = fn_onmessage;
     gEventSourceObj3_h.hits = [];
     gEventSourceObj3_h.hits['fn_onmessage'] = 0;
 
     setTimeout(function() {
       ok(gEventSourceObj3_h.hits['fn_onmessage'] > 1, "Test 3.h.1 failed");
       if (gEventSourceObj3_h.hits['fn_onmessage'] > 1) {
         ok(fnMessageListenerTest3h.msg_ok, "Test 3.h.2 failed");
         ok(fnMessageListenerTest3h.id_ok, "Test 3.h.3 failed");
       }
       gEventSourceObj3_h.close();
       setTestHasFinished(test_id);
-    }, parseInt(3000*stress_factor));
+    }, parseInt(6000*stress_factor));
   }
 
 // in order to test (4)
 //   a) close the object when it is in use, which is being processed and that is expected
 //      to dispatch more eventlisteners
 //   b) remove an eventlistener in use
 
   function fn_onmessage4_a(e)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3469,17 +3469,16 @@ class CGClearCachedValueMethod(CGAbstrac
                 slotIndex)
             regetMember = fill(
                 """
                 JS::Rooted<JS::Value> temp(aCx);
                 JSJitGetterCallArgs args(&temp);
                 JSAutoCompartment ac(aCx, obj);
                 if (!get_${name}(aCx, obj, aObject, args)) {
                   js::SetReservedSlot(obj, ${slotIndex}, oldValue);
-                  nsJSUtils::ReportPendingException(aCx);
                   return false;
                 }
                 return true;
                 """,
                 name=self.member.identifier.name,
                 slotIndex=slotIndex)
         else:
             declObj = "JSObject* obj;\n"
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -1,50 +1,51 @@
 /* -*- 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 "WebGL1Context.h"
+
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
-
 #include "mozilla/Telemetry.h"
 
-using namespace mozilla;
+namespace mozilla {
 
-// -----------------------------------------------------------------------------
-// CONSTRUCTOR & DESTRUCTOR
+/*static*/ WebGL1Context*
+WebGL1Context::Create()
+{
+    return new WebGL1Context();
+}
 
 WebGL1Context::WebGL1Context()
     : WebGLContext()
 {
-
 }
 
 WebGL1Context::~WebGL1Context()
 {
-
 }
 
-
-// -----------------------------------------------------------------------------
-// IMPLEMENT nsWrapperCache
+////////////////////////////////////////
+// nsWrapperCache
 
 JSObject*
-WebGL1Context::WrapObject(JSContext *cx)
+WebGL1Context::WrapObject(JSContext* cx)
 {
     return dom::WebGLRenderingContextBinding::Wrap(cx, this);
 }
 
+} // namespace mozilla
 
-// -----------------------------------------------------------------------------
-// INSTANCING nsIDOMWebGLRenderingContext
+////////////////////////////////////////
+// nsIDOMWebGLRenderingContext
 
 nsresult
-NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult)
+NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** out_result)
 {
-    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
-    nsIDOMWebGLRenderingContext* ctx = new WebGL1Context();
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::CANVAS_WEBGL_USED, 1);
 
-    NS_ADDREF(*aResult = ctx);
+    nsIDOMWebGLRenderingContext* ctx = mozilla::WebGL1Context::Create();
+
+    NS_ADDREF(*out_result = ctx);
     return NS_OK;
 }
-
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -1,46 +1,35 @@
 /* -*- Mode: C++; tab-width: 4; 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/. */
 
-#ifndef WEBGL1CONTEXT_H_
-#define WEBGL1CONTEXT_H_
+#ifndef WEBGL_1_CONTEXT_H_
+#define WEBGL_1_CONTEXT_H_
 
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 class WebGL1Context
     : public WebGLContext
 {
-// -----------------------------------------------------------------------------
-// PUBLIC
 public:
+    static WebGL1Context* Create();
 
-    // -------------------------------------------------------------------------
-    // CONSTRUCTOR & DESTRUCTOR
+private:
+    WebGL1Context();
 
-    WebGL1Context();
+public:
     virtual ~WebGL1Context();
 
-
-    // -------------------------------------------------------------------------
-    // IMPLEMENT WebGLContext
-
-    virtual bool IsWebGL2() const MOZ_OVERRIDE
-    {
+    virtual bool IsWebGL2() const MOZ_OVERRIDE {
         return false;
     }
 
-
-    // -------------------------------------------------------------------------
-    // IMPLEMENT nsWrapperCache
-
-    virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
-
-
+    // nsWrapperCache
+    virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
 };
 
 } // namespace mozilla
 
-#endif
+#endif // WEBGL_1_CONTEXT_H_
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -1,78 +1,218 @@
 /* -*- Mode: C++; tab-width: 4; 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 "WebGLSampler.h"
 #include "GLContext.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<WebGLSampler>
 WebGL2Context::CreateSampler()
 {
-    MOZ_CRASH("Not Implemented.");
-    return nullptr;
+    if (IsContextLost())
+        return nullptr;
+
+    GLuint sampler;
+    MakeContextCurrent();
+    gl->fGenSamplers(1, &sampler);
+
+    nsRefPtr<WebGLSampler> globj = new WebGLSampler(this, sampler);
+    return globj.forget();
 }
 
 void
 WebGL2Context::DeleteSampler(WebGLSampler* sampler)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!ValidateObjectAllowDeletedOrNull("deleteSampler", sampler))
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return;
+
+    sampler->RequestDelete();
 }
 
 bool
 WebGL2Context::IsSampler(WebGLSampler* sampler)
 {
-    MOZ_CRASH("Not Implemented.");
-    return false;
+    if (IsContextLost())
+        return false;
+
+    if (!sampler)
+        return false;
+
+    if (!ValidateObjectAllowDeleted("isSampler", sampler))
+        return false;
+
+    if (sampler->IsDeleted())
+        return false;
+
+    return !sampler->HasEverBeenBound();
 }
 
 void
 WebGL2Context::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!ValidateObjectAllowDeletedOrNull("bindSampler", sampler))
+        return;
+
+    if (GLint(unit) >= mGLMaxTextureUnits)
+        return ErrorInvalidValue("bindSampler: unit must be < %d", mGLMaxTextureUnits);
+
+    if (sampler && sampler->IsDeleted())
+        return ErrorInvalidOperation("bindSampler: binding deleted sampler");
+
+    WebGLContextUnchecked::BindSampler(unit, sampler);
 }
 
 void
 WebGL2Context::SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameteri: invalid sampler");
+
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameteri"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameteri(sampler, pname, param);
 }
 
 void
 WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Int32Array& param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameteriv: invalid sampler");
+
+    param.ComputeLengthAndData();
+    if (param.Length() < 1)
+        return /* TODO(djg): Error message */;
+
+    /* TODO(djg): All of these calls in ES3 only take 1 param */
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameteriv"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Data());
 }
 
 void
 WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLint>& param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameteriv: invalid sampler");
+
+    if (param.Length() < 1)
+        return /* TODO(djg): Error message */;
+
+    /* TODO(djg): All of these calls in ES3 only take 1 param */
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameteriv"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Elements());
 }
 
 void
 WebGL2Context::SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameterf: invalid sampler");
+
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameterf"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameterf(sampler, pname, param);
 }
 
 void
 WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Float32Array& param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameterfv: invalid sampler");
+
+    param.ComputeLengthAndData();
+    if (param.Length() < 1)
+        return /* TODO(djg): Error message */;
+
+    /* TODO(djg): All of these calls in ES3 only take 1 param */
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameterfv"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Data());
 }
 
 void
 WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLfloat>& param)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("samplerParameterfv: invalid sampler");
+
+    if (param.Length() < 1)
+        return /* TODO(djg): Error message */;
+
+    /* TODO(djg): All of these calls in ES3 only take 1 param */
+    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameterfv"))
+        return;
+
+    WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Elements());
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, WebGLSampler* sampler, GLenum pname, JS::MutableHandleValue retval)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sampler || sampler->IsDeleted())
+        return ErrorInvalidOperation("getSamplerParameter: invalid sampler");
+
+    if (!ValidateSamplerParameterName(pname, "getSamplerParameter"))
+        return;
+
+    retval.set(JS::NullValue());
+
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+    case LOCAL_GL_TEXTURE_WRAP_S:
+    case LOCAL_GL_TEXTURE_WRAP_T:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        retval.set(JS::Int32Value(
+            WebGLContextUnchecked::GetSamplerParameteriv(sampler, pname)));
+        return;
+
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+        retval.set(JS::Float32Value(
+            WebGLContextUnchecked::GetSamplerParameterfv(sampler, pname)));
+        return;
+    }
 }
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2,25 +2,26 @@
 /* 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 "WebGLContext.h"
 
 #include "WebGLContextLossHandler.h"
 #include "WebGL1Context.h"
-#include "WebGLObjectModel.h"
+#include "WebGLBuffer.h"
+#include "WebGLContextUtils.h"
 #include "WebGLExtensions.h"
-#include "WebGLContextUtils.h"
-#include "WebGLBuffer.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLMemoryTracker.h"
+#include "WebGLObjectModel.h"
+#include "WebGLQuery.h"
+#include "WebGLSampler.h"
+#include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
-#include "WebGLMemoryTracker.h"
-#include "WebGLFramebuffer.h"
-#include "WebGLVertexArray.h"
-#include "WebGLQuery.h"
 
 #include "GLBlitHelper.h"
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
@@ -221,17 +222,17 @@ WebGLContextOptions::WebGLContextOptions
       preserveDrawingBuffer(false)
 {
     // Set default alpha state based on preference.
     if (Preferences::GetBool("webgl.default-no-alpha", false))
         alpha = false;
 }
 
 WebGLContext::WebGLContext()
-    : gl(nullptr)
+    : WebGLContextUnchecked(nullptr)
     , mNeedsFakeNoAlpha(false)
 {
     mGeneration = 0;
     mInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
 
@@ -366,16 +367,18 @@ WebGLContext::DestroyResourcesAndContext
     while (!mFramebuffers.isEmpty())
         mFramebuffers.getLast()->DeleteOnce();
     while (!mShaders.isEmpty())
         mShaders.getLast()->DeleteOnce();
     while (!mPrograms.isEmpty())
         mPrograms.getLast()->DeleteOnce();
     while (!mQueries.isEmpty())
         mQueries.getLast()->DeleteOnce();
+    while (!mSamplers.isEmpty())
+        mSamplers.getLast()->DeleteOnce();
 
     mBlackOpaqueTexture2D = nullptr;
     mBlackOpaqueTextureCubeMap = nullptr;
     mBlackTransparentTexture2D = nullptr;
     mBlackTransparentTextureCubeMap = nullptr;
 
     if (mFakeVertexAttrib0BufferObject) {
         gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -10,16 +10,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 #include "GLDefs.h"
 #include "WebGLActiveInfo.h"
+#include "WebGLContextUnchecked.h"
 #include "WebGLObjectModel.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLStrongTypes.h"
 #include <stdarg.h>
 
 #include "nsTArray.h"
 #include "nsCycleCollectionNoteChild.h"
@@ -73,20 +74,22 @@ class WebGLExtensionBase;
 class WebGLBuffer;
 struct WebGLVertexAttribData;
 class WebGLShader;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLUniformLocation;
 class WebGLFramebuffer;
 class WebGLRenderbuffer;
+class WebGLSampler;
 class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
+
 namespace dom {
 class ImageData;
 class Element;
 
 struct WebGLContextAttributes;
 template<typename> struct Nullable;
 }
 
@@ -122,20 +125,39 @@ struct WebGLContextOptions {
     bool premultipliedAlpha;
     bool antialias;
     bool preserveDrawingBuffer;
 };
 
 // From WebGLContextUtils
 TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
 
+class WebGLIntOrFloat {
+    enum {
+        Int,
+        Float
+    } mType;
+    union {
+        GLint i;
+        GLfloat f;
+    } mValue;
+
+public:
+    explicit WebGLIntOrFloat(GLint i) : mType(Int) { mValue.i = i; }
+    explicit WebGLIntOrFloat(GLfloat f) : mType(Float) { mValue.f = f; }
+
+    GLint AsInt() const { return (mType == Int) ? mValue.i : NS_lroundf(mValue.f); }
+    GLfloat AsFloat() const { return (mType == Float) ? mValue.f : GLfloat(mValue.i); }
+};
+
 class WebGLContext :
     public nsIDOMWebGLRenderingContext,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference,
+    public WebGLContextUnchecked,
     public WebGLRectangleObject,
     public nsWrapperCache,
     public SupportsWeakPtr<WebGLContext>
 {
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
@@ -1000,18 +1022,16 @@ protected:
 
     virtual JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname);
 
     // Returns x rounded to the next highest multiple of y.
     static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) {
         return ((x + y - 1) / y) * y;
     }
 
-    nsRefPtr<gl::GLContext> gl;
-
     CheckedUint32 mGeneration;
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mMinCapability;
@@ -1133,16 +1153,20 @@ protected:
 
     bool ValidateGLSLVariableName(const nsAString& name, const char *info);
     bool ValidateGLSLCharacter(char16_t c);
     bool ValidateGLSLString(const nsAString& string, const char *info);
 
     bool ValidateCopyTexImage(GLenum internalformat,
                               WebGLTexImageFunc func,
                               WebGLTexDimensions dims);
+
+    bool ValidateSamplerParameterName(GLenum pname, const char* info);
+    bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info);
+
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
                           GLint xoffset, GLint yoffset, GLint zoffset,
                           GLint width, GLint height, GLint depth,
                           GLint border, GLenum format, GLenum type,
                           WebGLTexImageFunc func,
                           WebGLTexDimensions dims);
     bool ValidateTexImageTarget(GLenum target,
@@ -1320,16 +1344,19 @@ protected:
     LinkedList<WebGLBuffer> mBuffers;
     LinkedList<WebGLProgram> mPrograms;
     LinkedList<WebGLQuery> mQueries;
     LinkedList<WebGLShader> mShaders;
     LinkedList<WebGLRenderbuffer> mRenderbuffers;
     LinkedList<WebGLFramebuffer> mFramebuffers;
     LinkedList<WebGLVertexArray> mVertexArrays;
 
+    // TODO(djg): Does this need a rethink? Should it be WebGL2Context?
+    LinkedList<WebGLSampler> mSamplers;
+
     WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
 
     // PixelStore parameters
     uint32_t mPixelStorePackAlignment, mPixelStoreUnpackAlignment, mPixelStoreColorspaceConversion;
     bool mPixelStoreFlipY, mPixelStorePremultiplyAlpha;
 
     WebGLContextFakeBlackStatus mFakeBlackStatus;
 
@@ -1435,16 +1462,17 @@ public:
     void GenerateWarning(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
+    friend class WebGLSampler;
     friend class WebGLShader;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
     friend class WebGLVertexArrayFake;
     friend class WebGLVertexArrayGL;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLContextUnchecked.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 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 "WebGLContextUnchecked.h"
+
+#include "GLContext.h"
+#include "WebGLSampler.h"
+
+namespace mozilla {
+
+WebGLContextUnchecked::WebGLContextUnchecked(gl::GLContext* gl)
+    : gl(gl)
+{ }
+
+
+// -----------------------------------------------------------------------------
+// Sampler Objects
+
+void
+WebGLContextUnchecked::BindSampler(GLuint unit, WebGLSampler* sampler)
+{
+    gl->MakeCurrent();
+    gl->fBindSampler(unit, sampler ? sampler->GLName() : 0);
+    if (sampler)
+        sampler->BindTo(LOCAL_GL_SAMPLER_BINDING);
+}
+
+GLint
+WebGLContextUnchecked::GetSamplerParameteriv(WebGLSampler* sampler,
+                                             GLenum pname)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+
+    GLint param = 0;
+    gl->MakeCurrent();
+    gl->fGetSamplerParameteriv(sampler->GLName(), pname, &param);
+
+    return param;
+}
+
+GLfloat
+WebGLContextUnchecked::GetSamplerParameterfv(WebGLSampler* sampler,
+                                             GLenum pname)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+
+    GLfloat param = 0.0f;
+    gl->MakeCurrent();
+    gl->fGetSamplerParameterfv(sampler->GLName(), pname, &param);
+    return param;
+}
+
+void
+WebGLContextUnchecked::SamplerParameteri(WebGLSampler* sampler,
+                                         GLenum pname,
+                                         GLint param)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+    gl->MakeCurrent();
+    gl->fSamplerParameteri(sampler->GLName(), pname, param);
+}
+
+void
+WebGLContextUnchecked::SamplerParameteriv(WebGLSampler* sampler,
+                                          GLenum pname,
+                                          const GLint* param)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+    gl->MakeCurrent();
+    gl->fSamplerParameteriv(sampler->GLName(), pname, param);
+}
+
+void
+WebGLContextUnchecked::SamplerParameterf(WebGLSampler* sampler,
+                                         GLenum pname,
+                                         GLfloat param)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+    gl->MakeCurrent();
+    gl->fSamplerParameterf(sampler->GLName(), pname, param);
+}
+
+void
+WebGLContextUnchecked::SamplerParameterfv(WebGLSampler* sampler,
+                                          GLenum pname,
+                                          const GLfloat* param)
+{
+    MOZ_ASSERT(sampler, "Did you validate?");
+    gl->MakeCurrent();
+    gl->fSamplerParameterfv(sampler->GLName(), pname, param);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLContextUnchecked.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGLCONTEXTUNCHECKED_H
+#define WEBGLCONTEXTUNCHECKED_H
+
+#include "GLDefs.h"
+#include "WebGLTypes.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+class WebGLSampler;
+namespace gl {
+    class GLContext;
+}
+
+class WebGLContextUnchecked
+{
+public:
+    explicit WebGLContextUnchecked(gl::GLContext* gl);
+
+    // -------------------------------------------------------------------------
+    // Sampler Objects
+    void BindSampler(GLuint unit, WebGLSampler* sampler);
+
+    GLint   GetSamplerParameteriv(WebGLSampler* sampler, GLenum pname);
+    GLfloat GetSamplerParameterfv(WebGLSampler* sampler, GLenum pname);
+
+    void SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
+    void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const GLint* param);
+    void SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param);
+    void SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const GLfloat* param);
+
+protected: // data
+    nsRefPtr<gl::GLContext> gl;
+};
+
+} // namespace mozilla
+
+#endif // !WEBGLCONTEXTUNCHECKED_H
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -374,16 +374,128 @@ WebGLContext::ValidateFramebufferAttachm
         return true;
     }
 
     ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, attachment);
     return false;
 }
 
 /**
+ * Return true if pname is valid for GetSamplerParameter calls.
+ */
+bool
+WebGLContext::ValidateSamplerParameterName(GLenum pname, const char* info)
+{
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+    case LOCAL_GL_TEXTURE_WRAP_S:
+    case LOCAL_GL_TEXTURE_WRAP_T:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        return true;
+
+    default:
+        ErrorInvalidEnum("%s: invalid pname: %s", info, EnumName(pname));
+        return false;
+    }
+}
+
+/**
+ * Return true if pname and param are valid combination for SamplerParameter calls.
+ */
+bool
+WebGLContext::ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info)
+{
+    const GLenum p = param.AsInt();
+
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+        switch (p) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+        case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
+        case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
+        case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
+        case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
+            return true;
+
+        default:
+            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
+            return false;
+        }
+
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+        switch (p) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+            return true;
+
+        default:
+            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
+            return false;
+        }
+
+    case LOCAL_GL_TEXTURE_WRAP_S:
+    case LOCAL_GL_TEXTURE_WRAP_T:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        switch (p) {
+        case LOCAL_GL_CLAMP_TO_EDGE:
+        case LOCAL_GL_REPEAT:
+        case LOCAL_GL_MIRRORED_REPEAT:
+            return true;
+
+        default:
+            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
+            return false;
+        }
+
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+        return true;
+
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+        switch (param.AsInt()) {
+        case LOCAL_GL_NONE:
+        case LOCAL_GL_COMPARE_REF_TO_TEXTURE:
+            return true;
+
+        default:
+            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
+            return false;
+        }
+
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        switch (p) {
+        case LOCAL_GL_LEQUAL:
+        case LOCAL_GL_GEQUAL:
+        case LOCAL_GL_LESS:
+        case LOCAL_GL_GREATER:
+        case LOCAL_GL_EQUAL:
+        case LOCAL_GL_NOTEQUAL:
+        case LOCAL_GL_ALWAYS:
+        case LOCAL_GL_NEVER:
+            return true;
+
+        default:
+            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
+            return false;
+        }
+
+    default:
+        ErrorInvalidEnum("%s: invalid pname: %s", info, EnumName(pname));
+        return false;
+    }
+}
+
+
+/**
  * Return true if format is a valid texture image format for source,
  * taking into account enabled WebGL extensions.
  */
 bool
 WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func, WebGLTexDimensions dims)
 {
     /* Core WebGL texture formats */
     if (format == LOCAL_GL_ALPHA ||
@@ -1713,18 +1825,17 @@ WebGLContext::InitAndValidateGL()
         // Note that this used to cause crashes on old ATI drivers... hopefully not a significant
         // problem anymore. See bug 602183.
         gl->fEnable(LOCAL_GL_POINT_SPRITE);
     }
 
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
         gl->Vendor() == gl::GLVendor::ATI &&
-        nsCocoaFeatures::OSXVersionMajor() == 10 &&
-        nsCocoaFeatures::OSXVersionMinor() < 9)
+        !nsCocoaFeatures::IsAtLeastVersion(10,9))
     {
         // The Mac ATI driver, in all known OSX version up to and including 10.8,
         // renders points sprites upside-down. Apple bug 11778921
         gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT);
     }
 #endif
 
     // Check the shader validator pref
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -368,17 +368,17 @@ WebGLFramebuffer::Attachment::FinalizeAt
                                          LOCAL_GL_RENDERBUFFER, 0);
         }
 
         return;
     }
     MOZ_ASSERT(HasImage());
 
     if (Texture()) {
-        MOZ_ASSERT(gl == Texture()->Context()->gl);
+        MOZ_ASSERT(gl == Texture()->Context()->GL());
 
         const GLenum imageTarget = ImageTarget().get();
         const GLint mipLevel = MipLevel();
         const GLuint glName = Texture()->GLName();
 
         if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                       imageTarget, glName, mipLevel);
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -7,42 +7,45 @@
 #include "WebGLSampler.h"
 
 #include "GLContext.h"
 
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
 using namespace mozilla;
 
-WebGLSampler::WebGLSampler(WebGLContext* context)
-    : WebGLBindableName<GLenum>(0),
+WebGLSampler::WebGLSampler(WebGLContext* context, GLuint sampler)
+    : WebGLBindableName<GLenum>(sampler),
       WebGLContextBoundObject(context)
 {
-    MOZ_CRASH("Not Implemented.");
+    mContext->mSamplers.insertBack(this);
 }
 
 WebGLSampler::~WebGLSampler()
-{}
+{
+    DeleteOnce();
+}
 
 void
 WebGLSampler::Delete()
 {
-    MOZ_CRASH("Not Implemented.");
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteSamplers(1, &mGLName);
+
+    removeFrom(mContext->mSamplers);
 }
 
 WebGLContext*
 WebGLSampler::GetParentObject() const
 {
-    MOZ_CRASH("Not Implemented.");
-    return nullptr;
+    return Context();
 }
 
 JSObject*
 WebGLSampler::WrapObject(JSContext* cx)
 {
-    MOZ_CRASH("Not Implemented.");
     return dom::WebGLSamplerBinding::Wrap(cx, this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSampler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSampler, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSampler, Release)
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; 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/. */
 
-#ifndef WEBGL2SAMPLER_H_
-#define WEBGL2SAMPLER_H_
+#ifndef WEBGLSAMPLER_H_
+#define WEBGLSAMPLER_H_
 
 #include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/LinkedList.h"
 
@@ -21,26 +21,28 @@ class WebGLSampler MOZ_FINAL
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
 
 public:
 
-    explicit WebGLSampler(WebGLContext* aContext);
+    explicit WebGLSampler(WebGLContext* aContext, GLuint sampler);
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
 
+private:
+
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSampler)
 
 private:
 
     ~WebGLSampler();
 };
 
 } // namespace mozilla
 
-#endif // !WEBGL2SAMPLER_H_
+#endif // !WEBGLSAMPLER_H_
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -59,16 +59,17 @@ UNIFIED_SOURCES += [
     'WebGLContextBuffers.cpp',
     'WebGLContextDraw.cpp',
     'WebGLContextExtensions.cpp',
     'WebGLContextFramebufferOperations.cpp',
     'WebGLContextGL.cpp',
     'WebGLContextLossHandler.cpp',
     'WebGLContextReporter.cpp',
     'WebGLContextState.cpp',
+    'WebGLContextUnchecked.cpp',
     'WebGLContextUtils.cpp',
     'WebGLContextValidate.cpp',
     'WebGLContextVertexArray.cpp',
     'WebGLContextVertices.cpp',
     'WebGLElementArrayCache.cpp',
     'WebGLExtensionBase.cpp',
     'WebGLExtensionBlendMinMax.cpp',
     'WebGLExtensionColorBufferFloat.cpp',
--- a/dom/canvas/test/webgl-mochitest/test_webgl_request_mismatch.html
+++ b/dom/canvas/test/webgl-mochitest/test_webgl_request_mismatch.html
@@ -1,35 +1,90 @@
 <!DOCTYPE HTML>
-<title>WebGL test: Mismatched 'webgl' and 'experimental-webgl' context requests</title>
+<html>
+<head>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
 <body>
-<canvas id="c1"></canvas>
-<canvas id="c2"></canvas>
-<canvas id="c3"></canvas>
-<canvas id="c4"></canvas>
 <script>
 
-function testContextRetrieval(canvasId, creationId, requestId) {
-  var canvas = document.getElementById(canvasId);
-  ok(canvas, 'Invalid `canvasId`: ' + canvasId);
+WEBGL_TYPES = {};
+WEBGL_TYPES['experimental-webgl'] = true;
+WEBGL_TYPES['webgl'] = true;
 
-  var createdGL = canvas.getContext(creationId);
-  if (!createdGL)
-    return; // No WebGL on this machine?
+function AreBothIn(a, b, set) {
+    return (a in set) && (b in set);
+}
 
-  var requestedGL = canvas.getContext(requestId);
-  if (creationId == requestId) {
-    ok(requestedGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should succeed.');
-    ok(requestedGL == createdGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should match.');
-  } else {
-    ok(!requestedGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should fail.');
-  }
+function IsAlias(typeA, typeB) {
+    if (typeA == typeB)
+        return true;
+
+    if (AreBothIn(typeA, typeB, WEBGL_TYPES))
+        return true;
+
+    return false;
 }
 
-testContextRetrieval('c1', 'experimental-webgl', 'webgl');
-testContextRetrieval('c2', 'webgl', 'experimental-webgl');
-testContextRetrieval('c3', 'experimental-webgl', 'experimental-webgl');
-testContextRetrieval('c4', 'webgl', 'webgl');
+function TestContextRetrieval(creationType, requestType, functionalTypeSet) {
+    var canvas = document.createElement('canvas');
+    var createdGL = canvas.getContext(creationType);
+
+    var didCreationSucceed = (createdGL != null);
+    if (creationType in functionalTypeSet) {
+        ok(createdGL, 'Context creation should succeed for type \'' +
+                      creationType + '\'');
+    } else {
+        ok(!createdGL, 'Context creation should fail for type \'' +
+                       creationType + '\'');
+        return;
+    }
+
+    var requestedGL = canvas.getContext(requestType);
+
+    if (requestType in functionalTypeSet &&
+        IsAlias(creationType, requestType))
+    {
+        ok(requestedGL, 'Request for \'' + requestType + '\' from \'' +
+                        creationType + '\' should succeed.');
+        ok(requestedGL == createdGL, 'Request for \'' + requestType +
+                                     '\' from \'' + creationType +
+                                     '\' should match.');
+    } else {
+        ok(!requestedGL, 'Request for \'' + requestType + '\' from \'' +
+                         creationType + '\' should fail.');
+    }
+}
+
+function IsWebGLFunctional() {
+    var canvas = document.createElement('canvas');
+    return canvas.getContext('experimental-webgl') != null;
+}
+
+function IsWebGLConformant() {
+    var canvas = document.createElement('canvas');
+    return canvas.getContext('webgl') != null;
+}
+
+var typeList = ['2d', 'experimental-webgl', 'webgl'];
+var functionalTypeSet = {};
+functionalTypeSet['2d'] = true;
+
+if (IsWebGLFunctional())
+    functionalTypeSet['experimental-webgl'] = true;
+
+if (IsWebGLConformant())
+    functionalTypeSet['webgl'] = true;
+
+for (var i in typeList) {
+    var creationType = typeList[i];
+
+    for (var j in typeList) {
+        var requestType = typeList[j];
+
+        TestContextRetrieval(creationType, requestType, functionalTypeSet);
+    }
+}
 
 </script>
-
+</body>
+</html>
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -29,16 +29,17 @@
 #include "nsIWritablePropertyBag2.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsMathUtils.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "ActiveLayerTracker.h"
+#include "WebGL1Context.h"
 #include "WebGL2Context.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
@@ -643,147 +644,131 @@ HTMLCanvasElement::MozGetAsFileImpl(cons
   nsRefPtr<File> file =
     File::CreateMemoryFile(win, imgData, (uint32_t)imgSize, aName, type,
                            PR_Now());
 
   file.forget(aResult);
   return NS_OK;
 }
 
-nsresult
-HTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
-                                    nsICanvasRenderingContextInternal **aContext)
+static bool
+GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
 {
-  NS_ENSURE_ARG(aContext);
+  if (str.EqualsLiteral("2d")) {
+    *out_type = CanvasContextType::Canvas2D;
+    return true;
+  }
 
-  if (aContextId.EqualsLiteral("2d")) {
-    Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
-    nsRefPtr<CanvasRenderingContext2D> ctx =
-      new CanvasRenderingContext2D();
-
-    ctx->SetCanvasElement(this);
-    ctx.forget(aContext);
-    return NS_OK;
+  if (str.EqualsLiteral("experimental-webgl")) {
+    *out_type = CanvasContextType::WebGL1;
+    return true;
   }
 
-  if (WebGL2Context::IsSupported() &&
-      aContextId.EqualsLiteral("experimental-webgl2"))
-  {
-    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
-    nsRefPtr<WebGL2Context> ctx = WebGL2Context::Create();
-
-    if (ctx == nullptr) {
-      return NS_ERROR_NOT_IMPLEMENTED;
-    }
-
-    ctx->SetCanvasElement(this);
-    ctx.forget(aContext);
-    return NS_OK;
+#ifdef MOZ_WEBGL_CONFORMANT
+  if (str.EqualsLiteral("webgl")) {
+    /* WebGL 1.0, $2.1 "Context Creation":
+     *   If the user agent supports both the webgl and experimental-webgl
+     *   canvas context types, they shall be treated as aliases.
+     */
+    *out_type = CanvasContextType::WebGL1;
+    return true;
   }
-
-  NS_ConvertUTF16toUTF8 ctxId(aContextId);
+#endif
 
-  // check that ctxId is clamped to A-Za-z0-9_-
-  for (uint32_t i = 0; i < ctxId.Length(); i++) {
-    if ((ctxId[i] < 'A' || ctxId[i] > 'Z') &&
-        (ctxId[i] < 'a' || ctxId[i] > 'z') &&
-        (ctxId[i] < '0' || ctxId[i] > '9') &&
-        (ctxId[i] != '-') &&
-        (ctxId[i] != '_'))
-    {
-      // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-      return NS_OK;
+  if (WebGL2Context::IsSupported()) {
+    if (str.EqualsLiteral("experimental-webgl2")) {
+      *out_type = CanvasContextType::WebGL2;
+      return true;
     }
   }
 
-  nsCString ctxString("@mozilla.org/content/canvas-rendering-context;1?id=");
-  ctxString.Append(ctxId);
+  return false;
+}
+
+static already_AddRefed<nsICanvasRenderingContextInternal>
+CreateContextForCanvas(CanvasContextType contextType, HTMLCanvasElement* canvas)
+{
+  nsRefPtr<nsICanvasRenderingContextInternal> ret;
+
+  switch (contextType) {
+  case CanvasContextType::Canvas2D:
+    Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
+    ret = new CanvasRenderingContext2D();
+    break;
 
-  nsresult rv;
-  nsCOMPtr<nsICanvasRenderingContextInternal> ctx =
-    do_CreateInstance(ctxString.get(), &rv);
-  if (rv == NS_ERROR_OUT_OF_MEMORY) {
-    *aContext = nullptr;
-    return NS_ERROR_OUT_OF_MEMORY;
+  case CanvasContextType::WebGL1:
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
+
+    ret = WebGL1Context::Create();
+    if (!ret)
+      return nullptr;
+    break;
+
+  case CanvasContextType::WebGL2:
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
+
+    ret = WebGL2Context::Create();
+    if (!ret)
+      return nullptr;
+    break;
   }
-  if (NS_FAILED(rv)) {
-    *aContext = nullptr;
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    return NS_OK;
-  }
+  MOZ_ASSERT(ret);
 
-  ctx->SetCanvasElement(this);
-  ctx.forget(aContext);
-  return NS_OK;
+  ret->SetCanvasElement(canvas);
+  return ret.forget();
 }
 
 nsresult
 HTMLCanvasElement::GetContext(const nsAString& aContextId,
                               nsISupports** aContext)
 {
   ErrorResult rv;
-  *aContext =
-    GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
+  *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
   return rv.ErrorCode();
 }
 
-static bool
-IsContextIdWebGL(const nsAString& str)
-{
-  return str.EqualsLiteral("webgl") ||
-         str.EqualsLiteral("experimental-webgl");
-}
-
 already_AddRefed<nsISupports>
 HTMLCanvasElement::GetContext(JSContext* aCx,
                               const nsAString& aContextId,
                               JS::Handle<JS::Value> aContextOptions,
                               ErrorResult& rv)
 {
-  if (mCurrentContextId.IsEmpty()) {
-    rv = GetContextHelper(aContextId, getter_AddRefs(mCurrentContext));
-    if (rv.Failed() || !mCurrentContext) {
+  CanvasContextType contextType;
+  if (!GetCanvasContextType(aContextId, &contextType))
+    return nullptr;
+
+  if (!mCurrentContext) {
+    // This canvas doesn't have a context yet.
+
+    nsRefPtr<nsICanvasRenderingContextInternal> context;
+    context = CreateContextForCanvas(contextType, this);
+    if (!context)
       return nullptr;
-    }
 
     // Ensure that the context participates in CC.  Note that returning a
     // CC participant from QI doesn't addref.
-    nsXPCOMCycleCollectionParticipant *cp = nullptr;
-    CallQueryInterface(mCurrentContext, &cp);
+    nsXPCOMCycleCollectionParticipant* cp = nullptr;
+    CallQueryInterface(context, &cp);
     if (!cp) {
-      mCurrentContext = nullptr;
       rv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
+    mCurrentContext = context.forget();
+    mCurrentContextType = contextType;
+
     rv = UpdateContext(aCx, aContextOptions);
     if (rv.Failed()) {
       rv = NS_OK; // See bug 645792
       return nullptr;
     }
-    mCurrentContextId.Assign(aContextId);
-  }
-
-  if (!mCurrentContextId.Equals(aContextId)) {
-    if (IsContextIdWebGL(aContextId) &&
-        IsContextIdWebGL(mCurrentContextId))
-    {
-      // Warn when we get a request for a webgl context with an id that differs
-      // from the id it was created with.
-      nsCString creationId = NS_LossyConvertUTF16toASCII(mCurrentContextId);
-      nsCString requestId = NS_LossyConvertUTF16toASCII(aContextId);
-      JS_ReportWarning(aCx, "WebGL: Retrieving a WebGL context from a canvas "
-                            "via a request id ('%s') different from the id used "
-                            "to create the context ('%s') is not allowed.",
-                            requestId.get(),
-                            creationId.get());
-    }
-    
-    //XXX eventually allow for more than one active context on a given canvas
-    return nullptr;
+  } else {
+    // We already have a context of some type.
+    if (contextType != mCurrentContextType)
+      return nullptr;
   }
 
   nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
   return context.forget();
 }
 
 NS_IMETHODIMP
 HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
@@ -793,32 +778,38 @@ HTMLCanvasElement::MozGetIPCContext(cons
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // We only support 2d shmem contexts for now.
   if (!aContextId.EqualsLiteral("2d"))
     return NS_ERROR_INVALID_ARG;
 
-  if (mCurrentContextId.IsEmpty()) {
-    nsresult rv = GetContextHelper(aContextId, getter_AddRefs(mCurrentContext));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!mCurrentContext) {
+  CanvasContextType contextType = CanvasContextType::Canvas2D;
+
+  if (!mCurrentContext) {
+    // This canvas doesn't have a context yet.
+
+    nsRefPtr<nsICanvasRenderingContextInternal> context;
+    context = CreateContextForCanvas(contextType, this);
+    if (!context) {
+      *aContext = nullptr;
       return NS_OK;
     }
 
+    mCurrentContext = context;
     mCurrentContext->SetIsIPC(true);
-
-    rv = UpdateContext(nullptr, JS::NullHandleValue);
-    NS_ENSURE_SUCCESS(rv, rv);
+    mCurrentContextType = contextType;
 
-    mCurrentContextId.Assign(aContextId);
-  } else if (!mCurrentContextId.Equals(aContextId)) {
-    //XXX eventually allow for more than one active context on a given canvas
-    return NS_ERROR_INVALID_ARG;
+    nsresult rv = UpdateContext(nullptr, JS::NullHandleValue);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // We already have a context of some type.
+    if (contextType != mCurrentContextType)
+      return NS_ERROR_INVALID_ARG;
   }
 
   NS_ADDREF (*aContext = mCurrentContext);
   return NS_OK;
 }
 
 nsresult
 HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
@@ -826,31 +817,28 @@ HTMLCanvasElement::UpdateContext(JSConte
   if (!mCurrentContext)
     return NS_OK;
 
   nsIntSize sz = GetWidthHeight();
 
   nsresult rv = mCurrentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
   if (NS_FAILED(rv)) {
     mCurrentContext = nullptr;
-    mCurrentContextId.Truncate();
     return rv;
   }
 
   rv = mCurrentContext->SetContextOptions(aCx, aNewContextOptions);
   if (NS_FAILED(rv)) {
     mCurrentContext = nullptr;
-    mCurrentContextId.Truncate();
     return rv;
   }
 
   rv = mCurrentContext->SetDimensions(sz.width, sz.height);
   if (NS_FAILED(rv)) {
     mCurrentContext = nullptr;
-    mCurrentContextId.Truncate();
     return rv;
   }
 
   return rv;
 }
 
 nsIntSize
 HTMLCanvasElement::GetSize()
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -2,16 +2,17 @@
 /* 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/. */
 #if !defined(mozilla_dom_HTMLCanvasElement_h)
 #define mozilla_dom_HTMLCanvasElement_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/TypedEnum.h"
 #include "nsIDOMHTMLCanvasElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 
 #include "mozilla/gfx/Rect.h"
 
@@ -30,16 +31,22 @@ class SourceSurface;
 
 namespace dom {
 
 class File;
 class FileCallback;
 class HTMLCanvasPrintState;
 class PrintCallback;
 
+MOZ_BEGIN_ENUM_CLASS(CanvasContextType, uint8_t)
+  Canvas2D,
+  WebGL1,
+  WebGL2
+MOZ_END_ENUM_CLASS(CanvasContextType)
+
 class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement,
                                     public nsIDOMHTMLCanvasElement
 {
   enum {
     DEFAULT_CANVAS_WIDTH = 300,
     DEFAULT_CANVAS_HEIGHT = 150
   };
 
@@ -224,21 +231,19 @@ protected:
                        nsIInputStream** aStream);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
-  nsresult GetContextHelper(const nsAString& aContextId,
-                            nsICanvasRenderingContextInternal **aContext);
   void CallPrintCallback();
 
-  nsString mCurrentContextId;
+  CanvasContextType mCurrentContextType;
   nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
   nsRefPtr<PrintCallback> mPrintCallback;
   nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
   nsRefPtr<HTMLCanvasPrintState> mPrintState;
 
 public:
   // Record whether this canvas should be write-only or not.
   // We set this when script paints an image from a different origin.
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -454,70 +454,48 @@ ImageDocument::DOMToggleImageSize()
 }
 
 NS_IMETHODIMP
 ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
-    return OnStartContainer(aRequest, image);
+    return OnSizeAvailable(aRequest, image);
   }
 
-  // Do these two off a script runner because decode complete notifications often
-  // come during painting and these will trigger invalidation.
-  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
+  // Run this using a script runner because HAS_TRANSPARENCY notifications can
+  // come during painting and this will trigger invalidation.
+  if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
     nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &ImageDocument::AddDecodedClass);
-    nsContentUtils::AddScriptRunner(runnable);
-  }
-
-  if (aType == imgINotificationObserver::DISCARD) {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &ImageDocument::RemoveDecodedClass);
+      NS_NewRunnableMethod(this, &ImageDocument::OnHasTransparency);
     nsContentUtils::AddScriptRunner(runnable);
   }
 
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopRequest(aRequest, status);
+    return OnLoadComplete(aRequest, status);
   }
 
   return NS_OK;
 }
 
 void
-ImageDocument::AddDecodedClass()
+ImageDocument::OnHasTransparency()
 {
   if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
     return;
   }
 
   nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
   mozilla::ErrorResult rv;
-  // Update the background-color of the image only after the
-  // image has been decoded to prevent flashes of just the
-  // background-color.
-  classList->Add(NS_LITERAL_STRING("decoded"), rv);
-}
-
-void
-ImageDocument::RemoveDecodedClass()
-{
-  if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
-    return;
-  }
-
-  nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
-  mozilla::ErrorResult rv;
-  // Remove any decoded-related styling when the image is unloaded.
-  classList->Remove(NS_LITERAL_STRING("decoded"), rv);
+  classList->Add(NS_LITERAL_STRING("transparent"), rv);
 }
 
 void
 ImageDocument::SetModeClass(eModeClasses mode)
 {
   nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
   mozilla::ErrorResult rv;
 
@@ -530,34 +508,33 @@ ImageDocument::SetModeClass(eModeClasses
   if (mode == eOverflowing) {
     classList->Add(NS_LITERAL_STRING("overflowing"), rv);
   } else {
     classList->Remove(NS_LITERAL_STRING("overflowing"), rv);
   }
 }
 
 nsresult
-ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
+ImageDocument::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
 {
   // Styles have not yet been applied, so we don't know the final size. For now,
   // default to the image's intrinsic size.
   aImage->GetWidth(&mImageWidth);
   aImage->GetHeight(&mImageHeight);
 
   nsCOMPtr<nsIRunnable> runnable =
     NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing);
   nsContentUtils::AddScriptRunner(runnable);
   UpdateTitleAndCharset();
 
   return NS_OK;
 }
 
 nsresult
-ImageDocument::OnStopRequest(imgIRequest *aRequest,
-                             nsresult aStatus)
+ImageDocument::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
 {
   UpdateTitleAndCharset();
 
   // mImageContent can be null if the document is already destroyed
   if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
     nsAutoCString src;
     mDocumentURI->GetSpec(src);
     NS_ConvertUTF8toUTF16 srcString(src);
--- a/dom/html/ImageDocument.h
+++ b/dom/html/ImageDocument.h
@@ -46,19 +46,16 @@ public:
   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ImageDocument, MediaDocument)
 
   friend class ImageListener;
 
   void DefaultCheckOverflowing() { CheckOverflowing(mResizeImageByDefault); }
 
-  void AddDecodedClass();
-  void RemoveDecodedClass();
-
   // WebIDL API
   virtual JSObject* WrapNode(JSContext* aCx)
     MOZ_OVERRIDE;
 
   bool ImageResizingEnabled() const
   {
     return true;
   }
@@ -102,18 +99,19 @@ protected:
 
   enum eModeClasses {
     eNone,
     eShrinkToFit,
     eOverflowing
   };
   void SetModeClass(eModeClasses mode);
 
-  nsresult OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage);
-  nsresult OnStopRequest(imgIRequest *aRequest, nsresult aStatus);
+  nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
+  nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
+  void OnHasTransparency();
 
   nsCOMPtr<nsIContent>          mImageContent;
 
   float                         mVisibleWidth;
   float                         mVisibleHeight;
   int32_t                       mImageWidth;
   int32_t                       mImageHeight;
 
--- a/dom/imptests/editing/implementation.js
+++ b/dom/imptests/editing/implementation.js
@@ -1968,19 +1968,19 @@ function isSimpleModifiableElement(node)
 	//
 	// The weird extra node.style.length check is for Firefox, which as of
 	// 8.0a2 has annoying and weird behavior here.
 	if (["A", "FONT", "S", "SPAN", "STRIKE", "U"].indexOf(node.tagName) != -1
 	&& node.hasAttribute("style")
 	&& (node.style.length == 1
 	|| (node.style.length == 4
 		&& "MozTextBlink" in node.style
-		&& "MozTextDecorationColor" in node.style
-		&& "MozTextDecorationLine" in node.style
-		&& "MozTextDecorationStyle" in node.style)
+		&& "textDecorationColor" in node.style
+		&& "textDecorationLine" in node.style
+		&& "textDecorationStyle" in node.style)
 	)
 	&& (node.style.textDecoration == "line-through"
 	|| node.style.textDecoration == "underline"
 	|| node.style.textDecoration == "overline"
 	|| node.style.textDecoration == "none")) {
 		return true;
 	}
 
--- a/dom/ipc/ScreenManagerParent.cpp
+++ b/dom/ipc/ScreenManagerParent.cpp
@@ -116,16 +116,21 @@ ScreenManagerParent::RecvScreenForRect(c
 }
 
 bool
 ScreenManagerParent::RecvScreenForBrowser(PBrowserParent* aBrowser,
                                           ScreenDetails* aRetVal,
                                           bool* aSuccess)
 {
   *aSuccess = false;
+#ifdef MOZ_VALGRIND
+  // Zero this so that Valgrind doesn't complain when we send it to another
+  // process.
+  memset(aRetVal, 0, sizeof(ScreenDetails));
+#endif
 
   // Find the mWidget associated with the tabparent, and then return
   // the nsIScreen it's on.
   TabParent* tabParent = static_cast<TabParent*>(aBrowser);
   nsCOMPtr<nsIWidget> widget = tabParent->GetWidget();
   if (!widget) {
     return true;
   }
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -174,31 +174,56 @@ class MediaRecorder::Session: public nsI
       LOG(PR_LOG_DEBUG, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
       MOZ_ASSERT(NS_IsMainThread());
 
       nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
         return NS_OK;
       }
 
-      if (mSession->IsEncoderError()) {
-        recorder->NotifyError(NS_ERROR_UNEXPECTED);
-      }
       nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
       if (NS_FAILED(rv)) {
         recorder->NotifyError(rv);
       }
 
       return NS_OK;
     }
 
   private:
     nsRefPtr<Session> mSession;
   };
 
+  // Notify encoder error, run in main thread task. (Bug 1095381)
+  class EncoderErrorNotifierRunnable : public nsRunnable
+  {
+  public:
+    explicit EncoderErrorNotifierRunnable(Session* aSession)
+      : mSession(aSession)
+    { }
+
+    NS_IMETHODIMP Run()
+    {
+      LOG(PR_LOG_DEBUG, ("Session.ErrorNotifyRunnable s=(%p)", mSession.get()));
+      MOZ_ASSERT(NS_IsMainThread());
+
+      nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
+      if (!recorder) {
+        return NS_OK;
+      }
+
+      if (mSession->IsEncoderError()) {
+        recorder->NotifyError(NS_ERROR_UNEXPECTED);
+      }
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<Session> mSession;
+  };
+
   // Fire start event and set mimeType, run in main thread task.
   class DispatchStartEventRunnable : public nsRunnable
   {
   public:
     DispatchStartEventRunnable(Session* aSession, const nsAString & aEventName)
       : mSession(aSession)
       , mEventName(aEventName)
     { }
@@ -331,16 +356,17 @@ class MediaRecorder::Session: public nsI
       return NS_OK;
     }
 
   private:
     // Call mSession::Release automatically while DestroyRunnable be destroy.
     nsRefPtr<Session> mSession;
   };
 
+  friend class EncoderErrorNotifierRunnable;
   friend class PushBlobRunnable;
   friend class ExtractRunnable;
   friend class DestroyRunnable;
   friend class TracksAvailableCallback;
 
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder),
@@ -394,17 +420,18 @@ public:
     return NS_OK;
   }
 
   nsresult RequestData()
   {
     LOG(PR_LOG_DEBUG, ("Session.RequestData"));
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+    if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this))) ||
+        NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
       MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
   already_AddRefed<nsIDOMBlob> GetEncodedData()
@@ -475,16 +502,19 @@ private:
     // Whether push encoded data back to onDataAvailable automatically or we
     // need a flush.
     bool pushBlob = false;
     if ((mTimeSlice > 0) &&
         ((TimeStamp::Now()-mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice)) {
       pushBlob = true;
     }
     if (pushBlob || aForceFlush) {
+      if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed");
+      }
       if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       } else {
         mLastBlobTimeStamp = TimeStamp::Now();
       }
     }
   }
 
@@ -571,16 +601,19 @@ private:
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
     if (NS_FAILED(rv)) {
       mRecorder->NotifyError(rv);
     }
 
     CleanupStreams();
+    if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) {
+      MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed");
+    }
     if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
     }
     if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
     }
   }
   void CleanupStreams()
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -60,17 +60,17 @@ PRLogModuleInfo* gMediaStreamGraphLog;
 #  endif
 #else
 #  define LIFECYCLE_LOG(...)
 #endif
 
 /**
  * The singleton graph instance.
  */
-static MediaStreamGraphImpl* gGraph;
+static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   NS_ASSERTION(IsEmpty(),
                "All streams should have been destroyed by messages from the main thread");
   STREAM_LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p destroyed", this));
   LIFECYCLE_LOG("MediaStreamGraphImpl::~MediaStreamGraphImpl\n");
 }
@@ -1631,19 +1631,20 @@ MediaStreamGraphImpl::RunInStableState(b
         // synchronously because it spins the event loop waiting for threads
         // to shut down, and we don't want to do that in a stable state handler.
         mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
         LIFECYCLE_LOG("Sending MediaStreamGraphShutDownRunnable %p", this);
         nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this );
         NS_DispatchToMainThread(event);
 
         LIFECYCLE_LOG("Disconnecting MediaStreamGraph %p", this);
-        if (this == gGraph) {
+        MediaStreamGraphImpl* graph;
+        if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
           // null out gGraph if that's the graph being shut down
-          gGraph = nullptr;
+          gGraphs.Remove(mAudioChannel);
         }
       }
     } else {
       if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
         MessageBlock* block = mBackMessageQueue.AppendElement();
         block->mMessages.SwapElements(mCurrentTaskMessageQueue);
         block->mGraphUpdateIndex = mNextGraphUpdateIndex;
         ++mNextGraphUpdateIndex;
@@ -1784,19 +1785,22 @@ MediaStreamGraphImpl::AppendMessage(Cont
 #endif
     aMessage->RunDuringShutdown();
 #ifdef DEBUG
     mCanRunMessagesSynchronously = true;
 #endif
     delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
-      if (gGraph == this) {
-        gGraph = nullptr;
+
+      MediaStreamGraphImpl* graph;
+      if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
+        gGraphs.Remove(mAudioChannel);
       }
+
       Destroy();
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   EnsureRunInStableState();
 }
@@ -2736,16 +2740,17 @@ MediaStreamGraphImpl::MediaStreamGraphIm
 #endif
   , mMemoryReportMonitor("MSGIMemory")
   , mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
   , mAudioStreamSizes()
   , mNeedsMemoryReport(false)
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
+  , mAudioChannel(static_cast<uint32_t>(aChannel))
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   if (mRealtime) {
@@ -2774,50 +2779,65 @@ MediaStreamGraphImpl::Destroy()
   // Clear the self reference which will destroy this instance.
   mSelfRef = nullptr;
 }
 
 NS_IMPL_ISUPPORTS(MediaStreamGraphShutdownObserver, nsIObserver)
 
 static bool gShutdownObserverRegistered = false;
 
+namespace {
+
+PLDHashOperator
+ForceShutdownEnumerator(const uint32_t& /* aAudioChannel */,
+                        MediaStreamGraphImpl* aGraph,
+                        void* /* aUnused */)
+{
+  aGraph->ForceShutDown();
+  return PL_DHASH_NEXT;
+}
+
+} // anonymous namespace
+
 NS_IMETHODIMP
 MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
                                           const char *aTopic,
                                           const char16_t *aData)
 {
   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    if (gGraph) {
-      gGraph->ForceShutDown();
-    }
+    gGraphs.EnumerateRead(ForceShutdownEnumerator, nullptr);
     nsContentUtils::UnregisterShutdownObserver(this);
     gShutdownObserverRegistered = false;
   }
   return NS_OK;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioChannel aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
-  if (!gGraph) {
+  uint32_t channel = static_cast<uint32_t>(aChannel);
+  MediaStreamGraphImpl* graph = nullptr;
+
+  if (!gGraphs.Get(channel, &graph)) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
     CubebUtils::InitPreferredSampleRate();
 
-    gGraph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
+    graph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
+    gGraphs.Put(channel, graph);
 
-    STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
+    STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", graph));
   }
 
-  return gGraph;
+  return graph;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false, aSampleRate);
@@ -2990,17 +3010,20 @@ MediaStreamGraph::CreateAudioNodeStream(
   }
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 bool
 MediaStreamGraph::IsNonRealtime() const
 {
-  return this != gGraph;
+  const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
+  MediaStreamGraphImpl* graph;
+
+  return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl;
 }
 
 void
 MediaStreamGraph::StartNonRealtimeProcessing(TrackRate aRate, uint32_t aTicksToProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -656,16 +656,18 @@ public:
    * Hold a ref to the Latency logger
    */
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
   AudioMixer mMixer;
 #ifdef MOZ_WEBRTC
   nsRefPtr<AudioOutputObserver> mFarendObserverRef;
 #endif
 
+  uint32_t AudioChannel() const { return mAudioChannel; }
+
 private:
   virtual ~MediaStreamGraphImpl();
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * Used to signal that a memory report has been requested.
    */
@@ -689,13 +691,16 @@ private:
 
 #ifdef DEBUG
   /**
    * Used to assert when AppendMessage() runs ControlMessages synchronously.
    */
   bool mCanRunMessagesSynchronously;
 #endif
 
+  // We use uint32_t instead AudioChannel because this is just used as key for
+  // the hashtable gGraphs.
+  uint32_t mAudioChannel;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -53,17 +53,17 @@ public:
       printf_stderr("Failed to create SurfaceTexture for video decode\n");
       return NS_ERROR_FAILURE;
     }
 
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
-    mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
+    mp4_demuxer::AnnexB::ConvertSample(aSample);
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   virtual nsresult PostOutput(BufferInfo* aInfo, MediaFormat* aFormat, Microseconds aDuration) MOZ_OVERRIDE {
     VideoInfo videoInfo;
     videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
 
     bool isSync = false;
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -48,17 +48,16 @@ enum {
   kNotifyCodecReserved = 'core',
   kNotifyCodecCanceled = 'coca',
 };
 
 GonkVideoDecoderManager::GonkVideoDecoderManager(
                            mozilla::layers::ImageContainer* aImageContainer,
 		           const mp4_demuxer::VideoDecoderConfig& aConfig)
   : mImageContainer(aImageContainer)
-  , mConfig(aConfig)
   , mReaderCallback(nullptr)
   , mColorConverterBufferSize(0)
   , mNativeWindow(nullptr)
   , mPendingVideoBuffersLock("GonkVideoDecoderManager::mPendingVideoBuffersLock")
 {
   NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
   MOZ_ASSERT(mImageContainer);
   MOZ_COUNT_CTOR(GonkVideoDecoderManager);
@@ -391,17 +390,17 @@ GonkVideoDecoderManager::Input(mp4_demux
 {
   if (mDecoder == nullptr) {
     ALOG("Decoder is not inited");
     return NS_ERROR_UNEXPECTED;
   }
   status_t rv;
   if (aSample != nullptr) {
     // We must prepare samples in AVC Annex B.
-    mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
+    mp4_demuxer::AnnexB::ConvertSample(aSample);
     // Forward sample data to the decoder.
 
     const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
     uint32_t length = aSample->size;
     rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
   }
   else {
     // Inputted data is null, so it is going to notify decoder EOS
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -110,17 +110,16 @@ private:
   // For codec resource management
   void codecReserved();
   void codecCanceled();
   void onMessageReceived(const sp<AMessage> &aMessage);
 
   void ReleaseAllPendingVideoBuffersLocked();
   void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer);
 
-  const mp4_demuxer::VideoDecoderConfig& mConfig;
   uint32_t mVideoWidth;
   uint32_t mVideoHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
 
   android::sp<MediaCodecProxy> mDecoder;
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -35,17 +35,16 @@ namespace mozilla {
 WMFVideoMFTManager::WMFVideoMFTManager(
                             const mp4_demuxer::VideoDecoderConfig& aConfig,
                             mozilla::layers::LayersBackend aLayersBackend,
                             mozilla::layers::ImageContainer* aImageContainer,
                             bool aDXVAEnabled)
   : mVideoStride(0)
   , mVideoWidth(0)
   , mVideoHeight(0)
-  , mConfig(aConfig)
   , mImageContainer(aImageContainer)
   , mDXVAEnabled(aDXVAEnabled)
   , mLayersBackend(aLayersBackend)
   , mUseHwAccel(false)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
   MOZ_ASSERT(mImageContainer);
   MOZ_COUNT_CTOR(WMFVideoMFTManager);
@@ -142,17 +141,17 @@ WMFVideoMFTManager::Init()
 
   return decoder.forget();
 }
 
 HRESULT
 WMFVideoMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
 {
   // We must prepare samples in AVC Annex B.
-  mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
+  mp4_demuxer::AnnexB::ConvertSample(aSample);
   // Forward sample data to the decoder.
   const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
   uint32_t length = aSample->size;
   return mDecoder->Input(data, length, aSample->composition_timestamp);
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.h
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.h
@@ -51,18 +51,16 @@ private:
 
   // Video frame geometry.
   VideoInfo mVideoInfo;
   uint32_t mVideoStride;
   uint32_t mVideoWidth;
   uint32_t mVideoHeight;
   nsIntRect mPictureRegion;
 
-  const mp4_demuxer::VideoDecoderConfig& mConfig;
-
   RefPtr<MFTDecoder> mDecoder;
   RefPtr<layers::ImageContainer> mImageContainer;
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
   RefPtr<MediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   const bool mDXVAEnabled;
   const layers::LayersBackend mLayersBackend;
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -277,20 +277,16 @@ MediaCodecReader::ProcessCachedDataTask:
   MOZ_ASSERT(mReader, "Should have a valid MediaCodecReader.");
   MOZ_ASSERT(mOffset >= INT64_C(0), "Should have a valid offset.");
 }
 
 void
 MediaCodecReader::ProcessCachedDataTask::Run()
 {
   mReader->ProcessCachedData(mOffset, nullptr);
-  nsRefPtr<ReferenceKeeperRunnable<MediaCodecReader>> runnable(
-      new ReferenceKeeperRunnable<MediaCodecReader>(mReader));
-  mReader = nullptr;
-  NS_DispatchToMainThread(runnable.get());
 }
 
 MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
   : MediaOmxCommonReader(aDecoder)
   , mExtractor(nullptr)
   , mIsWaitingResources(false)
   , mTextureClientIndexesLock("MediaCodecReader::mTextureClientIndexesLock")
   , mColorConverterBufferSize(0)
@@ -300,17 +296,16 @@ MediaCodecReader::MediaCodecReader(Abstr
   , mParsedDataLength(INT64_C(0))
 {
   mHandler = new MessageHandler(this);
   mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
 }
 
 nsresult
 MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
 {
   return NS_OK;
 }
 
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -333,47 +333,16 @@ private:
     ProcessCachedDataTask(const ProcessCachedDataTask &rhs) MOZ_DELETE;
     const ProcessCachedDataTask &operator=(const ProcessCachedDataTask &rhs) MOZ_DELETE;
 
     nsRefPtr<MediaCodecReader> mReader;
     int64_t mOffset;
   };
   friend class ProcessCachedDataTask;
 
-  // This class is used to keep one reference count of T in it. And this class
-  // can make sure the stored reference count will be released on the dispatched
-  // thread. By using this class properly (ex. passing the pointer into this
-  // runnable first, then releasing the original pointer held by ourselves, and
-  // then dispatching this runnable onto the desired thread), we can avoid
-  // running the destructor of the referenced object on any other threads
-  // unexpectedly before this runnable has been executed.
-  template<class T>
-  class ReferenceKeeperRunnable : public nsRunnable
-  {
-  public:
-    ReferenceKeeperRunnable(nsRefPtr<T> aPointer)
-      : mPointer(aPointer)
-    {
-    }
-
-    NS_IMETHOD Run() MOZ_OVERRIDE
-    {
-      mPointer = nullptr;
-      return NS_OK;
-    }
-
-  private:
-    // Forbidden
-    ReferenceKeeperRunnable() MOZ_DELETE;
-    ReferenceKeeperRunnable(const ReferenceKeeperRunnable &rhs) MOZ_DELETE;
-    const ReferenceKeeperRunnable &operator=(const ReferenceKeeperRunnable &rhs) MOZ_DELETE;
-
-    nsRefPtr<T> mPointer;
-  };
-
   // Forbidden
   MediaCodecReader() MOZ_DELETE;
   const MediaCodecReader& operator=(const MediaCodecReader& rhs) MOZ_DELETE;
 
   bool ReallocateResources();
   void ReleaseCriticalResources();
   void ReleaseResources();
 
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -2578,16 +2578,58 @@ PeerConnectionWrapper.prototype = {
       ok(numRemoteCandidates, "Have remotecandidate stat(s)");
     } else {
       is(numLocalCandidates, 0, "Have no localcandidate stats");
       is(numRemoteCandidates, 0, "Have no remotecandidate stats");
     }
   },
 
   /**
+   * Compares the Ice server configured for this PeerConnectionWrapper
+   * with the ICE candidates received in the RTCP stats.
+   *
+   * @param {object} stats
+   *        The stats to be verified for relayed vs. direct connection.
+   */
+  checkStatsIceConnectionType : function PCW_checkStatsIceConnectionType(stats)
+  {
+    var lId;
+    var rId;
+    Object.keys(stats).forEach(function(name) {
+      if ((stats[name].type === "candidatepair") &&
+          (stats[name].selected)) {
+        lId = stats[name].localCandidateId;
+        rId = stats[name].remoteCandidateId;
+      }
+    });
+    info("checkStatsIceConnectionType verifying: local=" +
+         JSON.stringify(stats[lId]) + " remote=" + JSON.stringify(stats[rId]));
+    if ((typeof stats[lId] === 'undefined') ||
+        (typeof stats[rId] === 'undefined')) {
+      info("checkStatsIceConnectionType failed to find candidatepair IDs");
+      return;
+    }
+    var lType = stats[lId].candidateType;
+    var rType = stats[rId].candidateType;
+    var lIp = stats[lId].ipAddress;
+    var rIp = stats[rId].ipAddress;
+    if ((this.configuration) && (typeof this.configuration.iceServers !== 'undefined')) {
+      info("Ice Server configured");
+      // Note: the IP comparising is a workaround for bug 1097333
+      //       And this will fail if a TURN server address is a DNS name!
+      var serverIp = this.configuration.iceServers[0].url.split(':')[1];
+      ok((lType === "relayed" || rType === "relayed") ||
+         (lIp === serverIp || rIp === serverIp), "One peer uses a relay");
+    } else {
+      info("P2P configured");
+      ok(((lType !== "relayed") && (rType !== "relayed")), "Pure peer to peer call without a relay");
+    }
+  },
+
+  /**
    * Property-matching function for finding a certain stat in passed-in stats
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    * @param {object} props
    *        The properties to look for
    * @returns {boolean} Whether an entry containing all match-props was found.
    */
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -19,16 +19,27 @@ function deltaSeconds(date1, date2) {
 function dumpSdp(test) {
   if (typeof test._local_offer !== 'undefined') {
     dump("ERROR: SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, ''));
   }
   if (typeof test._remote_answer !== 'undefined') {
     dump("ERROR: SDP answer: " + test._remote_answer.sdp.replace(/[\r]/g, ''));
   }
 
+  if ((test.pcLocal) && (typeof test.pcLocal._local_ice_candidates !== 'undefined')) {
+    dump("pcLocal._local_ice_candidates: " + JSON.stringify(test.pcLocal._local_ice_candidates) + "\n");
+    dump("pcLocal._remote_ice_candidates: " + JSON.stringify(test.pcLocal._remote_ice_candidates) + "\n");
+    dump("pcLocal._ice_candidates_to_add: " + JSON.stringify(test.pcLocal._ice_candidates_to_add) + "\n");
+  }
+  if ((test.pcRemote) && (typeof test.pcRemote._local_ice_candidates !== 'undefined')) {
+    dump("pcRemote._local_ice_candidates: " + JSON.stringify(test.pcRemote._local_ice_candidates) + "\n");
+    dump("pcRemote._remote_ice_candidates: " + JSON.stringify(test.pcRemote._remote_ice_candidates) + "\n");
+    dump("pcRemote._ice_candidates_to_add: " + JSON.stringify(test.pcRemote._ice_candidates_to_add) + "\n");
+  }
+
   if ((test.pcLocal) && (typeof test.pcLocal.iceConnectionLog !== 'undefined')) {
     dump("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog + "\n");
   }
   if ((test.pcRemote) && (typeof test.pcRemote.iceConnectionLog !== 'undefined')) {
     dump("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog + "\n");
   }
 
   if ((test.pcLocal) && (test.pcRemote) &&
@@ -488,16 +499,34 @@ var commandsPeerConnection = [
     function (test) {
       test.pcRemote.getStats(null, function(stats) {
         test.pcRemote.checkStats(stats, test.steeplechase);
         test.next();
       });
     }
   ],
   [
+    'PC_LOCAL_CHECK_ICE_CONNECTION_TYPE',
+    function (test) {
+      test.pcLocal.getStats(null, function(stats) {
+        test.pcLocal.checkStatsIceConnectionType(stats);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_ICE_CONNECTION_TYPE',
+    function (test) {
+      test.pcRemote.getStats(null, function(stats) {
+        test.pcRemote.checkStatsIceConnectionType(stats);
+        test.next();
+      });
+    }
+  ],
+  [
     'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
     function (test) {
       var pc = test.pcLocal;
       var stream = pc._pc.getLocalStreams()[0];
       var track = stream && stream.getAudioTracks()[0];
       if (track) {
         var msg = "pcLocal.HasStat outbound audio rtp ";
         pc.getStats(track, function(stats) {
--- a/dom/media/tests/mochitest/turnConfig.js
+++ b/dom/media/tests/mochitest/turnConfig.js
@@ -1,12 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* An example of how to specify two TURN server configs:
+ *
+ * Note: If turn URL uses FQDN rather then an IP address the TURN relay
+ *       verification step in checkStatsIceConnectionType might fail.
+ *
  * var turnServers = {
  *   local: { iceServers: [{"username":"mozilla","credential":"mozilla","url":"turn:10.0.0.1"}] },
  *   remote: { iceServers: [{"username":"firefox","credential":"firefox","url":"turn:10.0.0.2"}] }
  * };
   */
 
 var turnServers = { };
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -664,22 +664,16 @@ AudioContext::Unmute() const
 }
 
 AudioChannel
 AudioContext::MozAudioChannelType() const
 {
   return mDestination->MozAudioChannelType();
 }
 
-void
-AudioContext::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
-{
-  mDestination->SetMozAudioChannelType(aValue, aRv);
-}
-
 AudioChannel
 AudioContext::TestAudioChannelInAudioNodeStream()
 {
   MediaStream* stream = mDestination->Stream();
   MOZ_ASSERT(stream);
 
   return stream->AudioChannelType();
 }
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -219,17 +219,16 @@ public:
   uint32_t MaxChannelCount() const;
 
   void Mute() const;
   void Unmute() const;
 
   JSObject* GetGlobalJSObject() const;
 
   AudioChannel MozAudioChannelType() const;
-  void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   AudioChannel TestAudioChannelInAudioNodeStream();
 
   void UpdateNodeCount(int32_t aDelta);
 
   double DOMTimeToStreamTime(double aTime) const
   {
     return aTime - ExtraCurrentTime();
--- a/dom/media/webaudio/test/test_mozaudiochannel.html
+++ b/dom/media/webaudio/test/test_mozaudiochannel.html
@@ -13,37 +13,33 @@
 
 function test_basic() {
   var ac = new AudioContext();
   ok(ac, "AudioContext created");
 
   // Default
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  // random wrong channel
-  ac.mozAudioChannelType = "foo";
+  // Unpermitted channels
+  ac = new AudioContext("content");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  // Unpermitted channels
-  ac.mozAudioChannelType = "content";
-  is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
-
-  ac.mozAudioChannelType = "notification";
+  ac = new AudioContext("notification");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac.mozAudioChannelType = "alarm";
+  ac = new AudioContext("alarm");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac.mozAudioChannelType = "telephony";
+  ac = new AudioContext("telephony");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac.mozAudioChannelType = "ringer";
+  ac = new AudioContext("ringer");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac.mozAudioChannelType = "publicnotification";
+  ac = new AudioContext("publicnotification");
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
   runTest();
 }
 
 function test_permission(aChannel) {
   var ac = new AudioContext();
   ok(ac, "AudioContext created");
@@ -51,17 +47,17 @@ function test_permission(aChannel) {
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
   var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream();
   is(channel, "normal", "AudioNodeStream is using the correct default audio channel.");
 
   SpecialPowers.pushPermissions(
     [{ "type": "audio-channel-" + aChannel, "allow": true, "context": document }],
     function() {
-      ac.mozAudioChannelType = aChannel;
+      var ac = new AudioContext(aChannel);
       is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'");
 
       var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream();
       is(channel, aChannel, "AudioNodeStream is using the correct new audio channel.");
 
       runTest();
     }
   );
@@ -142,13 +138,14 @@ function runTest() {
   }
 
   var test = tests.shift();
   test();
 }
 
 SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true ]]}, runTest);
 SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(5);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=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 "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
+#include "js/Debug.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Preferences.h"
@@ -319,31 +320,34 @@ Promise::CreateWrapper(ErrorResult& aRv)
 {
   AutoJSAPI jsapi;
   if (!jsapi.Init(mGlobal)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
   JSContext* cx = jsapi.cx();
 
-  JS::Rooted<JS::Value> ignored(cx);
-  if (!WrapNewBindingObject(cx, this, &ignored)) {
+  JS::Rooted<JS::Value> wrapper(cx);
+  if (!WrapNewBindingObject(cx, this, &wrapper)) {
     JS_ClearPendingException(cx);
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   dom::PreserveWrapper(this);
 
   // Now grab our allocation stack
   if (!CaptureStack(cx, mAllocationStack)) {
     JS_ClearPendingException(cx);
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
+
+  JS::RootedObject obj(cx, &wrapper.toObject());
+  JS::dbg::onNewPromise(cx, obj);
 }
 
 void
 Promise::MaybeResolve(JSContext* aCx,
                       JS::Handle<JS::Value> aValue)
 {
   MaybeResolveInternal(aCx, aValue);
 }
@@ -1108,29 +1112,29 @@ Promise::RejectInternal(JSContext* aCx,
                         JS::Handle<JS::Value> aValue)
 {
   mResolvePending = true;
 
   MaybeSettle(aValue, Rejected);
 }
 
 void
-Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
-                     PromiseState aState)
+Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
 {
-  // Promise.all() or Promise.race() implementations will repeatedly call
-  // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
-  // from asserting.
-  if (mState != Pending) {
-    return;
-  }
-
+  mSettlementTimestamp = TimeStamp::Now();
   SetResult(aValue);
   SetState(aState);
-  mSettlementTimestamp = TimeStamp::Now();
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSContext* cx = jsapi.cx();
+  JS::RootedObject wrapper(cx, GetWrapper());
+  MOZ_ASSERT(wrapper); // We preserved it
+  JSAutoCompartment ac(cx, wrapper);
+  JS::dbg::onPromiseSettled(cx, wrapper);
 
   // If the Promise was rejected, and there is no reject handler already setup,
   // watch for thread shutdown.
   if (aState == PromiseState::Rejected &&
       !mHadRejectCallback &&
       !NS_IsMainThread()) {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
@@ -1145,16 +1149,30 @@ Promise::MaybeSettle(JS::Handle<JS::Valu
       MaybeReportRejectedOnce();
     }
   }
 
   EnqueueCallbackTasks();
 }
 
 void
+Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
+                     PromiseState aState)
+{
+  // Promise.all() or Promise.race() implementations will repeatedly call
+  // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
+  // from asserting.
+  if (mState != Pending) {
+    return;
+  }
+
+  Settle(aValue, aState);
+}
+
+void
 Promise::EnqueueCallbackTasks()
 {
   nsTArray<nsRefPtr<PromiseCallback>> callbacks;
   callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
                                             : mRejectCallbacks);
   mResolveCallbacks.Clear();
   mRejectCallbacks.Clear();
 
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -220,18 +220,18 @@ private:
   }
 
   // This method enqueues promise's resolve/reject callbacks with promise's
   // result. It's executed when the resolver.resolve() or resolver.reject() is
   // called or when the promise already has a result and new callbacks are
   // appended by then(), catch() or done().
   void EnqueueCallbackTasks();
 
-  void MaybeSettle(JS::Handle<JS::Value> aValue,
-                   Promise::PromiseState aState);
+  void Settle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState);
+  void MaybeSettle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState);
 
   void AppendCallbacks(PromiseCallback* aResolveCallback,
                        PromiseCallback* aRejectCallback);
 
   // If we have been rejected and our mResult is a JS exception,
   // report it to the error console.
   // Use MaybeReportRejectedOnce() for actual calls.
   void MaybeReportRejected();
--- a/dom/promise/tests/chrome.ini
+++ b/dom/promise/tests/chrome.ini
@@ -1,3 +1,6 @@
 [DEFAULT]
 
 [test_dependentPromises.html]
+[test_on_new_promise.html]
+[test_on_promise_settled.html]
+[test_on_promise_settled_duplicates.html]
new file mode 100644
--- /dev/null
+++ b/dom/promise/tests/test_on_new_promise.html
@@ -0,0 +1,45 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1083210 - Sanity test for interaction between DOM promises and
+Debugger.prototype.onNewPromise.
+-->
+
+<html>
+<head>
+  <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript">
+  is(Object.prototype.toString.call(new Promise(function () {})),
+     "[object Promise]",
+     "We should have the native DOM promise implementation.");
+
+  var Cu = Components.utils;
+  Cu.import("resource://gre/modules/jsdebugger.jsm");
+  var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+  addDebuggerToGlobal(dbgGlobal);
+  var dbg = new dbgGlobal.Debugger(this);
+
+  var wrappedPromise;
+  dbg.onNewPromise = function (wp) { wrappedPromise = wp; };
+
+  var promise = new Promise(function () {});
+  debugger;
+  ok(wrappedPromise);
+  is(wrappedPromise.unsafeDereference(), promise);
+  </script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/promise/tests/test_on_promise_settled.html
@@ -0,0 +1,54 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1084065 - Sanity test for interaction between DOM promises and
+Debugger.prototype.onPromiseResolved.
+-->
+
+<html>
+<head>
+  <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  is(Object.prototype.toString.call(new Promise(function () {})),
+     "[object Promise]",
+     "We should have the native DOM promise implementation.");
+
+  var Cu = Components.utils;
+  Cu.import("resource://gre/modules/jsdebugger.jsm");
+  var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+  addDebuggerToGlobal(dbgGlobal);
+  var dbg = new dbgGlobal.Debugger(this);
+
+  var wrappedPromise;
+  dbg.onPromiseSettled = function (wp) { wrappedPromise = wp; };
+
+  var promise = Promise.resolve();
+  promise
+    .then(function () {
+      ok(wrappedPromise);
+      is(wrappedPromise.unsafeDereference(), promise);
+      dbg.onPromiseSettled = undefined;
+    })
+    .then(null, function (e) {
+      ok(false, "Got an unexpected error: " + e);
+    })
+    .then(SimpleTest.finish);
+  </script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/promise/tests/test_on_promise_settled_duplicates.html
@@ -0,0 +1,59 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1084065 - Test that Debugger.prototype.onPromiseResolved doesn't get dupes.
+-->
+
+<html>
+<head>
+  <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  is(Object.prototype.toString.call(new Promise(function () {})),
+     "[object Promise]",
+     "We should have the native DOM promise implementation.");
+
+  var Cu = Components.utils;
+  Cu.import("resource://gre/modules/jsdebugger.jsm");
+  var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+  addDebuggerToGlobal(dbgGlobal);
+  var dbg = new dbgGlobal.Debugger(this);
+
+  var seen = new Set();
+  dbg.onPromiseSettled = function (wp) {
+    is(seen.has(wp), false);
+    seen.add(wp);
+  };
+
+  var promise = new Promise(function (fulfill, reject) {
+    fulfill(1);
+    fulfill(2);
+    fulfill(3);
+  });
+
+  promise
+    .then(function () {
+      dbg.onPromiseSettled = undefined;
+    })
+    .then(null, function (e) {
+      ok(false, "Got an unexpected error: " + e);
+    })
+    .then(SimpleTest.finish);
+  </script>
+</pre>
+</body>
+</html>
+
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -73,18 +73,18 @@ interface AudioContext : EventTarget {
     [NewObject, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
 // Mozilla extensions
 partial interface AudioContext {
   // Read AudioChannel.webidl for more information about this attribute.
-  [Pref="media.useAudioChannelService", SetterThrows]
-  attribute AudioChannel mozAudioChannelType;
+  [Pref="media.useAudioChannelService"]
+  readonly attribute AudioChannel mozAudioChannelType;
 
   // These 2 events are dispatched when the AudioContext object is muted by
   // the AudioChannelService. It's call 'interrupt' because when this event is
   // dispatched on a HTMLMediaElement, the audio stream is paused.
   [Pref="media.useAudioChannelService"]
   attribute EventHandler onmozinterruptbegin;
 
   [Pref="media.useAudioChannelService"]
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -93,22 +93,22 @@ skip-if(Android||B2G) needs-focus == 462
 == readonly-editable.html readonly-editable-ref.html
 == dynamic-overflow-change.html dynamic-overflow-change-ref.html
 == 694880-1.html 694880-ref.html
 == 694880-2.html 694880-ref.html
 == 694880-3.html 694880-ref.html
 == 388980-1.html 388980-1-ref.html
 needs-focus == spellcheck-superscript-1.html spellcheck-superscript-1-ref.html
 skip-if(B2G) fails-if(Android) needs-focus != spellcheck-superscript-2.html spellcheck-superscript-2-ref.html # bug 783658
-needs-focus == 824080-1.html 824080-1-ref.html
+needs-focus pref(selectioncaret.enabled,false) == 824080-1.html 824080-1-ref.html
 needs-focus == 824080-2.html 824080-2-ref.html
-needs-focus test-pref(selectioncaret.enabled,false) == 824080-3.html 824080-3-ref.html
+needs-focus pref(selectioncaret.enabled,false) == 824080-3.html 824080-3-ref.html
 needs-focus != 824080-2.html 824080-3.html
-needs-focus == 824080-4.html 824080-4-ref.html
-needs-focus test-pref(selectioncaret.enabled,false) == 824080-5.html 824080-5-ref.html
+needs-focus pref(selectioncaret.enabled,false) == 824080-4.html 824080-4-ref.html
+needs-focus pref(selectioncaret.enabled,false) == 824080-5.html 824080-5-ref.html
 needs-focus != 824080-4.html 824080-5.html
 needs-focus == 824080-6.html 824080-6-ref.html
 needs-focus pref(selectioncaret.enabled,false) == 824080-7.html 824080-7-ref.html
 needs-focus != 824080-6.html 824080-7.html
 # Bug 674927: copy spellcheck-textarea tests to contenteditable
 == spellcheck-contenteditable-attr.html spellcheck-contenteditable-nofocus-ref.html
 fails-if(Android||B2G) needs-focus != spellcheck-contenteditable-attr.html spellcheck-contenteditable-ref.html # B2G no spellcheck underline
 needs-focus == spellcheck-contenteditable-focused.html spellcheck-contenteditable-ref.html
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -678,21 +678,22 @@ GLContext::InitWithPrefix(const char *pr
 
             if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) {
                 // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer)
                 // multisampling hardcodes blending with the default blendfunc, which breaks WebGL.
                 MarkUnsupported(GLFeature::framebuffer_multisample);
             }
 
 #ifdef XP_MACOSX
-            // The Mac Nvidia driver, for versions up to and including 10.8, don't seem
-            // to properly support this.  See 814839
+            // The Mac Nvidia driver, for versions up to and including 10.8,
+            // don't seem to properly support this.  See 814839
             // this has been fixed in Mac OS X 10.9. See 907946
+            // and it also works in 10.8.3 and higher.  See 1094338.
             if (Vendor() == gl::GLVendor::NVIDIA &&
-                !nsCocoaFeatures::OnMavericksOrLater())
+                !nsCocoaFeatures::IsAtLeastVersion(10,8,3))
             {
                 MarkUnsupported(GLFeature::depth_texture);
             }
 #endif
         }
 
         NS_ASSERTION(!IsExtensionSupported(GLContext::ARB_pixel_buffer_object) ||
                      (mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -389,20 +389,20 @@ struct ParamTraits<nsIntPoint>
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     return (ReadParam(msg, iter, &result->x) &&
             ReadParam(msg, iter, &result->y));
   }
 };
 
-template<>
-struct ParamTraits<mozilla::gfx::IntSize>
+template<typename T>
+struct ParamTraits<mozilla::gfx::IntSizeTyped<T> >
 {
-  typedef mozilla::gfx::IntSize paramType;
+  typedef mozilla::gfx::IntSizeTyped<T> paramType;
 
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.width);
     WriteParam(msg, param.height);
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
@@ -751,16 +751,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mIsRoot);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
     WriteParam(aMsg, aParam.mScrollGeneration);
     WriteParam(aMsg, aParam.mExtraResolution);
     WriteParam(aMsg, aParam.mBackgroundColor);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
+    WriteParam(aMsg, aParam.GetLineScrollAmount());
     WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
@@ -792,16 +793,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mIsRoot) &&
             ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
             ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
             ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
             ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
             ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
             ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
+            ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
             ReadContentDescription(aMsg, aIter, aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -60,16 +60,17 @@ public:
     , mSmoothScrollOffset(0, 0)
     , mRootCompositionSize(0, 0)
     , mDisplayPortMargins(0, 0, 0, 0)
     , mUseDisplayPortMargins(false)
     , mPresShellId(-1)
     , mViewport(0, 0, 0, 0)
     , mExtraResolution(1)
     , mBackgroundColor(0, 0, 0, 0)
+    , mLineScrollAmount(0, 0)
   {
   }
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
@@ -90,17 +91,18 @@ public:
            mScrollId == aOther.mScrollId &&
            mScrollParentId == aOther.mScrollParentId &&
            mScrollOffset == aOther.mScrollOffset &&
            mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
            mExtraResolution == aOther.mExtraResolution &&
            mBackgroundColor == aOther.mBackgroundColor &&
-           mDoSmoothScroll == aOther.mDoSmoothScroll;
+           mDoSmoothScroll == aOther.mDoSmoothScroll &&
+           mLineScrollAmount == aOther.mLineScrollAmount;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
   {
@@ -509,16 +511,26 @@ public:
     return mMayHaveTouchListeners;
   }
 
   void SetMayHaveTouchListeners(bool aMayHaveTouchListeners)
   {
     mMayHaveTouchListeners = aMayHaveTouchListeners;
   }
 
+  const LayoutDeviceIntSize& GetLineScrollAmount() const
+  {
+    return mLineScrollAmount;
+  }
+
+  void SetLineScrollAmount(const LayoutDeviceIntSize& size)
+  {
+    mLineScrollAmount = size;
+  }
+
 private:
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
 
   // Whether or not this frame may have a touch listeners.
   bool mMayHaveTouchListeners;
 
   // Whether or not this frame may have a touch caret.
@@ -600,16 +612,19 @@ private:
 
   // The background color to use when overscrolling.
   gfxRGBA mBackgroundColor;
 
   // A description of the content element corresponding to this frame.
   // This is empty unless this is a scrollable layer and the
   // apz.printtree pref is turned on.
   nsCString mContentDescription;
+
+  // The value of GetLineScrollAmount(), for scroll frames.
+  LayoutDeviceIntSize mLineScrollAmount;
 };
 
 /**
  * This class allows us to uniquely identify a scrollable layer. The
  * mLayersId identifies the layer tree (corresponding to a child process
  * and/or tab) that the scrollable layer belongs to. The mPresShellId
  * is a temporal identifier (corresponding to the document loaded that
  * contains the scrollable layer, which may change over time). The
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -606,16 +606,39 @@ APZCTreeManager::ReceiveInputEvent(Input
   nsEventStatus result = nsEventStatus_eIgnore;
   Matrix4x4 transformToApzc;
   HitTestResult hitResult = NoApzcHit;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
       result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
+    } case SCROLLWHEEL_INPUT: {
+      ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
+      nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
+                                                            &hitResult);
+      if (apzc) {
+        MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion);
+
+        transformToApzc = GetScreenToApzcTransform(apzc);
+        wheelInput.mLocalOrigin =
+          TransformTo<ParentLayerPixel>(transformToApzc, wheelInput.mOrigin);
+
+        result = mInputQueue->ReceiveInputEvent(
+          apzc,
+          /* aTargetConfirmed = */ hitResult,
+          wheelInput, aOutInputBlockId);
+
+        // Update the out-parameters so they are what the caller expects.
+        apzc->GetGuid(aOutTargetGuid);
+        Matrix4x4 transformToGecko = transformToApzc * GetApzcToGeckoTransform(apzc);
+        wheelInput.mOrigin =
+          TransformTo<ScreenPixel>(transformToGecko, wheelInput.mLocalOrigin);
+      }
+      break;
     } case PANGESTURE_INPUT: {
       PanGestureInput& panInput = aEvent.AsPanGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
                                                             &hitResult);
       if (apzc) {
         MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion);
         transformToApzc = GetScreenToApzcTransform(apzc);
         panInput.mLocalPanStartPoint = TransformTo<ParentLayerPixel>(
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -528,31 +528,19 @@ public:
   }
 
   /**
    * Advances a fling by an interpolated amount based on the passed in |aDelta|.
    * This should be called whenever sampling the content transform for this
    * frame. Returns true if the fling animation should be advanced by one frame,
    * or false if there is no fling or the fling has ended.
    */
-  virtual bool Sample(FrameMetrics& aFrameMetrics,
-                      const TimeDuration& aDelta) MOZ_OVERRIDE
+  virtual bool DoSample(FrameMetrics& aFrameMetrics,
+                        const TimeDuration& aDelta) MOZ_OVERRIDE
   {
-    // If the fling is handed off to our APZC from a child, on the first call to
-    // Sample() aDelta might be negative because it's computed as the sample time
-    // from SampleContentTransformForFrame() minus our APZC's mLastSampleTime
-    // which is the time the child handed off the fling from its call to
-    // SampleContentTransformForFrame() with the same sample time. If we allow
-    // the negative aDelta to be processed, it will yield a displacement in the
-    // direction opposite to the fling, which can cause us to overscroll and
-    // hand off the fling to _our_ parent, which effectively kills the fling.
-    if (aDelta.ToMilliseconds() <= 0) {
-      return true;
-    }
-
     float friction = gfxPrefs::APZFlingFriction();
     float threshold = gfxPrefs::APZFlingStoppedThreshold();
 
     bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
          shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
     // If we shouldn't continue the fling, let's just stop and repaint.
     if (!shouldContinueFlingX && !shouldContinueFlingY) {
       APZC_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
@@ -650,18 +638,18 @@ public:
                 CSSPoint aEndOffset, CSSToParentLayerScale aEndZoom)
     : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
     , mStartOffset(aStartOffset)
     , mStartZoom(aStartZoom)
     , mEndOffset(aEndOffset)
     , mEndZoom(aEndZoom)
   {}
 
-  virtual bool Sample(FrameMetrics& aFrameMetrics,
-                      const TimeDuration& aDelta) MOZ_OVERRIDE
+  virtual bool DoSample(FrameMetrics& aFrameMetrics,
+                        const TimeDuration& aDelta) MOZ_OVERRIDE
   {
     mDuration += aDelta;
     double animPosition = mDuration / mTotalDuration;
 
     if (animPosition >= 1.0) {
       aFrameMetrics.SetZoom(mEndZoom);
       aFrameMetrics.SetScrollOffset(mEndOffset);
       return false;
@@ -707,18 +695,18 @@ class OverscrollAnimation: public AsyncP
 public:
   explicit OverscrollAnimation(AsyncPanZoomController& aApzc, const ParentLayerPoint& aVelocity)
     : mApzc(aApzc)
   {
     mApzc.mX.SetVelocity(aVelocity.x);
     mApzc.mY.SetVelocity(aVelocity.y);
   }
 
-  virtual bool Sample(FrameMetrics& aFrameMetrics,
-                      const TimeDuration& aDelta) MOZ_OVERRIDE
+  virtual bool DoSample(FrameMetrics& aFrameMetrics,
+                        const TimeDuration& aDelta) MOZ_OVERRIDE
   {
     // Can't inline these variables due to short-circuit evaluation.
     bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
     bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
     return continueX || continueY;
   }
 private:
   AsyncPanZoomController& mApzc;
@@ -742,22 +730,17 @@ public:
   }
 
   /**
    * Advances a smooth scroll simulation based on the time passed in |aDelta|.
    * This should be called whenever sampling the content transform for this
    * frame. Returns true if the smooth scroll should be advanced by one frame,
    * or false if the smooth scroll has ended.
    */
-  bool Sample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
-
-    if (aDelta.ToMilliseconds() <= 0) {
-      return true;
-    }
-
+  bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
     if (mXAxisModel.IsFinished() && mYAxisModel.IsFinished()) {
       return false;
     }
 
     mXAxisModel.Simulate(aDelta);
     mYAxisModel.Simulate(aDelta);
 
     CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
@@ -1082,16 +1065,21 @@ nsEventStatus AsyncPanZoomController::Ha
       case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
       case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
       case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
       case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
       default: NS_WARNING("Unhandled pan gesture"); break;
     }
     break;
   }
+  case SCROLLWHEEL_INPUT: {
+    const ScrollWheelInput& scrollInput = aEvent.AsScrollWheelInput();
+    rv = OnScrollWheel(scrollInput);
+    break;
+  }
   default: return HandleGestureEvent(aEvent);
   }
 
   return rv;
 }
 
 nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
 {
@@ -1465,16 +1453,82 @@ AsyncPanZoomController::ConvertToGecko(c
       ReentrantMonitorAutoEnter lock(mMonitor);
       *aOut = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
     }
     return true;
   }
   return false;
 }
 
+nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
+{
+  double deltaX = aEvent.mDeltaX;
+  double deltaY = aEvent.mDeltaY;
+  switch (aEvent.mDeltaType) {
+    case ScrollWheelInput::SCROLLDELTA_LINE: {
+      LayoutDeviceIntSize scrollAmount = mFrameMetrics.GetLineScrollAmount();
+      deltaX *= scrollAmount.width;
+      deltaY *= scrollAmount.height;
+      break;
+    }
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
+      return nsEventStatus_eConsumeNoDefault;
+  }
+
+  switch (aEvent.mScrollMode) {
+    case ScrollWheelInput::SCROLLMODE_INSTANT: {
+      // Decompose into pan events for simplicity.
+      PanGestureInput start(PanGestureInput::PANGESTURE_START, aEvent.mTime, aEvent.mTimeStamp,
+                            aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
+      start.mLocalPanStartPoint = aEvent.mLocalOrigin;
+      OnPanBegin(start);
+
+      // Pan gestures use natural directions which are inverted from scroll
+      // wheel and touchpad scroll gestures, so we invert x/y here. Since the
+      // zoom includes any device : css pixel zoom, we convert to CSS pixels
+      // before applying the zoom.
+      LayoutDevicePoint devicePixelDelta(-deltaX, -deltaY);
+      ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.mDevPixelsPerCSSPixel) *
+                               mFrameMetrics.GetZoom();
+
+      PanGestureInput move(PanGestureInput::PANGESTURE_PAN, aEvent.mTime, aEvent.mTimeStamp,
+                           aEvent.mOrigin,
+                           ToScreenCoordinates(delta, aEvent.mLocalOrigin),
+                           aEvent.modifiers);
+      move.mLocalPanStartPoint = aEvent.mLocalOrigin;
+      move.mLocalPanDisplacement = delta;
+      OnPan(move, false);
+
+      PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp,
+                            aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
+      end.mLocalPanStartPoint = aEvent.mLocalOrigin;
+      OnPanEnd(start);
+      break;
+    }
+
+    case ScrollWheelInput::SCROLLMODE_SMOOTH: {
+      CSSPoint delta = LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.mDevPixelsPerCSSPixel;
+
+      // If we're already in a smooth scroll animation, don't cancel it. This
+      // lets us preserve the existing scrolling velocity.
+      if (mState != SMOOTH_SCROLL) {
+        CancelAnimation();
+        mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetScrollOffset() + delta);
+      } else {
+        mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta);
+      }
+      StartSmoothScroll();
+      break;
+    }
+  }
+
+  return nsEventStatus_eConsumeNoDefault;
+}
+
 nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
 
   mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
   mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
   if (mPanGestureState) {
     mPanGestureState->GetOverscrollHandoffChain()->CancelAnimations();
   } else {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -410,16 +410,21 @@ protected:
   nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
   nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
   nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
   nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
   nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
   nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
 
   /**
+   * Helper methods for handling scroll wheel events.
+   */
+  nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent);
+
+  /**
    * Helper methods for long press gestures.
    */
   nsEventStatus OnLongPress(const TapGestureInput& aEvent);
   nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
 
   /**
    * Helper method for single tap gestures.
    */
@@ -1120,18 +1125,30 @@ class AsyncPanZoomAnimation {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
 
 public:
   explicit AsyncPanZoomAnimation(const TimeDuration& aRepaintInterval =
                                  TimeDuration::Forever())
     : mRepaintInterval(aRepaintInterval)
   { }
 
-  virtual bool Sample(FrameMetrics& aFrameMetrics,
-                      const TimeDuration& aDelta) = 0;
+  virtual bool DoSample(FrameMetrics& aFrameMetrics,
+                        const TimeDuration& aDelta) = 0;
+
+  bool Sample(FrameMetrics& aFrameMetrics,
+              const TimeDuration& aDelta) {
+    // In some situations, particularly when handoff is involved, it's possible
+    // for |aDelta| to be negative on the first call to sample. Ignore such a
+    // sample here, to avoid each derived class having to deal with this case.
+    if (aDelta.ToMilliseconds() <= 0) {
+      return true;
+    }
+
+    return DoSample(aFrameMetrics, aDelta);
+  }
 
   /**
    * Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks|
    * for more information.
    * Clears |mDeferredTasks|.
    */
   Vector<Task*> TakeDeferredTasks() {
     Vector<Task*> result;
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -524,18 +524,16 @@ ClientLayerManager::StopFrameTimeRecordi
   if (renderer) {
     renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
   }
 }
 
 void
 ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
 {
-  gfxPlatform::GetPlatform()->FenceContentDrawing();
-
   mPhase = PHASE_FORWARD;
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
   TimeStamp transactionStart;
   if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
     transactionStart = mTransactionIdAllocator->GetTransactionStart();
   } else {
     transactionStart = mTransactionStart;
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -824,18 +824,16 @@ CompositorD3D11::BeginFrame(const nsIntR
   NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
   if (::IsIconic(mHwnd)) {
     *aRenderBoundsOut = Rect();
     return;
   }
 
   UpdateRenderTarget();
 
-  gfxPlatform::GetPlatform()->WaitContentDrawing();
-
   // Failed to create a render target or the view.
   if (!mDefaultRT || !mDefaultRT->mRTView ||
       mSize.width == 0 || mSize.height == 0) {
     *aRenderBoundsOut = Rect();
     return;
   }
 
   mContext->IASetInputLayout(mAttachments->mInputLayout);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -352,29 +352,29 @@ TextureClientD3D11::AllocateForSurface(g
   ID3D11Device* d3d11device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
 
   if (gfxPrefs::Direct2DUse1_1() && d3d11device) {
 
     CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
                                   aSize.width, aSize.height, 1, 1,
                                   D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 
-    newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+    newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
 
     hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
   } else
 #endif
   {
     ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
     CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
       aSize.width, aSize.height, 1, 1,
       D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
 
-    newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED;
+    newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
 
     hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture10));
   }
 
   if (FAILED(hr)) {
     gfx::gfxCriticalError() << "[D3D11] CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
     return false;
   }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -242,18 +242,16 @@ public:
      * rendering to offscreen surfaces on this platform, making it safe to
      * render content to data surfaces. This is generally false on platforms
      * which use different backends for each type of DrawTarget.
      */
     virtual bool CanRenderContentToDataSurface() const {
       return false;
     }
 
-    virtual void FenceContentDrawing() {}
-    virtual void WaitContentDrawing() {}
     /**
      * Returns true if we should use Azure to render content with aTarget. For
      * example, it is possible that we are using Direct2D for rendering and thus
      * using Azure. But we want to render to a CairoDrawTarget, in which case
      * SupportsAzureContent will return true but SupportsAzureContentForDrawTarget
      * will return false.
      */
     bool SupportsAzureContentForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1429,116 +1429,16 @@ gfxWindowsPlatform::GetD3D9DeviceManager
       NS_WARNING("Could not initialise device manager");
       mDeviceManager = nullptr;
     }
   }
 
   return mDeviceManager;
 }
 
-ID3D11Texture2D*
-gfxWindowsPlatform::GetD3D11Texture()
-{
-  if (mD3D11Texture) {
-    return mD3D11Texture;
-  }
-
-  MOZ_ASSERT(mD3D10Texture || mD3D11ContentTexture);
-
-  RefPtr<IDXGIResource> resource;
-  if (mD3D10Texture) {
-    mD3D10Texture->QueryInterface((IDXGIResource**)byRef(resource));
-  } else {
-    mD3D11ContentTexture->QueryInterface((IDXGIResource**)byRef(resource));
-  }
-  HANDLE sharedHandle;
-  HRESULT hr = resource->GetSharedHandle(&sharedHandle);
-
-  hr = GetD3D11Device()->OpenSharedResource(sharedHandle,
-    __uuidof(ID3D11Texture2D),
-    (void**)(ID3D11Texture2D**)byRef(mD3D11Texture));
-
-  return mD3D11Texture;
-}
-
-ID3D11Texture2D*
-gfxWindowsPlatform::GetD3D11ContentTexture()
-{
-  if (mD3D11ContentTexture) {
-    return mD3D11ContentTexture;
-  }
-  ID3D11Device* device = GetD3D11ContentDevice();
-
-  CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
-    1, 1, 1, 1,
-    D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
-
-  newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
-
-  HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mD3D11ContentTexture));
-  return mD3D11ContentTexture;
-}
-
-ID3D10Texture2D*
-gfxWindowsPlatform::GetD3D10Texture()
-{
-  if (mD3D10Texture) {
-    return mD3D10Texture;
-  }
-  ID3D10Device* device = GetD3D10Device();
-
-  CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
-    1, 1, 1, 1,
-    D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
-
-  newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
-
-  HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mD3D10Texture));
-  return mD3D10Texture;
-}
-
-void
-gfxWindowsPlatform::FenceContentDrawing()
-{
-#ifdef USE_D2D1_1
-  if (gfxPrefs::Direct2DUse1_1() && GetD3D11ContentDevice()) {
-    ID3D11Texture2D* tex = GetD3D11ContentTexture();
-    RefPtr<IDXGIKeyedMutex> mutex;
-    tex->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
-    mutex->AcquireSync(0, INFINITE);
-    RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForD3D11Texture(tex, SurfaceFormat::B8G8R8A8);
-    dt->ClearRect(Rect(0, 0, 1, 1));
-    dt->Flush();
-    dt = nullptr;
-    mutex->ReleaseSync(0);
-  } else
-#endif
-  if (GetD3D10Device()) {
-    ID3D10Texture2D* tex = GetD3D10Texture();
-    RefPtr<IDXGIKeyedMutex> mutex;
-    tex->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
-    mutex->AcquireSync(0, INFINITE);
-    RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForD3D10Texture(tex, SurfaceFormat::B8G8R8A8);
-    dt->ClearRect(Rect(0, 0, 1, 1));
-    dt->Flush();
-    dt = nullptr;
-    mutex->ReleaseSync(0);
-  }
-}
-
-void
-gfxWindowsPlatform::WaitContentDrawing()
-{
-  ID3D11Texture2D *sentinelTexture = GetD3D11Texture();
-  RefPtr<IDXGIKeyedMutex> mutex;
-  sentinelTexture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
-  mutex->AcquireSync(0, INFINITE);
-  mutex->ReleaseSync(0);
-}
-
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11Device()
 {
   if (mD3D11DeviceInitialized) {
     return mD3D11Device;
   }
 
   InitD3D11Devices();
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -50,17 +50,16 @@ class DrawTarget;
 namespace layers {
 class DeviceManagerD3D9;
 class ReadbackManagerD3D11;
 }
 }
 struct IDirect3DDevice9;
 struct ID3D11Device;
 struct IDXGIAdapter1;
-struct ID3D11Texture2D;
 
 class nsIMemoryReporter;
 
 /**
  * Utility to get a Windows HDC from a Moz2D DrawTarget.  If the DrawTarget is
  * not backed by a HDC this will get the HDC for the screen device context
  * instead.
  */
@@ -251,23 +250,16 @@ public:
     IDirect3DDevice9* GetD3D9Device();
 #ifdef CAIRO_HAS_D2D_SURFACE
     cairo_device_t *GetD2DDevice() { return mD2DDevice; }
     ID3D10Device1 *GetD3D10Device() { return mD2DDevice ? cairo_d2d_device_get_device(mD2DDevice) : nullptr; }
 #endif
     ID3D11Device *GetD3D11Device();
     ID3D11Device *GetD3D11ContentDevice();
 
-    ID3D10Texture2D* GetD3D10Texture();
-    ID3D11Texture2D* GetD3D11Texture();
-    ID3D11Texture2D* GetD3D11ContentTexture();
-
-    virtual void FenceContentDrawing();
-    virtual void WaitContentDrawing();
-
     mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
 
     static bool IsOptimus();
 
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
@@ -292,21 +284,16 @@ private:
 #endif
     mozilla::RefPtr<IDXGIAdapter1> mAdapter;
     nsRefPtr<mozilla::layers::DeviceManagerD3D9> mDeviceManager;
     mozilla::RefPtr<ID3D11Device> mD3D11Device;
     mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
     bool mD3D11DeviceInitialized;
     mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
 
-    mozilla::RefPtr<ID3D10Texture2D> mD3D10Texture;
-    mozilla::RefPtr<ID3D11Texture2D> mD3D11Texture;
-    mozilla::RefPtr<ID3D11Texture2D> mD3D11ContentTexture;
-
-
     virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
 
     // TODO: unify this with mPrefFonts (NB: holds families, not fonts) in gfxPlatformFontList
     nsDataHashtable<nsCStringHashKey, nsTArray<nsRefPtr<gfxFontEntry> > > mPrefFonts;
 };
 
 bool DoesD3D11DeviceWork(ID3D11Device *device);
 
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -7,16 +7,17 @@
 
 // This is a Cross-Platform BMP Decoder, which should work everywhere, including
 // Big-Endian machines like the PowerPC.
 
 #include <stdlib.h>
 
 #include "ImageLogging.h"
 #include "mozilla/Endian.h"
+#include "mozilla/Likely.h"
 #include "nsBMPDecoder.h"
 
 #include "nsIInputStream.h"
 #include "RasterImage.h"
 #include <algorithm>
 
 namespace mozilla {
 namespace image {
@@ -653,20 +654,20 @@ nsBMPDecoder::WriteInternal(const char* 
                                       (mCurLine - 1);
                     uint32_t heightDifference = GetHeight() -
                                                 mCurLine + 1;
                     uint32_t pixelCount = GetWidth() *
                                           heightDifference;
 
                     memset(start, 0, pixelCount * sizeof(uint32_t));
 
+                    PostHasTransparency();
                     mHaveAlphaData = true;
                   }
-                  SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ?
-                           p[3] : 0xFF);
+                  SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ?  p[3] : 0xFF);
                 } else {
                   SetPixel(d, p[2], p[1], p[0]);
                 }
                 p += 4;
                 --lpos;
               }
               break;
             default:
@@ -784,31 +785,37 @@ nsBMPDecoder::WriteInternal(const char* 
 
           case eRLEStateNeedXDelta:
             // Handle the XDelta and proceed to get Y Delta
             byte = *aBuffer++;
             aCount--;
             mCurPos += byte;
             // Delta encoding makes it possible to skip pixels
             // making the image transparent.
+            if (MOZ_UNLIKELY(!mHaveAlphaData)) {
+                PostHasTransparency();
+            }
             mUseAlphaData = mHaveAlphaData = true;
             if (mCurPos > mBIH.width) {
                 mCurPos = mBIH.width;
             }
 
             mState = eRLEStateNeedYDelta;
             continue;
 
           case eRLEStateNeedYDelta:
             // Get the Y Delta and then "handle" the move
             byte = *aBuffer++;
             aCount--;
             mState = eRLEStateInitial;
             // Delta encoding makes it possible to skip pixels
             // making the image transparent.
+            if (MOZ_UNLIKELY(!mHaveAlphaData)) {
+                PostHasTransparency();
+            }
             mUseAlphaData = mHaveAlphaData = true;
             mCurLine -= std::min<int32_t>(byte, mCurLine);
             break;
 
           case eRLEStateAbsoluteMode: // Absolute Mode
           case eRLEStateAbsoluteModePadded:
             if (mStateData) {
               // In absolute mode, the second byte (mStateData)
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -159,41 +159,47 @@ nsGIFDecoder2::BeginGIF()
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
 //******************************************************************************
 void
 nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
+  MOZ_ASSERT(HasSize());
+
   gfx::SurfaceFormat format;
   if (mGIFStruct.is_transparent) {
     format = gfx::SurfaceFormat::B8G8R8A8;
+    PostHasTransparency();
   } else {
     format = gfx::SurfaceFormat::B8G8R8X8;
   }
 
-  MOZ_ASSERT(HasSize());
-
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
                  mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
                  format, aDepth);
   } else {
     nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
 
     // Our first full frame is automatically created by the image decoding
     // infrastructure. Just use it as long as it matches up.
     if (!currentFrame->GetRect().IsEqualEdges(nsIntRect(mGIFStruct.x_offset,
                                                         mGIFStruct.y_offset,
                                                         mGIFStruct.width,
                                                         mGIFStruct.height))) {
+
+      // We need padding on the first frame, which means that we don't draw into
+      // part of the image at all. Report that as transparency.
+      PostHasTransparency();
+
       // Regardless of depth of input, image is decoded into 24bit RGB
       NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
                    mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
                    format);
     } else {
       // Our preallocated frame matches up, with the possible exception
       // of alpha.
       if (format == gfx::SurfaceFormat::B8G8R8X8) {
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -78,17 +78,18 @@ nsICODecoder::FinishInternal()
 {
   // We shouldn't be called in error cases
   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!");
 
   // Finish the internally used decoder as well
   if (mContainedDecoder) {
     mContainedDecoder->FinishSharedDecoder();
     mDecodeDone = mContainedDecoder->GetDecodeDone();
-    mProgress |= mContainedDecoder->GetProgress();
+    mProgress |= mContainedDecoder->TakeProgress();
+    mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
   }
 }
 
 // Returns a buffer filled with the bitmap file header in little endian:
 // Signature 2 bytes 'BM'
 // FileSize      4 bytes File size in bytes
 // reserved      4 bytes unused (=0)
 // DataOffset    4 bytes File offset to Raster Data
@@ -537,16 +538,18 @@ nsICODecoder::WriteInternal(const char* 
 
         // Ensure memory has been allocated before decoding.
         NS_ABORT_IF_FALSE(mRow, "mRow is null");
         if (!mRow) {
           PostDataError();
           return;
         }
 
+        uint8_t sawTransparency = 0;
+
         while (mCurLine > 0 && aCount > 0) {
           uint32_t toCopy = std::min(rowSize - mRowBytes, aCount);
           if (toCopy) {
             memcpy(mRow + mRowBytes, aBuffer, toCopy);
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
           }
@@ -562,37 +565,45 @@ nsICODecoder::WriteInternal(const char* 
               return;
             }
             uint32_t* decoded = imageData + mCurLine * GetRealWidth();
             uint32_t* decoded_end = decoded + GetRealWidth();
             uint8_t* p = mRow;
             uint8_t* p_end = mRow + rowSize;
             while (p < p_end) {
               uint8_t idx = *p++;
+              sawTransparency |= idx;
               for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
                 // Clear pixel completely for transparency.
                 if (idx & bit) {
                   *decoded = 0;
                 }
                 decoded++;
               }
             }
           }
         }
+
+        // If any bits are set in sawTransparency, then we know at least one
+        // pixel was transparent.
+        if (sawTransparency) {
+            PostHasTransparency();
+        }
       }
     }
   }
 }
 
 bool
 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount,
                                       DecodeStrategy aStrategy)
 {
   mContainedDecoder->Write(aBuffer, aCount, aStrategy);
-  mProgress |= mContainedDecoder->GetProgress();
+  mProgress |= mContainedDecoder->TakeProgress();
+  mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
   if (mContainedDecoder->HasDataError()) {
     mDataError = mContainedDecoder->HasDataError();
   }
   if (mContainedDecoder->HasDecoderError()) {
     PostDecoderError(mContainedDecoder->GetDecoderError());
   }
   return !HasError();
 }
@@ -627,17 +638,18 @@ nsICODecoder::NeedsNewFrame() const
 }
 
 nsresult
 nsICODecoder::AllocateFrame()
 {
   if (mContainedDecoder) {
     nsresult rv = mContainedDecoder->AllocateFrame();
     mCurrentFrame = mContainedDecoder->GetCurrentFrame();
-    mProgress |= mContainedDecoder->GetProgress();
+    mProgress |= mContainedDecoder->TakeProgress();
+    mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
     return rv;
   }
 
   return Decoder::AllocateFrame();
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -140,19 +140,24 @@ nsPNGDecoder::~nsPNGDecoder()
   }
 }
 
 // CreateFrame() is used for both simple and animated images
 void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                                int32_t width, int32_t height,
                                gfx::SurfaceFormat format)
 {
+  MOZ_ASSERT(HasSize());
+
+  if (format == gfx::SurfaceFormat::B8G8R8A8) {
+    PostHasTransparency();
+  }
+
   // Our first full frame is automatically created by the image decoding
   // infrastructure. Just use it as long as it matches up.
-  MOZ_ASSERT(HasSize());
   nsIntRect neededRect(x_offset, y_offset, width, height);
   nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
   if (mNumFrames != 0 || !currentFrame->GetRect().IsEqualEdges(neededRect)) {
     NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
   } else if (mNumFrames == 0) {
     // Our preallocated frame matches up, with the possible exception of alpha.
     if (format == gfx::SurfaceFormat::B8G8R8X8) {
       currentFrame->SetHasNoAlpha();
--- a/image/public/imgINotificationObserver.idl
+++ b/image/public/imgINotificationObserver.idl
@@ -9,22 +9,49 @@
 interface imgIRequest;
 
 %{C++
 #include "nsRect.h"
 %}
 
 [ptr] native nsIntRect(nsIntRect);
 
-[scriptable, builtinclass, uuid(ac65c702-7771-4f6d-b18b-1c7d806ce3c1)]
+[scriptable, builtinclass, uuid(03da5641-a333-454a-a859-036d0bb683b7)]
 interface imgINotificationObserver : nsISupports
 {
+  // GetWidth() and GetHeight() can now be used to retrieve the size of the
+  // image.
   const long SIZE_AVAILABLE = 1;
+
+  // A region of the image (indicated by the |aRect| argument to |notify|) has
+  // changed, and needs to be redrawn. This is triggered both for incremental
+  // rendering as the image gets decoded and for changes due to animation.
   const long FRAME_UPDATE = 2;
+
+  // The first frame of the image is now decoded and ready to draw.
   const long FRAME_COMPLETE = 3;
+
+  // The entire image has been loaded. That doesn't mean that it has been
+  // decoded, but it does mean that imgIContainer::Draw is guaranteed to succeed
+  // (modulo decode errors, at least) if you specify FLAG_SYNC_DECODE.
   const long LOAD_COMPLETE = 4;
+
+  // The entire image has been decoded.
   const long DECODE_COMPLETE = 5;
+
+  // The decoded version of the image has been discarded. Content should never
+  // change as a result of this notification - discarding is an implementation
+  // detail. This notification should normally only be observed by tests.
   const long DISCARD = 6;
+
+  // The image was drawn without being locked. This notification is part of the
+  // image locking mechanism that prevents visible images from being discarded;
+  // generally only image locking code needs to observe it.
   const long UNLOCKED_DRAW = 7;
+
+  // The image is animated.
   const long IS_ANIMATED = 8;
 
+  // The image is transparent.
+  const long HAS_TRANSPARENCY = 9;
+
   [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect);
 };
--- a/image/public/imgIScriptedNotificationObserver.idl
+++ b/image/public/imgIScriptedNotificationObserver.idl
@@ -13,9 +13,10 @@ interface imgIScriptedNotificationObserv
 {
   void sizeAvailable(in imgIRequest aRequest);
   void frameUpdate(in imgIRequest aRequest);
   void frameComplete(in imgIRequest aRequest);
   void loadComplete(in imgIRequest aRequest);
   void decodeComplete(in imgIRequest aRequest);
   void discard(in imgIRequest aRequest);
   void isAnimated(in imgIRequest aRequest);
+  void hasTransparency(in imgIRequest aRequest);
 };
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -30,16 +30,20 @@ Decoder::Decoder(RasterImage &aImage)
   , mInitialized(false)
   , mSizeDecode(false)
   , mInFrame(false)
   , mIsAnimated(false)
 { }
 
 Decoder::~Decoder()
 {
+  MOZ_ASSERT(mProgress == NoProgress,
+             "Destroying Decoder without taking all its progress changes");
+  MOZ_ASSERT(mInvalidRect.IsEmpty(),
+             "Destroying Decoder without taking all its invalidations");
   mInitialized = false;
 }
 
 /*
  * Common implementation of the decoder interface.
  */
 
 void
@@ -172,17 +176,17 @@ Decoder::Finish(RasterImage::eShutdownIn
     // completed.
     if (usable) {
       if (mInFrame) {
         PostFrameStop();
       }
       PostDecodeDone();
     } else {
       if (!IsSizeDecode()) {
-        mProgress |= FLAG_DECODE_STOPPED | FLAG_ONLOAD_UNBLOCKED;
+        mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
       }
       mProgress |= FLAG_HAS_ERROR;
     }
   }
 
   // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
   mImageMetadata.SetOnImage(&mImage);
 
@@ -276,17 +280,23 @@ Decoder::PostSize(int32_t aWidth,
   // Validate
   NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
   NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
 
   // Tell the image
   mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
 
   // Record this notification.
-  mProgress |= FLAG_HAS_SIZE;
+  mProgress |= FLAG_SIZE_AVAILABLE;
+}
+
+void
+Decoder::PostHasTransparency()
+{
+  mProgress |= FLAG_HAS_TRANSPARENCY;
 }
 
 void
 Decoder::PostFrameStart()
 {
   // We shouldn't already be mid-frame
   NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
 
@@ -325,17 +335,17 @@ Decoder::PostFrameStop(FrameBlender::Fra
     mCurrentFrame->SetHasNoAlpha();
   }
 
   mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
   mCurrentFrame->SetRawTimeout(aTimeout);
   mCurrentFrame->SetBlendMethod(aBlendMethod);
   mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
 
-  mProgress |= FLAG_FRAME_STOPPED | FLAG_ONLOAD_UNBLOCKED;
+  mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
 }
 
 void
 Decoder::PostInvalidation(nsIntRect& aRect)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
@@ -351,17 +361,17 @@ Decoder::PostDecodeDone(int32_t aLoopCou
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
   NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
   mDecodeDone = true;
 
   mImageMetadata.SetLoopCount(aLoopCount);
   mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
 
-  mProgress |= FLAG_DECODE_STOPPED;
+  mProgress |= FLAG_DECODE_COMPLETE;
 }
 
 void
 Decoder::PostDataError()
 {
   mDataError = true;
 }
 
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -75,16 +75,28 @@ public:
    */
   nsIntRect TakeInvalidRect()
   {
     nsIntRect invalidRect = mInvalidRect;
     mInvalidRect.SetEmpty();
     return invalidRect;
   }
 
+  /**
+   * Gets the progress changes accumulated by the decoder so far, and clears
+   * them. This means that each call to TakeProgress() returns only the changes
+   * accumulated since the last call to TakeProgress().
+   */
+  Progress TakeProgress()
+  {
+    Progress progress = mProgress;
+    mProgress = NoProgress;
+    return progress;
+  }
+
   // We're not COM-y, so we don't get refcounts by default
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
 
   /*
    * State.
    */
 
   // If we're doing a "size decode", we more or less pass through the image
@@ -94,18 +106,16 @@ public:
   void SetSizeDecode(bool aSizeDecode)
   {
     NS_ABORT_IF_FALSE(!mInitialized, "Can't set size decode after Init()!");
     mSizeDecode = aSizeDecode;
   }
 
   size_t BytesDecoded() const { return mBytesDecoded; }
 
-  Progress GetProgress() const { return mProgress; }
-
   // The number of frames we have, including anything in-progress. Thus, this
   // is only 0 if we haven't begun any frames.
   uint32_t GetFrameCount() { return mFrameCount; }
 
   // The number of complete frames we have (ie, not including anything in-progress).
   uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
 
   // Error tracking
@@ -183,16 +193,19 @@ protected:
    */
 
   // Called by decoders when they determine the size of the image. Informs
   // the image of its size and sends notifications.
   void PostSize(int32_t aWidth,
                 int32_t aHeight,
                 Orientation aOrientation = Orientation());
 
+  // Called by decoders if they determine that the image has transparency.
+  void PostHasTransparency();
+
   // Called by decoders when they begin a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   void PostFrameStart();
 
   // Called by decoders when they end a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
@@ -222,33 +235,32 @@ protected:
 
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
   nsRefPtr<imgFrame> mCurrentFrame;
   ImageMetadata mImageMetadata;
+  nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
   Progress mProgress;
 
   uint8_t* mImageData;       // Pointer to image data in either Cairo or 8bit format
   uint32_t mImageDataLength;
   uint32_t* mColormap;       // Current colormap to be used in Cairo format
   uint32_t mColormapSize;
 
   uint32_t mDecodeFlags;
   size_t mBytesDecoded;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
-  nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
-
   nsresult mFailCode;
 
   struct NewFrameData
   {
     NewFrameData()
     {}
 
     NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety,
--- a/image/src/ProgressTracker.cpp
+++ b/image/src/ProgressTracker.cpp
@@ -41,55 +41,57 @@ ProgressTrackerInit::~ProgressTrackerIni
   mTracker->ResetImage();
 }
 
 static void
 CheckProgressConsistency(Progress aProgress)
 {
   // Check preconditions for every progress bit.
 
-  if (aProgress & FLAG_REQUEST_STARTED) {
+  if (aProgress & FLAG_SIZE_AVAILABLE) {
     // No preconditions.
   }
-  if (aProgress & FLAG_HAS_SIZE) {
-    MOZ_ASSERT(aProgress & FLAG_REQUEST_STARTED);
-  }
   if (aProgress & FLAG_DECODE_STARTED) {
-    MOZ_ASSERT(aProgress & FLAG_REQUEST_STARTED);
+    // No preconditions.
   }
-  if (aProgress & FLAG_DECODE_STOPPED) {
+  if (aProgress & FLAG_DECODE_COMPLETE) {
     MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
   }
-  if (aProgress & FLAG_FRAME_STOPPED) {
+  if (aProgress & FLAG_FRAME_COMPLETE) {
     MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
   }
-  if (aProgress & FLAG_REQUEST_STOPPED) {
-    MOZ_ASSERT(aProgress & FLAG_REQUEST_STARTED);
+  if (aProgress & FLAG_LOAD_COMPLETE) {
+    // No preconditions.
   }
   if (aProgress & FLAG_ONLOAD_BLOCKED) {
     if (aProgress & FLAG_IS_MULTIPART) {
       MOZ_ASSERT(aProgress & FLAG_ONLOAD_UNBLOCKED);
     } else {
       MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
     }
   }
   if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
     MOZ_ASSERT(aProgress & FLAG_ONLOAD_BLOCKED);
-    MOZ_ASSERT(aProgress & (FLAG_FRAME_STOPPED |
+    MOZ_ASSERT(aProgress & (FLAG_FRAME_COMPLETE |
                             FLAG_IS_MULTIPART |
                             FLAG_HAS_ERROR));
   }
   if (aProgress & FLAG_IS_ANIMATED) {
     MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
+    MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
+  }
+  if (aProgress & FLAG_HAS_TRANSPARENCY) {
+    MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED);
+    MOZ_ASSERT(aProgress & FLAG_SIZE_AVAILABLE);
   }
   if (aProgress & FLAG_IS_MULTIPART) {
     // No preconditions.
   }
-  if (aProgress & FLAG_MULTIPART_STOPPED) {
-    MOZ_ASSERT(aProgress & FLAG_REQUEST_STOPPED);
+  if (aProgress & FLAG_LAST_PART_COMPLETE) {
+    MOZ_ASSERT(aProgress & FLAG_LOAD_COMPLETE);
   }
   if (aProgress & FLAG_HAS_ERROR) {
     // No preconditions.
   }
 }
 
 void
 ProgressTracker::SetImage(Image* aImage)
@@ -122,38 +124,38 @@ void ProgressTracker::SetIsMultipart()
 }
 
 bool
 ProgressTracker::IsLoading() const
 {
   // Checking for whether OnStopRequest has fired allows us to say we're
   // loading before OnStartRequest gets called, letting the request properly
   // get removed from the cache in certain cases.
-  return !(mProgress & FLAG_REQUEST_STOPPED);
+  return !(mProgress & FLAG_LOAD_COMPLETE);
 }
 
 uint32_t
 ProgressTracker::GetImageStatus() const
 {
   uint32_t status = imgIRequest::STATUS_NONE;
 
   // Translate our current state to a set of imgIRequest::STATE_* flags.
-  if (mProgress & FLAG_HAS_SIZE) {
+  if (mProgress & FLAG_SIZE_AVAILABLE) {
     status |= imgIRequest::STATUS_SIZE_AVAILABLE;
   }
   if (mProgress & FLAG_DECODE_STARTED) {
     status |= imgIRequest::STATUS_DECODE_STARTED;
   }
-  if (mProgress & FLAG_DECODE_STOPPED) {
+  if (mProgress & FLAG_DECODE_COMPLETE) {
     status |= imgIRequest::STATUS_DECODE_COMPLETE;
   }
-  if (mProgress & FLAG_FRAME_STOPPED) {
+  if (mProgress & FLAG_FRAME_COMPLETE) {
     status |= imgIRequest::STATUS_FRAME_COMPLETE;
   }
-  if (mProgress & FLAG_REQUEST_STOPPED) {
+  if (mProgress & FLAG_LOAD_COMPLETE) {
     status |= imgIRequest::STATUS_LOAD_COMPLETE;
   }
   if (mProgress & FLAG_HAS_ERROR) {
     status |= imgIRequest::STATUS_ERROR;
   }
 
   return status;
 }
@@ -298,63 +300,59 @@ ProgressTracker::NotifyCurrentState(imgR
 
 /* static */ void
 ProgressTracker::SyncNotifyInternal(ProxyArray& aProxies,
                                     bool aHasImage,
                                     Progress aProgress,
                                     const nsIntRect& aDirtyRect)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  // OnStartRequest
-  if (aProgress & FLAG_REQUEST_STARTED)
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartRequest());
 
-  // OnStartContainer
-  if (aProgress & FLAG_HAS_SIZE)
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartContainer());
+  if (aProgress & FLAG_SIZE_AVAILABLE)
+    NOTIFY_IMAGE_OBSERVERS(aProxies, OnSizeAvailable());
 
-  // OnStartDecode
   if (aProgress & FLAG_DECODE_STARTED)
     NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartDecode());
 
-  // BlockOnload
   if (aProgress & FLAG_ONLOAD_BLOCKED)
     NOTIFY_IMAGE_OBSERVERS(aProxies, BlockOnload());
 
   if (aHasImage) {
     // OnFrameUpdate
     // If there's any content in this frame at all (always true for
     // vector images, true for raster images that have decoded at
     // least one frame) then send OnFrameUpdate.
     if (!aDirtyRect.IsEmpty())
       NOTIFY_IMAGE_OBSERVERS(aProxies, OnFrameUpdate(&aDirtyRect));
 
-    if (aProgress & FLAG_FRAME_STOPPED)
-      NOTIFY_IMAGE_OBSERVERS(aProxies, OnStopFrame());
+    if (aProgress & FLAG_FRAME_COMPLETE)
+      NOTIFY_IMAGE_OBSERVERS(aProxies, OnFrameComplete());
 
-    // OnImageIsAnimated
+    if (aProgress & FLAG_HAS_TRANSPARENCY)
+      NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageHasTransparency());
+
     if (aProgress & FLAG_IS_ANIMATED)
       NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageIsAnimated());
   }
 
   // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
   // observers that can fire events when they receive those notifications to do
   // so then, instead of being forced to wait for UnblockOnload.
   if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
     NOTIFY_IMAGE_OBSERVERS(aProxies, UnblockOnload());
   }
 
-  if (aProgress & FLAG_DECODE_STOPPED) {
+  if (aProgress & FLAG_DECODE_COMPLETE) {
     MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStopDecode());
+    NOTIFY_IMAGE_OBSERVERS(aProxies, OnDecodeComplete());
   }
 
-  if (aProgress & FLAG_REQUEST_STOPPED) {
+  if (aProgress & FLAG_LOAD_COMPLETE) {
     NOTIFY_IMAGE_OBSERVERS(aProxies,
-                           OnStopRequest(aProgress & FLAG_MULTIPART_STOPPED));
+                           OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE));
   }
 }
 
 void
 ProgressTracker::SyncNotifyProgress(Progress aProgress,
                                     const nsIntRect& aInvalidRect /* = nsIntRect() */)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
@@ -405,28 +403,22 @@ ProgressTracker::SyncNotify(imgRequestPr
 void
 ProgressTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
                                         nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "SyncNotifyState and mConsumers are not threadsafe");
   nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy);
 
-  // In certain cases the request might not have started yet.
-  // We still need to fulfill the contract.
-  if (!(mProgress & FLAG_REQUEST_STARTED)) {
-    aProxy->OnStartRequest();
-  }
-
   if (mProgress & FLAG_ONLOAD_BLOCKED && !(mProgress & FLAG_ONLOAD_UNBLOCKED)) {
     aProxy->UnblockOnload();
   }
 
-  if (!(mProgress & FLAG_REQUEST_STOPPED)) {
-    aProxy->OnStopRequest(true);
+  if (!(mProgress & FLAG_LOAD_COMPLETE)) {
+    aProxy->OnLoadComplete(true);
   }
 }
 
 void
 ProgressTracker::AddConsumer(imgRequestProxy* aConsumer)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mConsumers.AppendElementUnlessExists(aConsumer);
--- a/image/src/ProgressTracker.h
+++ b/image/src/ProgressTracker.h
@@ -22,41 +22,41 @@ namespace mozilla {
 namespace image {
 
 class AsyncNotifyRunnable;
 class AsyncNotifyCurrentStateRunnable;
 class Image;
 
 // Image progress bitflags.
 enum {
-  FLAG_REQUEST_STARTED    = 1u << 0,
-  FLAG_HAS_SIZE           = 1u << 1,  // STATUS_SIZE_AVAILABLE
-  FLAG_DECODE_STARTED     = 1u << 2,  // STATUS_DECODE_STARTED
-  FLAG_DECODE_STOPPED     = 1u << 3,  // STATUS_DECODE_COMPLETE
-  FLAG_FRAME_STOPPED      = 1u << 4,  // STATUS_FRAME_COMPLETE
-  FLAG_REQUEST_STOPPED    = 1u << 5,  // STATUS_LOAD_COMPLETE
-  FLAG_ONLOAD_BLOCKED     = 1u << 6,
-  FLAG_ONLOAD_UNBLOCKED   = 1u << 7,
-  FLAG_IS_ANIMATED        = 1u << 8,
+  FLAG_SIZE_AVAILABLE     = 1u << 0,  // STATUS_SIZE_AVAILABLE
+  FLAG_DECODE_STARTED     = 1u << 1,  // STATUS_DECODE_STARTED
+  FLAG_DECODE_COMPLETE    = 1u << 2,  // STATUS_DECODE_COMPLETE
+  FLAG_FRAME_COMPLETE     = 1u << 3,  // STATUS_FRAME_COMPLETE
+  FLAG_LOAD_COMPLETE      = 1u << 4,  // STATUS_LOAD_COMPLETE
+  FLAG_ONLOAD_BLOCKED     = 1u << 5,
+  FLAG_ONLOAD_UNBLOCKED   = 1u << 6,
+  FLAG_IS_ANIMATED        = 1u << 7,
+  FLAG_HAS_TRANSPARENCY   = 1u << 8,
   FLAG_IS_MULTIPART       = 1u << 9,
-  FLAG_MULTIPART_STOPPED  = 1u << 10,
+  FLAG_LAST_PART_COMPLETE = 1u << 10,
   FLAG_HAS_ERROR          = 1u << 11  // STATUS_ERROR
 };
 
 typedef uint32_t Progress;
 
 const uint32_t NoProgress = 0;
 
-inline Progress OnStopRequestProgress(bool aLastPart,
-                                      bool aError,
-                                      nsresult aStatus)
+inline Progress LoadCompleteProgress(bool aLastPart,
+                                     bool aError,
+                                     nsresult aStatus)
 {
-  Progress progress = FLAG_REQUEST_STOPPED;
+  Progress progress = FLAG_LOAD_COMPLETE;
   if (aLastPart) {
-    progress |= FLAG_MULTIPART_STOPPED;
+    progress |= FLAG_LAST_PART_COMPLETE;
   }
   if (NS_FAILED(aStatus) || aError) {
     progress |= FLAG_HAS_ERROR;
   }
   return progress;
 }
 
 /**
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1621,29 +1621,16 @@ RasterImage::DoImageDataComplete()
   if (mDecoder) {
     nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   {
     ReentrantMonitorAutoEnter lock(mDecodingMonitor);
 
-    // If we're not storing any source data, then there's nothing more we can do
-    // once we've tried decoding for size.
-    if (!StoringSourceData() && mDecoder) {
-      nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
-      CONTAINER_ENSURE_SUCCESS(rv);
-    }
-
-    // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
-    // finish decoding this image.
-    if (mDecoder) {
-      DecodePool::Singleton()->RequestDecode(this);
-    }
-
     // Free up any extra space in the backing buffer
     mSourceData.Compact();
   }
 
   // Log header information
   if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) {
     char buf[9];
     get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
@@ -1674,17 +1661,17 @@ RasterImage::OnImageDataComplete(nsIRequ
   if (NS_FAILED(aStatus))
     finalStatus = aStatus;
 
   // We just recorded OnStopRequest; we need to inform our listeners.
   {
     ReentrantMonitorAutoEnter lock(mDecodingMonitor);
     FinishedSomeDecoding(eShutdownIntent_Done,
                          nullptr,
-                         OnStopRequestProgress(aLastPart, mError, finalStatus));
+                         LoadCompleteProgress(aLastPart, mError, finalStatus));
   }
 
   return finalStatus;
 }
 
 nsresult
 RasterImage::OnImageDataAvailable(nsIRequest*,
                                   nsISupports*,
@@ -2932,17 +2919,17 @@ RasterImage::FinishedSomeDecoding(eShutd
   bool done = false;
   bool wasSize = false;
   nsIntRect invalidRect;
   nsresult rv = NS_OK;
   Progress progress = aProgress;
 
   if (image->mDecoder) {
     invalidRect = image->mDecoder->TakeInvalidRect();
-    progress |= image->mDecoder->GetProgress();
+    progress |= image->mDecoder->TakeProgress();
 
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
     if (!image->mHasSize && image->mDecoder->HasSize()) {
       image->mDecoder->SetSizeOnImage();
     }
@@ -2974,18 +2961,19 @@ RasterImage::FinishedSomeDecoding(eShutd
 
       // We need to shut down the decoder first, in order to ensure all
       // decoding routines have been finished.
       rv = image->ShutdownDecoder(aIntent);
       if (NS_FAILED(rv)) {
         image->DoError();
       }
 
-      // If there were any final progress changes, grab them.
-      progress |= decoder->GetProgress();
+      // If there were any final changes, grab them.
+      invalidRect.Union(decoder->TakeInvalidRect());
+      progress |= decoder->TakeProgress();
     }
   }
 
   if (GetCurrentFrameIndex() > 0) {
     // Don't send invalidations for animated frames after the first; let
     // RequestRefresh take care of that.
     invalidRect = nsIntRect();
   }
@@ -2995,17 +2983,16 @@ RasterImage::FinishedSomeDecoding(eShutd
                            : nsIntRect();
   }
 
   if (mNotifying) {
     // Accumulate the progress changes. We don't permit recursive notifications
     // because they cause subtle concurrency bugs, so we'll delay sending out
     // the notifications until we pop back to the lowest invocation of
     // FinishedSomeDecoding on the stack.
-    NS_WARNING("Recursively notifying in RasterImage::FinishedSomeDecoding!");
     mNotifyProgress |= progress;
     mNotifyInvalidRect.Union(invalidRect);
   } else {
     MOZ_ASSERT(mNotifyProgress == NoProgress && mNotifyInvalidRect.IsEmpty(),
                "Shouldn't have an accumulated change at this point");
 
     progress = image->mProgressTracker->Difference(progress);
 
--- a/image/src/ScriptedNotificationObserver.cpp
+++ b/image/src/ScriptedNotificationObserver.cpp
@@ -41,13 +41,15 @@ ScriptedNotificationObserver::Notify(img
   if (aType == imgINotificationObserver::DECODE_COMPLETE)
     return mInner->DecodeComplete(aRequest);
   if (aType == imgINotificationObserver::LOAD_COMPLETE)
     return mInner->LoadComplete(aRequest);
   if (aType == imgINotificationObserver::DISCARD)
     return mInner->Discard(aRequest);
   if (aType == imgINotificationObserver::IS_ANIMATED)
     return mInner->IsAnimated(aRequest);
+  if (aType == imgINotificationObserver::HAS_TRANSPARENCY)
+    return mInner->HasTransparency(aRequest);
   return NS_OK;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -437,19 +437,19 @@ VectorImage::OnImageDataComplete(nsIRequ
   nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
 
   // Give precedence to Necko failure codes.
   if (NS_FAILED(aStatus))
     finalStatus = aStatus;
 
   // Actually fire OnStopRequest.
   if (mProgressTracker) {
-    mProgressTracker->SyncNotifyProgress(OnStopRequestProgress(aLastPart,
-                                                               mError,
-                                                               finalStatus));
+    mProgressTracker->SyncNotifyProgress(LoadCompleteProgress(aLastPart,
+                                                              mError,
+                                                              finalStatus));
   }
   return finalStatus;
 }
 
 nsresult
 VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
                                   nsISupports* aContext,
                                   nsIInputStream* aInStr,
@@ -561,17 +561,17 @@ VectorImage::SendInvalidationNotificatio
   // called for them. Ordinarily this isn't needed, since we send out
   // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
   // SVG document may not be 100% ready to render at that time. In those cases
   // we would miss the subsequent invalidations if we didn't send out the
   // notifications directly in |InvalidateObservers...|.
 
   if (mProgressTracker) {
     SurfaceCache::Discard(this);
-    mProgressTracker->SyncNotifyProgress(FLAG_FRAME_STOPPED,
+    mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
                                          nsIntRect::GetMaxSizedIntRect());
   }
 }
 
 NS_IMETHODIMP_(nsIntRect)
 VectorImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   return aRect;
@@ -1105,19 +1105,20 @@ VectorImage::OnSVGDocumentLoaded()
   mIsFullyLoaded = true;
   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
 
   // Start listening to our image for rendering updates.
   mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
 
   // Tell *our* observers that we're done loading.
   if (mProgressTracker) {
-    mProgressTracker->SyncNotifyProgress(FLAG_HAS_SIZE |
-                                         FLAG_FRAME_STOPPED |
-                                         FLAG_DECODE_STOPPED |
+    mProgressTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE |
+                                         FLAG_HAS_TRANSPARENCY |
+                                         FLAG_FRAME_COMPLETE |
+                                         FLAG_DECODE_COMPLETE |
                                          FLAG_ONLOAD_UNBLOCKED,
                                          nsIntRect::GetMaxSizedIntRect());
   }
 
   EvaluateAnimation();
 }
 
 void
@@ -1127,17 +1128,17 @@ VectorImage::OnSVGDocumentError()
 
   // XXXdholbert Need to do something more for the parsing failed case -- right
   // now, this just makes us draw the "object" icon, rather than the (jagged)
   // "broken image" icon.  See bug 594505.
   mError = true;
 
   if (mProgressTracker) {
     // Unblock page load.
-    mProgressTracker->SyncNotifyProgress(FLAG_DECODE_STOPPED |
+    mProgressTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE |
                                          FLAG_ONLOAD_UNBLOCKED |
                                          FLAG_HAS_ERROR);
   }
 }
 
 //------------------------------------------------------------------------------
 // nsIStreamListener method
 
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -668,17 +668,16 @@ NS_IMETHODIMP imgRequest::OnStartRequest
     nsCOMPtr<nsIChannel> chan;
     mpchan->GetBaseChannel(getter_AddRefs(chan));
     mRequest = chan;
   }
 
   // Note: refreshing progressTracker in case OnNewSourceData changed it.
   progressTracker = GetProgressTracker();
   progressTracker->ResetForNewRequest();
-  progressTracker->SyncNotifyProgress(FLAG_REQUEST_STARTED);
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   if (channel)
     channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
 
   /* Get our principal */
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   if (chan) {
@@ -788,17 +787,17 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
     // stops animations, removes from cache
     this->Cancel(status);
   }
 
   if (!mImage) {
     // We have to fire the OnStopRequest notifications ourselves because there's
     // no image capable of doing so.
     Progress progress =
-      OnStopRequestProgress(lastPart, /* aError = */ false, status);
+      LoadCompleteProgress(lastPart, /* aError = */ false, status);
 
     nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     progressTracker->SyncNotifyProgress(progress);
   }
 
   mTimedChannel = nullptr;
   return NS_OK;
 }
@@ -890,17 +889,16 @@ imgRequest::OnDataAvailable(nsIRequest *
       // new status tracker to give to the image, because we don't have one of
       // our own any more.
       if (resniffMimeType) {
         MOZ_ASSERT(mIsMultiPartChannel, "Resniffing a non-multipart image");
 
         // Initialize a new status tracker.
         nsRefPtr<ProgressTracker> freshTracker = new ProgressTracker(nullptr);
         freshTracker->SetIsMultipart();
-        freshTracker->SyncNotifyProgress(FLAG_REQUEST_STARTED);
 
         // Replace the old status tracker with it.
         nsRefPtr<ProgressTracker> oldProgressTracker = GetProgressTracker();
         freshTracker->AdoptConsumers(oldProgressTracker);
         mProgressTracker = freshTracker.forget();
       }
 
       SetProperties(chan);
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -108,18 +108,17 @@ imgRequestProxy::imgRequestProxy() :
   mListener(nullptr),
   mLoadFlags(nsIRequest::LOAD_NORMAL),
   mLockCount(0),
   mAnimationConsumers(0),
   mCanceled(false),
   mIsInLoadGroup(false),
   mListenerIsStrongRef(false),
   mDecodeRequested(false),
-  mDeferNotifications(false),
-  mSentStartContainer(false)
+  mDeferNotifications(false)
 {
   /* member initializers and constructor code */
 
 }
 
 imgRequestProxy::~imgRequestProxy()
 {
   /* destructor code */
@@ -730,68 +729,63 @@ void imgRequestProxy::OnStartDecode()
     // OnStartDecodes which indicates the beginning of a new decode.  The cache
     // entry's size therefore needs to be reset to 0 here.  If we do not do
     // this, the code in ProgressTrackerObserver::OnStopFrame will continue to
     // increase the data size cumulatively.
     GetOwner()->ResetCacheEntry();
   }
 }
 
-void imgRequestProxy::OnStartContainer()
+void imgRequestProxy::OnSizeAvailable()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
 
-  if (mListener && !mCanceled && !mSentStartContainer) {
+  if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
-    mSentStartContainer = true;
   }
 }
 
 void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect)
 {
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDataAvailable");
+  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnFrameUpdate");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect);
   }
 }
 
-void imgRequestProxy::OnStopFrame()
+void imgRequestProxy::OnFrameComplete()
 {
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopFrame");
+  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnFrameComplete");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr);
   }
 }
 
-void imgRequestProxy::OnStopDecode()
+void imgRequestProxy::OnDecodeComplete()
 {
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopDecode");
+  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDecodeComplete");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
   }
 
   if (GetOwner()) {
     // We finished the decode, and thus have the decoded frames. Update the cache
     // entry size to take this into account.
     GetOwner()->UpdateCacheEntrySize();
-
-    // Multipart needs reset for next OnStartContainer.
-    if (GetOwner()->GetMultipart())
-      mSentStartContainer = false;
   }
 }
 
 void imgRequestProxy::OnDiscard()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard");
 
   if (mListener && !mCanceled) {
@@ -811,36 +805,37 @@ void imgRequestProxy::OnUnlockedDraw()
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr);
   }
 }
 
+void imgRequestProxy::OnImageHasTransparency()
+{
+  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageHasTransparency");
+  if (mListener && !mCanceled) {
+    // Hold a ref to the listener while we call it, just in case.
+    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
+    mListener->Notify(this, imgINotificationObserver::HAS_TRANSPARENCY, nullptr);
+  }
+}
+
 void imgRequestProxy::OnImageIsAnimated()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr);
   }
 }
 
-void imgRequestProxy::OnStartRequest()
-{
-#ifdef PR_LOGGING
-  nsAutoCString name;
-  GetName(name);
-  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStartRequest", "name", name.get());
-#endif
-}
-
-void imgRequestProxy::OnStopRequest(bool lastPart)
+void imgRequestProxy::OnLoadComplete(bool aLastPart)
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStopRequest", "name", name.get());
 #endif
   // There's all sorts of stuff here that could kill us (the OnStopRequest call
   // on the listener, the removal from the loadgroup, the release of the
@@ -852,27 +847,27 @@ void imgRequestProxy::OnStopRequest(bool
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
   }
 
   // If we're expecting more data from a multipart channel, re-add ourself
   // to the loadgroup so that the document doesn't lose track of the load.
   // If the request is already a background request and there's more data
   // coming, we can just leave the request in the loadgroup as-is.
-  if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
-    RemoveFromLoadGroup(lastPart);
+  if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
+    RemoveFromLoadGroup(aLastPart);
     // More data is coming, so change the request to be a background request
     // and put it back in the loadgroup.
-    if (!lastPart) {
+    if (!aLastPart) {
       mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
       AddToLoadGroup();
     }
   }
 
-  if (mListenerIsStrongRef && lastPart) {
+  if (mListenerIsStrongRef && aLastPart) {
     NS_PRECONDITION(mListener, "How did that happen?");
     // Drop our strong ref to the listener now that we're done with
     // everything.  Note that this can cancel us and other fun things
     // like that.  Don't add anything in this method after this point.
     imgINotificationObserver* obs = mListener;
     mListenerIsStrongRef = false;
     NS_RELEASE(obs);
   }
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -144,28 +144,28 @@ protected:
       nsRefPtr<imgRequestProxy> mOwner;
       nsresult mStatus;
   };
 
   // The following notification functions are protected to ensure that (friend
   // class) ProgressTracker is the only class allowed to send us
   // notifications.
 
-  void OnStartDecode     ();
-  void OnStartContainer  ();
-  void OnFrameUpdate     (const nsIntRect * aRect);
-  void OnStopFrame       ();
-  void OnStopDecode      ();
-  void OnDiscard         ();
-  void OnUnlockedDraw    ();
-  void OnImageIsAnimated ();
+  void OnStartDecode();
+  void OnSizeAvailable();
+  void OnFrameUpdate(const nsIntRect* aRect);
+  void OnFrameComplete();
+  void OnDecodeComplete();
+  void OnDiscard();
+  void OnUnlockedDraw();
+  void OnImageHasTransparency();
+  void OnImageIsAnimated();
 
   /* non-virtual sort-of-nsIRequestObserver methods */
-  void OnStartRequest();
-  void OnStopRequest(bool aLastPart);
+  void OnLoadComplete(bool aLastPart);
 
   /* non-virtual imgIOnloadBlocker methods */
   void BlockOnload();
   void UnblockOnload();
 
   /* Finish up canceling ourselves */
   void DoCancel(nsresult status);
 
@@ -222,20 +222,16 @@ private:
   bool mCanceled;
   bool mIsInLoadGroup;
   bool mListenerIsStrongRef;
   bool mDecodeRequested;
 
   // Whether we want to defer our notifications by the non-virtual Observer
   // interfaces as image loads proceed.
   bool mDeferNotifications;
-
-  // We only want to send OnStartContainer once for each proxy, but we might
-  // get multiple OnStartContainer calls.
-  bool mSentStartContainer;
 };
 
 // Used for static image proxies for which no requests are available, so
 // certain behaviours must be overridden to compensate.
 class imgRequestProxyStatic : public imgRequestProxy
 {
 
 public:
--- a/image/src/imgTools.cpp
+++ b/image/src/imgTools.cpp
@@ -82,23 +82,22 @@ NS_IMETHODIMP imgTools::DecodeImage(nsII
 
   // Figure out how much data we've been passed.
   uint64_t length;
   rv = inStream->Available(&length);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   // Send the source data to the Image.
-  tracker->SyncNotifyProgress(FLAG_REQUEST_STARTED);
   rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Let the Image know we've sent all the data.
   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
-  tracker->SyncNotifyProgress(FLAG_REQUEST_STOPPED);
+  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All done.
   NS_ADDREF(*aContainer = image.get());
   return NS_OK;
 }
 
 /**
--- a/image/test/mochitest/chrome.ini
+++ b/image/test/mochitest/chrome.ini
@@ -6,35 +6,43 @@ support-files =
   animated1.gif
   animated2.gif
   animation.svg
   animationPolling.js
   bad.jpg
   damon.jpg
   filter-final.svg
   filter.svg
+  first-frame-padding.gif
+  ico-bmp-opaque.ico
+  ico-bmp-transparent.ico
   iframe.html
   imgutils.js
   invalid.jpg
   lime-anim-100x100-2.svg
   lime-anim-100x100.svg
   lime100x100.svg
+  opaque.bmp
   purple.gif
+  red.gif
   red.png
   ref-iframe.html
   rillybad.jpg
+  transparent.gif
+  transparent.png
 
 [test_animSVGImage.html]
 [test_animSVGImage2.html]
 [test_animation.html]
 [test_animation2.html]
 [test_background_image_anim.html]
 [test_bullet_animation.html]
 [test_changeOfSource.html]
 [test_changeOfSource2.html]
+[test_has_transparency.html]
 [test_net_failedtoprocess.html]
 [test_removal_ondecode.html]
 [test_removal_onload.html]
 [test_staticClone.html]
 [test_svg_animatedGIF.html]
 [test_svg_filter_animation.html]
 [test_synchronized_animation.html]
 [test_undisplayed_iframe.html]
copy from image/test/reftest/gif/one-color-offset.gif
copy to image/test/mochitest/first-frame-padding.gif
copy from image/test/reftest/ico/ico-bmp-8bpp/ico-size-1x1-8bpp.ico
copy to image/test/mochitest/ico-bmp-opaque.ico
copy from image/test/reftest/ico/ico-bmp-32bpp/ico-transparent-32bpp.ico
copy to image/test/mochitest/ico-bmp-transparent.ico
--- a/image/test/mochitest/imgutils.js
+++ b/image/test/mochitest/imgutils.js
@@ -112,16 +112,17 @@ function getImagePref(pref)
   }
   else
     return null;
 }
 
 // JS implementation of imgIScriptedNotificationObserver with stubs for all of its methods.
 function ImageDecoderObserverStub()
 {
-  this.sizeAvailable = function sizeAvailable(aRequest)   {}
-  this.frameComplete = function frameComplete(aRequest)   {}
-  this.decodeComplete = function decodeComplete(aRequest) {}
-  this.loadComplete = function loadComplete(aRequest)     {}
-  this.frameUpdate = function frameUpdate(aRequest)       {}
-  this.discard = function discard(aRequest)               {}
-  this.isAnimated = function isAnimated(aRequest)         {}
+  this.sizeAvailable = function sizeAvailable(aRequest)     {}
+  this.frameComplete = function frameComplete(aRequest)     {}
+  this.decodeComplete = function decodeComplete(aRequest)   {}
+  this.loadComplete = function loadComplete(aRequest)       {}
+  this.frameUpdate = function frameUpdate(aRequest)         {}
+  this.discard = function discard(aRequest)                 {}
+  this.isAnimated = function isAnimated(aRequest)           {}
+  this.hasTransparency = function hasTransparency(aRequest) {}
 }
copy from image/test/reftest/bmp/bmp-8bpp/bmp-size-2x2-8bpp.bmp
copy to image/test/mochitest/opaque.bmp
copy from image/test/reftest/gif/red.gif
copy to image/test/mochitest/red.gif
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_has_transparency.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1089880
+-->
+<head>
+  <title>Test for Bug 1089880</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1089880">Mozilla Bug 1089880</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+/** Test for Bug 1089880 **/
+
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const gContent = document.getElementById("content");
+
+var gImg;
+var gMyDecoderObserver;
+var gIsTestFinished = false;
+var gFiles;
+var gCurrentFileIsTransparent = false;
+var gHasTransparencyWasCalled = false;
+
+function testFiles() {
+  // [A, B] where 'A' is the image and 'B' is whether it's transparent.
+
+  // PNGs and GIFs may be transparent or not.
+  yield ["red.png", false];
+  yield ["transparent.png", true];
+  yield ["red.gif", false];
+  yield ["transparent.gif", true];
+
+  // GIFs with padding on the first frame are always transparent.
+  yield ["first-frame-padding.gif", true];
+
+  // JPEGs and BMPs are never transparent.
+  yield ["damon.jpg", false];
+  yield ["opaque.bmp", false];
+
+  // ICO files which contain BMPs have an additional type of transparency - the
+  // AND mask - that warrants separate testing.
+  yield ["ico-bmp-opaque.ico", false];
+  yield ["ico-bmp-transparent.ico", true];
+
+  // SVGs are always transparent.
+  yield ["lime100x100.svg", true];
+}
+
+function loadNext() {
+  var currentFile = "";
+  try {
+    gHasTransparencyWasCalled = false;
+    [currentFile, gCurrentFileIsTransparent] = gFiles.next();
+    gImg.setAttribute("src", currentFile);
+  } catch (e) {
+    // We ran out of test files.
+    cleanUpAndFinish();
+  }
+}
+
+function onHasTransparency(aRequest) {
+  gHasTransparencyWasCalled = true;
+}
+
+function onDecodeComplete(aRequest) {
+  if (!gCurrentFileIsTransparent) {
+    ok(!gHasTransparencyWasCalled,
+       "onHasTransparency was not called for non-transparent file " + gImg.src);
+  } else {
+    ok(gHasTransparencyWasCalled,
+       "onHasTransparency was called for transparent file " + gImg.src);
+  }
+  loadNext();
+}
+
+function onError() {
+  if (gIsTestFinished) {
+    return;
+  }
+  ok(false, "Should successfully load " + gImg.src);
+  loadNext();
+}
+
+function onLoad() {
+  if (gIsTestFinished) {
+    return;
+  }
+  ok(true, "Should successfully load " + gImg.src);
+}
+
+function failTest() {
+  ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
+            "currently displaying " + gImg.src);
+  cleanUpAndFinish();
+}
+
+function cleanUpAndFinish() {
+  if (gIsTestFinished) {
+    return;
+  }
+  gIsTestFinished = true;
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.removeObserver(gMyDecoderObserver);
+  SimpleTest.finish();
+}
+
+function main() {
+  gFiles = testFiles();
+  gImg = new Image();
+  gImg.onload = onLoad;
+  gImg.onerror = onError;
+
+  // Create, customize & attach decoder observer.
+  observer = new ImageDecoderObserverStub();
+  observer.hasTransparency = onHasTransparency;
+  observer.decodeComplete = onDecodeComplete;
+  gMyDecoderObserver =
+    Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+      .createScriptedObserver(observer);
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.addObserver(gMyDecoderObserver);
+
+  // We want to test the cold loading behavior, so clear cache in case an
+  // earlier test got our image in there already.
+  clearImageCache();
+
+  // Load the first image.
+  loadNext();
+
+  // In case something goes wrong, fail earlier than mochitest timeout,
+  // and with more information.
+  setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
copy from image/test/reftest/gif/in-colormap-trans.gif
copy to image/test/mochitest/transparent.gif
copy from image/test/reftest/pngsuite-transparency/tbbn1g04.png
copy to image/test/mochitest/transparent.png
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -702,21 +702,16 @@ bool Channel::ChannelImpl::ProcessOutgoi
           reinterpret_cast<int*>(CMSG_DATA(cmsg)));
       msgh.msg_controllen = cmsg->cmsg_len;
 
       msg->header()->num_fds = num_fds;
 #if defined(OS_MACOSX)
       msg->set_fd_cookie(++last_pending_fd_id_);
 #endif
     }
-#ifdef MOZ_TASK_TRACER
-    GetCurTraceInfo(&msg->header()->source_event_id,
-                    &msg->header()->parent_task_id,
-                    &msg->header()->source_event_type);
-#endif
 
     size_t amt_to_write = msg->size() - message_send_bytes_written_;
     DCHECK(amt_to_write != 0);
     const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
         message_send_bytes_written_;
 
     struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
     msgh.msg_iov = &iov;
@@ -881,16 +876,22 @@ void Channel::ChannelImpl::CloseDescript
     }
   }
   DCHECK(false) << "pending_fd_id not in our list!";
 }
 #endif
 
 void Channel::ChannelImpl::OutputQueuePush(Message* msg)
 {
+#ifdef MOZ_TASK_TRACER
+  // Save the current TaskTracer info into the message header.
+  GetCurTraceInfo(&msg->header()->source_event_id,
+                  &msg->header()->parent_task_id,
+                  &msg->header()->source_event_type);
+#endif
   output_queue_.push(msg);
   output_queue_length_++;
 }
 
 void Channel::ChannelImpl::OutputQueuePop()
 {
   output_queue_.pop();
   output_queue_length_--;
--- a/js/public/Debug.h
+++ b/js/public/Debug.h
@@ -258,13 +258,41 @@ class BuilderOrigin : public Builder {
 // consuming. To do this, it needs a function that accepts a pointer to a block,
 // and returns the number of bytes allocated to that block. SpiderMonkey itself
 // doesn't know which function is appropriate to use, but the embedding does.
 
 // Tell Debuggers in |runtime| to use |mallocSizeOf| to find the size of
 // malloc'd blocks.
 void SetDebuggerMallocSizeOf(JSRuntime *runtime, mozilla::MallocSizeOf mallocSizeOf);
 
+
+// Handlers for observing Promises
+// -------------------------------
+//
+// The Debugger wants to observe behavior of promises, which are implemented by
+// Gecko with webidl and which SpiderMonkey knows nothing about. On the other
+// hand, Gecko knows nothing about which (if any) debuggers are observing a
+// promise's global. The compromise is that Gecko is responsible for calling
+// these handlers at the appropriate times, and SpiderMonkey will handle
+// notifying any Debugger instances that are observing the given promise's
+// global.
+
+// Notify any Debugger instances observing this promise's global that a new
+// promise was allocated.
+JS_PUBLIC_API(void)
+onNewPromise(JSContext *cx, HandleObject promise);
+
+// Notify any Debugger instances observing this promise's global that the
+// promise has settled (ie, it has either been fulfilled or rejected). Note that
+// this is *not* equivalent to the promise resolution (ie, the promise's fate
+// getting locked in) because you can resolve a promise with another pending
+// promise, in which case neither promise has settled yet.
+//
+// It is Gecko's responsibility to ensure that this is never called on the same
+// promise more than once (because a promise can only make the transition from
+// unsettled to settled once).
+void onPromiseSettled(JSContext *cx, HandleObject promise);
+
 } // namespace dbg
 } // namespace JS
 
 
 #endif /* js_Debug_h */
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -79,17 +79,18 @@ JS_ENUM_HEADER(JSValueType, uint8_t)
     JSVAL_TYPE_NULL                = 0x07,
     JSVAL_TYPE_OBJECT              = 0x08,
 
     /* These never appear in a jsval; they are only provided as an out-of-band value. */
     JSVAL_TYPE_UNKNOWN             = 0x20,
     JSVAL_TYPE_MISSING             = 0x21
 } JS_ENUM_FOOTER(JSValueType);
 
-JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
+static_assert(sizeof(JSValueType) == 1,
+              "compiler typed enum support is apparently buggy");
 
 #if defined(JS_NUNBOX32)
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32_t)
 {
     JSVAL_TAG_CLEAR                = 0xFFFFFF80,
     JSVAL_TAG_INT32                = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
@@ -97,17 +98,18 @@ JS_ENUM_HEADER(JSValueTag, uint32_t)
     JSVAL_TAG_STRING               = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
     JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
     JSVAL_TAG_OBJECT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
 } JS_ENUM_FOOTER(JSValueTag);
 
-JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
+static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
+              "compiler typed enum support is apparently buggy");
 
 #elif defined(JS_PUNBOX64)
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32_t)
 {
     JSVAL_TAG_MAX_DOUBLE           = 0x1FFF0,
     JSVAL_TAG_INT32                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
@@ -115,35 +117,47 @@ JS_ENUM_HEADER(JSValueTag, uint32_t)
     JSVAL_TAG_STRING               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
     JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
     JSVAL_TAG_OBJECT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
 } JS_ENUM_FOOTER(JSValueTag);
 
-JS_STATIC_ASSERT(sizeof(JSValueTag) == sizeof(uint32_t));
+static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
+              "compiler typed enum support is apparently buggy");
 
 JS_ENUM_HEADER(JSValueShiftedTag, uint64_t)
 {
     JSVAL_SHIFTED_TAG_MAX_DOUBLE   = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
     JSVAL_SHIFTED_TAG_INT32        = (((uint64_t)JSVAL_TAG_INT32)      << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_UNDEFINED    = (((uint64_t)JSVAL_TAG_UNDEFINED)  << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_STRING       = (((uint64_t)JSVAL_TAG_STRING)     << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_SYMBOL       = (((uint64_t)JSVAL_TAG_SYMBOL)     << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_BOOLEAN      = (((uint64_t)JSVAL_TAG_BOOLEAN)    << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_MAGIC        = (((uint64_t)JSVAL_TAG_MAGIC)      << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_NULL         = (((uint64_t)JSVAL_TAG_NULL)       << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_OBJECT       = (((uint64_t)JSVAL_TAG_OBJECT)     << JSVAL_TAG_SHIFT)
 } JS_ENUM_FOOTER(JSValueShiftedTag);
 
-JS_STATIC_ASSERT(sizeof(JSValueShiftedTag) == sizeof(uint64_t));
+static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
+              "compiler typed enum support is apparently buggy");
 
 #endif
 
+/*
+ * All our supported compilers implement C++11 |enum Foo : T| syntax, so don't
+ * expose these macros. (This macro exists *only* because gcc bug 51242
+ * <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51242> makes bit-fields of
+ * typed enums trigger a warning that can't be turned off. Don't expose it
+ * beyond this file!)
+ */
+#undef JS_ENUM_HEADER
+#undef JS_ENUM_FOOTER
+
 #else  /* !defined(__SUNPRO_CC) && !defined(__xlC__) */
 
 typedef uint8_t JSValueType;
 #define JSVAL_TYPE_DOUBLE            ((uint8_t)0x00)
 #define JSVAL_TYPE_INT32             ((uint8_t)0x01)
 #define JSVAL_TYPE_UNDEFINED         ((uint8_t)0x02)
 #define JSVAL_TYPE_BOOLEAN           ((uint8_t)0x03)
 #define JSVAL_TYPE_MAGIC             ((uint8_t)0x04)
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -382,17 +382,17 @@ class AsmJSModule
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, Exit *out) const;
     };
 
     struct EntryArg {
         uint64_t lo;
         uint64_t hi;
     };
-    JS_STATIC_ASSERT(sizeof(EntryArg) >= jit::Simd128DataSize);
+
     typedef int32_t (*CodePtr)(EntryArg *args, uint8_t *global);
 
     // An Exit holds bookkeeping information about an exit; the ExitDatum
     // struct overlays the actual runtime data stored in the global data
     // section.
     struct ExitDatum
     {
         uint8_t *exit;
--- a/js/src/asmjs/AsmJSSignalHandlers.cpp
+++ b/js/src/asmjs/AsmJSSignalHandlers.cpp
@@ -475,20 +475,22 @@ AsmJSFaultHandler(LPEXCEPTION_POINTERS e
 
 #elif defined(XP_MACOSX)
 # include <mach/exc.h>
 
 static uint8_t **
 ContextToPC(x86_thread_state_t &state)
 {
 # if defined(JS_CPU_X64)
-    JS_STATIC_ASSERT(sizeof(state.uts.ts64.__rip) == sizeof(void*));
+    static_assert(sizeof(state.uts.ts64.__rip) == sizeof(void*),
+                  "stored IP should be compile-time pointer-sized");
     return reinterpret_cast<uint8_t**>(&state.uts.ts64.__rip);
 # else
-    JS_STATIC_ASSERT(sizeof(state.uts.ts32.__eip) == sizeof(void*));
+    static_assert(sizeof(state.uts.ts32.__eip) == sizeof(void*),
+                  "stored IP should be compile-time pointer-sized");
     return reinterpret_cast<uint8_t**>(&state.uts.ts32.__eip);
 # endif
 }
 
 # if defined(JS_CODEGEN_X64)
 static bool
 SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
                               const AsmJSHeapAccess &heapAccess)
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -7681,17 +7681,19 @@ GenerateEntry(ModuleCompiler &m, unsigne
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
         switch (iter->kind()) {
           case ABIArg::GPR:
             masm.load32(src, iter->gpr());
             break;
-          case ABIArg::FPU:
+          case ABIArg::FPU: {
+            static_assert(sizeof(AsmJSModule::EntryArg) >= jit::Simd128DataSize,
+                          "EntryArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType_Int32x4:
                 masm.loadUnalignedInt32x4(src, iter->fpu());
                 break;
               case MIRType_Float32x4:
                 masm.loadUnalignedFloat32x4(src, iter->fpu());
                 break;
               case MIRType_Double:
@@ -7700,16 +7702,17 @@ GenerateEntry(ModuleCompiler &m, unsigne
               case MIRType_Float32:
                 masm.loadFloat32(src, iter->fpu());
                 break;
               default:
                 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected FPU type");
                 break;
             }
             break;
+          }
           case ABIArg::Stack:
             switch (type) {
               case MIRType_Int32:
                 masm.load32(src, scratch);
                 masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
                 break;
               case MIRType_Double:
                 masm.loadDouble(src, ScratchDoubleReg);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -16,16 +16,17 @@
 #include "jsobj.h"
 #ifndef JS_MORE_DETERMINISTIC
 #include "jsprf.h"
 #endif
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSValidate.h"
+#include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeTraverse.h"
 #include "js/Vector.h"
 #include "vm/ForkJoin.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
@@ -951,16 +952,60 @@ OOMAfterAllocations(JSContext *cx, unsig
     if (!JS::ToUint32(cx, args[0], &count))
         return false;
 
     OOM_maxAllocations = OOM_counter + count;
     return true;
 }
 #endif
 
+static const js::Class FakePromiseClass = {
+    "Promise", JSCLASS_IS_ANONYMOUS,
+    JS_PropertyStub,       /* addProperty */
+    JS_DeletePropertyStub, /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+static bool
+MakeFakePromise(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedObject scope(cx, cx->global());
+    if (!scope)
+        return false;
+
+    RootedObject obj(cx, NewObjectWithGivenProto(cx, &FakePromiseClass, nullptr, scope));
+    if (!obj)
+        return false;
+
+    JS::dbg::onNewPromise(cx, obj);
+    args.rval().setObject(*obj);
+    return true;
+}
+
+static bool
+SettleFakePromise(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "settleFakePromise", 1))
+        return false;
+    if (!args[0].isObject() || args[0].toObject().getClass() != &FakePromiseClass) {
+        JS_ReportError(cx, "first argument must be a (fake) Promise object");
+        return false;
+    }
+
+    RootedObject promise(cx, &args[0].toObject());
+    JS::dbg::onPromiseSettled(cx, promise);
+    return true;
+}
+
 static unsigned finalizeCount = 0;
 
 static void
 finalize_counter_finalize(JSFreeOp *fop, JSObject *obj)
 {
     ++finalizeCount;
 }
 
@@ -2230,16 +2275,29 @@ static const JSFunctionSpecWithHelp Test
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
 "oomAfterAllocations(count)",
 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
 "  (return NULL)."),
 #endif
 
+    JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0,
+"makeFakePromise()",
+"  Create an object whose [[Class]] name is 'Promise' and call\n"
+"  JS::dbg::onNewPromise on it before returning it. It doesn't actually have\n"
+"  any of the other behavior associated with promises."),
+
+    JS_FN_HELP("settleFakePromise", SettleFakePromise, 1, 0,
+"settleFakePromise(promise)",
+"  'Settle' a 'promise' created by makeFakePromise(). This doesn't have any\n"
+"  observable effects outside of firing any onPromiseSettled hooks set on\n"
+"  Debugger instances that are observing the given promise's global as a\n"
+"  debuggee."),
+
     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
 "makeFinalizeObserver()",
 "  Get a special object whose finalization increases the counter returned\n"
 "  by the finalizeCount function."),
 
     JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
 "finalizeCount()",
 "  Return the current value of the finalization counter that is incremented\n"
--- a/js/src/doc/Debugger/Debugger.md
+++ b/js/src/doc/Debugger/Debugger.md
@@ -76,22 +76,30 @@ On a new `Debugger` instance, each of th
 function or `undefined`; otherwise a `TypeError` is thrown.
 
 Handler functions run in the same thread in which the event occurred.
 They run in the compartment to which they belong, not in a debuggee
 compartment.
 
 <code>onNewScript(<i>script</i>, <i>global</i>)</code>
 :   New code, represented by the [`Debugger.Script`][script] instance
-    <i>script</i>, has been loaded in the scope of the debuggee global
-    object <i>global</i>. <i>global</i> is a [`Debugger.Object`][object]
-    instance whose referent is the global object.
+    <i>script</i>, has been loaded in the scope of the debuggees.
 
     This method's return value is ignored.
 
+<code>onNewPromise(<i>promise</i>)</code>
+:   A new Promise object, referenced by the [`Debugger.Object`][object] instance
+    *promise*, has been allocated in the scope of the debuggees.
+
+    This handler method should return a [resumption value][rv] specifying how
+    the debuggee's execution should proceed. However, note that a <code>{ return:
+    <i>value</i> }</code> resumption value is treated like `undefined` ("continue
+    normally"); <i>value</i> is ignored. (Allowing the handler to substitute
+    its own value for the new global object doesn't seem useful.)
+
 <code>onDebuggerStatement(<i>frame</i>)</code>
 :   Debuggee code has executed a <i>debugger</i> statement in <i>frame</i>.
     This method should return a [resumption value][rv] specifying how the
     debuggee's execution should proceed.
 
 <code>onEnterFrame(<i>frame</i>)</code>
 :   The stack frame <i>frame</i> is about to begin executing code.
     (Naturally, <i>frame</i> is currently the youngest
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1156,18 +1156,22 @@ TokenStream::getTokenInternal(TokenKind 
                 updateFlagsForEOL();
             }
 
             goto retry;
         }
 
         tp = newToken(-1);
 
-        // '$' and '_' don't pass IsLetter, but they're < 128 so never appear here.
-        JS_STATIC_ASSERT('$' < 128 && '_' < 128);
+        static_assert('$' < 128,
+                      "IdentifierStart contains '$', but as !IsLetter('$'), "
+                      "ensure that '$' is never handled here");
+        static_assert('_' < 128,
+                      "IdentifierStart contains '_', but as !IsLetter('_'), "
+                      "ensure that '_' is never handled here");
         if (IsLetter(c)) {
             identStart = userbuf.addressOfNextRawChar() - 1;
             hadUnicodeEscape = false;
             goto identifier;
         }
 
         goto badchar;
     }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -32,16 +32,21 @@ namespace gc {
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 struct FinalizePhase;
 class MarkingValidator;
 struct AutoPrepareForTracing;
 class AutoTraceSession;
 
+#ifdef JSGC_COMPACTING
+struct ArenasToUpdate;
+struct MovingTracer;
+#endif
+
 class ChunkPool
 {
     Chunk *head_;
     size_t count_;
 
   public:
     ChunkPool() : head_(nullptr), count_(0) {}
 
@@ -554,16 +559,18 @@ class GCRuntime
     void sweepBackgroundThings();
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
 #ifdef JSGC_COMPACTING
     void sweepTypesAfterCompacting(Zone *zone);
     void sweepZoneAfterCompacting(Zone *zone);
     void compactPhase(bool lastGC);
     ArenaHeader *relocateArenas();
+    void updateAllCellPointersParallel(ArenasToUpdate &source);
+    void updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source);
     void updatePointersToRelocatedCells();
     void releaseRelocatedArenas(ArenaHeader *relocatedList);
 #ifdef DEBUG
     void protectRelocatedArenas(ArenaHeader *relocatedList);
     void unprotectRelocatedArenas(ArenaHeader *relocatedList);
 #endif
 #endif
     void finishCollection();
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -626,16 +626,19 @@ struct ArenaHeader : public JS::shadow::
     inline ArenaHeader *getNextDelayedMarking() const;
     inline void setNextDelayedMarking(ArenaHeader *aheader);
     inline void unsetDelayedMarking();
 
     inline ArenaHeader *getNextAllocDuringSweep() const;
     inline void setNextAllocDuringSweep(ArenaHeader *aheader);
     inline void unsetAllocDuringSweep();
 
+    inline void setNextArenaToUpdate(ArenaHeader *aheader);
+    inline ArenaHeader *getNextArenaToUpdateAndUnlink();
+
     void unmarkAll();
 
 #ifdef JSGC_COMPACTING
     size_t countUsedCells();
     size_t countFreeCells();
 #endif
 };
 
@@ -1144,16 +1147,33 @@ ArenaHeader::setNextAllocDuringSweep(Are
 inline void
 ArenaHeader::unsetAllocDuringSweep()
 {
     MOZ_ASSERT(allocatedDuringIncremental);
     allocatedDuringIncremental = 0;
     auxNextLink = 0;
 }
 
+inline ArenaHeader *
+ArenaHeader::getNextArenaToUpdateAndUnlink()
+{
+    MOZ_ASSERT(!hasDelayedMarking && !allocatedDuringIncremental && !markOverflow);
+    ArenaHeader *next = &reinterpret_cast<Arena *>(auxNextLink << ArenaShift)->aheader;
+    auxNextLink = 0;
+    return next;
+}
+
+inline void
+ArenaHeader::setNextArenaToUpdate(ArenaHeader *aheader)
+{
+    MOZ_ASSERT(!hasDelayedMarking && !allocatedDuringIncremental && !markOverflow);
+    MOZ_ASSERT(!auxNextLink);
+    auxNextLink = aheader->arenaAddress() >> ArenaShift;
+}
+
 static void
 AssertValidColor(const TenuredCell *thing, uint32_t color)
 {
 #ifdef DEBUG
     ArenaHeader *aheader = thing->arenaHeader();
     MOZ_ASSERT(color < aheader->getThingSize() / CellSize);
 #endif
 }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -187,43 +187,47 @@ CheckMarkedThing(JSTracer *trc, T **thin
 
     /*
      * Permanent atoms are not associated with this runtime, but will be ignored
      * during marking.
      */
     if (ThingIsPermanentAtom(thing))
         return;
 
-    MOZ_ASSERT(thing->zone());
-    MOZ_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime());
+    Zone *zone = thing->zoneFromAnyThread();
+    JSRuntime *rt = trc->runtime();
+
+#ifdef JSGC_COMPACTING
+    MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
+    MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
+#else
+    MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+#endif
+
+    MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime());
     MOZ_ASSERT(trc->hasTracingDetails());
 
-    DebugOnly<JSRuntime *> rt = trc->runtime();
-
     bool isGcMarkingTracer = IS_GC_MARKING_TRACER(trc);
 
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-
-    MOZ_ASSERT_IF(thing->zone()->requireGCTracer(), isGcMarkingTracer);
+    MOZ_ASSERT_IF(zone->requireGCTracer(), isGcMarkingTracer);
 
     MOZ_ASSERT(thing->isAligned());
 
     MOZ_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
 
     if (isGcMarkingTracer) {
         GCMarker *gcMarker = static_cast<GCMarker *>(trc);
         MOZ_ASSERT_IF(gcMarker->shouldCheckCompartments(),
-                      thing->zone()->isCollecting() || rt->isAtomsZone(thing->zone()));
+                      zone->isCollecting() || rt->isAtomsZone(zone));
 
         MOZ_ASSERT_IF(gcMarker->getMarkColor() == GRAY,
-                      !thing->zone()->isGCMarkingBlack() || rt->isAtomsZone(thing->zone()));
+                      !zone->isGCMarkingBlack() || rt->isAtomsZone(zone));
 
-        MOZ_ASSERT(!(thing->zone()->isGCSweeping() ||
-                     thing->zone()->isGCFinished() ||
-                     thing->zone()->isGCCompacting()));
+        MOZ_ASSERT(!(zone->isGCSweeping() || zone->isGCFinished() || zone->isGCCompacting()));
     }
 
     /*
      * Try to assert that the thing is allocated.  This is complicated by the
      * fact that allocated things may still contain the poison pattern if that
      * part has not been overwritten, and that the free span list head in the
      * ArenaHeader may not be synced with the real one in ArenaLists.
      */
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -319,17 +319,17 @@ static const PhaseInfo phases[] = {
         { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
         { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP },
         { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP },
         { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP },
         { PHASE_DESTROY, "Deallocate", PHASE_SWEEP },
     { PHASE_COMPACT, "Compact", PHASE_NO_PARENT },
         { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT },
         { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, },
-            { PHASE_COMPACT_UPDATE_GRAY, "Compact Update Gray", PHASE_COMPACT_UPDATE, },
+            { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE, },
     { PHASE_GC_END, "End Callback", PHASE_NO_PARENT },
     { PHASE_LIMIT, nullptr, PHASE_NO_PARENT }
 };
 
 static void
 FormatPhaseTimes(StatisticsSerializer &ss, const char *name, int64_t *times)
 {
     ss.beginObject(name);
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -63,17 +63,17 @@ enum Phase {
     PHASE_SWEEP_SCRIPT,
     PHASE_SWEEP_SHAPE,
     PHASE_SWEEP_JITCODE,
     PHASE_FINALIZE_END,
     PHASE_DESTROY,
     PHASE_COMPACT,
     PHASE_COMPACT_MOVE,
     PHASE_COMPACT_UPDATE,
-    PHASE_COMPACT_UPDATE_GRAY,
+    PHASE_COMPACT_UPDATE_CELLS,
     PHASE_GC_END,
 
     PHASE_LIMIT
 };
 
 enum Stat {
     STAT_NEW_CHUNK,
     STAT_DESTROY_CHUNK,
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -187,20 +187,27 @@ struct Zone : public JS::shadow::Zone,
 
     bool isCollecting() const {
         if (runtimeFromMainThread()->isHeapCollecting())
             return gcState_ != NoGC;
         else
             return needsIncrementalBarrier();
     }
 
+    bool isCollectingFromAnyThread() const {
+        if (runtimeFromAnyThread()->isHeapCollecting())
+            return gcState_ != NoGC;
+        else
+            return needsIncrementalBarrier();
+    }
+
     // If this returns true, all object tracing must be done with a GC marking
     // tracer.
     bool requireGCTracer() const {
-        JSRuntime *rt = runtimeFromMainThread();
+        JSRuntime *rt = runtimeFromAnyThread();
         return rt->isHeapMajorCollecting() && !rt->isHeapCompacting() && gcState_ != NoGC;
     }
 
     bool isGCMarking() {
         if (runtimeFromMainThread()->isHeapCollecting())
             return gcState_ == Mark || gcState_ == MarkGray;
         else
             return needsIncrementalBarrier();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-01.js
@@ -0,0 +1,17 @@
+// Test that the onNewPromise hook gets called when promises are allocated in
+// the scope of debuggee globals.
+
+var g = newGlobal();
+var dbg = new Debugger();
+var gw = dbg.addDebuggee(g);
+
+
+let promisesFound = [];
+dbg.onNewPromise = p => { promisesFound.push(p); };
+
+let p1 = g.makeFakePromise()
+dbg.enabled = false;
+let p2 = g.makeFakePromise();
+
+assertEq(promisesFound.indexOf(gw.makeDebuggeeValue(p1)) != -1, true);
+assertEq(promisesFound.indexOf(gw.makeDebuggeeValue(p2)) == -1, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-02.js
@@ -0,0 +1,24 @@
+// onNewPromise handlers fire, until they are removed.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+log = '';
+g.makeFakePromise();
+assertEq(log, '');
+
+dbg.onNewPromise = function (promise) {
+  log += 'n';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+};
+
+log = '';
+g.makeFakePromise();
+assertEq(log, 'n');
+
+log = '';
+dbg.onNewPromise = undefined;
+g.makeFakePromise();
+assertEq(log, '');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-03.js
@@ -0,0 +1,41 @@
+// onNewPromise handlers on different Debugger instances are independent.
+
+var g = newGlobal();
+var dbg1 = new Debugger(g);
+var log1;
+function h1(promise) {
+  log1 += 'n';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+}
+
+var dbg2 = new Debugger(g);
+var log2;
+function h2(promise) {
+  log2 += 'n';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+}
+
+log1 = log2 = '';
+g.makeFakePromise();
+assertEq(log1, '');
+assertEq(log2, '');
+
+log1 = log2 = '';
+dbg1.onNewPromise = h1;
+g.makeFakePromise();
+assertEq(log1, 'n');
+assertEq(log2, '');
+
+log1 = log2 = '';
+dbg2.onNewPromise = h2;
+g.makeFakePromise();
+assertEq(log1, 'n');
+assertEq(log2, 'n');
+
+log1 = log2 = '';
+dbg1.onNewPromise = undefined;
+g.makeFakePromise();
+assertEq(log1, '');
+assertEq(log2, 'n');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-04.js
@@ -0,0 +1,15 @@
+// An onNewPromise handler can disable itself.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+dbg.onNewPromise = function (promise) {
+  log += 'n';
+  dbg.onNewPromise = undefined;
+};
+
+log = '';
+g.makeFakePromise();
+g.makeFakePromise();
+assertEq(log, 'n');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-05.js
@@ -0,0 +1,24 @@
+// Creating a promise within an onNewPromise handler causes a recursive handler
+// invocation.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+var depth;
+
+dbg.onNewPromise = function (promise) {
+  log += '('; depth++;
+
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+
+  if (depth < 3)
+    g.makeFakePromise();
+
+  log += ')'; depth--;
+};
+
+log = '';
+depth = 0;
+g.makeFakePromise();
+assertEq(log, '((()))');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-06.js
@@ -0,0 +1,35 @@
+// Resumption values from onNewPromise handlers are disallowed.
+
+load(libdir + 'asserts.js');
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+dbg.onNewPromise = function (g) { log += 'n'; return undefined; };
+log = '';
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(log, 'n');
+
+dbg.uncaughtExceptionHook = function (ex) { assertEq(/disallowed/.test(ex), true); log += 'u'; }
+dbg.onNewPromise = function (g) { log += 'n'; return { return: "snoo" }; };
+log = '';
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(log, 'nu');
+
+dbg.onNewPromise = function (g) { log += 'n'; return { throw: "snoo" }; };
+log = '';
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(log, 'nu');
+
+dbg.onNewPromise = function (g) { log += 'n'; return null; };
+log = '';
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(log, 'nu');
+
+dbg.uncaughtExceptionHook = function (ex) { assertEq(/foopy/.test(ex), true); log += 'u'; }
+dbg.onNewPromise = function (g) { log += 'n'; throw "foopy"; };
+log = '';
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(log, 'nu');
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-07.js
@@ -0,0 +1,13 @@
+// Errors in onNewPromise handlers are reported correctly, and don't mess up the
+// promise creation.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+let e;
+dbg.uncaughtExceptionHook = ee => { e = ee; };
+dbg.onNewPromise = () => { throw new Error("woops!"); };
+
+assertEq(typeof g.makeFakePromise(), "object");
+assertEq(!!e, true);
+assertEq(!!e.message.match(/woops/), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-01.js
@@ -0,0 +1,25 @@
+// Test that the onPromiseSettled hook gets called when a promise settles.
+
+var g = newGlobal();
+var dbg = new Debugger();
+var gw = dbg.addDebuggee(g);
+
+let log = "";
+let pw;
+dbg.onPromiseSettled = pw_ => {
+  pw = pw_;
+  log += "s";
+};
+
+let p = g.makeFakePromise();
+g.settleFakePromise(p);
+
+assertEq(log, "s");
+assertEq(pw, gw.makeDebuggeeValue(p));
+
+log = "";
+dbg.enabled = false;
+p = g.makeFakePromise();
+g.settleFakePromise(p);
+
+assertEq(log, "");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-02.js
@@ -0,0 +1,24 @@
+// onPromiseSettled handlers fire, until they are removed.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, '');
+
+dbg.onPromiseSettled = function (promise) {
+  log += 's';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+};
+
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 's');
+
+log = '';
+dbg.onPromiseSettled = undefined;
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, '');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-03.js
@@ -0,0 +1,41 @@
+// onPromiseSettled handlers on different Debugger instances are independent.
+
+var g = newGlobal();
+var dbg1 = new Debugger(g);
+var log1;
+function h1(promise) {
+  log1 += 's';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+}
+
+var dbg2 = new Debugger(g);
+var log2;
+function h2(promise) {
+  log2 += 's';
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+}
+
+log1 = log2 = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log1, '');
+assertEq(log2, '');
+
+log1 = log2 = '';
+dbg1.onPromiseSettled = h1;
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log1, 's');
+assertEq(log2, '');
+
+log1 = log2 = '';
+dbg2.onPromiseSettled = h2;
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log1, 's');
+assertEq(log2, 's');
+
+log1 = log2 = '';
+dbg1.onPromiseSettled = undefined;
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log1, '');
+assertEq(log2, 's');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-04.js
@@ -0,0 +1,15 @@
+// An onPromiseSettled handler can disable itself.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+dbg.onPromiseSettled = function (promise) {
+  log += 's';
+  dbg.onPromiseSettled = undefined;
+};
+
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 's');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-05.js
@@ -0,0 +1,24 @@
+// Settling a promise within an onPromiseSettled handler causes a recursive
+// handler invocation.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+var depth;
+
+dbg.onPromiseSettled = function (promise) {
+  log += '('; depth++;
+
+  assertEq(promise.seen, undefined);
+  promise.seen = true;
+
+  if (depth < 3)
+    g.settleFakePromise(g.makeFakePromise());
+
+  log += ')'; depth--;
+};
+
+log = '';
+depth = 0;
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, '((()))');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-06.js
@@ -0,0 +1,35 @@
+// Resumption values from onPromiseSettled handlers are disallowed.
+
+load(libdir + 'asserts.js');
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var log;
+
+dbg.onPromiseSettled = function (g) { log += 's'; return undefined; };
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 's');
+
+dbg.uncaughtExceptionHook = function (ex) { assertEq(/disallowed/.test(ex), true); log += 'u'; }
+dbg.onPromiseSettled = function (g) { log += 's'; return { return: "snoo" }; };
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 'su');
+
+dbg.onPromiseSettled = function (g) { log += 's'; return { throw: "snoo" }; };
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 'su');
+
+dbg.onPromiseSettled = function (g) { log += 's'; return null; };
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 'su');
+
+dbg.uncaughtExceptionHook = function (ex) { assertEq(/foopy/.test(ex), true); log += 'u'; }
+dbg.onPromiseSettled = function (g) { log += 's'; throw "foopy"; };
+log = '';
+g.settleFakePromise(g.makeFakePromise());
+assertEq(log, 'su');
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/bug1098947.js
@@ -0,0 +1,11 @@
+function f() {
+    try {
+        let foo = 3;
+        for (var i=0; i<50; i++)
+            yield i + foo;
+    } catch(e) {}
+}
+var it = f();
+for (var i=0; i<40; i++)
+    it.next();
+it.close();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3575,16 +3575,17 @@ BaselineCompiler::emit_JSOP_RESUME()
         {
             masm.pushValue(Address(scratch2, 0));
             masm.addPtr(Imm32(sizeof(Value)), scratch2);
             masm.sub32(Imm32(1), initLength);
             masm.jump(&loop);
         }
         masm.bind(&loopDone);
 
+        masm.patchableCallPreBarrier(exprStackSlot, MIRType_Value);
         masm.storeValue(NullValue(), exprStackSlot);
         regs.add(initLength);
     }
 
     masm.bind(&noExprStack);
     masm.pushValue(retVal);
 
     if (resumeKind == GeneratorObject::NEXT) {
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4534,18 +4534,17 @@ ICGetElem_Dense::Compiler::generateStubC
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Bounds check.
     Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
 
     // Hole check and load value.
-    JS_STATIC_ASSERT(sizeof(Value) == 8);
-    BaseIndex element(scratchReg, key, TimesEight);
+    BaseObjectElementIndex element(scratchReg, key);
     masm.branchTestMagic(Assembler::Equal, element, &failure);
 
     // Check if __noSuchMethod__ should be called.
 #if JS_HAS_NO_SUCH_METHOD
 #ifdef DEBUG
     entersStubFrame_ = true;
 #endif
     if (isCallElem_) {
@@ -4705,20 +4704,19 @@ ICGetElem_Arguments::Compiler::generateS
         // Load num actual arguments
         Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
         masm.loadPtr(actualArgs, scratch);
 
         // Ensure idx < argc
         masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure);
 
         // Load argval
-        JS_STATIC_ASSERT(sizeof(Value) == 8);
         masm.movePtr(BaselineFrameReg, scratch);
         masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch);
-        BaseIndex element(scratch, idx, TimesEight);
+        BaseValueIndex element(scratch, idx);
         masm.loadValue(element, R0);
 
         // Enter type monitor IC to type-check result.
         EmitEnterTypeMonitorIC(masm);
 
         masm.bind(&failure);
         EmitStubGuardFailure(masm);
         return true;
@@ -4782,17 +4780,17 @@ ICGetElem_Arguments::Compiler::generateS
     // Don't bother testing specific bit, if any bit is set in the word, fail.
     masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(nullptr), &failureReconstructInputs);
 
     // Load the value.  use scratchReg and tempReg to form a ValueOperand to load into.
     masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
     regs.add(scratchReg);
     regs.add(tempReg);
     ValueOperand tempVal = regs.takeAnyValue();
-    masm.loadValue(BaseIndex(argData, idxReg, ScaleFromElemWidth(sizeof(Value))), tempVal);
+    masm.loadValue(BaseValueIndex(argData, idxReg), tempVal);
 
     // Makesure that this is not a FORWARD_TO_CALL_SLOT magic value.
     masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs);
 
 #if JS_HAS_NO_SUCH_METHOD
     if (isCallElem_) {
         Label afterNoSuchMethod;
         Label skipNoSuchMethod;
@@ -9262,18 +9260,17 @@ ICCallStubCompiler::pushCallerArguments(
 {
     // Initialize copyReg to point to start caller arguments vector.
     // Initialize argcReg to poitn to the end of it.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.loadPtr(Address(BaselineFrameReg, 0), startReg);
     masm.loadPtr(Address(startReg, BaselineFrame::offsetOfNumActualArgs()), endReg);
     masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), startReg);
-    JS_STATIC_ASSERT(sizeof(Value) == 8);
-    masm.lshiftPtr(Imm32(3), endReg);
+    masm.lshiftPtr(Imm32(ValueShift), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
     masm.bind(&copyStart);
     masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
     masm.subPtr(Imm32(sizeof(Value)), endReg);
@@ -9289,18 +9286,17 @@ ICCallStubCompiler::pushArrayArguments(M
     // Load start and end address of values to copy.
     // guardFunApply has already gauranteed that the array is packed and contains
     // no holes.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.extractObject(arrayVal, startReg);
     masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
     masm.load32(Address(startReg, ObjectElements::offsetOfInitializedLength()), endReg);
-    JS_STATIC_ASSERT(sizeof(Value) == 8);
-    masm.lshiftPtr(Imm32(3), endReg);
+    masm.lshiftPtr(Imm32(ValueShift), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
     masm.bind(&copyStart);
     masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
     masm.subPtr(Imm32(sizeof(Value)), endReg);
@@ -9449,18 +9445,17 @@ ICCallScriptedCompiler::generateStubCode
     if (isSpread_)
         guardSpreadCall(masm, argcReg, &failure);
 
     // Load the callee in R1.
     // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ]
     if (isSpread_) {
         masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R1);
     } else {
-        BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight,
-                             ICStackValueOffset + sizeof(Value));
+        BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value));
         masm.loadValue(calleeSlot, R1);
     }
     regs.take(R1);
 
     // Ensure callee is an object.
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     // Ensure callee is a function.
@@ -9510,18 +9505,18 @@ ICCallScriptedCompiler::generateStubCode
         masm.push(argcReg);
 
         // Stack now looks like:
         //      [..., Callee, ThisV, Arg0V, ..., ArgNV, StubFrameHeader, ArgC ]
         if (isSpread_) {
             masm.loadValue(Address(BaselineStackReg,
                                    2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)), R1);
         } else {
-            BaseIndex calleeSlot2(BaselineStackReg, argcReg, TimesEight,
-                                  sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t));
+            BaseValueIndex calleeSlot2(BaselineStackReg, argcReg,
+                                       sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t));
             masm.loadValue(calleeSlot2, R1);
         }
         masm.push(masm.extractObject(R1, ExtractTemp0));
         if (!callVM(CreateThisInfoBaseline, masm))
             return false;
 
         // Return of CreateThis must be an object.
 #ifdef DEBUG
@@ -9543,34 +9538,33 @@ ICCallScriptedCompiler::generateStubCode
         masm.pop(argcReg);
 
         // Save "this" value back into pushed arguments on stack.  R0 can be clobbered after that.
         // Stack now looks like:
         //      [..., Callee, ThisV, Arg0V, ..., ArgNV, StubFrameHeader ]
         if (isSpread_) {
             masm.storeValue(R0, Address(BaselineStackReg, sizeof(Value) + STUB_FRAME_SIZE));
         } else {
-            BaseIndex thisSlot(BaselineStackReg, argcReg, TimesEight, STUB_FRAME_SIZE);
+            BaseValueIndex thisSlot(BaselineStackReg, argcReg, STUB_FRAME_SIZE);
             masm.storeValue(R0, thisSlot);
         }
 
         // Restore the stub register from the baseline stub frame.
         masm.loadPtr(Address(BaselineStackReg, STUB_FRAME_SAVED_STUB_OFFSET), BaselineStubReg);
 
         // Reload callee script. Note that a GC triggered by CreateThis may
         // have destroyed the callee BaselineScript and IonScript. CreateThis is
         // safely repeatable though, so in this case we just leave the stub frame
         // and jump to the next stub.
 
         // Just need to load the script now.
         if (isSpread_) {
             masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + STUB_FRAME_SIZE), R0);
         } else {
-            BaseIndex calleeSlot3(BaselineStackReg, argcReg, TimesEight,
-                                  sizeof(Value) + STUB_FRAME_SIZE);
+            BaseValueIndex calleeSlot3(BaselineStackReg, argcReg, sizeof(Value) + STUB_FRAME_SIZE);
             masm.loadValue(calleeSlot3, R0);
         }
         callee = masm.extractObject(R0, ExtractTemp0);
         regs.add(R0);
         regs.takeUnchecked(callee);
         masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
 
         code = regs.takeAny();
@@ -9659,18 +9653,18 @@ ICCallScriptedCompiler::generateStubCode
         // pushed copies of argument values for non-spread, increment it to account for skipping
         // actual argument values and array object for spread.  Additionally, we need to add:
         // STUB_FRAME_SIZE + sizeof(ThisVal) + sizeof(size_t) + sizeof(void *) + sizoef(size_t)
         // for: stub frame, this value, actual argc, callee, and descriptor
         if (isSpread_)
             masm.add32(Imm32(1), scratchReg);
         else
             masm.lshiftPtr(Imm32(1), scratchReg);
-        BaseIndex reloadThisSlot(BaselineStackReg, scratchReg, TimesEight,
-                                 STUB_FRAME_SIZE + sizeof(Value) + 3*sizeof(size_t));
+        BaseValueIndex reloadThisSlot(BaselineStackReg, scratchReg,
+                                      STUB_FRAME_SIZE + sizeof(Value) + 3 * sizeof(size_t));
         masm.loadValue(reloadThisSlot, JSReturnOperand);
 #ifdef DEBUG
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
         masm.assumeUnreachable("Return of constructing call should be an object.");
 #endif
         masm.bind(&skipThisReplace);
     }
 
@@ -9838,17 +9832,17 @@ ICCall_Native::Compiler::generateStubCod
 
     if (isSpread_)
         guardSpreadCall(masm, argcReg, &failure);
 
     // Load the callee in R1.
     if (isSpread_) {
         masm.loadValue(Address(BaselineStackReg, ICStackValueOffset + 2 * sizeof(Value)), R1);
     } else {
-        BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset + sizeof(Value));
+        BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value));
         masm.loadValue(calleeSlot, R1);
     }
     regs.take(R1);
 
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     // Ensure callee matches this stub's callee.
     Register callee = masm.extractObject(R1, ExtractTemp0);
@@ -9940,17 +9934,17 @@ ICCall_ClassHook::Compiler::generateStub
     Label failure;
     GeneralRegisterSet regs(availableGeneralRegs(0));
 
     Register argcReg = R0.scratchReg();
     regs.take(argcReg);
     regs.takeUnchecked(BaselineTailCallReg);
 
     // Load the callee in R1.
-    BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset + sizeof(Value));
+    BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value));
     masm.loadValue(calleeSlot, R1);
     regs.take(R1);
 
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     // Ensure the callee's class matches the one in this stub.
     Register callee = masm.extractObject(R1, ExtractTemp0);
     Register scratch = regs.takeAny();
@@ -10244,17 +10238,17 @@ ICCall_ScriptedFunCall::Compiler::genera
     MOZ_ASSERT(argcReg != ArgumentsRectifierReg);
 
     regs.take(argcReg);
     regs.take(ArgumentsRectifierReg);
     regs.takeUnchecked(BaselineTailCallReg);
 
     // Load the callee in R1.
     // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ]
-    BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset + sizeof(Value));
+    BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value));
     masm.loadValue(calleeSlot, R1);
     regs.take(R1);
 
     // Ensure callee is js_fun_call.
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     Register callee = masm.extractObject(R1, ExtractTemp0);
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
--- a/js/src/jit/BitSet.h
+++ b/js/src/jit/BitSet.h
@@ -124,18 +124,17 @@ class BitSet::Iterator
         // Skip words containing only zeros.
         unsigned numWords = set_.numWords();
         const uint32_t *bits = set_.bits_;
         while (value_ == 0) {
             word_++;
             if (word_ == numWords)
                 return;
 
-            JS_STATIC_ASSERT(sizeof(value_) * 8 == BitSet::BitsPerWord);
-            index_ = word_ * sizeof(value_) * 8;
+            index_ = word_ * BitSet::BitsPerWord;
             value_ = bits[word_];
         }
 
         // Be careful: the result of CountTrailingZeroes32 is undefined if the
         // input is 0.
         int numZeros = mozilla::CountTrailingZeroes32(value_);
         index_ += numZeros;
         value_ >>= numZeros;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2092,20 +2092,22 @@ CodeGenerator::visitOsrEntry(LOsrEntry *
     if (gen->info().executionMode() == SequentialExecution) {
         if (!emitTracelogStopEvent(TraceLogger::Baseline))
             return false;
         if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
             return false;
     }
 #endif
 
-    // Allocate the full frame for this function.
-    uint32_t size = frameSize();
-    if (size != 0)
-        masm.subPtr(Imm32(size), StackPointer);
+    // Allocate the full frame for this function
+    // Note we have a new entry here. So we reset MacroAssembler::framePushed()
+    // to 0, before reserving the stack.
+    MOZ_ASSERT(masm.framePushed() == frameSize());
+    masm.setFramePushed(0);
+    masm.reserveStack(frameSize());
     return true;
 }
 
 bool
 CodeGenerator::visitOsrScopeChain(LOsrScopeChain *lir)
 {
     const LAllocation *frame   = lir->getOperand(0);
     const LDefinition *object  = lir->getDef(0);
@@ -3273,19 +3275,19 @@ CodeGenerator::emitPushArguments(LApplyA
     masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
 
     // Copy arguments.
     {
         Register count = extraStackSpace; // <- argcreg
         Label loop;
         masm.bind(&loop);
 
-        // We remove sizeof(void*) from argvOffset because withtout it we target
+        // We remove sizeof(void*) from argvOffset because without it we target
         // the address after the memory area that we want to copy.
-        BaseIndex disp(StackPointer, argcreg, ScaleFromElemWidth(sizeof(Value)), argvOffset - sizeof(void*));
+        BaseValueIndex disp(StackPointer, argcreg, argvOffset - sizeof(void*));
 
         // Do not use Push here because other this account to 1 in the framePushed
         // instead of 0.  These push are only counted by argcreg.
         masm.loadPtr(disp, copyreg);
         masm.push(copyreg);
 
         // Handle 32 bits architectures.
         if (sizeof(Value) == 2 * sizeof(void*)) {
@@ -3293,17 +3295,18 @@ CodeGenerator::emitPushArguments(LApplyA
             masm.push(copyreg);
         }
 
         masm.decBranchPtr(Assembler::NonZero, count, Imm32(1), &loop);
     }
 
     // Compute the stack usage.
     masm.movePtr(argcreg, extraStackSpace);
-    masm.lshiftPtr(Imm32::ShiftOf(ScaleFromElemWidth(sizeof(Value))), extraStackSpace);
+    NativeObject::elementsSizeMustNotOverflow();
+    masm.lshiftPtr(Imm32(ValueShift), extraStackSpace);
 
     // Join with all arguments copied and the extra stack usage computed.
     masm.bind(&end);
 
     // Push |this|.
     masm.addPtr(Imm32(sizeof(Value)), extraStackSpace);
     masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
 }
@@ -3588,33 +3591,16 @@ CodeGenerator::generateArgumentsChecks(b
     // This function can be used the normal way to check the argument types,
     // before entering the function and bailout when arguments don't match.
     // For debug purpose, this is can also be used to force/check that the
     // arguments are correct. Upon fail it will hit a breakpoint.
 
     MIRGraph &mir = gen->graph();
     MResumePoint *rp = mir.entryResumePoint();
 
-    // Reserve the amount of stack the actual frame will use. We have to undo
-    // this before falling through to the method proper though, because the
-    // monomorphic call case will bypass this entire path.
-
-    // On windows, we cannot skip very far down the stack without touching the
-    // memory pages in-between.  This is a corner-case code for situations where the
-    // Ion frame data for a piece of code is very large.  To handle this special case,
-    // for frames over 1k in size we allocate memory on the stack incrementally, touching
-    // it as we go.
-    uint32_t frameSizeLeft = frameSize();
-    while (frameSizeLeft > 4096) {
-        masm.reserveStack(4096);
-        masm.store32(Imm32(0), Address(StackPointer, 0));
-        frameSizeLeft -= 4096;
-    }
-    masm.reserveStack(frameSizeLeft);
-
     // No registers are allocated yet, so it's safe to grab anything.
     Register temp = GeneralRegisterSet(EntryTempMask).getAny();
 
     CompileInfo &info = gen->info();
 
     Label miss;
     for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
         // All initial parameters are guaranteed to be MParameters.
@@ -3639,18 +3625,16 @@ CodeGenerator::generateArgumentsChecks(b
             Label success;
             masm.jump(&success);
             masm.bind(&miss);
             masm.assumeUnreachable("Argument check fail.");
             masm.bind(&success);
         }
     }
 
-    masm.freeStack(frameSize());
-
     return true;
 }
 
 // Out-of-line path to report over-recursed error and fail.
 class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
 {
     LInstruction *lir_;
 
@@ -7233,17 +7217,17 @@ CodeGenerator::visitGetFrameArgument(LGe
     size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
 
     if (index->isConstant()) {
         int32_t i = index->toConstant()->toInt32();
         Address argPtr(StackPointer, sizeof(Value) * i + argvOffset);
         masm.loadValue(argPtr, result);
     } else {
         Register i = ToRegister(index);
-        BaseIndex argPtr(StackPointer, i, ScaleFromElemWidth(sizeof(Value)), argvOffset);
+        BaseValueIndex argPtr(StackPointer, i, argvOffset);
         masm.loadValue(argPtr, result);
     }
     return true;
 }
 
 bool
 CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir)
 {
@@ -7450,65 +7434,60 @@ CodeGenerator::generate()
         return false;
 
     if (!snapshots_.init())
         return false;
 
     if (!safepoints_.init(gen->alloc(), graph.totalSlotCount()))
         return false;
 
-#ifdef JS_TRACE_LOGGING
-    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
-        if (!emitTracelogScriptStart())
-            return false;
-        if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
-            return false;
-    }
-#endif
+    if (!generatePrologue())
+        return false;
 
     // Before generating any code, we generate type checks for all parameters.
     // This comes before deoptTable_, because we can't use deopt tables without
     // creating the actual frame.
     if (!generateArgumentsChecks())
         return false;
 
     if (frameClass_ != FrameSizeClass::None()) {
         deoptTable_ = gen->jitRuntime()->getBailoutTable(frameClass_);
         if (!deoptTable_)
             return false;
     }
 
-#ifdef JS_TRACE_LOGGING
-    Label skip;
-    masm.jump(&skip);
-#endif
-
-    // Remember the entry offset to skip the argument check.
+    // Skip over the alternative entry to IonScript code.
+    Label skipPrologue;
+    masm.jump(&skipPrologue);
+
+    // An alternative entry to the IonScript code, which doesn't test the
+    // arguments.
     masm.flushBuffer();
     setSkipArgCheckEntryOffset(masm.size());
+    masm.setFramePushed(0);
+    if (!generatePrologue())
+        return false;
+
+    masm.bind(&skipPrologue);
 
 #ifdef JS_TRACE_LOGGING
     if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
         if (!emitTracelogScriptStart())
             return false;
         if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
             return false;
     }
-    masm.bind(&skip);
 #endif
 
 #ifdef DEBUG
     // Assert that the argument types are correct.
     if (!generateArgumentsChecks(/* bailout = */ false))
         return false;
 #endif
 
-    if (!generatePrologue())
-        return false;
-
     // Reset native => bytecode map table with top-level script and startPc.
     if (!addNativeToBytecodeEntry(startSite))
         return false;
 
     if (!generateBody())
         return false;
 
     // Reset native => bytecode map table with top-level script and startPc.
@@ -8842,20 +8821,22 @@ CodeGenerator::visitLoadElementT(LLoadEl
 }
 
 bool
 CodeGenerator::visitLoadElementV(LLoadElementV *load)
 {
     Register elements = ToRegister(load->elements());
     const ValueOperand out = ToOutValue(load);
 
-    if (load->index()->isConstant())
+    if (load->index()->isConstant()) {
+        NativeObject::elementsSizeMustNotOverflow();
         masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
-    else
-        masm.loadValue(BaseIndex(elements, ToRegister(load->index()), TimesEight), out);
+    } else {
+        masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())), out);
+    }
 
     if (load->mir()->needsHoleCheck()) {
         Label testMagic;
         masm.branchTestMagic(Assembler::Equal, out, &testMagic);
         if (!bailoutFrom(&testMagic, load->snapshot()))
             return false;
     }
 
@@ -8871,20 +8852,21 @@ CodeGenerator::visitLoadElementHole(LLoa
 
     const MLoadElementHole *mir = lir->mir();
 
     // If the index is out of bounds, load |undefined|. Otherwise, load the
     // value.
     Label undefined, done;
     if (lir->index()->isConstant()) {
         masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(ToInt32(lir->index())), &undefined);
+        NativeObject::elementsSizeMustNotOverflow();
         masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
     } else {
         masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
-        masm.loadValue(BaseIndex(elements, ToRegister(lir->index()), TimesEight), out);
+        masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
     }
 
     // If a hole check is needed, and the value wasn't a hole, we're done.
     // Otherwise, we'll load undefined.
     if (lir->mir()->needsHoleCheck())
         masm.branchTestMagic(Assembler::NotEqual, out, &done);
     else
         masm.jump(&done);
@@ -9236,16 +9218,17 @@ CodeGenerator::visitInArray(LInArray *li
             ool = oolCallVM(OperatorInIInfo, lir,
                             (ArgList(), Imm32(index), ToRegister(lir->object())),
                             StoreRegisterTo(output));
             failedInitLength = ool->entry();
         }
 
         masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
         if (mir->needsHoleCheck()) {
+            NativeObject::elementsSizeMustNotOverflow();
             Address address = Address(elements, index * sizeof(Value));
             masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
         }
     } else {
         Label negativeIntCheck;
         Register index = ToRegister(lir->index());
 
         if (mir->needsNegativeIntCheck())
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -444,24 +444,27 @@ JitCompartment::notifyOfActiveParallelEn
     // already in the set.
     if (script->parallelIonScript()->isParallelEntryScript()) {
         MOZ_ASSERT(activeParallelEntryScripts_ && activeParallelEntryScripts_->has(script));
         script->parallelIonScript()->resetParallelAge();
         return true;
     }
 
     if (!activeParallelEntryScripts_) {
-        activeParallelEntryScripts_ = cx->new_<ScriptSet>(cx);
-        if (!activeParallelEntryScripts_ || !activeParallelEntryScripts_->init())
+        ScriptSet *scripts = js_new<ScriptSet>();
+        if (!scripts || !scripts->init()) {
+            js_delete(scripts);
+            js_ReportOutOfMemory(cx);
             return false;
+        }
+        activeParallelEntryScripts_ = scripts;
     }
 
     script->parallelIonScript()->setIsParallelEntryScript();
-    ScriptSet::AddPtr p = activeParallelEntryScripts_->lookupForAdd(script);
-    return p || activeParallelEntryScripts_->add(p, script);
+    return activeParallelEntryScripts_->put(script);
 }
 
 bool
 JitCompartment::hasRecentParallelActivity() const
 {
     return activeParallelEntryScripts_ && !activeParallelEntryScripts_->empty();
 }
 
@@ -583,30 +586,31 @@ JitCompartment::mark(JSTracer *trc, JSCo
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
 
             // If the script has since been invalidated or was attached by an
             // off-thread helper too late (i.e., the ForkJoin finished with
             // warmup doing all the work), remove it.
             if (!script->hasParallelIonScript() ||
-                !script->parallelIonScript()->isParallelEntryScript() ||
-                trc->runtime()->gc.shouldCleanUpEverything())
+                !script->parallelIonScript()->isParallelEntryScript())
             {
                 e.removeFront();
                 continue;
             }
 
             // Check and increment the age. If the script is below the max
             // age, mark it.
             //
             // Subtlety: We depend on the tracing of the parallel IonScript's
             // callTargetEntries to propagate the parallel age to the entire
             // call graph.
-            if (script->parallelIonScript()->shouldPreserveParallelCode(IonScript::IncreaseAge)) {
+            if (!trc->runtime()->gc.shouldCleanUpEverything() &&
+                script->parallelIonScript()->shouldPreserveParallelCode(IonScript::IncreaseAge))
+            {
                 MarkScript(trc, const_cast<PreBarrieredScript *>(&e.front()), "par-script");
                 MOZ_ASSERT(script == e.front());
             } else {
                 script->parallelIonScript()->clearIsParallelEntryScript();
                 e.removeFront();
             }
         }
     }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -654,16 +654,17 @@ IsCacheableGetPropCallPropertyOp(JSObjec
     return true;
 }
 
 static inline void
 EmitLoadSlot(MacroAssembler &masm, NativeObject *holder, Shape *shape, Register holderReg,
              TypedOrValueRegister output, Register scratchReg)
 {
     MOZ_ASSERT(holder);
+    NativeObject::slotsSizeMustNotOverflow();
     if (holder->isFixedSlot(shape->slot())) {
         Address addr(holderReg, NativeObject::getFixedSlotOffset(shape->slot()));
         masm.loadTypedOrValue(addr, output);
     } else {
         masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
 
         Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
         masm.loadTypedOrValue(addr, output);
@@ -2001,16 +2002,17 @@ GenerateSetSlot(JSContext *cx, MacroAsse
             Register scratchReg = object;
             masm.push(scratchReg);
 
             masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
             masm.pop(object);
         }
     }
 
+    NativeObject::slotsSizeMustNotOverflow();
     if (obj->isFixedSlot(shape->slot())) {
         Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
 
         if (cx->zone()->needsIncrementalBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
 
         masm.storeConstantOrRegister(value, addr);
     } else {
@@ -2607,16 +2609,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse
         masm.jump(&skipPop);
         masm.bind(&noTypeChange);
         masm.pop(object);
         masm.bind(&skipPop);
     }
 
     // Set the value on the object. Since this is an add, obj->lastProperty()
     // must be the shape of the property we are adding.
+    NativeObject::slotsSizeMustNotOverflow();
     if (obj->isFixedSlot(newShape->slot())) {
         Address addr(object, NativeObject::getFixedSlotOffset(newShape->slot()));
         masm.storeConstantOrRegister(value, addr);
     } else {
         Register slotsReg = object;
 
         masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
 
@@ -3177,17 +3180,17 @@ GenerateDenseElement(JSContext *cx, Macr
 
     Label hole;
 
     // Guard on the initialized length.
     Address initLength(object, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
 
     // Check for holes & load the value.
-    masm.loadElementTypedOrValue(BaseIndex(object, indexReg, TimesEight),
+    masm.loadElementTypedOrValue(BaseObjectElementIndex(object, indexReg),
                                  output, true, &hole);
 
     masm.pop(object);
     attacher.jumpRejoin(masm);
 
     // All failures flow to here.
     masm.bind(&hole);
     masm.pop(object);
@@ -3421,17 +3424,17 @@ GetElementIC::attachArgumentsElement(JSC
     masm.branchPtr(Assembler::NotEqual, tmpReg, ImmPtr(nullptr), &failurePopIndex);
 
     // Get the address to load from into tmpReg
     masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg);
     masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), tmpReg);
 
     // Restore original index register value, to use for indexing element.
     masm.pop(indexReg);
-    BaseIndex elemIdx(tmpReg, indexReg, ScaleFromElemWidth(sizeof(Value)));
+    BaseValueIndex elemIdx(tmpReg, indexReg);
 
     // Ensure result is not magic value, and type-check result.
     masm.branchTestMagic(Assembler::Equal, elemIdx, &failureRestoreIndex);
 
     if (output().hasTyped()) {
         MOZ_ASSERT(!output().typedReg().isFloat());
         MOZ_ASSERT(index().reg().type() == MIRType_Boolean ||
                    index().reg().type() == MIRType_Int32 ||
@@ -3595,17 +3598,17 @@ IsTypedArrayElementSetInlineable(JSObjec
 {
     // Don't bother attaching stubs for assigning strings and objects.
     return IsAnyTypedArray(obj) && idval.isInt32() &&
            !value.isString() && !value.isObject();
 }
 
 static void
 StoreDenseElement(MacroAssembler &masm, ConstantOrRegister value, Register elements,
-                  BaseIndex target)
+                  BaseObjectElementIndex target)
 {
     // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
     // have to be converted to double first. If the value is not int32, it can
     // always be stored directly.
 
     Address elementsFlags(elements, ObjectElements::offsetOfFlags());
     if (value.constant()) {
         Value v = value.value();
@@ -3679,17 +3682,17 @@ GenerateSetDenseElement(JSContext *cx, M
     Register index = masm.extractInt32(indexVal, tempToUnboxIndex);
 
     {
         // Load obj->elements.
         Register elements = temp;
         masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
 
         // Compute the location of the element.
-        BaseIndex target(elements, index, TimesEight);
+        BaseObjectElementIndex target(elements, index);
 
         // If TI cannot help us deal with HOLES by preventing indexed properties
         // on the prototype chain, we have to be very careful to check for ourselves
         // to avoid stomping on what should be a setter call. Start by only allowing things
         // within the initialized length.
         if (guardHoles) {
             Address initLength(elements, ObjectElements::offsetOfInitializedLength());
             masm.branch32(Assembler::BelowOrEqual, initLength, index, &outOfBounds);
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -539,18 +539,21 @@ HandleClosingGeneratorReturn(JSContext *
     if (!cx->getPendingException(&exception))
         return;
     if (!exception.isMagic(JS_GENERATOR_CLOSING))
         return;
 
     cx->clearPendingException();
     frame.baselineFrame()->setReturnValue(UndefinedValue());
 
-    if (frame.baselineFrame()->isDebuggee() && unwoundScopeToPc)
-        frame.baselineFrame()->setUnwoundScopeOverridePc(unwoundScopeToPc);
+    if (unwoundScopeToPc) {
+        if (frame.baselineFrame()->isDebuggee())
+            frame.baselineFrame()->setUnwoundScopeOverridePc(unwoundScopeToPc);
+        pc = unwoundScopeToPc;
+    }
 
     ForcedReturn(cx, frame, pc, rfe, calledDebugEpilogue);
 }
 
 static void
 HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
                         jsbytecode **unwoundScopeToPc, bool *calledDebugEpilogue)
 {
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -457,17 +457,18 @@ class JitCompartment
     JitCode *stringConcatStub_;
     JitCode *parallelStringConcatStub_;
     JitCode *regExpExecStub_;
     JitCode *regExpTestStub_;
 
     // Set of JSScripts invoked by ForkJoin (i.e. the entry script). These
     // scripts are marked if their respective parallel IonScripts' age is less
     // than a certain amount. See IonScript::parallelAge_.
-    typedef HashSet<PreBarrieredScript> ScriptSet;
+    typedef HashSet<PreBarrieredScript, DefaultHasher<PreBarrieredScript>, SystemAllocPolicy>
+        ScriptSet;
     ScriptSet *activeParallelEntryScripts_;
 
     JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
     JitCode *generateRegExpExecStub(JSContext *cx);
     JitCode *generateRegExpTestStub(JSContext *cx);
 
   public:
     JitCode *getStubCode(uint32_t key) {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -35,16 +35,17 @@ using JS::GenericNaN;
 CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
 bool
 CodeGeneratorARM::generatePrologue()
 {
+    MOZ_ASSERT(masm.framePushed() == 0);
     MOZ_ASSERT(!gen->compilingAsmJS());
 
     // Note that this automatically sets MacroAssembler::framePushed().
     masm.reserveStack(frameSize());
     masm.checkStackAlignment();
     return true;
 }
 
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -35,17 +35,19 @@ using JS::GenericNaN;
 CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
 bool
 CodeGeneratorMIPS::generatePrologue()
 {
+    MOZ_ASSERT(masm.framePushed() == 0);
     MOZ_ASSERT(!gen->compilingAsmJS());
+
     // Note that this automatically sets MacroAssembler::framePushed().
     masm.reserveStack(frameSize());
     masm.checkStackAlignment();
     return true;
 }
 
 bool
 CodeGeneratorMIPS::generateEpilogue()
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -33,16 +33,21 @@ static_assert(Simd128DataSize == 2 * siz
 
 enum Scale {
     TimesOne = 0,
     TimesTwo = 1,
     TimesFour = 2,
     TimesEight = 3
 };
 
+static_assert(sizeof(JS::Value) == 8,
+              "required for TimesEight and 3 below to be correct");
+static const Scale ValueScale = TimesEight;
+static const size_t ValueShift = 3;
+
 static inline unsigned
 ScaleToShift(Scale scale)
 {
     return unsigned(scale);
 }
 
 static inline bool
 IsShiftInScaleRange(int i)
@@ -304,16 +309,50 @@ struct BaseIndex
 
     BaseIndex(Register base, Register index, Scale scale, int32_t offset = 0)
       : base(base), index(index), scale(scale), offset(offset)
     { }
 
     BaseIndex() { mozilla::PodZero(this); }
 };
 
+// A BaseIndex used to access Values.  Note that |offset| is *not* scaled by
+// sizeof(Value).  Use this *only* if you're indexing into a series of Values
+// that aren't object elements or object slots (for example, values on the
+// stack, values in an arguments object, &c.).  If you're indexing into an
+// object's elements or slots, don't use this directly!  Use
+// BaseObject{Element,Slot}Index instead.
+struct BaseValueIndex : BaseIndex
+{
+    BaseValueIndex(Register base, Register index, int32_t offset = 0)
+      : BaseIndex(base, index, ValueScale, offset)
+    { }
+};
+
+// Specifies the address of an indexed Value within object elements from a
+// base.  The index must not already be scaled by sizeof(Value)!
+struct BaseObjectElementIndex : BaseValueIndex
+{
+    BaseObjectElementIndex(Register base, Register index)
+      : BaseValueIndex(base, index)
+    {
+        NativeObject::elementsSizeMustNotOverflow();
+    }
+};
+
+// Like BaseObjectElementIndex, except for object slots.
+struct BaseObjectSlotIndex : BaseValueIndex
+{
+    BaseObjectSlotIndex(Register base, Register index)
+      : BaseValueIndex(base, index)
+    {
+        NativeObject::slotsSizeMustNotOverflow();
+    }
+};
+
 class Relocation {
   public:
     enum Kind {
         // The target is immovable, so patching is only needed if the source
         // buffer is relocated and the reference is relative.
         HARDCODED,
 
         // The target is the start of a JitCode buffer, which must be traced
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -36,21 +36,21 @@ namespace jit {
 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
 bool
 CodeGeneratorX86Shared::generatePrologue()
 {
+    MOZ_ASSERT(masm.framePushed() == 0);
     MOZ_ASSERT(!gen->compilingAsmJS());
 
     // Note that this automatically sets MacroAssembler::framePushed().
     masm.reserveStack(frameSize());
-
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::generateEpilogue()
 {
     MOZ_ASSERT(!gen->compilingAsmJS());
 
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -547,18 +547,30 @@ class MacroAssemblerX64 : public MacroAs
         cmpPtr(lhs, rhs);
         emitSet(cond, dest);
     }
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
     void reserveStack(uint32_t amount) {
-        if (amount)
-            subq(Imm32(amount), StackPointer);
+        if (amount) {
+            // On windows, we cannot skip very far down the stack without touching the
+            // memory pages in-between.  This is a corner-case code for situations where the
+            // Ion frame data for a piece of code is very large.  To handle this special case,
+            // for frames over 1k in size we allocate memory on the stack incrementally, touching
+            // it as we go.
+            uint32_t amountLeft = amount;
+            while (amountLeft > 4096) {
+                subq(Imm32(4096), StackPointer);
+                store32(Imm32(0), Address(StackPointer, 0));
+                amountLeft -= 4096;
+            }
+            subq(Imm32(amountLeft), StackPointer);
+        }
         framePushed_ += amount;
     }
     void freeStack(uint32_t amount) {
         MOZ_ASSERT(amount <= framePushed_);
         if (amount)
             addq(Imm32(amount), StackPointer);
         framePushed_ -= amount;
     }
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -560,18 +560,30 @@ class MacroAssemblerX86 : public MacroAs
         cmpPtr(lhs, rhs);
         emitSet(cond, dest);
     }
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
     void reserveStack(uint32_t amount) {
-        if (amount)
-            subl(Imm32(amount), StackPointer);
+        if (amount) {
+            // On windows, we cannot skip very far down the stack without touching the
+            // memory pages in-between.  This is a corner-case code for situations where the
+            // Ion frame data for a piece of code is very large.  To handle this special case,
+            // for frames over 1k in size we allocate memory on the stack incrementally, touching
+            // it as we go.
+            uint32_t amountLeft = amount;
+            while (amountLeft > 4096) {
+                subl(Imm32(4096), StackPointer);
+                store32(Imm32(0), Address(StackPointer, 0));
+                amountLeft -= 4096;
+            }
+            subl(Imm32(amountLeft), StackPointer);
+        }
         framePushed_ += amount;
     }
     void freeStack(uint32_t amount) {
         MOZ_ASSERT(amount <= framePushed_);
         if (amount)
             addl(Imm32(amount), StackPointer);
         framePushed_ -= amount;
     }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -110,20 +110,16 @@ using JS::AutoGCRooter;
 using js::frontend::Parser;
 
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
-/* Make sure that char16_t is two bytes unsigned integer */
-JS_STATIC_ASSERT((char16_t)-1 > 0);
-JS_STATIC_ASSERT(sizeof(char16_t) == 2);
-
 bool
 JS::CallArgs::requireAtLeast(JSContext *cx, const char *fnname, unsigned required) {
     if (length() < required) {
         char numArgsStr[40];
         JS_snprintf(numArgsStr, sizeof numArgsStr, "%u", required - 1);
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                              fnname, numArgsStr, required == 2 ? "" : "s");
         return false;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -485,26 +485,16 @@ exn_toSource(JSContext *cx, unsigned arg
     JSString *str = sb.finishString();
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 #endif
 
-/* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
-JS_STATIC_ASSERT(JSEXN_ERR == 0);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR  == JSProto_InternalError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR      == JSProto_EvalError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR     == JSProto_RangeError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR    == JSProto_SyntaxError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR      == JSProto_TypeError);
-JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR       == JSProto_URIError);
-
 /* static */ JSObject *
 ErrorObject::createProto(JSContext *cx, JSProtoKey key)
 {
     RootedObject errorProto(cx, GenericCreatePrototype(cx, key));
     if (!errorProto)
         return nullptr;
 
     Rooted<ErrorObject*> err(cx, &errorProto->as<ErrorObject>());
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -81,16 +81,29 @@ js_ErrorFromException(JSContext *cx, js:
  *
  * errobj may be in a different compartment than cx, but it must be an Error
  * object (not a wrapper of one) and it must not be one of the standard error
  * prototype objects (errobj->getPrivate() must not be nullptr).
  */
 extern JSObject *
 js_CopyErrorObject(JSContext *cx, JS::Handle<js::ErrorObject*> errobj);
 
+static_assert(JSEXN_ERR == 0 &&
+              JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError &&
+              JSProto_Error + JSEXN_EVALERR == JSProto_EvalError &&
+              JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError &&
+              JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError &&
+              JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError &&
+              JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError &&
+              JSProto_Error + JSEXN_URIERR == JSProto_URIError &&
+              JSEXN_URIERR + 1 == JSEXN_LIMIT,
+              "GetExceptionProtoKey and ExnTypeFromProtoKey require that "
+              "each corresponding JSExnType and JSProtoKey value be separated "
+              "by the same constant value");
+
 static inline JSProtoKey
 GetExceptionProtoKey(JSExnType exn)
 {
     MOZ_ASSERT(JSEXN_ERR <= exn);
     MOZ_ASSERT(exn < JSEXN_LIMIT);
     return JSProtoKey(JSProto_Error + int(exn));
 }
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -53,21 +53,18 @@ class JSFunction : public js::NativeObje
         /* Derived Flags values for convenience: */
         NATIVE_FUN = 0,
         ASMJS_CTOR = ASMJS | NATIVE_CTOR,
         ASMJS_LAMBDA_CTOR = ASMJS | NATIVE_CTOR | LAMBDA,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA,
         INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW
     };
 
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT);
-        static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
-                      "shadow interface must match actual interface");
-    }
+    static_assert(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT,
+                  "jsfriendapi.h's JSFunction::INTERPRETED-alike is wrong");
 
   private:
     uint16_t        nargs_;       /* number of formal arguments
                                      (including defaults and the rest parameter unlike f.length) */
     uint16_t        flags_;       /* bitfield composed of the above Flags enum */
     union U {
         class Native {
             friend class JSFunction;
@@ -418,18 +415,23 @@ class JSFunction : public js::NativeObje
     }
 
     void setJitInfo(const JSJitInfo *data) {
         MOZ_ASSERT(isNative());
         u.n.jitinfo = data;
     }
 
     static unsigned offsetOfNativeOrScript() {
-        JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.s.script_));
-        JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
+        static_assert(offsetof(U, n.native) == offsetof(U, i.s.script_),
+                      "native and script pointers must be in the same spot "
+                      "for offsetOfNativeOrScript() have any sense");
+        static_assert(offsetof(U, n.native) == offsetof(U, nativeOrScript),
+                      "U::nativeOrScript must be at the same offset as "
+                      "native");
+
         return offsetof(JSFunction, u.nativeOrScript);
     }
 
 #if JS_BITS_PER_WORD == 32
     static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND;
     static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
 #else
     static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
@@ -455,17 +457,16 @@ class JSFunction : public js::NativeObje
     size_t getBoundFunctionArgumentCount() const;
 
   private:
     inline js::FunctionExtended *toExtended();
     inline const js::FunctionExtended *toExtended() const;
 
   public:
     inline bool isExtended() const {
-        JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind);
         MOZ_ASSERT_IF(isTenured(), !!(flags() & EXTENDED) == (asTenured().getAllocKind() == ExtendedFinalizeKind));
         return !!(flags() & EXTENDED);
     }
 
     /*
      * Accessors for data stored in extended functions. Use setExtendedSlot if
      * the function has already been initialized. Otherwise use
      * initExtendedSlot.
@@ -476,24 +477,31 @@ class JSFunction : public js::NativeObje
     inline const js::Value &getExtendedSlot(size_t which) const;
 
     /* Constructs a new type for the function if necessary. */
     static bool setTypeForScriptedFunction(js::ExclusiveContext *cx, js::HandleFunction fun,
                                            bool singleton = false);
 
     /* GC support. */
     js::gc::AllocKind getAllocKind() const {
+        static_assert(FinalizeKind != ExtendedFinalizeKind,
+                      "extended/non-extended AllocKinds have to be different "
+                      "for getAllocKind() to have a reason to exist");
+
         js::gc::AllocKind kind = FinalizeKind;
         if (isExtended())
             kind = ExtendedFinalizeKind;
         MOZ_ASSERT_IF(isTenured(), kind == asTenured().getAllocKind());
         return kind;
     }
 };
 
+static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
+              "shadow interface must match actual interface");
+
 extern JSString *
 fun_toStringHelper(JSContext *cx, js::HandleObject obj, unsigned indent);
 
 inline JSFunction::Flags
 JSAPIToJSFunctionFlags(unsigned flags)
 {
     return (flags & JSFUN_CONSTRUCTOR)
            ? JSFunction::NATIVE_CTOR
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2354,17 +2354,16 @@ void
 MovingTracer::Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     TenuredCell *thing = TenuredCell::fromPointer(*thingp);
     Zone *zone = thing->zoneFromAnyThread();
     if (!zone->isGCCompacting()) {
         MOZ_ASSERT(!IsForwarded(thing));
         return;
     }
-    MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
 
     if (IsForwarded(thing)) {
         Cell *dst = Forwarded(thing);
         *thingp = dst;
     }
 }
 
 void
@@ -2410,31 +2409,260 @@ GCRuntime::sweepZoneAfterCompacting(Zone
         c->sweepSelfHostingScriptSource();
         c->sweepDebugScopes();
         c->sweepJitCompartment(fop);
         c->sweepWeakMaps();
         c->sweepNativeIterators();
     }
 }
 
-/*
- * Update the interal pointers for all cells of the specified kind in a zone.
- */
 template <typename T>
 static void
-UpdateCellPointersByKind(MovingTracer *trc, ArenaLists &al, AllocKind thingKind) {
-    JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
-    MOZ_ASSERT(MapTypeToTraceKind<T>::kind == traceKind);
-    for (ArenaHeader *arena = al.getFirstArena(thingKind); arena; arena = arena->next) {
-        for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
-            T *cell = reinterpret_cast<T*>(i.getCell());
-            cell->fixupAfterMovingGC();
-            TraceChildren(trc, cell, traceKind);
+UpdateCellPointersTyped(MovingTracer *trc, ArenaHeader *arena, JSGCTraceKind traceKind)
+{
+    for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
+        T *cell = reinterpret_cast<T*>(i.getCell());
+        cell->fixupAfterMovingGC();
+        TraceChildren(trc, cell, traceKind);
+    }
+}
+
+/*
+ * Update the interal pointers for all cells in an arena.
+ */
+static void
+UpdateCellPointers(MovingTracer *trc, ArenaHeader *arena)
+{
+    AllocKind kind = arena->getAllocKind();
+    JSGCTraceKind traceKind = MapAllocToTraceKind(kind);
+
+    switch (kind) {
+      case FINALIZE_OBJECT0:
+      case FINALIZE_OBJECT0_BACKGROUND:
+      case FINALIZE_OBJECT2:
+      case FINALIZE_OBJECT2_BACKGROUND:
+      case FINALIZE_OBJECT4:
+      case FINALIZE_OBJECT4_BACKGROUND:
+      case FINALIZE_OBJECT8:
+      case FINALIZE_OBJECT8_BACKGROUND:
+      case FINALIZE_OBJECT12:
+      case FINALIZE_OBJECT12_BACKGROUND:
+      case FINALIZE_OBJECT16:
+      case FINALIZE_OBJECT16_BACKGROUND:
+        UpdateCellPointersTyped<JSObject>(trc, arena, traceKind);
+        return;
+      case FINALIZE_SCRIPT:
+        UpdateCellPointersTyped<JSScript>(trc, arena, traceKind);
+        return;
+      case FINALIZE_LAZY_SCRIPT:
+        UpdateCellPointersTyped<LazyScript>(trc, arena, traceKind);
+        return;
+      case FINALIZE_SHAPE:
+        UpdateCellPointersTyped<Shape>(trc, arena, traceKind);
+        return;
+      case FINALIZE_ACCESSOR_SHAPE:
+        UpdateCellPointersTyped<AccessorShape>(trc, arena, traceKind);
+        return;
+      case FINALIZE_BASE_SHAPE:
+        UpdateCellPointersTyped<BaseShape>(trc, arena, traceKind);
+        return;
+      case FINALIZE_TYPE_OBJECT:
+        UpdateCellPointersTyped<types::TypeObject>(trc, arena, traceKind);
+        return;
+      case FINALIZE_JITCODE:
+        UpdateCellPointersTyped<jit::JitCode>(trc, arena, traceKind);
+        return;
+      default:
+        MOZ_CRASH("Invalid alloc kind for UpdateCellPointers");
+    }
+}
+
+namespace js {
+namespace gc {
+
+struct ArenasToUpdate
+{
+    ArenasToUpdate(JSRuntime *rt);
+    bool done() { return initialized && arena == nullptr; }
+    ArenaHeader* next();
+    ArenaHeader *getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned max);
+
+  private:
+    bool initialized;
+    GCZonesIter zone;    // Current zone to process, unless zone.done()
+    unsigned kind;       // Current alloc kind to process
+    ArenaHeader *arena;  // Next arena to process
+
+    bool shouldProcessKind(unsigned kind);
+};
+
+bool ArenasToUpdate::shouldProcessKind(unsigned kind)
+{
+    MOZ_ASSERT(kind >= 0 && kind < FINALIZE_LIMIT);
+    return
+        kind != FINALIZE_FAT_INLINE_STRING &&
+        kind != FINALIZE_STRING &&
+        kind != FINALIZE_EXTERNAL_STRING &&
+        kind != FINALIZE_SYMBOL;
+}
+
+ArenasToUpdate::ArenasToUpdate(JSRuntime *rt)
+  : initialized(false), zone(rt, SkipAtoms)
+{}
+
+ArenaHeader *
+ArenasToUpdate::next()
+{
+    // Find the next arena to update.
+    //
+    // Note that this uses a generator-like arrangement. The first time this is
+    // called, |initialized| is false and the for-loops below are entered in the
+    // normal way, returning the first arena found. In subsequent invocations we
+    // jump directly into the body of the for loops just after the previous
+    // return. All state is stored in class members and so preserved between
+    // invocations.
+
+    if (initialized) {
+        MOZ_ASSERT(arena);
+        MOZ_ASSERT(shouldProcessKind(kind));
+        MOZ_ASSERT(!zone.done());
+        goto resumePoint;
+    }
+
+    initialized = true;
+    for (; !zone.done(); zone.next()) {
+        for (kind = 0; kind < FINALIZE_LIMIT; ++kind) {
+            if (shouldProcessKind(kind)) {
+                for (arena = zone.get()->allocator.arenas.getFirstArena(AllocKind(kind));
+                     arena;
+                     arena = arena->next)
+                {
+                    return arena;
+                    resumePoint:;
+                }
+            }
         }
     }
+    return nullptr;
+}
+
+ArenaHeader *
+ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned count)
+{
+    if (zone.done())
+        return nullptr;
+
+    ArenaHeader *head = nullptr;
+    ArenaHeader *tail = nullptr;
+
+    for (unsigned i = 0; i < count; ++i) {
+        ArenaHeader *arena = next();
+        if (!arena)
+            break;
+
+        if (tail)
+            tail->setNextArenaToUpdate(arena);
+        else
+            head = arena;
+        tail = arena;
+    }
+
+    return head;
+}
+
+struct UpdateCellPointersTask : public GCParallelTask
+{
+    // Number of arenas to update in one block.
+#ifdef DEBUG
+    static const unsigned ArenasToProcess = 16;
+#else
+    static const unsigned ArenasToProcess = 256;
+#endif
+
+    UpdateCellPointersTask() : rt_(nullptr), source_(nullptr), arenaList_(nullptr) {}
+    void init(JSRuntime *rt, ArenasToUpdate *source, AutoLockHelperThreadState& lock);
+
+  private:
+    JSRuntime *rt_;
+    ArenasToUpdate *source_;
+    ArenaHeader *arenaList_;
+
+    virtual void run() MOZ_OVERRIDE;
+    void getArenasToUpdate(AutoLockHelperThreadState& lock);
+    void updateArenas();
+};
+
+void
+UpdateCellPointersTask::init(JSRuntime *rt, ArenasToUpdate *source, AutoLockHelperThreadState& lock)
+{
+    rt_ = rt;
+    source_ = source;
+    getArenasToUpdate(lock);
+}
+
+void
+UpdateCellPointersTask::getArenasToUpdate(AutoLockHelperThreadState& lock)
+{
+    arenaList_ = source_->getArenasToUpdate(lock, ArenasToProcess);
+}
+
+void
+UpdateCellPointersTask::updateArenas()
+{
+    MovingTracer trc(rt_);
+    for (ArenaHeader *arena = arenaList_;
+         arena;
+         arena = arena->getNextArenaToUpdateAndUnlink())
+    {
+        UpdateCellPointers(&trc, arena);
+    }
+    arenaList_ = nullptr;
+}
+
+/* virtual */ void
+UpdateCellPointersTask::run()
+{
+    while (arenaList_) {
+        updateArenas();
+        {
+            AutoLockHelperThreadState lock;
+            getArenasToUpdate(lock);
+        }
+    }
+}
+
+} // namespace gc
+} // namespace js
+
+void
+GCRuntime::updateAllCellPointersParallel(ArenasToUpdate &source)
+{
+    AutoDisableProxyCheck noProxyCheck(rt); // These checks assert when run in parallel.
+
+    const size_t maxTasks = 8;
+    unsigned taskCount = Min(HelperThreadState().cpuCount, maxTasks);
+    UpdateCellPointersTask updateTasks[maxTasks];
+
+    AutoLockHelperThreadState lock;
+    unsigned i;
+    for (i = 0; i < taskCount && !source.done(); ++i) {
+        updateTasks[i].init(rt, &source, lock);
+        startTask(updateTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS);
+    }
+    unsigned tasksStarted = i;
+
+    for (i = 0; i < tasksStarted; ++i)
+        joinTask(updateTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS);
+}
+
+void
+GCRuntime::updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source)
+{
+    while (ArenaHeader *arena = source.next())
+        UpdateCellPointers(trc, arena);
 }
 
 /*
  * Update pointers to relocated cells by doing a full heap traversal and sweep.
  *
  * The latter is necessary to update weak references which are not marked as
  * part of the traversal.
  */
@@ -2449,29 +2677,24 @@ GCRuntime::updatePointersToRelocatedCell
     // Fixup compartment global pointers as these get accessed during marking.
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->fixupAfterMovingGC();
 
     // Fixup cross compartment wrappers as we assert the existence of wrappers in the map.
     for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next())
         comp->sweepCrossCompartmentWrappers();
 
-    // Iterate through all cells that can contain JSObject pointers to update them.
-    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        ArenaLists &al = zone->allocator.arenas;
-        for (unsigned i = 0; i < FINALIZE_OBJECT_LIMIT; ++i)
-            UpdateCellPointersByKind<JSObject>(&trc, al, AllocKind(i));
-        UpdateCellPointersByKind<JSScript>(&trc, al, FINALIZE_SCRIPT);
-        UpdateCellPointersByKind<LazyScript>(&trc, al, FINALIZE_LAZY_SCRIPT);
-        UpdateCellPointersByKind<Shape>(&trc, al, FINALIZE_SHAPE);
-        UpdateCellPointersByKind<AccessorShape>(&trc, al, FINALIZE_ACCESSOR_SHAPE);
-        UpdateCellPointersByKind<BaseShape>(&trc, al, FINALIZE_BASE_SHAPE);
-        UpdateCellPointersByKind<types::TypeObject>(&trc, al, FINALIZE_TYPE_OBJECT);
-        UpdateCellPointersByKind<jit::JitCode>(&trc, al, FINALIZE_JITCODE);
-    }
+    // Iterate through all cells that can contain JSObject pointers to update
+    // them. Since updating each cell is independent we try to parallelize this
+    // as much as possible.
+    ArenasToUpdate source(rt);
+    if (CanUseExtraThreads())
+        updateAllCellPointersParallel(source);
+    else
+        updateAllCellPointersSerial(&trc, source);
 
     // Mark roots to update them.
     markRuntime(&trc, MarkRuntime);
     Debugger::markAll(&trc);
     Debugger::markCrossCompartmentDebuggerObjectReferents(&trc);
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         WeakMapBase::markAll(c, &trc);
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -374,28 +374,30 @@ class ZoneCellIter : public ZoneCellIter
 };
 
 class GCZonesIter
 {
   private:
     ZonesIter zone;
 
   public:
-    explicit GCZonesIter(JSRuntime *rt) : zone(rt, WithAtoms) {
+    explicit GCZonesIter(JSRuntime *rt, ZoneSelector selector = WithAtoms)
+      : zone(rt, selector)
+    {
         if (!zone->isCollecting())
             next();
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
         MOZ_ASSERT(!done());
         do {
             zone.next();
-        } while (!zone.done() && !zone->isCollecting());
+        } while (!zone.done() && !zone->isCollectingFromAnyThread());
     }
 
     JS::Zone *get() const {
         MOZ_ASSERT(!done());
         return zone;
     }
 
     operator JS::Zone *() const { return get(); }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4671,17 +4671,17 @@ EnsureHasAutoClearTypeInferenceStateOnOO
  * compartment to fixup weak references: property type sets referencing dead
  * JS and type objects, and singleton JS objects whose type is not referenced
  * elsewhere. This is done either incrementally as part of the sweep, or on
  * demand as type objects are accessed before their contents have been swept.
  */
 void
 TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom)
 {
-    if (generation() == zone()->types.generation) {
+    if (generation() == zoneFromAnyThread()->types.generation) {
         // No sweeping required.
         return;
     }
 
     setGeneration(zone()->types.generation);
 
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
     MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1196,23 +1196,17 @@ struct TypeObject : public gc::TenuredCe
 
     bool hasTenuredProto() {
         return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
     }
 
     gc::InitialHeap initialHeap(CompilerConstraintList *constraints);
 
     bool canPreTenure() {
-        // Only types associated with particular allocation sites or 'new'
-        // scripts can be marked as needing pretenuring. Other types can be
-        // used for different purposes across the compartment and can't use
-        // this bit reliably.
-        if (unknownProperties())
-            return false;
-        return fromAllocationSite() || newScript();
+        return !unknownProperties();
     }
 
     bool fromAllocationSite() {
         return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE;
     }
 
     void setShouldPreTenure(ExclusiveContext *cx) {
         MOZ_ASSERT(canPreTenure());
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -537,19 +537,21 @@ num_toSource_impl(JSContext *cx, CallArg
 static bool
 num_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsNumber, num_toSource_impl>(cx, args);
 }
 #endif
 
-ToCStringBuf::ToCStringBuf() :dbuf(nullptr)
+ToCStringBuf::ToCStringBuf() : dbuf(nullptr)
 {
-    JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
+    static_assert(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE,
+                  "builtin space must be large enough to store even the "
+                  "longest string produced by a conversion");
 }
 
 ToCStringBuf::~ToCStringBuf()
 {
     js_free(dbuf);
 }
 
 MOZ_ALWAYS_INLINE
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -42,23 +42,25 @@ const Class js::JSONClass = {
     JS_DeletePropertyStub,  /* delProperty */
     JS_PropertyStub,        /* getProperty */
     JS_StrictPropertyStub,  /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
-static inline bool IsQuoteSpecialCharacter(char16_t c)
+static inline bool
+IsQuoteSpecialCharacter(char16_t c)
 {
-    JS_STATIC_ASSERT('\b' < ' ');
-    JS_STATIC_ASSERT('\f' < ' ');
-    JS_STATIC_ASSERT('\n' < ' ');
-    JS_STATIC_ASSERT('\r' < ' ');
-    JS_STATIC_ASSERT('\t' < ' ');
+    static_assert('\b' < ' ', "'\\b' must be treated as special below");
+    static_assert('\f' < ' ', "'\\f' must be treated as special below");
+    static_assert('\n' < ' ', "'\\n' must be treated as special below");
+    static_assert('\r' < ' ', "'\\r' must be treated as special below");
+    static_assert('\t' < ' ', "'\\t' must be treated as special below");
+
     return c == '"' || c == '\\' || c < ' ';
 }
 
 /* ES5 15.12.3 Quote. */
 template <typename CharT>
 static bool
 Quote(StringBuffer &sb, JSLinearString *str)
 {
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -318,22 +318,22 @@ Shape::fixupShapeTreeAfterMovingGC()
         BaseShape *base = key->base();
         if (IsForwarded(base))
             base = Forwarded(base);
         UnownedBaseShape *unowned = base->unowned();
         if (IsForwarded(unowned))
             unowned = Forwarded(unowned);
 
         PropertyOp getter = key->getter();
-        if (key->hasGetterObject() && IsForwarded(key->getterObject()))
-            getter = PropertyOp(Forwarded(key->getterObject()));
+        if (key->hasGetterObject())
+            getter = PropertyOp(MaybeForwarded(key->getterObject()));
 
         StrictPropertyOp setter = key->setter();
-        if (key->hasSetterObject() && IsForwarded(key->setterObject()))
-            setter = StrictPropertyOp(Forwarded(key->setterObject()));
+        if (key->hasSetterObject())
+            setter = StrictPropertyOp(MaybeForwarded(key->setterObject()));
 
         StackShape lookup(unowned,
                           const_cast<Shape *>(key)->propidRef(),
                           key->slotInfo & Shape::SLOT_MASK,
                           key->attrs,
                           key->flags);
         lookup.updateGetterSetter(getter, setter);
         e.rekeyFront(lookup, key);
--- a/js/src/jstypes.h
+++ b/js/src/jstypes.h
@@ -17,16 +17,17 @@
 ** and take the following steps only in those C files, we take steps once here
 ** for all C files.
 **/
 
 #ifndef jstypes_h
 #define jstypes_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Casting.h"
 #include "mozilla/Types.h"
 
 // jstypes.h is (or should be!) included by every file in SpiderMonkey.
 // js-config.h and jsversion.h also should be included by every file.
 // So include them here.
 // XXX: including them in js/RequiredDefines.h should be a better option, since
 // that is by definition the header file that should be included in all
 // SpiderMonkey code.  However, Gecko doesn't do this!  See bug 909576.
@@ -189,36 +190,30 @@
 #else
 # define JS_BITS_PER_WORD 32
 #endif
 
 /***********************************************************************
 ** MACROS:      JS_FUNC_TO_DATA_PTR
 **              JS_DATA_TO_FUNC_PTR
 ** DESCRIPTION:
-**      Macros to convert between function and data pointers assuming that
-**      they have the same size. Use them like this:
+**      Macros to convert between function and data pointers of the same
+**      size. Use them like this:
 **
 **      JSPropertyOp nativeGetter;
 **      JSObject *scriptedGetter;
 **      ...
 **      scriptedGetter = JS_FUNC_TO_DATA_PTR(JSObject *, nativeGetter);
 **      ...
 **      nativeGetter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, scriptedGetter);
 **
 ***********************************************************************/
 
-#ifdef __GNUC__
-# define JS_FUNC_TO_DATA_PTR(type, fun) (__extension__ (type) (size_t) (fun))
-# define JS_DATA_TO_FUNC_PTR(type, ptr) (__extension__ (type) (size_t) (ptr))
-#else
-/* Use an extra (void *) cast for MSVC. */
-# define JS_FUNC_TO_DATA_PTR(type, fun) ((type) (void *) (fun))
-# define JS_DATA_TO_FUNC_PTR(type, ptr) ((type) (void *) (ptr))
-#endif
+#define JS_FUNC_TO_DATA_PTR(type, fun)  (mozilla::BitwiseCast<type>(fun))
+#define JS_DATA_TO_FUNC_PTR(type, ptr)  (mozilla::BitwiseCast<type>(ptr))
 
 #ifdef __GNUC__
 # define JS_EXTENSION __extension__
 # define JS_EXTENSION_(s) __extension__ ({ s; })
 #else
 # define JS_EXTENSION
 # define JS_EXTENSION_(s) s
 #endif
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -28,22 +28,16 @@ using mozilla::CeilingLog2Size;
 using mozilla::PodArrayZero;
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 /* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */
 JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX;
 JS_PUBLIC_DATA(uint32_t) OOM_counter = 0;
 #endif
 
-/*
- * Checks the assumption that JS_FUNC_TO_DATA_PTR and JS_DATA_TO_FUNC_PTR
- * macros uses to implement casts between function and data pointers.
- */
-JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)()));
-
 JS_PUBLIC_API(void)
 JS_Assert(const char *s, const char *file, int ln)
 {
     MOZ_ReportAssertionFailure(s, file, ln);
     MOZ_CRASH();
 }
 
 #ifdef __linux__
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -41,17 +41,17 @@ js::Debugger::onEnterFrame(JSContext *cx
     return slowPathOnEnterFrame(cx, frame);
 }
 
 /* static */ JSTrapStatus
 js::Debugger::onDebuggerStatement(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp)
 {
     MOZ_ASSERT_IF(frame.script()->isDebuggee(), frame.isDebuggee());
     return frame.isDebuggee()
-           ? dispatchHook(cx, vp, OnDebuggerStatement)
+           ? dispatchHook(cx, vp, OnDebuggerStatement, NullPtr())
            : JSTRAP_CONTINUE;
 }
 
 /* static */ JSTrapStatus
 js::Debugger::onExceptionUnwind(JSContext *cx, AbstractFramePtr frame)
 {
     if (!cx->compartment()->isDebuggee())
         return JSTRAP_CONTINUE;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -15,17 +15,16 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
-#include "js/Debug.h"
 #include "js/GCAPI.h"
 #include "js/UbiNodeTraverse.h"
 #include "js/Vector.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/SPSProfiler.h"
 #include "vm/WrapperObject.h"
 
@@ -686,17 +685,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
         MOZ_CRASH("bad final trap status");
     }
 }
 
 JSTrapStatus
 Debugger::slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame)
 {
     RootedValue rval(cx);
-    JSTrapStatus status = dispatchHook(cx, &rval, OnExceptionUnwind);
+    JSTrapStatus status = dispatchHook(cx, &rval, OnExceptionUnwind, NullPtr());
 
     switch (status) {
       case JSTRAP_CONTINUE:
         break;
 
       case JSTRAP_THROW:
         cx->setPendingException(rval);
         break;
@@ -1192,19 +1191,22 @@ Debugger::fireNewScript(JSContext *cx, H
 
     RootedValue scriptObject(cx, ObjectValue(*dsobj));
     RootedValue rv(cx);
     if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptObject.address(), &rv))
         handleUncaughtException(ac, true);
 }
 
 /* static */ JSTrapStatus
-Debugger::dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which)
-{
-    MOZ_ASSERT(which == OnDebuggerStatement || which == OnExceptionUnwind);
+Debugger::dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which, HandleObject payload)
+{
+    MOZ_ASSERT(which == OnDebuggerStatement ||
+               which == OnExceptionUnwind ||
+               which == OnNewPromise ||
+               which == OnPromiseSettled);
 
     /*
      * Determine which debuggers will receive this event, and in what order.
      * Make a copy of the list, since the original is mutable and we will be
      * calling into arbitrary JS.
      *
      * Note: In the general case, 'triggered' contains references to objects in
      * different compartments--every compartment *except* this one.
@@ -1223,19 +1225,31 @@ Debugger::dispatchHook(JSContext *cx, Mu
 
     /*
      * Deliver the event to each debugger, checking again to make sure it
      * should still be delivered.
      */
     for (Value *p = triggered.begin(); p != triggered.end(); p++) {
         Debugger *dbg = Debugger::fromJSObject(&p->toObject());
         if (dbg->debuggees.has(global) && dbg->enabled && dbg->getHook(which)) {
-            JSTrapStatus st = (which == OnDebuggerStatement)
-                              ? dbg->fireDebuggerStatement(cx, vp)
-                              : dbg->fireExceptionUnwind(cx, vp);
+            JSTrapStatus st;
+            switch (which) {
+              case OnDebuggerStatement:
+                st = dbg->fireDebuggerStatement(cx, vp);
+                break;
+              case OnExceptionUnwind:
+                st = dbg->fireExceptionUnwind(cx, vp);
+                break;
+              case OnNewPromise:
+              case OnPromiseSettled:
+                st = dbg->firePromiseHook(cx, which, payload, vp);
+                break;
+              default:
+                MOZ_ASSERT_UNREACHABLE("Unexpected debugger hook");
+            }
             if (st != JSTRAP_CONTINUE)
                 return st;
         }
     }
     return JSTRAP_CONTINUE;
 }
 
 static bool
@@ -1581,16 +1595,59 @@ Debugger::appendAllocationSite(JSContext
 void
 Debugger::emptyAllocationsLog()
 {
     while (!allocationsLog.isEmpty())
         js_delete(allocationsLog.getFirst());
     allocationsLogLength = 0;
 }
 
+JSTrapStatus
+Debugger::firePromiseHook(JSContext *cx, Hook hook, HandleObject promise, MutableHandleValue vp)
+{
+    MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
+
+    RootedObject hookObj(cx, getHook(hook));
+    MOZ_ASSERT(hookObj);
+    MOZ_ASSERT(hookObj->isCallable());
+
+    Maybe<AutoCompartment> ac;
+    ac.emplace(cx, object);
+
+    RootedValue dbgObj(cx, ObjectValue(*promise));
+    if (!wrapDebuggeeValue(cx, &dbgObj))
+        return handleUncaughtException(ac, false);
+
+    // Like onNewGlobalObject, the Promise hooks are infallible and the comments
+    // in |Debugger::fireNewGlobalObject| apply here as well.
+
+    RootedValue rv(cx);
+    bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hookObj), 1, dbgObj.address(), &rv);
+    if (ok && !rv.isUndefined()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                             JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
+        ok = false;
+    }
+
+    JSTrapStatus status = ok ? JSTRAP_CONTINUE
+                             : handleUncaughtException(ac, vp, true);
+    MOZ_ASSERT(!cx->isExceptionPending());
+    return status;
+}
+
+/* static */ void
+Debugger::slowPathPromiseHook(JSContext *cx, Hook hook, HandleObject promise)
+{
+    MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
+    RootedValue rval(cx);
+    JSTrapStatus status = dispatchHook(cx, &rval, hook, promise);
+    MOZ_ASSERT(status == JSTRAP_CONTINUE);
+    MOZ_ASSERT(!cx->isExceptionPending());
+}
+
 
 /*** Debugger code invalidation for observing execution ******************************************/
 
 class MOZ_STACK_CLASS ExecutionObservableCompartments : public Debugger::ExecutionObservableSet
 {
     HashSet<JSCompartment *> compartments_;
     HashSet<Zone *> zones_;
 
@@ -1974,16 +2031,17 @@ Debugger::setObservesAllExecution(JSCont
             // compartment, so don't add the compartment to the set.
             comp->unsetDebugObservesAllExecution();
         }
     }
 
     return updateExecutionObservability(cx, obs, observing);
 }
 
+
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
 Debugger::markKeysInCompartment(JSTracer *trc)
 {
     /*
      * WeakMap::Range is deliberately private, to discourage C++ code from
@@ -2426,16 +2484,40 @@ Debugger::getOnNewScript(JSContext *cx, 
 
 /* static */ bool
 Debugger::setOnNewScript(JSContext *cx, unsigned argc, Value *vp)
 {
     return setHookImpl(cx, argc, vp, OnNewScript);
 }
 
 /* static */ bool
+Debugger::getOnNewPromise(JSContext *cx, unsigned argc, Value *vp)
+{
+    return getHookImpl(cx, argc, vp, OnNewPromise);
+}
+
+/* static */ bool
+Debugger::setOnNewPromise(JSContext *cx, unsigned argc, Value *vp)
+{
+    return setHookImpl(cx, argc, vp, OnNewPromise);
+}
+
+/* static */ bool
+Debugger::getOnPromiseSettled(JSContext *cx, unsigned argc, Value *vp)
+{
+    return getHookImpl(cx, argc, vp, OnPromiseSettled);
+}
+
+/* static */ bool
+Debugger::setOnPromiseSettled(JSContext *cx, unsigned argc, Value *vp)
+{
+    return setHookImpl(cx, argc, vp, OnPromiseSettled);
+}
+
+/* static */ bool
 Debugger::getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp)
 {
     return getHookImpl(cx, argc, vp, OnEnterFrame);
 }
 
 /* static */ bool
 Debugger::setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -3657,16 +3739,18 @@ Debugger::makeGlobalObjectReference(JSCo
 
 const JSPropertySpec Debugger::properties[] = {
     JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
     JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
             Debugger::setOnDebuggerStatement, 0),
     JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
             Debugger::setOnExceptionUnwind, 0),
     JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
+    JS_PSGS("onNewPromise", Debugger::getOnNewPromise, Debugger::setOnNewPromise, 0),
+    JS_PSGS("onPromiseSettled", Debugger::getOnPromiseSettled, Debugger::setOnPromiseSettled, 0),
     JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame, Debugger::setOnEnterFrame, 0),
     JS_PSGS("onNewGlobalObject", Debugger::getOnNewGlobalObject, Debugger::setOnNewGlobalObject, 0),
     JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook,
             Debugger::setUncaughtExceptionHook, 0),
     JS_PSG("memory", Debugger::getMemory, 0),
     JS_PS_END
 };
 const JSFunctionSpec Debugger::methods[] = {
@@ -7111,8 +7195,31 @@ JS_DefineDebuggerObject(JSContext *cx, H
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto));
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO, ObjectValue(*objectProto));
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO, ObjectValue(*scriptProto));
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO, ObjectValue(*sourceProto));
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO, ObjectValue(*envProto));
     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO, ObjectValue(*memoryProto));
     return true;
 }
+
+static inline void
+AssertIsPromise(JSContext *cx, HandleObject promise)
+{
+    MOZ_ASSERT(promise);
+    assertSameCompartment(cx, promise);
+    MOZ_ASSERT(strcmp(promise->getClass()->name, "Promise") == 0 ||
+               strcmp(promise->getClass()->name, "MozAbortablePromise") == 0);
+}
+
+JS_PUBLIC_API(void)
+JS::dbg::onNewPromise(JSContext *cx, HandleObject promise)
+{
+    AssertIsPromise(cx, promise);
+    Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
+}
+
+JS_PUBLIC_API(void)
+JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise)
+{
+    AssertIsPromise(cx, promise);
+    Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
+}
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -12,16 +12,17 @@
 
 #include "jsclist.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
 #include "gc/Barrier.h"
+#include "js/Debug.h"
 #include "js/HashTable.h"
 #include "vm/GlobalObject.h"
 #include "vm/SavedStacks.h"
 
 typedef enum JSTrapStatus {
     JSTRAP_ERROR,
     JSTRAP_CONTINUE,
     JSTRAP_RETURN,
@@ -172,24 +173,28 @@ typedef JSObject Env;
 class Debugger : private mozilla::LinkedListElement<Debugger>
 {
     friend class Breakpoint;
     friend class DebuggerMemory;
     friend class SavedStacks;
     friend class mozilla::LinkedListElement<Debugger>;
     friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
     friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
+    friend void JS::dbg::onNewPromise(JSContext *cx, HandleObject promise);
+    friend void JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise);
 
   public:
     enum Hook {
         OnDebuggerStatement,
         OnExceptionUnwind,
         OnNewScript,
         OnEnterFrame,
         OnNewGlobalObject,
+        OnNewPromise,
+        OnPromiseSettled,
         HookCount
     };
     enum {
         JSSLOT_DEBUG_PROTO_START,
         JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
         JSSLOT_DEBUG_ENV_PROTO,
         JSSLOT_DEBUG_OBJECT_PROTO,
         JSSLOT_DEBUG_SCRIPT_PROTO,
@@ -364,16 +369,20 @@ class Debugger : private mozilla::Linked
     static bool getOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
     static bool setOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
     static bool getOnNewScript(JSContext *cx, unsigned argc, Value *vp);
     static bool setOnNewScript(JSContext *cx, unsigned argc, Value *vp);
     static bool getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
     static bool setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
     static bool getOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
     static bool setOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
+    static bool getOnNewPromise(JSContext *cx, unsigned argc, Value *vp);
+    static bool setOnNewPromise(JSContext *cx, unsigned argc, Value *vp);
+    static bool getOnPromiseSettled(JSContext *cx, unsigned argc, Value *vp);
+    static bool setOnPromiseSettled(JSContext *cx, unsigned argc, Value *vp);
     static bool getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
     static bool setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
     static bool getMemory(JSContext *cx, unsigned argc, Value *vp);
     static bool addDebuggee(JSContext *cx, unsigned argc, Value *vp);
     static bool addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp);
     static bool removeDebuggee(JSContext *cx, unsigned argc, Value *vp);
     static bool removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp);
     static bool hasDebuggee(JSContext *cx, unsigned argc, Value *vp);
@@ -415,22 +424,25 @@ class Debugger : private mozilla::Linked
     static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame);
     static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
     static JSTrapStatus slowPathOnExceptionUnwind(JSContext *cx, AbstractFramePtr frame);
     static void slowPathOnNewScript(JSContext *cx, HandleScript script,
                                     GlobalObject *compileAndGoGlobal);
     static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
     static bool slowPathOnLogAllocationSite(JSContext *cx, HandleSavedFrame frame,
                                             int64_t when, GlobalObject::DebuggerVector &dbgs);
-    static JSTrapStatus dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which);
+    static void slowPathPromiseHook(JSContext *cx, Hook hook, HandleObject promise);
+    static JSTrapStatus dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which,
+                                     HandleObject payload);
 
     JSTrapStatus fireDebuggerStatement(JSContext *cx, MutableHandleValue vp);
     JSTrapStatus fireExceptionUnwind(JSContext *cx, MutableHandleValue vp);
     JSTrapStatus fireEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp);
     JSTrapStatus fireNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global, MutableHandleValue vp);
+    JSTrapStatus firePromiseHook(JSContext *cx, Hook hook, HandleObject promise, MutableHandleValue vp);
 
     /*
      * Allocate and initialize a Debugger.Script instance whose referent is
      * |script|.
      */
     JSObject *newDebuggerScript(JSContext *cx, HandleScript script);
 
     /*
@@ -821,17 +833,17 @@ Debugger::observesNewGlobalObject() cons
 }
 
 bool
 Debugger::observesGlobal(GlobalObject *global) const
 {
     return debuggees.has(global);
 }
 
-void
+/* static */ void
 Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal)
 {
     MOZ_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal);
     MOZ_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal == &script->uninlinedGlobal());
     // We early return in slowPathOnNewScript for self-hosted scripts, so we can
     // ignore those in our assertion here.
     MOZ_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
                   !script->selfHosted(),
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -21,19 +21,16 @@
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
 using mozilla::RoundUpPow2;
 
-JS_STATIC_ASSERT(int32_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) ==
-                 int64_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
-
 PropDesc::PropDesc()
 {
     setUndefined();
 }
 
 void
 PropDesc::setUndefined()
 {
@@ -117,19 +114,19 @@ ObjectElements::ConvertElementsToDoubles
 
     header->setShouldConvertDoubleElements();
     return true;
 }
 
 /* static */ bool
 ObjectElements::MakeElementsCopyOnWrite(ExclusiveContext *cx, NativeObject *obj)
 {
-    // Make sure there is enough room for the owner object pointer at the end
-    // of the elements.
-    JS_STATIC_ASSERT(sizeof(HeapSlot) >= sizeof(HeapPtrObject));
+    static_assert(sizeof(HeapSlot) >= sizeof(HeapPtrObject),
+                  "there must be enough room for the owner object pointer at "
+                  "the end of the elements");
     if (!obj->ensureElements(cx, obj->getDenseInitializedLength() + 1))
         return false;
 
     ObjectElements *header = obj->getElementsHeader();
 
     // Note: this method doesn't update type information to indicate that the
     // elements might be copy on write. Handling this is left to the caller.
     MOZ_ASSERT(!header->isCopyOnWrite());
@@ -456,16 +453,17 @@ NativeObject::growSlots(ThreadSafeContex
     MOZ_ASSERT(newCount > oldCount);
     MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
      */
+    NativeObject::slotsSizeMustNotOverflow();
     MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
 
     if (!oldCount) {
         obj->slots_ = AllocateSlots(cx, obj, newCount);
         if (!obj->slots_)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(obj->slots_, newCount);
         return true;
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -545,16 +545,24 @@ class NativeObject : public JSObject
   public:
     bool generateOwnShape(ThreadSafeContext *cx, Shape *newShape = nullptr) {
         return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
     }
 
     bool shadowingShapeChange(ExclusiveContext *cx, const Shape &shape);
     bool clearFlag(ExclusiveContext *cx, BaseShape::Flag flag);
 
+    static void slotsSizeMustNotOverflow() {
+        static_assert((NativeObject::NELEMENTS_LIMIT - 1) <= INT32_MAX / sizeof(JS::Value),
+                      "every caller of this method requires that a slot "
+                      "number (or slot count) count multiplied by "
+                      "sizeof(Value) can't overflow uint32_t (and sometimes "
+                      "int32_t, too)");
+    }
+
     uint32_t numFixedSlots() const {
         return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
     }
     uint32_t numUsedFixedSlots() const {
         uint32_t nslots = lastProperty()->slotSpan(getClass());
         return Min(nslots, numFixedSlots());
     }
 
@@ -887,16 +895,23 @@ class NativeObject : public JSObject
      */
     static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp);
 
     /* Elements accessors. */
 
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
+    static void elementsSizeMustNotOverflow() {
<