merge m-c to cedar
authorMike de Boer <mdeboer@mozilla.com>
Tue, 15 Nov 2016 13:11:16 +0100
changeset 440551 079d4a6ff68727709e9bbe1e562e92d4ca268c09
parent 438399 b32d6eefe4a871d5966bbe765b1f77eaed788ef1 (current diff)
parent 439125 e16d1a881481d7d83493481d9cc2c4ef1e6b9a36 (diff)
child 440552 798319d63092cdcd8064f40fc071ca55e8babc58
push id36260
push userjwein@mozilla.com
push dateThu, 17 Nov 2016 19:58:19 +0000
milestone53.0a1
merge m-c to cedar MozReview-Commit-ID: 8cX4peiofx5
dom/canvas/WebGLTimerQuery.cpp
dom/canvas/WebGLTimerQuery.h
dom/html/nsIHTMLMenu.idl
media/webrtc/signaling/test/sdp_file_parser.cpp
mobile/android/base/java/org/mozilla/gecko/ScreenManagerHelper.java
xpcom/tests/external/TestMinStringAPI.cpp
xpcom/tests/external/moz.build
--- a/.clang-format
+++ b/.clang-format
@@ -1,4 +1,30 @@
 BasedOnStyle: Mozilla
 
 # Ignore all comments because they aren't reflowed properly.
 CommentPragmas: "^"
+
+# Force pointers to the type for C++.
+DerivePointerAlignment: false
+PointerAlignment: Left
+
+# Prevent the loss of indentation with these macros
+MacroBlockBegin: "^\
+NS_INTERFACE_MAP_BEGIN|\
+NS_INTERFACE_TABLE_HEAD|\
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION|\
+NS_IMPL_CYCLE_COLLECTION_.*_BEGIN|\
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED|\
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED|\
+NS_INTERFACE_TABLE_BEGIN|\
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED|\
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED$"
+MacroBlockEnd: "^\
+NS_INTERFACE_MAP_END|\
+NS_IMPL_CYCLE_COLLECTION_.*_END|\
+NS_INTERFACE_TABLE_END|\
+NS_INTERFACE_MAP_END_INHERITING|\
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END_INHERITED|\
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED$"
+
+SortIncludes: false
+
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -1,3 +1,70 @@
-^mfbt/.*
-^js/.*
-^media/.*
+^build/clang-plugin/tests/.*
+^config/gcc-stl-wrapper.template.h
+^config/msvc-stl-wrapper.template.h
+^js/src/jsapi-tests/.*
+
+# Generated from ./tools/rewriting/ThirdPartyPaths.txt
+# awk '{print "^"$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
+^browser/components/translation/cld2/.*
+^build/stlport/.*
+^db/sqlite3/src/.*
+^dom/media/platforms/ffmpeg/libav.*
+^extensions/spellcheck/hunspell/src/.*
+^gfx/2d/convolver.*
+^gfx/2d/image_operations.*
+^gfx/angle/.*
+^gfx/cairo/.*
+^gfx/graphite2/.*
+^gfx/harfbuzz/.*
+^gfx/ots/.*
+^gfx/qcms/.*
+^gfx/skia/.*
+^gfx/ycbcr/.*
+^intl/hyphenation/hyphen/.*
+^intl/icu/.*
+^ipc/chromium/.*
+^js/src/ctypes/libffi/.*
+^js/src/dtoa.c.*
+^js/src/jit/arm64/vixl/.*
+^media/gmp-clearkey/0.1/openaes/.*
+^media/kiss_fft/.*
+^media/libav/.*
+^media/libcubeb/.*
+^media/libjpeg/.*
+^media/libmkv/.*
+^media/libnestegg/.*
+^media/libogg/.*
+^media/libopus/.*
+^media/libpng/.*
+^media/libsoundtouch/.*
+^media/libspeex_resampler/.*
+^media/libstagefright/.*
+^media/libtheora/.*
+^media/libtremor/.*
+^media/libvorbis/.*
+^media/libvpx/.*
+^media/libyuv/.*
+^media/mtransport/.*
+^media/openmax_dl/.*
+^media/pocketsphinx/.*
+^media/sphinxbase/.*
+^media/webrtc/trunk/.*
+^memory/jemalloc/src/.*
+^mfbt/decimal/.*
+^mfbt/double-conversion/.*
+^mfbt/lz4.*
+^mobile/android/thirdparty/.*
+^modules/brotli/.*
+^modules/freetype2/.*
+^modules/libbz2/.*
+^modules/libmar/.*
+^modules/zlib/.*
+^netwerk/sctp/src/.*
+^netwerk/srtp/src/.*
+^nsprpub/.*
+^other-licenses/.*
+^security/sandbox/chromium/.*
+^testing/gtest/gmock/.*
+^testing/gtest/gtest/.*
+^toolkit/components/protobuf/.*
+^toolkit/crashreporter/google-breakpad/.*
--- a/.eslintignore
+++ b/.eslintignore
@@ -90,17 +90,16 @@ devtools/client/netmonitor/har/test/**
 devtools/client/projecteditor/**
 devtools/client/promisedebugger/**
 devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/*.jsm
 devtools/client/shared/webgl-utils.js
 devtools/client/shared/widgets/*.jsm
-devtools/client/sourceeditor/test/*.js
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/**
 !devtools/client/webconsole/panel.js
 !devtools/client/webconsole/jsterm.js
 !devtools/client/webconsole/console-commands.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/*.js
@@ -167,16 +166,17 @@ devtools/shared/pretty-fast/*
 devtools/shared/sourcemap/*
 devtools/shared/sprintfjs/*
 devtools/shared/qrcode/decoder/*
 devtools/shared/qrcode/encoder/*
 devtools/client/shared/demangle.js
 devtools/client/shared/vendor/*
 devtools/client/sourceeditor/codemirror/*.js
 devtools/client/sourceeditor/codemirror/**/*.js
+devtools/client/sourceeditor/test/cm_mode_ruby.js
 devtools/client/sourceeditor/test/codemirror/*
 devtools/client/inspector/markup/test/lib_*
 devtools/client/jsonview/lib/require.js
 devtools/server/actors/utils/automation-timeline.js
 
 # Ignore devtools files testing sourcemaps / code style
 devtools/client/debugger/test/mochitest/code_binary_search.js
 devtools/client/debugger/test/mochitest/code_math.min.js
--- a/.hgtags
+++ b/.hgtags
@@ -124,8 +124,9 @@ fcef8ded82219c89298b4e376cfbdfba79a1d35a
 67a788db9f07822cfef52351bbbe3745dff8bd7f FIREFOX_AURORA_44_BASE
 99137d6d4061f408ae0869122649d8bdf489cc30 FIREFOX_AURORA_45_BASE
 67c66c2878aed17ae3096d7db483ddbb2293c503 FIREFOX_AURORA_46_BASE
 68d3781deda0d4d58ec9877862830db89669b3a5 FIREFOX_AURORA_47_BASE
 1c6385ae1fe7e37d8f23f958ce14582f07af729e FIREFOX_AURORA_48_BASE
 d98f20c25feeac4dd7ebbd1c022957df1ef58af4 FIREFOX_AURORA_49_BASE
 465d150bc8be5bbf9f02a8607d4552b6a5e1697c FIREFOX_AURORA_50_BASE
 fc69febcbf6c0dcc4b3dfc7a346d8d348798a65f FIREFOX_AURORA_51_BASE
+1196bf3032e1bce1fb07a01fd9082a767426c5fb FIREFOX_AURORA_52_BASE
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1277704 - jemalloc may need a clobber
+Merge day clobber
\ No newline at end of file
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -458,21 +458,16 @@ HTMLTextFieldAccessible::DoAction(uint8_
 
 already_AddRefed<nsIEditor>
 HTMLTextFieldAccessible::GetEditor() const
 {
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
   if (!editableElt)
     return nullptr;
 
-  // nsGenericHTMLElement::GetEditor has a security check.
-  // Make sure we're not restricted by the permissions of
-  // whatever script is currently running.
-  mozilla::dom::AutoNoJSAPI nojsapi;
-
   nsCOMPtr<nsIEditor> editor;
   editableElt->GetEditor(getter_AddRefs(editor));
 
   return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTextFieldAccessible: Widgets
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1479042428179" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1479128827245" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i988" id="{b12785f5-d8d0-4530-a3ea-5c4263b85bef}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i398" id="{377e5d4d-77e5-476a-8716-7e70a9272da0}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -346,18 +346,18 @@
     <emItem blockID="i618" id="toolbar@ask.com">
       <prefs/>
       <versionRange minVersion="3.15.24" maxVersion="3.15.24.*" severity="1"/>
       <versionRange minVersion="3.15.13" maxVersion="3.15.13.*" severity="1"/>
       <versionRange minVersion="3.15.28" maxVersion="3.15.28.*" severity="1"/>
       <versionRange minVersion="3.15.22" maxVersion="3.15.22.*" severity="1"/>
       <versionRange minVersion="3.15.8" maxVersion="3.15.8.*" severity="1"/>
       <versionRange minVersion="3.15.10" maxVersion="3.15.11.*" severity="1"/>
+      <versionRange minVersion="3.15.18" maxVersion="3.15.20.*" severity="1"/>
       <versionRange minVersion="3.15.5" maxVersion="3.15.5.*" severity="1"/>
-      <versionRange minVersion="3.15.18" maxVersion="3.15.20.*" severity="1"/>
       <versionRange minVersion="3.15.31" maxVersion="3.15.31.*" severity="1"/>
       <versionRange minVersion="3.15.26" maxVersion="3.15.26.*" severity="1"/>
     </emItem>
     <emItem blockID="i15" id="personas@christopher.beard">
       <prefs/>
       <versionRange minVersion="1.6" maxVersion="1.6">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="3.6.*" minVersion="3.6"/>
@@ -451,28 +451,28 @@
     <emItem blockID="i1232" id="nosquint@urandom.ca">
       <prefs/>
       <versionRange minVersion="0" maxVersion="2.1.9.1-signed.1-signed" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="47"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i650" id="jid1-qj0w91o64N7Eeg@jetpack">
-      <prefs/>
-      <versionRange minVersion="39.5.1" maxVersion="47.0.4" severity="3"/>
-    </emItem>
     <emItem blockID="i748" id="{32da2f20-827d-40aa-a3b4-2fc4a294352e}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i544" id="/^(93abedcf-8e3a-4d02-b761-d1441e437c09@243f129d-aee2-42c2-bcd1-48858e1c22fd\.com|9acfc440-ac2d-417a-a64c-f6f14653b712@09f9a966-9258-4b12-af32-da29bdcc28c5\.com|58ad0086-1cfb-48bb-8ad2-33a8905572bc@5715d2be-69b9-4930-8f7e-64bdeb961cfd\.com)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i650" id="jid1-qj0w91o64N7Eeg@jetpack">
+      <prefs/>
+      <versionRange minVersion="39.5.1" maxVersion="47.0.4" severity="3"/>
+    </emItem>
     <emItem blockID="i640" id="jid0-l9BxpNUhx1UUgRfKigWzSfrZqAc@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i628" id="ffxtlbr@iminent.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
@@ -593,28 +593,28 @@
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i730" id="25p@9eAkaLq.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i400" id="{dd6b651f-dfb9-4142-b0bd-09912ad22674}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i1227" id="{A34CAF42-A3E3-11E5-945F-18C31D5D46B0}">
       <prefs>
         <pref>security.csp.enable</pref>
         <pref>security.fileuri.strict_origin_policy</pref>
         <pref>security.mixed_content.block_active_content</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i400" id="{dd6b651f-dfb9-4142-b0bd-09912ad22674}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i11" id="yslow@yahoo-inc.com">
       <prefs/>
       <versionRange minVersion="2.0.5" maxVersion="2.0.5">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.5.7"/>
         </targetApplication>
       </versionRange>
     </emItem>
@@ -665,21 +665,21 @@
     <emItem blockID="i396" id="/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\.com$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i560" id="adsremoval@adsremoval.net">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i968" id="{184AA5E6-741D-464a-820E-94B3ABC2F3B4}">
+    <emItem blockID="i538" id="{354dbb0a-71d5-4e9f-9c02-6c88b9d387ba}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i538" id="{354dbb0a-71d5-4e9f-9c02-6c88b9d387ba}">
+    <emItem blockID="i968" id="{184AA5E6-741D-464a-820E-94B3ABC2F3B4}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i792" id="{8f894ed3-0bf2-498e-a103-27ef6e88899f}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i742" id="{f894a29a-f065-40c3-bb19-da6057778493}">
@@ -858,53 +858,53 @@
     <emItem blockID="i1022" id="g99hiaoekjoasiijdkoleabsy278djasi@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i718" id="G4Ce4@w.net">
+      <prefs>
+        <pref>browser.startup.homepage</pref>
+      </prefs>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
+    <emItem blockID="i21" id="support@update-firefox.com">
+      <prefs/>
+    </emItem>
     <emItem blockID="i75" id="firebug@software.joehewitt.com" os="Darwin,Linux">
       <prefs/>
       <versionRange minVersion="1.9.0" maxVersion="1.9.0" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="9.*" minVersion="9.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i718" id="G4Ce4@w.net">
-      <prefs>
-        <pref>browser.startup.homepage</pref>
-      </prefs>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
-    <emItem blockID="i21" id="support@update-firefox.com">
-      <prefs/>
-    </emItem>
     <emItem blockID="i491" id="{515b2424-5911-40bd-8a2c-bdb20286d8f5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i65" id="activity@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i476" id="mbroctone@facebook.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i532" id="249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i1264" id="suchpony@suchpony.de">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.6.7" severity="3"/>
     </emItem>
+    <emItem blockID="i476" id="mbroctone@facebook.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
       <prefs/>
       <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
     </emItem>
@@ -1155,48 +1155,48 @@
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i972" id="831778-poidjao88DASfsAnindsd@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i515" id="/^({bf9194c2-b86d-4ebc-9b53-1c08b6ff779e}|{61a83e16-7198-49c6-8874-3e4e8faeb4f3}|{f0af464e-5167-45cf-9cf0-66b396d1918c}|{5d9968c3-101c-4944-ba71-72d77393322d}|{01e86e69-a2f8-48a0-b068-83869bdba3d0})$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i844" id="e9d197d59f2f45f382b1aa5c14d82@8706aaed9b904554b5cb7984e9.com">
       <prefs>
         <pref>browser.startup.homepage</pref>
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i515" id="/^({bf9194c2-b86d-4ebc-9b53-1c08b6ff779e}|{61a83e16-7198-49c6-8874-3e4e8faeb4f3}|{f0af464e-5167-45cf-9cf0-66b396d1918c}|{5d9968c3-101c-4944-ba71-72d77393322d}|{01e86e69-a2f8-48a0-b068-83869bdba3d0})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i216" id="fdm_ffext@freedownloadmanager.org">
       <prefs/>
       <versionRange minVersion="1.0" maxVersion="1.3.1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.0a1"/>
         </targetApplication>
       </versionRange>
       <versionRange minVersion="1.5.7.5" maxVersion="1.5.7.5" severity="1"/>
     </emItem>
     <emItem blockID="i596" id="{b99c8534-7800-48fa-bd71-519a46cdc7e1}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i818" id="contentarget@maildrop.cc">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i23" id="firefox@bandoo.com">
       <prefs/>
       <versionRange minVersion="5.0" maxVersion="5.0" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.7a1pre"/>
         </targetApplication>
       </versionRange>
     </emItem>
@@ -1226,28 +1226,28 @@
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i92" id="play5@vide04flash.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*"/>
+    </emItem>
     <emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
       <prefs/>
       <versionRange minVersion="0.1" maxVersion="7.9.20.6" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="8.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i92" id="play5@vide04flash.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*"/>
-    </emItem>
     <emItem blockID="i220" id="pricepeep@getpricepeep.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="2.1.0.19.99" severity="1"/>
     </emItem>
     <emItem blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -1888,28 +1888,28 @@
           <versionRange maxVersion="*" minVersion="3.7a1pre"/>
         </targetApplication>
       </versionRange>
     </emItem>
     <emItem blockID="i1129" id="youtubeunblocker__web@unblocker.yt">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i1214" id="firefoxdav@icloud.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.4.22" severity="1"/>
     </emItem>
     <emItem blockID="i66" id="youtubeer@youtuber.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i4" id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
       <prefs/>
       <versionRange minVersion="1.2" maxVersion="1.2">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -634,16 +634,22 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #back-button > .toolbarbutton-menu-dropmarker,
 #forward-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 .unified-nav-current {
   font-weight: bold;
 }
 
+.bookmark-item > label {
+  /* ensure we use the direction of the bookmarks label instead of the
+   * browser locale */
+  unicode-bidi: plaintext;
+}
+
 toolbarbutton.bookmark-item {
   max-width: 13em;
 }
 
 /* Apply crisp rendering for favicons at exactly 2dppx resolution */
 @media (resolution: 2dppx) {
   #alltabs-popup > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon,
   .menuitem-with-favicon > .menu-iconic-left > .menu-iconic-icon {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -794,19 +794,16 @@
 
                 if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
                   let findBar = this.mTabBrowser.getFindBar(this.mTab);
 
                   // Close the Find toolbar if we're in old-style TAF mode
                   if (findBar.findMode != findBar.FIND_NORMAL) {
                     findBar.close();
                   }
-
-                  // fix bug 253793 - turn off highlight when page changes
-                  findBar.getElement("highlight").checked = false;
                 }
 
                 // Don't clear the favicon if this onLocationChange was
                 // triggered by a pushState or a replaceState (bug 550565) or
                 // a hash change (bug 408415).
                 if (aWebProgress.isLoadingDocument && !isSameDocument) {
                   this.mBrowser.mIconURL = null;
                 }
@@ -2128,38 +2125,40 @@
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aRelatedBrowser;
             var aOriginPrincipal;
+            var aDisallowInheritPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
-              aReferrerURI          = params.referrerURI;
-              aReferrerPolicy       = params.referrerPolicy;
-              aCharset              = params.charset;
-              aPostData             = params.postData;
-              aOwner                = params.ownerTab;
-              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
-              aFromExternal         = params.fromExternal;
-              aRelatedToCurrent     = params.relatedToCurrent;
-              aSkipAnimation        = params.skipAnimation;
-              aAllowMixedContent    = params.allowMixedContent;
-              aForceNotRemote       = params.forceNotRemote;
-              aNoReferrer           = params.noReferrer;
-              aUserContextId        = params.userContextId;
-              aEventDetail          = params.eventDetail;
-              aRelatedBrowser       = params.relatedBrowser;
-              aOriginPrincipal      = params.originPrincipal;
-              aOpener               = params.opener;
+              aReferrerURI              = params.referrerURI;
+              aReferrerPolicy           = params.referrerPolicy;
+              aCharset                  = params.charset;
+              aPostData                 = params.postData;
+              aOwner                    = params.ownerTab;
+              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
+              aFromExternal             = params.fromExternal;
+              aRelatedToCurrent         = params.relatedToCurrent;
+              aSkipAnimation            = params.skipAnimation;
+              aAllowMixedContent        = params.allowMixedContent;
+              aForceNotRemote           = params.forceNotRemote;
+              aNoReferrer               = params.noReferrer;
+              aUserContextId            = params.userContextId;
+              aEventDetail              = params.eventDetail;
+              aRelatedBrowser           = params.relatedBrowser;
+              aOriginPrincipal          = params.originPrincipal;
+              aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+              aOpener                   = params.opener;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2236,31 +2235,33 @@
             t.dispatchEvent(evt);
 
             if (!usingPreloadedContent && aOriginPrincipal) {
               b.createAboutBlankContentViewer(aOriginPrincipal);
             }
 
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
-            if (!usingPreloadedContent && !uriIsAboutBlank) {
+            if (!usingPreloadedContent && (!uriIsAboutBlank || aDisallowInheritPrincipal)) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
                 b.userTypedValue = aURI;
 
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup) {
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
               }
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               if (aAllowMixedContent)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
+              if (aDisallowInheritPrincipal)
+                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
               try {
                 b.loadURIWithFlags(aURI, {
                   flags,
                   referrerURI: aNoReferrer ? null: aReferrerURI,
                   referrerPolicy: aReferrerPolicy,
                   charset: aCharset,
                   postData: aPostData,
                 });
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -140,18 +140,17 @@ add_task(function* checkWrongSystemTimeW
   let formatter = new Intl.DateTimeFormat();
 
   // pretend we have a positively skewed (ahead) system time
   let serverDate = new Date("2015/10/27");
   let serverDateFmt = formatter.format(serverDate);
   let localDateFmt = formatter.format(new Date());
 
   let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   let message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
   ok(message.text.includes("because your clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
@@ -162,47 +161,44 @@ add_task(function* checkWrongSystemTimeW
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // pretend we have a negatively skewed (behind) system time
   serverDate = new Date();
   serverDate.setYear(serverDate.getFullYear() + 1);
   serverDateFmt = formatter.format(serverDate);
 
   skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
   ok(message.text.includes("because your clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // pretend we only have a slightly skewed system time, four hours
   skew = 60 * 60 * 4;
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with an only slightly skewed clock");
   message = yield Task.spawn(setUpPage);
 
   is(message.divDisplay, "none", "Wrong time message information is not visible");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // now pretend we have no skewed system time
   skew = 0;
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with no skewed clock");
   message = yield Task.spawn(setUpPage);
 
   is(message.divDisplay, "none", "Wrong time message information is not visible");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_bug537013.js
+++ b/browser/base/content/test/general/browser_bug537013.js
@@ -40,17 +40,17 @@ function test() {
   });
   texts.forEach(aText => addTabWithText(aText));
 
   // Set up the first tab
   gBrowser.selectedTab = tabs[0];
 
   setFindString(texts[0]);
   // Turn on highlight for testing bug 891638
-  gFindBar.getElement("highlight").checked = true;
+  gFindBar.toggleHighlight(true);
 
   // Make sure the second tab is correct, then set it up
   gBrowser.selectedTab = tabs[1];
   gBrowser.selectedTab.addEventListener("TabFindInitialized", continueTests1);
   // Initialize the findbar
   gFindBar;
 }
 function continueTests1() {
@@ -67,30 +67,29 @@ function continueTests1() {
   gBrowser.selectedTab = tabs[0];
   ok(!gFindBar.hidden, "First tab shows find bar!");
   // When the Find Clipboard is supported, this test not relevant.
   if (!HasFindClipboard)
     is(gFindBar._findField.value, texts[0], "First tab persists find value!");
   ok(gFindBar.getElement("highlight").checked,
      "Highlight button state persists!");
 
-  // While we're here, let's test bug 253793
+  // While we're here, let's test the backout of bug 253793.
   gBrowser.reload();
   gBrowser.addEventListener("DOMContentLoaded", continueTests2, true);
 }
 
 function continueTests2() {
   gBrowser.removeEventListener("DOMContentLoaded", continueTests2, true);
-  waitForCondition(() => !gFindBar.getElement("highlight").checked,
-                   continueTests3,
-                   "Highlight never reset!");
+  ok(gFindBar.getElement("highlight").checked, "Highlight never reset!");
+  continueTests3();
 }
 
 function continueTests3() {
-  ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
+  ok(gFindBar.getElement("highlight").checked, "Highlight button reset!");
   gFindBar.close();
   ok(gFindBar.hidden, "First tab doesn't show find bar!");
   gBrowser.selectedTab = tabs[1];
   ok(!gFindBar.hidden, "Second tab shows find bar!");
   // Test for bug 892384
   is(gFindBar._findField.getAttribute("focused"), "true",
      "Open findbar refocused on tab change!");
   gURLBar.focus();
@@ -124,13 +123,13 @@ function continueTests3() {
 // Test that findbar gets restored when a tab is moved to a new window.
 function checkNewWindow() {
   ok(!newWindow.gFindBar.hidden, "New window shows find bar!");
   // Disabled the following assertion due to intermittent failure on OSX 10.6 Debug.
   if (!HasFindClipboard) {
     is(newWindow.gFindBar._findField.value, texts[1],
        "New window find bar has correct find value!");
   }
-  ok(!newWindow.gFindBar.getElement("find-next").disabled,
-     "New window findbar has enabled buttons!");
+  ok(newWindow.gFindBar.getElement("find-next").disabled,
+     "New window findbar has disabled buttons!");
   newWindow.close();
   finish();
 }
--- a/browser/base/content/test/general/browser_bug719271.js
+++ b/browser/base/content/test/general/browser_bug719271.js
@@ -19,16 +19,22 @@ function test() {
     yield FullZoomHelper.load(gTab1, TEST_PAGE);
     yield FullZoomHelper.load(gTab2, TEST_VIDEO);
   }).then(zoomTab1, FullZoomHelper.failAndContinue(finish));
 }
 
 function zoomTab1() {
   Task.spawn(function* () {
     is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
+
+    // Reset zoom level if we run this test > 1 time in same browser session.
+    var level1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
+    if (level1 > 1)
+      FullZoom.reduce();
+
     FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
     FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
 
     FullZoom.enlarge();
     gLevel1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
 
     ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
     FullZoomHelper.zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
--- a/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js
+++ b/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js
@@ -2,16 +2,19 @@
 // clearing site-specific settings in Clear Recent History dialog
 
 var tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 var Sanitizer = tempScope.Sanitizer;
 
 add_task(function*() {
+  // getLoginSavingEnabled always returns false if password capture is disabled.
+  yield SpecialPowers.pushPrefEnv({"set": [["signon.rememberSignons", true]]});
+
   var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
 
   // Add a disabled host
   pwmgr.setLoginSavingEnabled("http://example.com", false);
   // Sanity check
   is(pwmgr.getLoginSavingEnabled("http://example.com"), false,
      "example.com should be disabled for password saving since we haven't cleared that yet.");
 
@@ -31,9 +34,11 @@ add_task(function*() {
   itemPrefs.setBoolPref("siteSettings", true);
 
   // Clear it
   yield s.sanitize();
 
   // Make sure it's gone
   is(pwmgr.getLoginSavingEnabled("http://example.com"), true,
      "example.com should be enabled for password saving again now that we've cleared.");
+
+  yield SpecialPowers.popPrefEnv();
 });
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -543,16 +543,20 @@ extensions.registerSchemaAPI("tabs", "ad
               if (!containerId) {
                 return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
               }
 
               options.userContextId = containerId;
             }
           }
 
+          // Make sure things like about:blank and data: URIs never inherit,
+          // and instead always get a NullPrincipal.
+          options.disallowInheritPrincipal = true;
+
           tabListener.initTabReady();
           let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
 
           let active = true;
           if (createProperties.active !== null) {
             active = createProperties.active;
           }
           if (active) {
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup_resize.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup_resize.js
@@ -1,16 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-function* openPanel(extension, win = window) {
+function* openPanel(extension, win = window, awaitLoad = false) {
   clickBrowserAction(extension, win);
 
-  return yield awaitExtensionPanel(extension, win, false);
+  return yield awaitExtensionPanel(extension, win, awaitLoad);
 }
 
 add_task(function* testBrowserActionPopupResize() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {
         "default_popup": "popup.html",
         "browser_style": true,
@@ -108,17 +108,17 @@ function* testPopupSize(standardsMode, b
   });
 
   yield extension.startup();
 
   /* eslint-disable mozilla/no-cpows-in-tests */
 
   if (arrowSide == "top") {
     // Test the standalone panel for a toolbar button.
-    let browser = yield openPanel(extension, browserWin);
+    let browser = yield openPanel(extension, browserWin, true);
 
     let dims = yield promiseContentDimensions(browser);
 
     is(dims.isStandards, standardsMode, "Document has the expected compat mode");
 
     let {innerWidth, innerHeight} = dims.window;
 
     dims = yield alterContent(browser, () => {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
@@ -83,16 +83,30 @@ add_task(function* testExecuteScript() {
           browser.test.assertEq(2, result.length, "Result has correct length");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
           browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
         }),
 
         browser.tabs.executeScript({
           code: "location.href;",
+          allFrames: true,
+          matchAboutBlank: true,
+        }).then(result => {
+          browser.test.assertTrue(Array.isArray(result), "Result is an array");
+
+          browser.test.assertEq(3, result.length, "Result has correct length");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
+          browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
+          browser.test.assertEq("about:blank", result[2], "Thirds result is correct");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
           runAt: "document_end",
         }).then(result => {
           browser.test.assertEq(1, result.length, "Expected callback result");
           browser.test.assertEq("string", typeof result[0], "Result is a string");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "Result is correct");
         }),
 
@@ -182,16 +196,22 @@ add_task(function* testExecuteScript() {
         browser.tabs.create({url: "http://example.com/"}).then(async tab => {
           let result = await browser.tabs.executeScript(tab.id, {code: "location.href"});
 
           browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab");
 
           await browser.tabs.remove(tab.id);
         }),
 
+        browser.tabs.create({url: "about:blank"}).then(async tab => {
+          const result = await browser.tabs.executeScript(tab.id, {code: "location.href", matchAboutBlank: true});
+          browser.test.assertEq("about:blank", result[0], "Script executed correctly in new tab");
+          await browser.tabs.remove(tab.id);
+        }),
+
         new Promise(resolve => {
           browser.runtime.onMessage.addListener(message => {
             browser.test.assertEq("script ran", message, "Expected runtime message");
             resolve();
           });
         }),
       ]);
 
--- a/browser/components/extensions/test/browser/file_iframe_document.html
+++ b/browser/components/extensions/test/browser/file_iframe_document.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <title></title>
 </head>
 <body>
   <iframe src="/"></iframe>
+  <iframe src="about:blank"></iframe>
 </body>
 </html>
--- a/browser/components/places/content/places.css
+++ b/browser/components/places/content/places.css
@@ -1,16 +1,22 @@
 /* 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/. */
 
 tree[type="places"] {
   -moz-binding: url("chrome://browser/content/places/tree.xml#places-tree");
 }
 
+tree[type="places"] > treechildren::-moz-tree-cell {
+  /* ensure we use the direction of the website title / url instead of the
+   * browser locale */
+  unicode-bidi: plaintext;
+}
+
 .toolbar-drop-indicator {
   position: relative;
   z-index: 1;
 }
 
 menupopup[placespopup="true"] {
   -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
 }
--- a/browser/components/places/content/placesOverlay.xul
+++ b/browser/components/places/content/placesOverlay.xul
@@ -37,17 +37,17 @@
   <script type="application/javascript"
           src="chrome://browser/content/places/treeView.js"/>
 
   <!-- Bookmarks and history tooltip -->
   <tooltip id="bhTooltip" noautohide="true"
            onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)">
     <vbox id="bhTooltipTextBox" flex="1">
       <label id="bhtTitleText" class="tooltip-label" />
-      <label id="bhtUrlText" crop="center" class="tooltip-label" />
+      <label id="bhtUrlText" crop="center" class="tooltip-label uri-element" />
     </vbox>
   </tooltip>
 
   <commandset id="placesCommands"
               commandupdater="true"
               events="focus,sort,places"
               oncommandupdate="goUpdatePlacesCommands();">
     <command id="placesCmd_open"
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -641,35 +641,36 @@ nsWindowsShellService::LaunchControlPane
     pAARUI->Release();
   }
   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsWindowsShellService::LaunchControlPanelDefaultPrograms()
 {
-  // Default Programs is a Vista+ feature
-  if (!IsVistaOrLater()) {
+  // This Default Programs feature is Win7+ only.
+  if (!IsWin7OrLater()) {
     return NS_ERROR_FAILURE;
   }
 
   // Build the path control.exe path safely
   WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
   if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
     return NS_ERROR_FAILURE;
   }
   LPCWSTR controlEXE = L"control.exe";
   if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
     return NS_ERROR_FAILURE;
   }
   if (!PathAppendW(controlEXEPath, controlEXE)) {
     return NS_ERROR_FAILURE;
   }
 
-  WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram";
+  WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page "
+    "pageDefaultProgram\\pageAdvancedSettings?pszAppName=" APP_REG_NAME;
   STARTUPINFOW si = {sizeof(si), 0};
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_SHOWDEFAULT;
   PROCESS_INFORMATION pi = {0};
   if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
                       0, nullptr, nullptr, &si, &pi)) {
     return NS_ERROR_FAILURE;
   }
@@ -723,18 +724,20 @@ SettingsAppBelievesConnected()
   }
 
   return !!value;
 }
 
 nsresult
 nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
 {
-  if (!IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
-    // Use the classic Control Panel to work around a bug of Windows 10.
+  if (!IsWindowsBuildOrLater(14965) &&
+      !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
+    // Use the classic Control Panel to work around a bug of older
+    // builds of Windows 10.
     return LaunchControlPanelDefaultPrograms();
   }
 
   IApplicationActivationManager* pActivator;
   HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
                                 nullptr,
                                 CLSCTX_INPROC,
                                 IID_IApplicationActivationManager,
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-52.0a1
+53.0a1
--- a/browser/config/version_display.txt
+++ b/browser/config/version_display.txt
@@ -1,1 +1,1 @@
-52.0a1
+53.0a1
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -378,16 +378,26 @@ description#identity-popup-content-verif
 .identity-popup-permission-icon.in-use {
   animation: 1.5s ease in-use-blink infinite;
 }
 
 @keyframes in-use-blink {
   50% { opacity: 0; }
 }
 
+.identity-popup-permission-label,
+.identity-popup-permission-state-label {
+  /* We need to align the action buttons and permission icons with the text.
+     This is tricky because the icon height is defined in pixels, while the
+     font height can vary with platform and system settings, and at least on
+     Windows the default font metrics reserve more extra space for accents.
+     This value is a good compromise for different platforms and font sizes. */
+  margin-top: -0.1em;
+}
+
 .identity-popup-permission-label {
   margin-inline-start: 1em;
 }
 
 .identity-popup-permission-state-label {
   margin-inline-end: 5px;
   text-align: end;
   color: graytext;
--- a/build/moz.configure/compile-checks.configure
+++ b/build/moz.configure/compile-checks.configure
@@ -70,22 +70,22 @@ def check_header(header, language='C++',
 @template
 def check_headers(*headers, **kwargs):
     checks = []
     for header in headers:
         checks.append(check_header(header, **kwargs))
     return checks
 
 
-@depends(c_compiler)
-def warnings_cflags(c_compiler):
+@dependable
+def warnings_cflags():
     return []
 
-@depends(cxx_compiler)
-def warnings_cxxflags(cxx_compiler):
+@dependable
+def warnings_cxxflags():
     return []
 
 
 # Tests whether GCC or clang support the given warning flag, and if it is,
 # add it to the list of warning flags for the build.
 # - `warning` is the warning flag (e.g. -Wfoo)
 # - `compiler` (optional) is the compiler to test against (c_compiler or
 #   cxx_compiler, from toolchain.configure). When omitted, both compilers
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -487,18 +487,18 @@ add_old_configure_assignment('HAVE_64BIT
 
 # Autoconf needs these set
 @depends(host)
 def host_for_old_configure(host):
     return '--host=%s' % host.alias
 
 add_old_configure_arg(host_for_old_configure)
 
-@depends(host, target)
-def target_for_old_configure(host, target):
+@depends(target)
+def target_for_old_configure(target):
     target_alias = target.alias
     # old-configure does plenty of tests against $target and $target_os
     # and expects darwin for iOS, so make it happy.
     if target.os == 'iOS':
         target_alias = target_alias.replace('-ios', '-darwin')
     return '--target=%s' % target_alias
 
 add_old_configure_arg(target_for_old_configure)
@@ -754,20 +754,20 @@ def project_flag(env=None, set_for_old_c
 
 # milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set.
 @depends(milestone, '--help')
 def enabled_in_nightly(milestone, _):
     return milestone.is_nightly
 
 # Set the MOZ_CONFIGURE_OPTIONS variable with all the options that
 # were passed somehow (environment, command line, mozconfig)
-@depends(mozconfig_options)
+@dependable
 @imports(_from='mozbuild.shellutil', _import='quote')
 @imports('__sandbox__')
-def all_configure_options(_):
+def all_configure_options():
     result = []
     previous = None
     for option in __sandbox__._options.itervalues():
         # __sandbox__._options contains items for both option.name and
         # option.env. But it's also an OrderedDict, meaning both are
         # consecutive.
         # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not
         # interesting.
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -66,23 +66,23 @@ def rust_compiler(value, rustc, rustc_in
             version {} of the 'rustc' toolchain and make sure it is
             first in your path.
             You can verify this by typing 'rustc --version'.
             '''.format(version, min_version)))
         return True
 
 set_config('MOZ_RUST', rust_compiler)
 
-@depends(rust_compiler, rustc, target, cross_compiling)
+@depends(rust_compiler, rustc, target)
 @imports('os')
 @imports('subprocess')
 @imports(_from='mozbuild.configure.util', _import='LineIO')
 @imports(_from='mozbuild.shellutil', _import='quote')
 @imports(_from='tempfile', _import='mkstemp')
-def rust_target(rust_compiler, rustc, target, cross_compiling):
+def rust_target(rust_compiler, rustc, target):
     if rust_compiler:
         # Rust's --target options are similar to, but not exactly the same
         # as, the autoconf-derived targets we use.  An example would be that
         # Rust uses distinct target triples for targetting the GNU C++ ABI
         # and the MSVC C++ ABI on Win32, whereas autoconf has a single
         # triple and relies on the user to ensure that everything is
         # compiled for the appropriate ABI.  We need to perform appropriate
         # munging to get the correct option to rustc.
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -151,18 +151,18 @@ def using_compiler_wrapper(compiler_wrap
 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
 
 
 # Cross-compilation related things.
 # ==============================================================
 js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
           help='Prefix for the target toolchain')
 
-@depends('--with-toolchain-prefix', target, host, cross_compiling)
-def toolchain_prefix(value, target, host, cross_compiling):
+@depends('--with-toolchain-prefix', target, cross_compiling)
+def toolchain_prefix(value, target, cross_compiling):
     if value:
         return tuple(value)
     if cross_compiling:
         return ('%s-' % target.toolchain, '%s-' % target.alias)
 
 @depends(toolchain_prefix, target)
 def first_toolchain_prefix(toolchain_prefix, target):
     # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
@@ -878,18 +878,18 @@ add_old_configure_assignment('MOZ_DEBUG_
 @depends(c_compiler, target)
 def libcxx_inline_visibility(c_compiler, target):
     if c_compiler.type == 'clang' and target.os == 'Android':
         return ''
 
 set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility)
 set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility)
 
-@depends(c_compiler, target, check_build_environment)
-def visibility_flags(c_compiler, target, env):
+@depends(target, check_build_environment)
+def visibility_flags(target, env):
     if target.os != 'WINNT':
         if target.kernel == 'Darwin':
             return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
         return ('-I%s/system_wrappers' % os.path.join(env.dist),
                 '-include',
                 '%s/config/gcc_hidden.h' % env.topsrcdir)
 
 @depends(target, visibility_flags)
--- a/build/sanitizers/asan_blacklist_win.txt
+++ b/build/sanitizers/asan_blacklist_win.txt
@@ -1,15 +1,15 @@
 # This is originally copied from Chromium tools/memory/asan/blacklist_win.txt.
 # The rules in this file are only applied at compile time. If you can modify the
 # source in question, consider function attributes to disable instrumentation.
 
 # Bug 1200740 - ASan crash due to child process function interceptions
 # Sandbox executes some of its code before the ASan RTL gets initialized and
-# maps shadow memory.  As a result, instrmented code tries to access unavailable
+# maps shadow memory.  As a result, instrumented code tries to access unavailable
 # shadow memory and faults.
 fun:*TargetNtSetInformationThread@20
 fun:*TargetNtOpenThreadToken@20
 fun:*TargetNtOpenThreadTokenEx@24
 fun:*TargetNtMapViewOfSection@44
 fun:*AutoProtectMemory*sandbox*
 fun:*EatResolverThunk*sandbox*
 fun:*InterceptionAgent*sandbox*
@@ -19,8 +19,9 @@ fun:*ProcessState*sandbox*
 src:*pe_image.h
 src:*pe_image.cc
 src:*resolver_32.cc
 src:*filesystem_interception.cc
 src:*process_thread_interception.cc
 src:*registry_interception.cc
 src:*sandbox_nt_util.cc
 src:*sync_interception.cc
+src:*interceptors_64.cc
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -685,16 +685,17 @@ SSL_ResetHandshake
 SSL_SendAdditionalKeyShares
 SSL_SetCanFalseStartCallback
 SSL_SetDowngradeCheckVersion
 SSL_SetNextProtoNego
 SSL_SetPKCS11PinArg
 SSL_SetSockPeerID
 SSL_SetSRTPCiphers
 SSL_SetStapledOCSPResponses
+SSL_SetTrustAnchors
 SSL_SetURL
 SSL_ShutdownServerSessionIDCache
 SSL_SignatureSchemePrefSet
 SSL_SNISocketConfigHook
 SSL_VersionRangeGet
 SSL_VersionRangeGetDefault
 SSL_VersionRangeGetSupported
 SSL_VersionRangeSet
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.py.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-52.0a1
+53.0a1
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -56,15 +56,16 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_animation_timeline_rewind_button.js]
 [browser_animation_timeline_scrubber_exists.js]
 [browser_animation_timeline_scrubber_movable.js]
 [browser_animation_timeline_scrubber_moves.js]
 [browser_animation_timeline_setCurrentTime.js]
 [browser_animation_timeline_shows_delay.js]
 [browser_animation_timeline_shows_endDelay.js]
 [browser_animation_timeline_shows_iterations.js]
+[browser_animation_timeline_shows_name_label.js]
 [browser_animation_timeline_shows_time_info.js]
 [browser_animation_timeline_takes_rate_into_account.js]
 [browser_animation_timeline_ui.js]
 [browser_animation_toggle_button_resets_on_navigate.js]
 [browser_animation_toggle_button_toggles_animations.js]
 [browser_animation_toolbar_exists.js]
 [browser_animation_ui_updates_when_animation_data_changes.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_shows_name_label.js
@@ -0,0 +1,46 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Check the text content and width of name label.
+
+add_task(function* () {
+  yield addTab(URL_ROOT + "doc_simple_animation.html");
+  let {inspector, panel} = yield openAnimationInspector();
+
+  info("Selecting 'simple-animation' animation which is running on compositor");
+  yield selectNodeAndWaitForAnimations(".animated", inspector);
+  checkNameLabel(panel.animationsTimelineComponent.rootWrapperEl, "simple-animation");
+
+  info("Selecting 'no-compositor' animation which is not running on compositor");
+  yield selectNodeAndWaitForAnimations(".no-compositor", inspector);
+  checkNameLabel(panel.animationsTimelineComponent.rootWrapperEl, "no-compositor");
+});
+
+function checkNameLabel(rootWrapperEl, expectedLabelContent) {
+  const timeblockEl = rootWrapperEl.querySelector(".time-block");
+  const labelEl = rootWrapperEl.querySelector(".name div");
+  is(labelEl.textContent, expectedLabelContent,
+     `Text content of labelEl sould be ${ expectedLabelContent }`);
+
+  // Expand timeblockEl to avoid max-width of the label.
+  timeblockEl.style.width = "10000px";
+  const originalLabelWidth = labelEl.clientWidth;
+  ok(originalLabelWidth < timeblockEl.clientWidth / 2,
+     "Label width should be less than 50%");
+
+  // Set timeblockEl width to double of original label width.
+  timeblockEl.style.width = `${ originalLabelWidth * 2 }px`;
+  is(labelEl.clientWidth + labelEl.offsetLeft, originalLabelWidth,
+     `Label width + offsetLeft should be ${ originalLabelWidth }px`);
+
+  // Shrink timeblockEl to enable max-width.
+  timeblockEl.style.width = `${ originalLabelWidth }px`;
+  is(labelEl.clientWidth + labelEl.offsetLeft,
+     Math.round(timeblockEl.clientWidth / 2),
+     "Label width + offsetLeft should be half of timeblockEl");
+}
--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
@@ -10,16 +10,19 @@
  * As part of bug 1077403, the leaking uncaught rejection should be fixed.
  */
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is " +
   "still waiting for a WebGL context to be created.");
 
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
 
+// Bug 1277805: Too slow for debug runs
+requestLongerTimeout(2);
+
 /**
  * Bug 979536: Ensure fronts are destroyed after toolbox close.
  *
  * The fronts need to be destroyed manually to unbind their onPacket handlers.
  *
  * When you initialize a front and call |this.manage|, it adds a client actor
  * pool that the DebuggerClient uses to route packet replies to that actor.
  *
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -3,16 +3,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* global window */
 
 "use strict";
 
+var Cu = Components.utils;
+var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var Services = require("Services");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var EventEmitter = require("devtools/shared/event-emitter");
 const {executeSoon} = require("devtools/shared/DevToolsUtils");
 var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 var {Task} = require("devtools/shared/task");
 const {initCssProperties} = require("devtools/shared/fronts/css-properties");
@@ -408,31 +410,36 @@ Inspector.prototype = {
         str = INSPECTOR_L10N.getStr("inspector.searchResultsNone");
       }
     }
 
     this.searchResultsLabel.textContent = str;
   },
 
   get React() {
-    return require("devtools/client/shared/vendor/react");
+    return this._toolbox.React;
   },
 
   get ReactDOM() {
-    return require("devtools/client/shared/vendor/react-dom");
+    return this._toolbox.ReactDOM;
   },
 
   get ReactRedux() {
-    return require("devtools/client/shared/vendor/react-redux");
+    return this._toolbox.ReactRedux;
+  },
+
+  get browserRequire() {
+    return this._toolbox.browserRequire;
   },
 
   get InspectorTabPanel() {
     if (!this._InspectorTabPanel) {
       this._InspectorTabPanel =
-        this.React.createFactory(require("devtools/client/inspector/components/inspector-tab-panel"));
+        this.React.createFactory(this.browserRequire(
+        "devtools/client/inspector/components/inspector-tab-panel"));
     }
     return this._InspectorTabPanel;
   },
 
   /**
    * Check if the inspector should use the landscape mode.
    *
    * @return {Boolean} true if the inspector should be in landscape mode.
@@ -442,17 +449,18 @@ Inspector.prototype = {
     return clientWidth > PORTRAIT_MODE_WIDTH;
   },
 
   /**
    * Build Splitter located between the main and side area of
    * the Inspector panel.
    */
   setupSplitter: function () {
-    let SplitBox = this.React.createFactory(require("devtools/client/shared/components/splitter/split-box"));
+    let SplitBox = this.React.createFactory(this.browserRequire(
+      "devtools/client/shared/components/splitter/split-box"));
 
     let splitter = SplitBox({
       className: "inspector-sidebar-splitter",
       initialWidth: INITIAL_SIDEBAR_SIZE,
       initialHeight: INITIAL_SIDEBAR_SIZE,
       splitterSize: 1,
       endPanelControl: true,
       startPanel: this.InspectorTabPanel({
@@ -555,17 +563,17 @@ Inspector.prototype = {
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
-      const {LayoutView} = require("devtools/client/inspector/layout/layout");
+      const {LayoutView} = this.browserRequire("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
@@ -603,17 +611,18 @@ Inspector.prototype = {
   addSidebarTab: function (id, title, panel, selected) {
     this.sidebar.addTab(id, title, panel, selected);
   },
 
   setupToolbar: function () {
     this.teardownToolbar();
 
     // Setup the sidebar toggle button.
-    let SidebarToggle = this.React.createFactory(require("devtools/client/shared/components/sidebar-toggle"));
+    let SidebarToggle = this.React.createFactory(this.browserRequire(
+      "devtools/client/shared/components/sidebar-toggle"));
 
     let sidebarToggle = SidebarToggle({
       onClick: this.onPaneToggleButtonClicked,
       collapsed: false,
       expandPaneTitle: INSPECTOR_L10N.getStr("inspector.expandPane"),
       collapsePaneTitle: INSPECTOR_L10N.getStr("inspector.collapsePane"),
     });
 
@@ -1842,17 +1851,16 @@ Inspector.prototype = {
 // URL constructor doesn't support chrome: scheme
 let href = window.location.href.replace(/chrome:/, "http://");
 let url = new window.URL(href);
 
 // Only use this method to attach the toolbox if some query parameters are given
 if (url.search.length > 1) {
   const { targetFromURL } = require("devtools/client/framework/target-from-url");
   const { attachThread } = require("devtools/client/framework/attach-thread");
-  const Cu = Components.utils;
   const { BrowserLoader } =
     Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 
   const { Selection } = require("devtools/client/framework/selection");
   const { InspectorFront } = require("devtools/shared/fronts/inspector");
   const { getHighlighterUtils } = require("devtools/client/framework/toolbox-highlighter-utils");
 
   Task.spawn(function* () {
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -21,23 +21,16 @@
   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
   <link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
   <link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>
 
   <script type="application/javascript;version=1.8"
           src="chrome://devtools/content/shared/theme-switching.js"></script>
-  <script type="application/javascript;version=1.8">
-    const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
-    const { require } = BrowserLoader({
-      window,
-      baseURI: "resource://devtools/client/inspector/",
-    });
-    </script>
   <script type="application/javascript;version=1.8" src="inspector.js" defer="true"></script>
 </head>
 <body class="theme-body" role="application">
   <div class="inspector-responsive-container theme-body inspector">
 
     <!-- Main Panel Content -->
     <div id="inspector-main-content" class="devtools-main-content">
       <div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true"
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-urls.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-urls.js
@@ -63,17 +63,17 @@ function* startTest() {
 }
 
 function* testCopyUrlToClipboard({view, inspector}, type, selector, expected) {
   info("Select node in inspector panel");
   yield selectNode(selector, inspector);
 
   info("Retrieve background-image link for selected node in current " +
        "styleinspector view");
-  let property = getBackgroundImageProperty(inspector, view, selector);
+  let property = getBackgroundImageProperty(view, selector);
   let imageLink = property.valueSpan.querySelector(".theme-link");
   ok(imageLink, "Background-image link element found");
 
   info("Simulate right click on the background-image URL");
   let allMenuItems = openStyleContextMenuAndGetAllItems(view, imageLink);
   let menuitemCopyUrl = allMenuItems.find(item => item.label ===
     STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyUrl"));
   let menuitemCopyImageDataUrl = allMenuItems.find(item => item.label ===
@@ -95,15 +95,15 @@ function* testCopyUrlToClipboard({view, 
     yield waitForClipboardPromise(() => {
       return menuitemCopyUrl.click();
     }, expected);
   }
 
   info("Hide context menu");
 }
 
-function getBackgroundImageProperty(inspector, view, selector) {
-  let isRuleView = view === inspector.ruleview.view;
+function getBackgroundImageProperty(view, selector) {
+  let isRuleView = view instanceof CssRuleView;
   if (isRuleView) {
     return getRuleViewProperty(view, selector, "background-image");
   }
   return getComputedViewProperty(view, "background-image");
 }
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -5,16 +5,17 @@
 /* import-globals-from ../../test/head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
+var {CssRuleView} = require("devtools/client/inspector/rules/rules");
 var {getInplaceEditorForSpan: inplaceEditor} =
   require("devtools/client/shared/inplace-editor");
 const {getColor: getThemeColor} = require("devtools/client/shared/theme");
 
 const TEST_URL_ROOT =
   "http://example.com/browser/devtools/client/inspector/shared/test/";
 const TEST_URL_ROOT_SSL =
   "https://example.com/browser/devtools/client/inspector/shared/test/";
--- a/devtools/client/inspector/toolsidebar.js
+++ b/devtools/client/inspector/toolsidebar.js
@@ -56,24 +56,29 @@ ToolSidebar.prototype = {
   get React() {
     return this._toolPanel.React;
   },
 
   get ReactDOM() {
     return this._toolPanel.ReactDOM;
   },
 
+  get browserRequire() {
+    return this._toolPanel.browserRequire;
+  },
+
   get InspectorTabPanel() {
     return this._toolPanel.InspectorTabPanel;
   },
 
   // Rendering
 
   render: function () {
-    let Tabbar = this.React.createFactory(require("devtools/client/shared/components/tabs/tabbar"));
+    let Tabbar = this.React.createFactory(this.browserRequire(
+      "devtools/client/shared/components/tabs/tabbar"));
 
     let sidebar = Tabbar({
       toolbox: this._toolPanel._toolbox,
       showAllTabsMenu: true,
       onSelect: this.handleSelectionChange.bind(this),
     });
 
     this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -236,16 +236,17 @@ sticky_pref("devtools.theme", "light");
 #endif
 
 // Web console filters
 pref("devtools.webconsole.filter.error", true);
 pref("devtools.webconsole.filter.warn", true);
 pref("devtools.webconsole.filter.info", true);
 pref("devtools.webconsole.filter.log", true);
 pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
 pref("devtools.webconsole.filter.net", false);
 pref("devtools.webconsole.filter.netxhr", false);
 // Deprecated - old console frontend
 pref("devtools.webconsole.filter.network", true);
 pref("devtools.webconsole.filter.networkinfo", false);
 pref("devtools.webconsole.filter.netwarn", true);
 pref("devtools.webconsole.filter.csserror", true);
 pref("devtools.webconsole.filter.cssparser", false);
--- a/devtools/client/shared/components/reps/element-node.js
+++ b/devtools/client/shared/components/reps/element-node.js
@@ -82,24 +82,41 @@ define(function (require, exports, modul
         "<",
         nodeNameElement,
         ...attributeElements,
         ">",
       ];
     },
 
     render: function () {
-      let {object, mode} = this.props;
+      let {
+        object,
+        mode,
+        onDOMNodeMouseOver,
+        onDOMNodeMouseOut
+      } = this.props;
       let elements = this.getElements(object, mode);
-      const baseElement = span({className: "objectBox"}, ...elements);
+      let objectLink = this.props.objectLink || span;
 
-      if (this.props.objectLink) {
-        return this.props.objectLink({object}, baseElement);
+      let baseConfig = {className: "objectBox objectBox-node"};
+      if (onDOMNodeMouseOver) {
+        Object.assign(baseConfig, {
+          onMouseOver: _ => onDOMNodeMouseOver(object)
+        });
       }
-      return baseElement;
+
+      if (onDOMNodeMouseOut) {
+        Object.assign(baseConfig, {
+          onMouseOut: onDOMNodeMouseOut
+        });
+      }
+
+      return objectLink({object},
+        span(baseConfig, ...elements)
+      );
     },
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/reps/error.js
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
+  // Dependencies
+  const { isGrip } = require("./rep-utils");
+  // Shortcuts
+  const { span } = React.DOM;
+
+  /**
+   * Renders Error objects.
+   */
+  const ErrorRep = React.createClass({
+    displayName: "Error",
+
+    propTypes: {
+      object: React.PropTypes.object.isRequired,
+      mode: React.PropTypes.string
+    },
+
+    render: function () {
+      let object = this.props.object;
+      let preview = object.preview;
+      let name = preview && preview.name
+        ? preview.name
+        : "Error";
+
+      let content = this.props.mode === "tiny"
+        ? name
+        : `${name}: ${preview.message}`;
+
+      if (preview.stack && this.props.mode !== "tiny") {
+        /*
+         * Since Reps are used in the JSON Viewer, we can't localize
+         * the "Stack trace" label (defined in debugger.properties as
+         * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
+         */
+        content = `${content}\nStack trace:\n${preview.stack}`;
+      }
+
+      let objectLink = this.props.objectLink || span;
+      return (
+        objectLink({object, className: "objectBox-stackTrace"},
+          span({}, content)
+        )
+      );
+    },
+  });
+
+  // Registration
+  function supportsObject(object, type) {
+    if (!isGrip(object)) {
+      return false;
+    }
+    return (object.preview && type === "Error");
+  }
+
+  // Exports from this module
+  exports.ErrorRep = {
+    rep: ErrorRep,
+    supportsObject: supportsObject
+  };
+});
--- a/devtools/client/shared/components/reps/moz.build
+++ b/devtools/client/shared/components/reps/moz.build
@@ -7,16 +7,17 @@
 DevToolsModules(
     'array.js',
     'attribute.js',
     'caption.js',
     'comment-node.js',
     'date-time.js',
     'document.js',
     'element-node.js',
+    'error.js',
     'event.js',
     'function.js',
     'grip-array.js',
     'grip-map.js',
     'grip.js',
     'infinity.js',
     'long-string.js',
     'nan.js',
--- a/devtools/client/shared/components/reps/rep.js
+++ b/devtools/client/shared/components/reps/rep.js
@@ -32,16 +32,17 @@ define(function (require, exports, modul
   const { Event } = require("./event");
   const { Func } = require("./function");
   const { PromiseRep } = require("./promise");
   const { RegExp } = require("./regexp");
   const { StyleSheet } = require("./stylesheet");
   const { CommentNode } = require("./comment-node");
   const { ElementNode } = require("./element-node");
   const { TextNode } = require("./text-node");
+  const { ErrorRep } = require("./error");
   const { Window } = require("./window");
   const { ObjectWithText } = require("./object-with-text");
   const { ObjectWithURL } = require("./object-with-url");
   const { GripArray } = require("./grip-array");
   const { GripMap } = require("./grip-map");
   const { Grip } = require("./grip");
 
   // List of all registered template.
@@ -59,16 +60,17 @@ define(function (require, exports, modul
     LongStringRep,
     Func,
     PromiseRep,
     ArrayRep,
     Document,
     Window,
     ObjectWithText,
     ObjectWithURL,
+    ErrorRep,
     GripArray,
     GripMap,
     Grip,
     Undefined,
     Null,
     StringRep,
     Number,
     SymbolRep,
--- a/devtools/client/shared/components/reps/text-node.js
+++ b/devtools/client/shared/components/reps/text-node.js
@@ -39,30 +39,43 @@ define(function (require, exports, modul
       }
       return "";
     },
 
     render: function () {
       let grip = this.props.object;
       let mode = this.props.mode || "short";
 
+      let baseConfig = {className: "objectBox objectBox-textNode"};
+      if (this.props.onDOMNodeMouseOver) {
+        Object.assign(baseConfig, {
+          onMouseOver: _ => this.props.onDOMNodeMouseOver(grip)
+        });
+      }
+
+      if (this.props.onDOMNodeMouseOut) {
+        Object.assign(baseConfig, {
+          onMouseOut: this.props.onDOMNodeMouseOut
+        });
+      }
+
       if (mode == "short" || mode == "tiny") {
         return (
-          DOM.span({className: "objectBox objectBox-textNode"},
+          DOM.span(baseConfig,
             this.getTitle(grip),
             DOM.span({className: "nodeValue"},
               "\"" + this.getTextContent(grip) + "\""
             )
           )
         );
       }
 
       let objectLink = this.props.objectLink || DOM.span;
       return (
-        DOM.span({className: "objectBox objectBox-textNode"},
+        DOM.span(baseConfig,
           this.getTitle(grip),
           objectLink({
             object: grip
           }, "<"),
           DOM.span({className: "nodeTag"}, "TextNode"),
           " textContent=\"",
           DOM.span({className: "nodeValue"},
             this.getTextContent(grip)
--- a/devtools/client/shared/components/test/mochitest/chrome.ini
+++ b/devtools/client/shared/components/test/mochitest/chrome.ini
@@ -8,16 +8,17 @@ support-files =
 [test_notification_box_02.html]
 [test_notification_box_03.html]
 [test_reps_array.html]
 [test_reps_attribute.html]
 [test_reps_comment-node.html]
 [test_reps_date-time.html]
 [test_reps_document.html]
 [test_reps_element-node.html]
+[test_reps_error.html]
 [test_reps_event.html]
 [test_reps_function.html]
 [test_reps_grip.html]
 [test_reps_grip-array.html]
 [test_reps_grip-map.html]
 [test_reps_infinity.html]
 [test_reps_long-string.html]
 [test_reps_nan.html]
--- a/devtools/client/shared/components/test/mochitest/test_reps_element-node.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_element-node.html
@@ -26,16 +26,19 @@ window.onload = Task.async(function* () 
     yield testBodyNode();
     yield testDocumentElement();
     yield testNode();
     yield testNodeWithLeadingAndTrailingSpacesClassName();
     yield testNodeWithoutAttributes();
     yield testLotsOfAttributes();
     yield testSvgNode();
     yield testSvgNodeInXHTML();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBodyNode() {
     const stub = getGripStub("testBodyNode");
@@ -166,16 +169,49 @@ window.onload = Task.async(function* () 
       "Element node rep has expected text content for XHTML SVG element node");
 
     const tinyRenderedComponent = renderComponent(
       ElementNode.rep, { object: stub, mode: "tiny" });
     is(tinyRenderedComponent.textContent, `svg:circle.svg-element`,
       "Element node rep has expected text content for XHTML SVG element in tiny mode");
   }
 
+  function testOnMouseOver() {
+    const stub = getGripStub("testNode");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      ElementNode.rep, {object: stub, onDOMNodeMouseOver});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOver(node);
+
+    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
+      "when mouseover is fired on the Rep");
+  }
+
+  function testOnMouseOut() {
+    const stub = getGripStub("testNode");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      ElementNode.rep, {object: stub, onDOMNodeMouseOut});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOut(node);
+
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
+
   function getGripStub(name) {
     switch (name) {
       case "testBodyNode":
         return {
           "type": "object",
           "actor": "server1.conn1.child1/obj30",
           "class": "HTMLBodyElement",
           "ownPropertyLength": 0,
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/test_reps_error.html
@@ -0,0 +1,421 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test Error rep
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Rep test - Error</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>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+"use strict";
+
+window.onload = Task.async(function* () {
+  let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
+  let { ErrorRep } = browserRequire("devtools/client/shared/components/reps/error");
+
+  try {
+    // Test errors with different properties
+    yield testSimpleError();
+    yield testMultilineStackError();
+    yield testErrorWithoutStacktrace();
+
+    // Test different kind of errors
+    yield testEvalError();
+    yield testInternalError();
+    yield testRangeError();
+    yield testReferenceError();
+    yield testSyntaxError();
+    yield testTypeError();
+    yield testURIError();
+  } catch (e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+
+  function testSimpleError() {
+    // Test object = `new Error("Error message")`
+    const stub = getGripStub("testSimpleError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: Error message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:1:13\n",
+      "Error Rep has expected text content for a simple error");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for a simple error in tiny mode");
+  }
+
+  function testMultilineStackError() {
+    /*
+     * Test object = `
+     *   function errorFoo() {
+     *     errorBar();
+     *   }
+     *   function errorBar() {
+     *     console.log(new Error("bar"));
+     *   }
+     *   errorFoo();`
+     */
+    const stub = getGripStub("testMultilineStackError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: bar\n" +
+      "Stack trace:\n" +
+      "errorBar@debugger eval code:6:15\n" +
+      "errorFoo@debugger eval code:3:3\n" +
+      "@debugger eval code:8:1\n",
+      "Error Rep has expected text content for an error with a multiple line");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for an error with a multiple line in tiny mode");
+  }
+
+  function testErrorWithoutStacktrace() {
+    const stub = getGripStub("testErrorWithoutStacktrace");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: Error message",
+      "Error Rep has expected text content for an error without stacktrace");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for an error without stacktrace in tiny mode");
+  }
+
+  function testEvalError() {
+    // Test object = `new EvalError("EvalError message")`
+    const stub = getGripStub("testEvalError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for EvalError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "EvalError: EvalError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:10:13\n",
+      "Error Rep has expected text content for an EvalError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "EvalError",
+      "Error Rep has expected text content for an EvalError in tiny mode");
+  }
+
+  function testInternalError() {
+    // Test object = `new InternalError("InternalError message")`
+    const stub = getGripStub("testInternalError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for InternalError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "InternalError: InternalError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:11:13\n",
+      "Error Rep has expected text content for an InternalError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "InternalError",
+      "Error Rep has expected text content for an InternalError in tiny mode");
+  }
+
+  function testRangeError() {
+    // Test object = `new RangeError("RangeError message")`
+    const stub = getGripStub("testRangeError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for RangeError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "RangeError: RangeError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:12:13\n",
+      "Error Rep has expected text content for RangeError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "RangeError",
+      "Error Rep has expected text content for RangeError in tiny mode");
+  }
+
+  function testReferenceError() {
+    // Test object = `new ReferenceError("ReferenceError message"`
+    const stub = getGripStub("testReferenceError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for ReferenceError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "ReferenceError: ReferenceError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:13:13\n",
+      "Error Rep has expected text content for ReferenceError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "ReferenceError",
+      "Error Rep has expected text content for ReferenceError in tiny mode");
+  }
+
+  function testSyntaxError() {
+    // Test object = `new SyntaxError("SyntaxError message"`
+    const stub = getGripStub("testSyntaxError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for SyntaxError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "SyntaxError: SyntaxError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:14:13\n",
+      "Error Rep has expected text content for SyntaxError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "SyntaxError",
+      "SyntaxError Rep has expected text content for SyntaxError in tiny mode");
+  }
+
+  function testTypeError() {
+    // Test object = `new TypeError("TypeError message"`
+    const stub = getGripStub("testTypeError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for TypeError`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "TypeError: TypeError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:15:13\n",
+      "Error Rep has expected text content for TypeError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "TypeError",
+      "Error Rep has expected text content for a TypeError in tiny mode");
+  }
+
+  function testURIError() {
+    // Test object = `new URIError("URIError message")`
+    const stub = getGripStub("testURIError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for URIError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "URIError: URIError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:16:13\n",
+      "Error Rep has expected text content for URIError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "URIError",
+      "Error Rep has expected text content for URIError in tiny mode");
+  }
+
+  function getGripStub(name) {
+    switch (name) {
+      case "testSimpleError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1020",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "Error message",
+            "stack": "@debugger eval code:1:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 1,
+            "columnNumber": 13
+          }
+        };
+      case "testMultilineStackError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1021",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "bar",
+            "stack": "errorBar@debugger eval code:6:15\n" +
+                     "errorFoo@debugger eval code:3:3\n" +
+                     "@debugger eval code:8:1\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 6,
+            "columnNumber": 15
+          }
+        };
+      case "testErrorWithoutStacktrace":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1020",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "Error message",
+          }
+        };
+      case "testEvalError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1022",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "EvalError",
+            "message": "EvalError message",
+            "stack": "@debugger eval code:10:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 10,
+            "columnNumber": 13
+          }
+        };
+      case "testInternalError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1023",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "InternalError",
+            "message": "InternalError message",
+            "stack": "@debugger eval code:11:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 11,
+            "columnNumber": 13
+          }
+        };
+      case "testRangeError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1024",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "RangeError",
+            "message": "RangeError message",
+            "stack": "@debugger eval code:12:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 12,
+            "columnNumber": 13
+          }
+        };
+      case "testReferenceError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1025",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "ReferenceError",
+            "message": "ReferenceError message",
+            "stack": "@debugger eval code:13:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 13,
+            "columnNumber": 13
+          }
+        };
+      case "testSyntaxError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1026",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "SyntaxError",
+            "message": "SyntaxError message",
+            "stack": "@debugger eval code:14:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 14,
+            "columnNumber": 13
+          }
+        };
+      case "testTypeError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1027",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "TypeError",
+            "message": "TypeError message",
+            "stack": "@debugger eval code:15:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 15,
+            "columnNumber": 13
+          }
+        };
+      case "testURIError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1028",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "URIError",
+            "message": "URIError message",
+            "stack": "@debugger eval code:16:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 16,
+            "columnNumber": 13
+          }
+        };
+    }
+
+    return null;
+  }
+});
+</script>
+</pre>
+</body>
+</html>
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -33,16 +33,21 @@ window.onload = Task.async(function* () 
     yield testMaxProps();
     yield testMoreThanShortMaxProps();
     yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
     yield testPreviewLimit();
     yield testNamedNodeMap();
     yield testNodeList();
     yield testDocumentFragment();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
+
+
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test array: `[]`
@@ -300,16 +305,53 @@ window.onload = Task.async(function* () 
         mode: "long",
         expectedOutput: longOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+
+
+  function testOnMouseOver() {
+    const stub = getGripStub("testNodeList");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      GripArray.rep, {object: stub, onDOMNodeMouseOver});
+
+    const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    nodes.forEach((node, index) => {
+      TestUtils.Simulate.mouseOver(node);
+
+      is(mouseOverValue, stub.preview.items[index], "onDOMNodeMouseOver is called with " +
+        "the expected argument when mouseover is fired on the Rep");
+    });
+  }
+
+  function testOnMouseOut() {
+    const stub = getGripStub("testNodeList");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      GripArray.rep, {object: stub, onDOMNodeMouseOut});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOut(node);
+
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testBasic":
         return {
           "type": "object",
           "class": "Array",
           "actor": "server1.conn0.obj35",
           "extensible": true,
--- a/devtools/client/shared/components/test/mochitest/test_reps_text-node.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_text-node.html
@@ -44,16 +44,19 @@ window.onload = Task.async(function* () 
       object: gripStubs.get("testRendering")
     });
 
     is(renderedRep.type, TextNode.rep,
       `Rep correctly selects ${TextNode.rep.displayName}`);
 
     yield testRendering();
     yield testRenderingWithEOL();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testRendering() {
     const stub = gripStubs.get("testRendering");
@@ -103,13 +106,42 @@ window.onload = Task.async(function* () 
       {
         mode: "long",
         expectedOutput: defaultLongOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testRenderingWithEOL", TextNode, stub);
   }
+
+  function testOnMouseOver() {
+    const stub = gripStubs.get("testRendering");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      TextNode.rep, {object: stub, onDOMNodeMouseOver});
+
+    TestUtils.Simulate.mouseOver(renderedComponent);
+    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
+      "when mouseover is fired on the Rep");
+  }
+
+  function testOnMouseOut() {
+    const stub = gripStubs.get("testRendering");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      TextNode.rep, {object: stub, onDOMNodeMouseOut});
+
+    TestUtils.Simulate.mouseOut(renderedComponent);
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/sourceeditor/test/.eslintrc.js
+++ b/devtools/client/sourceeditor/test/.eslintrc.js
@@ -1,6 +1,10 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../.eslintrc.mochitests.js"
+  "extends": "../../../.eslintrc.mochitests.js",
+  "globals": {
+    "runCodeMirrorTest": true,
+    "gBrowser": true
+  }
 };
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -8,17 +8,17 @@ const CSSCompleter = require("devtools/c
 const {InspectorFront} = require("devtools/shared/fronts/inspector");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_autocompletion_tests.json";
 
 const source = read(CSS_URI);
-const tests = eval(read(TESTS_URI));
+const {tests} = JSON.parse(read(TESTS_URI));
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS State machine tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -55,17 +55,19 @@ const TEST_URI = "data:text/html;charset
    "  <div id='devtools-toolbarbutton' class='devtools-menulist'></div>",
    "  <div id='devtools-anotherone'></div>",
    "  <div id='devtools-yetagain'></div>",
    "  <div id='devtools-itjustgoeson'></div>",
    "  <div id='devtools-okstopitnow'></div>",
    "  <div class='hidden-labels-box devtools-toolbarbutton devtools-menulist'></div>",
    "  <div class='devtools-menulist'></div>",
    "  <div class='devtools-menulist'></div>",
+   /* eslint-disable max-len */
    "  <tabs class='devtools-toolbarbutton'><tab></tab><tab></tab><tab></tab></tabs><tabs></tabs>",
+   /* eslint-enable max-len */
    "  <button class='category-name visible'></button>",
    "  <div class='devtools-toolbarbutton' label='true'>",
    "   <hbox class='toolbarbutton-menubutton-button'></hbox></div>",
    " </body>",
    " </html>"
   ].join("\n"));
 
 let doc = null;
@@ -73,17 +75,19 @@ let index = 0;
 let completer = null;
 let progress;
 let progressDiv;
 let inspector;
 
 function test() {
   waitForExplicitFinish();
   addTab(TEST_URI).then(function () {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
 }
 
 function runTests() {
   progress = doc.getElementById("progress");
   progressDiv = doc.querySelector("#progress > div");
   let target = TargetFactory.forTab(gBrowser.selectedTab);
--- a/devtools/client/sourceeditor/test/browser_css_getInfo.js
+++ b/devtools/client/sourceeditor/test/browser_css_getInfo.js
@@ -51,16 +51,18 @@ const source = [
 // Format of test cases :
 // [
 //  {line, ch}, - The caret position at which the getInfo call should be made
 //  expectedState, - The expected state at the caret
 //  expectedSelector, - The expected selector for the state
 //  expectedProperty, - The expected property name for states value and property
 //  expectedValue, - If state is value, then the expected value
 // ]
+
+/* eslint-disable max-len */
 const tests = [
   [{line: 0, ch: 13}, "selector", ".devtools-toolbar"],
   [{line: 8, ch: 13}, "property", ["#devtools-menu.devtools-menulist",
                                    ".devtools-toolbarbutton#devtools-menu "], "-moz-appearance"],
   [{line: 28, ch: 25}, "value", [".devtools-menulist:active",
                                  "#devtools-toolbarbutton:focus "], "outline-offset", "-4px"],
   [{line: 4, ch: 1}, "null"],
   [{line: 5, ch: 0}, "null"],
@@ -74,16 +76,17 @@ const tests = [
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
   [{line: 16, ch: 3}, "value", ["#devtools-menu.devtools-menulist",
                                 ".devtools-toolbarbutton#devtools-menu "], "background",
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
   [{line: 15, ch: 25}, "value", ["#devtools-menu.devtools-menulist",
                                  ".devtools-toolbarbutton#devtools-menu "], "background",
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
 ];
+/* eslint-enable max-len */
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS contextual information tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -120,24 +123,28 @@ const TEST_URI = "data:text/html;charset
    " </html>"
   ].join("\n"));
 
 let doc = null;
 function test() {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
   gBrowser.loadURI(TEST_URI);
 }
 
 function runTests() {
-  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
+  let completer = new CSSCompleter({
+    cssProperties: getClientCssProperties()
+  });
   let matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]);
   let checkState = (expected, actual) => {
     if (expected[0] == "null" && actual == null) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "selector" &&
                expected[1] == actual.selector) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "property" &&
--- a/devtools/client/sourceeditor/test/browser_css_statemachine.js
+++ b/devtools/client/sourceeditor/test/browser_css_statemachine.js
@@ -7,17 +7,17 @@
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_statemachine_tests.json";
 
 const source = read(CSS_URI);
-const tests = eval(read(TESTS_URI));
+const {tests} = JSON.parse(read(TESTS_URI));
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS State machine tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -54,23 +54,27 @@ const TEST_URI = "data:text/html;charset
    " </html>"
   ].join("\n"));
 
 var doc = null;
 function test() {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab(TEST_URI);
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
 }
 
 function runTests() {
-  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
+  let completer = new CSSCompleter({
+    cssProperties: getClientCssProperties()
+  });
   let checkState = state => {
     if (state[0] == "null" && (!completer.state || completer.state == "null")) {
       return true;
     } else if (state[0] == completer.state && state[0] == "selector" &&
                state[1] == completer.selectorState &&
                state[2] == completer.completing &&
                state[3] == completer.selector) {
       return true;
@@ -84,25 +88,25 @@ function runTests() {
       return true;
     }
     return false;
   };
 
   let progress = doc.getElementById("progress");
   let progressDiv = doc.querySelector("#progress > div");
   let i = 0;
-  for (let test of tests) {
+  for (let testcase of tests) {
     progress.dataset.progress = ++i;
     progressDiv.style.width = 100 * i / tests.length + "%";
-    completer.resolveState(limit(source, test[0]),
-                           {line: test[0][0], ch: test[0][1]});
-    if (checkState(test[1])) {
+    completer.resolveState(limit(source, testcase[0]),
+                           {line: testcase[0][0], ch: testcase[0][1]});
+    if (checkState(testcase[1])) {
       ok(true, "Test " + i + " passed. ");
     } else {
-      ok(false, "Test " + i + " failed. Expected state : [" + test[1] + "] " +
+      ok(false, "Test " + i + " failed. Expected state : [" + testcase[1] + "] " +
          "but found [" + completer.state + ", " + completer.selectorState +
          ", " + completer.completing + ", " +
          (completer.propertyName || completer.selector) + "].");
       progress.classList.add("failed");
     }
   }
   gBrowser.removeCurrentTab();
   finish();
--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -1,16 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {InspectorFront} = require("devtools/shared/fronts/inspector");
-const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
 const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
                  "<div id='baz'></div><body></html>";
 
 add_task(function* () {
   yield addTab(TEST_URI);
   yield runTests();
 });
 
@@ -94,17 +93,19 @@ function* testKeyboardCycleForPrefixedSt
 
 function* testKeyboardCSSComma(ed, win) {
   ed.focus();
   ed.setText("b");
   ed.setCursor({line: 1, ch: 1});
 
   let isPopupOpened = false;
   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
-  popupOpened.then(() => isPopupOpened = true);
+  popupOpened.then(() => {
+    isPopupOpened = true;
+  });
 
   EventUtils.synthesizeKey(",", { }, win);
 
   yield wait(500);
 
   ok(!isPopupOpened, "Autocompletion shouldn't be opened");
 }
 
--- a/devtools/client/sourceeditor/test/browser_editor_find_again.js
+++ b/devtools/client/sourceeditor/test/browser_editor_find_again.js
@@ -10,17 +10,18 @@ const L10N = new LocalizationHelper("dev
 
 const { OS } = Services.appinfo;
 
 // On linux, getting immediately the selection's range here fails, returning
 const FIND_KEY = L10N.getStr("find.key");
 const FINDNEXT_KEY = L10N.getStr("findNext.key");
 const FINDPREV_KEY = L10N.getStr("findPrev.key");
 // the replace's key with the appropriate modifiers based on OS
-const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key") : L10N.getStr("replaceAll.key");
+const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key")
+                                   : L10N.getStr("replaceAll.key");
 
 // values like it's not selected – even if the selection is visible.
 // For the record, setting the selection's range immediately doesn't have
 // any effect.
 // It's like the <input> is not ready yet.
 // Therefore, we trigger the UI focus event to the <input>, waiting for the
 // response.
 // Using a timeout could also work, but that is more precise, ensuring also
--- a/devtools/client/sourceeditor/test/cm_script_injection_test.js
+++ b/devtools/client/sourceeditor/test/cm_script_injection_test.js
@@ -1,8 +1,10 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* global editor */
+
 "use strict";
 
 window.addEventListener("editorReady", function () {
   editor.setText("Script successfully injected!");
 });
--- a/devtools/client/sourceeditor/test/css_autocompletion_tests.json
+++ b/devtools/client/sourceeditor/test/css_autocompletion_tests.json
@@ -1,39 +1,43 @@
-// Test states to be tested for css state machine in css-autocompelter.js file.
-// Test cases are of the following format:
-// [
-//   [
-//     line, // The line location of the cursor
-//     ch    // The column locaiton of the cursor
-//   ],
-//   suggestions // Array of expected results
-// ]
-[
-  [[0, 10], []],
-  [[4,  7], ['.devtools-menulist', '.devtools-toolbarbutton']],
-  [[5,  8], ['-moz-animation', '-moz-animation-delay', '-moz-animation-direction',
-             '-moz-animation-duration', '-moz-animation-fill-mode',
-             '-moz-animation-iteration-count', '-moz-animation-name',
-             '-moz-animation-play-state', '-moz-animation-timing-function',
-             '-moz-appearance']],
-  [[12, 20], ['none', 'number-input']],
-  [[12, 22], ['none']],
-  [[17, 22], ['hsl', 'hsla']],
-  [[19, 10], ['background', 'background-attachment', 'background-blend-mode',
-              'background-clip', 'background-color', 'background-image',
-              'background-origin', 'background-position', 'background-position-x',
-              'background-position-y', 'background-repeat', 'background-size']],
-  [[21,  9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
-  [[25, 26], ['.devtools-toolbarbutton > tab',
-              '.devtools-toolbarbutton > hbox',
-              '.devtools-toolbarbutton > .toolbarbutton-menubutton-button']],
-  [[25, 31], ['.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button']],
-  [[29, 20], ['.devtools-menulist:after', '.devtools-menulist:active']],
-  [[30, 10], ['#devtools-anotherone', '#devtools-itjustgoeson', '#devtools-menu',
-              '#devtools-okstopitnow', '#devtools-toolbarbutton', '#devtools-yetagain']],
-  [[39, 39], ['.devtools-toolbarbutton:not([label]) > tab']],
-  [[43, 51], ['.devtools-toolbarbutton:not([checked=true]):hover:after',
-              '.devtools-toolbarbutton:not([checked=true]):hover:active']],
-  [[58, 36], ['!important;']],
-  [[73, 42], [':lang(', ':last-of-type', ':link', ':last-child']],
-  [[77, 25], ['.visible']],
-]
+{
+  "description": [
+    "Test states to be tested for css state machine in css-autocompelter.js file.",
+    "Test cases are of the following format:",
+    "[",
+    "  [",
+    "    line, # The line location of the cursor",
+    "    ch    # The column locaiton of the cursor",
+    "  ],",
+    "  suggestions # Array of expected results",
+    "]"
+  ],
+  "tests": [
+    [[0, 10], []],
+    [[4,  7], [".devtools-menulist", ".devtools-toolbarbutton"]],
+    [[5,  8], ["-moz-animation", "-moz-animation-delay", "-moz-animation-direction",
+               "-moz-animation-duration", "-moz-animation-fill-mode",
+               "-moz-animation-iteration-count", "-moz-animation-name",
+               "-moz-animation-play-state", "-moz-animation-timing-function",
+               "-moz-appearance"]],
+    [[12, 20], ["none", "number-input"]],
+    [[12, 22], ["none"]],
+    [[17, 22], ["hsl", "hsla"]],
+    [[19, 10], ["background", "background-attachment", "background-blend-mode",
+                "background-clip", "background-color", "background-image",
+                "background-origin", "background-position", "background-position-x",
+                "background-position-y", "background-repeat", "background-size"]],
+    [[21,  9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
+    [[25, 26], [".devtools-toolbarbutton > tab",
+                ".devtools-toolbarbutton > hbox",
+                ".devtools-toolbarbutton > .toolbarbutton-menubutton-button"]],
+    [[25, 31], [".devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button"]],
+    [[29, 20], [".devtools-menulist:after", ".devtools-menulist:active"]],
+    [[30, 10], ["#devtools-anotherone", "#devtools-itjustgoeson", "#devtools-menu",
+                "#devtools-okstopitnow", "#devtools-toolbarbutton", "#devtools-yetagain"]],
+    [[39, 39], [".devtools-toolbarbutton:not([label]) > tab"]],
+    [[43, 51], [".devtools-toolbarbutton:not([checked=true]):hover:after",
+                ".devtools-toolbarbutton:not([checked=true]):hover:active"]],
+    [[58, 36], ["!important;"]],
+    [[73, 42], [":lang(", ":last-of-type", ":link", ":last-child"]],
+    [[77, 25], [".visible"]]
+  ]
+}
--- a/devtools/client/sourceeditor/test/css_statemachine_tests.json
+++ b/devtools/client/sourceeditor/test/css_statemachine_tests.json
@@ -1,84 +1,88 @@
-// Test states to be tested for css state machine in css-autocompelter.js file.
-// Test cases are of the following format:
-// [
-//   [
-//     line, // The line location of the cursor
-//     ch    // The column locaiton of the cursor
-//   ],
-//   [
-//     state,         // one of CSS_STATES
-//     selectorState, // one of SELECTOR_STATES
-//     completing,    // what is being completed
-//     propertyName,  // what property is being completed in case of value state
-//                    // or the current selector that is being completed
-//   ]
-// ]
-[
-  [[0, 10], ['null', '', '', '']],
-  [[4,  3], ['selector', 'class', 'de', '.de']],
-  [[5,  8], ['property', 'null', '-moz-a']],
-  [[5, 21], ['value', 'null', 'no', '-moz-appearance']],
-  [[6, 18], ['property', 'null', 'padding']],
-  [[6, 24], ['value', 'null', '3', 'padding']],
-  [[6, 29], ['property', 'null', 'bo']],
-  [[6, 50], ['value', 'null', '1p', 'border-bottom-width']],
-  [[7, 24], ['value', 'null', 's', 'border-bottom-style']],
-  [[9,  0], ['null', 'null', '', '']],
-  [[10, 6], ['selector', 'id', 'devto', '#devto']],
-  [[10, 17], ['selector', 'class', 'de', '#devtools-menu.de']],
-  [[11,  5], ['selector', 'class', 'devt', '.devt']],
-  [[11, 30], ['selector', 'id', 'devtoo', '.devtools-toolbarbutton#devtoo']],
-  [[12, 10], ['property', 'null', '-moz-app']],
-  [[16, 27], ['value', 'null', 'hsl', 'text-shadow']],
-  [[19, 24], ['value', 'null', 'linear-gra', 'background']],
-  [[19, 55], ['value', 'null', 'hsl', 'background']],
-  [[19, 79], ['value', 'null', 'paddin', 'background']],
-  [[20, 47], ['value', 'null', 'ins', 'box-shadow']],
-  [[22, 15], ['value', 'null', 'inheri', 'color']],
-  [[25, 26], ['selector', 'null', '', '.devtools-toolbarbutton > ']],
-  [[25, 28], ['selector', 'tag', 'hb', '.devtools-toolbarbutton > hb']],
-  [[25, 41], ['selector', 'class', 'toolbarbut', '.devtools-toolbarbutton > hbox.toolbarbut']],
-  [[29, 21], ['selector', 'pseudo', 'ac', '.devtools-menulist:ac']],
-  [[30, 27], ['selector', 'pseudo', 'foc', '#devtools-toolbarbutton:foc']],
-  [[31, 18], ['value', 'null', 'dot', 'outline']],
-  [[32, 25], ['value', 'null', '-4p', 'outline-offset']],
-  [[35, 26], ['selector', 'pseudo', 'no', '.devtools-toolbarbutton:no']],
-  [[35, 28], ['selector', 'null', 'not', '']],
-  [[35, 30], ['selector', 'attribute', 'l', '[l']],
-  [[39, 46], ['selector', 'class', 'toolba', '.devtools-toolbarbutton:not([label]) > .toolba']],
-  [[43, 39], ['selector', 'value', 'tr', '[checked=tr']],
-  [[43, 47], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton:not([checked=true]):hov']],
-  [[43, 53], ['selector', 'pseudo', 'act', '.devtools-toolbarbutton:not([checked=true]):hover:act']],
-  [[47, 22], ['selector', 'attribute', 'op', '.devtools-menulist[op']],
-  [[47, 33], ['selector', 'value', 'tr', '.devtools-menulist[open =tr']],
-  [[48, 38], ['selector', 'value', 'tr', '.devtools-toolbarbutton[open = tr']],
-  [[49, 40], ['selector', 'value', 'true', '.devtools-toolbarbutton[checked= true']],
-  [[53, 34], ['selector', 'value', '=', '.devtools-toolbarbutton[checked=']],
-  [[58, 38], ['value', 'null', '!impor', 'background-color']],
-  [[61, 41], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton[checked=true]:hov']],
-  [[65, 47], ['selector', 'class', 'to', '.devtools-toolbarbutton[type=menu-button] > .to']],
-  [[69, 44], ['selector', 'pseudo', 'first-of', '.devtools-sidebar-tabs > tabs > tab:first-of']],
-  [[73, 45], ['selector', 'pseudo', 'last', ':last']],
-  [[77, 27], ['selector', 'class', 'vis', '.vis']],
-  [[78, 34], ['selector', 'class', 'hidd', '.hidden-labels-box.visible ~ .hidd']],
-  [[83,  5], ['media', 'null', 'medi']],
-  [[83, 22], ['media', 'null', '800']],
-  [[84,  9], ['selector', 'class', 'catego', '.catego']],
-  [[89,  9], ['media', 'null', 'al']],
-  [[90,  6], ['selector', 'id', 'err', '#err']],
-  [[93, 11], ['property', 'null', 'backgro']],
-  [[98,  6], ['selector', 'tag', 'butt', 'butt']],
-  [[99, 22], ['value', 'null', '!impor', 'width']],
-  [[103, 5], ['keyframes', 'null', 'ke']],
-  [[104, 7], ['frame', 'null', 'fro']],
-  [[104, 15], ['property', 'null', 'opac']],
-  [[104, 29], ['property', 'null', 'transf']],
-  [[104, 38], ['value', 'null', 'scal', 'transform']],
-  [[105,  8], ['frame', 'null', '']],
-  [[113,  6], ['keyframes', 'null', 'keyfr']],
-  [[114,  4], ['frame', 'null', 'fr']],
-  [[115,  3], ['frame', 'null', '2']],
-  [[117,  8], ['property', 'null', 'opac']],
-  [[117, 16], ['value', 'null', '0', 'opacity']],
-  [[121,  0], ['null', '', '']],
-]
+{
+  "description": [
+    "Test states to be tested for css state machine in css-autocompleter.js file.",
+    "Test cases are of the following format:",
+    "[",
+    "  [",
+    "    line, // The line location of the cursor",
+    "    ch    // The column locaiton of the cursor",
+    "  ],",
+    "  [",
+    "    state,         // one of CSS_STATES",
+    "    selectorState, // one of SELECTOR_STATES",
+    "    completing,    // what is being completed",
+    "    propertyName,  // what property is being completed in case of value state",
+    "                   // or the current selector that is being completed",
+    "  ]",
+    "]"
+  ],
+  "tests": [
+    [[0, 10], ["null", "", "", ""]],
+    [[4,  3], ["selector", "class", "de", ".de"]],
+    [[5,  8], ["property", "null", "-moz-a"]],
+    [[5, 21], ["value", "null", "no", "-moz-appearance"]],
+    [[6, 18], ["property", "null", "padding"]],
+    [[6, 24], ["value", "null", "3", "padding"]],
+    [[6, 29], ["property", "null", "bo"]],
+    [[6, 50], ["value", "null", "1p", "border-bottom-width"]],
+    [[7, 24], ["value", "null", "s", "border-bottom-style"]],
+    [[9,  0], ["null", "null", "", ""]],
+    [[10, 6], ["selector", "id", "devto", "#devto"]],
+    [[10, 17], ["selector", "class", "de", "#devtools-menu.de"]],
+    [[11,  5], ["selector", "class", "devt", ".devt"]],
+    [[11, 30], ["selector", "id", "devtoo", ".devtools-toolbarbutton#devtoo"]],
+    [[12, 10], ["property", "null", "-moz-app"]],
+    [[16, 27], ["value", "null", "hsl", "text-shadow"]],
+    [[19, 24], ["value", "null", "linear-gra", "background"]],
+    [[19, 55], ["value", "null", "hsl", "background"]],
+    [[19, 79], ["value", "null", "paddin", "background"]],
+    [[20, 47], ["value", "null", "ins", "box-shadow"]],
+    [[22, 15], ["value", "null", "inheri", "color"]],
+    [[25, 26], ["selector", "null", "", ".devtools-toolbarbutton > "]],
+    [[25, 28], ["selector", "tag", "hb", ".devtools-toolbarbutton > hb"]],
+    [[25, 41], ["selector", "class", "toolbarbut", ".devtools-toolbarbutton > hbox.toolbarbut"]],
+    [[29, 21], ["selector", "pseudo", "ac", ".devtools-menulist:ac"]],
+    [[30, 27], ["selector", "pseudo", "foc", "#devtools-toolbarbutton:foc"]],
+    [[31, 18], ["value", "null", "dot", "outline"]],
+    [[32, 25], ["value", "null", "-4p", "outline-offset"]],
+    [[35, 26], ["selector", "pseudo", "no", ".devtools-toolbarbutton:no"]],
+    [[35, 28], ["selector", "null", "not", ""]],
+    [[35, 30], ["selector", "attribute", "l", "[l"]],
+    [[39, 46], ["selector", "class", "toolba", ".devtools-toolbarbutton:not([label]) > .toolba"]],
+    [[43, 39], ["selector", "value", "tr", "[checked=tr"]],
+    [[43, 47], ["selector", "pseudo", "hov", ".devtools-toolbarbutton:not([checked=true]):hov"]],
+    [[43, 53], ["selector", "pseudo", "act", ".devtools-toolbarbutton:not([checked=true]):hover:act"]],
+    [[47, 22], ["selector", "attribute", "op", ".devtools-menulist[op"]],
+    [[47, 33], ["selector", "value", "tr", ".devtools-menulist[open =tr"]],
+    [[48, 38], ["selector", "value", "tr", ".devtools-toolbarbutton[open = tr"]],
+    [[49, 40], ["selector", "value", "true", ".devtools-toolbarbutton[checked= true"]],
+    [[53, 34], ["selector", "value", "=", ".devtools-toolbarbutton[checked="]],
+    [[58, 38], ["value", "null", "!impor", "background-color"]],
+    [[61, 41], ["selector", "pseudo", "hov", ".devtools-toolbarbutton[checked=true]:hov"]],
+    [[65, 47], ["selector", "class", "to", ".devtools-toolbarbutton[type=menu-button] > .to"]],
+    [[69, 44], ["selector", "pseudo", "first-of", ".devtools-sidebar-tabs > tabs > tab:first-of"]],
+    [[73, 45], ["selector", "pseudo", "last", ":last"]],
+    [[77, 27], ["selector", "class", "vis", ".vis"]],
+    [[78, 34], ["selector", "class", "hidd", ".hidden-labels-box.visible ~ .hidd"]],
+    [[83,  5], ["media", "null", "medi"]],
+    [[83, 22], ["media", "null", "800"]],
+    [[84,  9], ["selector", "class", "catego", ".catego"]],
+    [[89,  9], ["media", "null", "al"]],
+    [[90,  6], ["selector", "id", "err", "#err"]],
+    [[93, 11], ["property", "null", "backgro"]],
+    [[98,  6], ["selector", "tag", "butt", "butt"]],
+    [[99, 22], ["value", "null", "!impor", "width"]],
+    [[103, 5], ["keyframes", "null", "ke"]],
+    [[104, 7], ["frame", "null", "fro"]],
+    [[104, 15], ["property", "null", "opac"]],
+    [[104, 29], ["property", "null", "transf"]],
+    [[104, 38], ["value", "null", "scal", "transform"]],
+    [[105,  8], ["frame", "null", ""]],
+    [[113,  6], ["keyframes", "null", "keyfr"]],
+    [[114,  4], ["frame", "null", "fr"]],
+    [[115,  3], ["frame", "null", "2"]],
+    [[117,  8], ["property", "null", "opac"]],
+    [[117, 16], ["value", "null", "0", "opacity"]],
+    [[121,  0], ["null", "", ""]]
+  ]
+}
\ No newline at end of file
--- a/devtools/client/sourceeditor/test/head.js
+++ b/devtools/client/sourceeditor/test/head.js
@@ -1,12 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from ../../framework/test/shared-head.js */
+/* exported promiseWaitForFocus, setup, ch, teardown, loadHelperScript,
+            limit, ch, read, codemirrorSetStatus */
+
 "use strict";
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
@@ -99,26 +102,26 @@ function loadHelperScript(filePath) {
   let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
   Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
 }
 
 /**
  * This method returns the portion of the input string `source` up to the
  * [line, ch] location.
  */
-function limit(source, [line, ch]) {
+function limit(source, [line, char]) {
   line++;
   let list = source.split("\n");
   if (list.length < line) {
     return source;
   }
   if (line == 1) {
-    return list[0].slice(0, ch);
+    return list[0].slice(0, char);
   }
-  return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n");
+  return [...list.slice(0, line - 1), list[line - 1].slice(0, char)].join("\n");
 }
 
 function read(url) {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
   let channel = NetUtil.newChannel({
     uri: url,
--- a/devtools/client/sourceeditor/test/helper_codemirror_runner.js
+++ b/devtools/client/sourceeditor/test/helper_codemirror_runner.js
@@ -1,11 +1,13 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* exported runCodeMirrorTest */
 /* globals codemirrorSetStatus */
 
 "use strict";
 
 function runCodeMirrorTest(browser) {
   let mm = browser.messageManager;
   mm.addMessageListener("setStatus", function listener({data}) {
     let {statusMsg, type, customMsg} = data;
@@ -19,20 +21,22 @@ function runCodeMirrorTest(browser) {
     mm = null;
     finish();
   });
 
   // Interact with the content iframe, giving it a function to
   //  1) Proxy CM test harness calls into ok() calls
   //  2) Detecting when it finishes by checking the DOM and
   //     setting a timeout to check again if not.
+  /* eslint-disable max-len */
   mm.loadFrameScript("data:," +
     "content.wrappedJSObject.mozilla_setStatus = function(statusMsg, type, customMsg) {" +
     "  sendSyncMessage('setStatus', {statusMsg: statusMsg, type: type, customMsg: customMsg});" +
     "};" +
     "function check() { " +
     "  var doc = content.document; var out = doc.getElementById('status'); " +
     "  if (!out || !out.classList.contains('done')) { return setTimeout(check, 100); }" +
     "  sendAsyncMessage('done', { failed: content.wrappedJSObject.failed });" +
     "}" +
     "check();"
   , true);
+  /* eslint-enable max-len */
 }
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -392,20 +392,16 @@ body {
   /* Flex items don't support text-overflow, so a child div is used */
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
   background-color: rgba(255, 255, 255, 0.7);
   max-width: 50%;
 }
 
-.animation-timeline .fast-track .name div {
-  width: calc(100% - var(--fast-track-icon-width));
-}
-
 .animation-timeline .fast-track .name::after {
   /* Animations running on the compositor have the fast-track background image*/
   content: "";
   display: block;
   position: absolute;
   top: 1px;
   right: 0;
   height: 100%;
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -111,16 +111,22 @@ const FilterBar = createClass({
             label: "Debug",
             filterKey: MESSAGE_LEVEL.DEBUG,
             dispatch
           }),
           dom.span({
             className: "devtools-separator",
           }),
           FilterButton({
+            active: filter.css,
+            label: "CSS",
+            filterKey: "css",
+            dispatch
+          }),
+          FilterButton({
             active: filter.netxhr,
             label: "XHR",
             filterKey: "netxhr",
             dispatch
           }),
           FilterButton({
             active: filter.net,
             label: "Requests",
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -40,28 +40,37 @@ GripMessageBody.propTypes = {
 function GripMessageBody(props) {
   const { grip, userProvidedStyle, serviceContainer } = props;
 
   let styleObject;
   if (userProvidedStyle && userProvidedStyle !== "") {
     styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
   }
 
+  let onDOMNodeMouseOver;
+  let onDOMNodeMouseOut;
+  if (serviceContainer) {
+    onDOMNodeMouseOver = (object) => serviceContainer.highlightDomElement(object);
+    onDOMNodeMouseOut = serviceContainer.unHighlightDomElement;
+  }
+
   return (
     // @TODO once there is a longString rep, also turn off quotes for those.
     typeof grip === "string"
       ? StringRep({
         object: grip,
         useQuotes: false,
         mode: props.mode,
         style: styleObject
       })
       : Rep({
         object: grip,
         objectLink: VariablesViewLink,
+        onDOMNodeMouseOver,
+        onDOMNodeMouseOut,
         defaultRep: Grip,
         mode: props.mode,
       })
   );
 }
 
 function cleanupStyle(userProvidedStyle, createElement) {
   // Regular expression that matches the allowed CSS property names.
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -61,16 +61,17 @@ const MessageContainer = createClass({
 });
 
 function getMessageComponent(message) {
   switch (message.source) {
     case MESSAGE_SOURCE.CONSOLE_API:
       return componentMap.get("ConsoleApiCall");
     case MESSAGE_SOURCE.NETWORK:
       return componentMap.get("NetworkEventMessage");
+    case MESSAGE_SOURCE.CSS:
     case MESSAGE_SOURCE.JAVASCRIPT:
       switch (message.type) {
         case MESSAGE_TYPE.COMMAND:
           return componentMap.get("ConsoleCommand");
         case MESSAGE_TYPE.RESULT:
           return componentMap.get("EvaluationResult");
         // @TODO this is probably not the right behavior, but works for now.
         // Chrome doesn't distinguish between page errors and log messages. We
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -35,17 +35,17 @@ function EvaluationResult(props) {
     exceptionDocURL,
     frame,
   } = message;
 
   let messageBody;
   if (message.messageText) {
     messageBody = message.messageText;
   } else {
-    messageBody = GripMessageBody({grip: message.parameters});
+    messageBody = GripMessageBody({grip: message.parameters, serviceContainer});
   }
 
   const topLevelClasses = ["cm-s-mozilla"];
 
   const childProps = {
     source,
     type,
     level,
--- a/devtools/client/webconsole/new-console-output/components/variables-view-link.js
+++ b/devtools/client/webconsole/new-console-output/components/variables-view-link.js
@@ -15,20 +15,20 @@ const {openVariablesView} = require("dev
 
 VariablesViewLink.displayName = "VariablesViewLink";
 
 VariablesViewLink.propTypes = {
   object: PropTypes.object.isRequired
 };
 
 function VariablesViewLink(props) {
-  const { object, children } = props;
+  const { className, object, children } = props;
 
   return (
     dom.a({
       onClick: openVariablesView.bind(null, object),
-      className: "cm-variable",
+      className: className || "cm-variable",
       draggable: false,
     }, children)
   );
 }
 
 module.exports = VariablesViewLink;
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -21,28 +21,30 @@ const actionTypes = {
 const prefs = {
   PREFS: {
     FILTER: {
       ERROR: "devtools.webconsole.filter.error",
       WARN: "devtools.webconsole.filter.warn",
       INFO: "devtools.webconsole.filter.info",
       LOG: "devtools.webconsole.filter.log",
       DEBUG: "devtools.webconsole.filter.debug",
+      CSS: "devtools.webconsole.filter.css",
       NET: "devtools.webconsole.filter.net",
       NETXHR: "devtools.webconsole.filter.netxhr",
     },
     UI: {
       FILTER_BAR: "devtools.webconsole.ui.filterbar"
     }
   }
 };
 
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
     XML: "xml",
+    CSS: "css",
     JAVASCRIPT: "javascript",
     NETWORK: "network",
     CONSOLE_API: "console-api",
     STORAGE: "storage",
     APPCACHE: "appcache",
     RENDERING: "rendering",
     SECURITY: "security",
     OTHER: "other",
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -53,17 +53,27 @@ NewConsoleOutputWrapper.prototype = {
           return this.toolbox.selectTool("netmonitor").then(panel => {
             return panel.panelWin.NetMonitorController.inspectRequest(requestId);
           });
         },
         sourceMapService: this.toolbox ? this.toolbox._sourceMapService : null,
         openLink: url => this.jsterm.hud.owner.openLink.call(this.jsterm.hud.owner, url),
         createElement: nodename => {
           return this.document.createElementNS("http://www.w3.org/1999/xhtml", nodename);
-        }
+        },
+        highlightDomElement: (grip, options = {}) => {
+          return this.toolbox && this.toolbox.highlighterUtils
+            ? this.toolbox.highlighterUtils.highlightDomValueGrip(grip, options)
+            : null;
+        },
+        unHighlightDomElement: (forceHide = false) => {
+          return this.toolbox && this.toolbox.highlighterUtils
+            ? this.toolbox.highlighterUtils.unhighlight(forceHide)
+            : null;
+        },
       }
     });
     let filterBar = FilterBar({
       serviceContainer: {
         attachRefToHud
       }
     });
     let provider = React.createElement(
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js
+++ b/devtools/client/webconsole/new-console-output/reducers/filters.js
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
 const FilterState = Immutable.Record({
+  css: false,
   debug: true,
   error: true,
   info: true,
   log: true,
   net: false,
   netxhr: false,
   text: "",
   warn: true,
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -24,16 +24,17 @@ function getAllMessages(state) {
   return prune(
     messages.filter(message => {
       return (
         isInOpenedGroup(message, groups, messagesUI)
         && (
           isUnfilterable(message)
           || (
             matchLevelFilters(message, filters)
+            && matchCssFilters(message, filters)
             && matchNetworkFilters(message, filters)
             && matchSearchFilters(message, filters)
           )
         )
       );
     }),
     logLimit
   );
@@ -91,16 +92,23 @@ function matchLevelFilters(message, filt
 function matchNetworkFilters(message, filters) {
   return (
     message.source !== MESSAGE_SOURCE.NETWORK
     || (filters.get("net") === true && message.isXHR === false)
     || (filters.get("netxhr") === true && message.isXHR === true)
   );
 }
 
+function matchCssFilters(message, filters) {
+  return (
+    message.source != MESSAGE_SOURCE.CSS
+    || filters.get("css") === true
+  );
+}
+
 function matchSearchFilters(message, filters) {
   let text = filters.text || "";
   return (
     text === ""
     // @TODO currently we return true for any object grip. We should find a way to
     // search object grips.
     || (message.parameters !== null && !Array.isArray(message.parameters))
     // Look for a match in location.
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -25,16 +25,17 @@ function configureStore() {
     prefs: new PrefState({
       logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
     }),
     filters: new FilterState({
       error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
       warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
       info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
       log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
+      css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
       net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
       netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
     }),
     ui: new UiState({
       filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
     })
   };
 
--- a/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
@@ -34,16 +34,22 @@ describe("MessageContainer component:", 
       },
       {
         component: EvaluationResult,
         message: stubPreparedMessages.get("new Date(0)")
       },
       {
         component: PageError,
         message: stubPreparedMessages.get("ReferenceError: asdf is not defined")
+      },
+      {
+        component: PageError,
+        message: stubPreparedMessages.get(
+          "Unknown property ‘such-unknown-property’.  Declaration dropped."
+        )
       }
     ];
 
     messageTypes.forEach(info => {
       const { component, message } = info;
       const rendered = shallowRenderComponent(MessageContainer, {
         message,
         serviceContainer,
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
@@ -1,18 +1,22 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   !/devtools/client/framework/test/shared-head.js
   test-console-api.html
+  test-css-message.html
   test-network-event.html
+  test-tempfile.css
   test-tempfile.js
 
 [browser_webconsole_update_stubs_console_api.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
+[browser_webconsole_update_stubs_css_message.js]
+skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_evaluation_result.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_network_event.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_page_error.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/osfile.jsm");
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html";
+
+const { cssMessage: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
+
+let stubs = {
+  preparedMessages: [],
+  packets: [],
+};
+
+add_task(function* () {
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+  ok(true, "make the test not fail");
+
+  for (let [key, code] of snippets) {
+    OS.File.writeAtomic(TEMP_CSS_FILE_PATH, code);
+    let received = new Promise(resolve => {
+      /* CSS errors are considered as pageError on the server */
+      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+        toolbox.target.client.removeListener("pageError", onPacket);
+        info("Received css message:" + e + " " + JSON.stringify(packet, null, "\t"));
+
+        let message = prepareMessage(packet, {getNextId: () => 1});
+        stubs.packets.push(formatPacket(message.messageText, packet));
+        stubs.preparedMessages.push(formatStub(message.messageText, packet));
+        resolve();
+      });
+    });
+
+    yield ContentTask.spawn(gBrowser.selectedBrowser, key, function (snippetKey) {
+      let stylesheet = content.document.createElement("link");
+      stylesheet.rel = "stylesheet";
+      stylesheet.href = "test-tempfile.css?key=" + encodeURIComponent(snippetKey);
+      content.document.body.appendChild(stylesheet);
+    });
+
+    yield received;
+  }
+
+  let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "cssMessage.js");
+  OS.File.writeAtomic(filePath, formatFile(stubs));
+  OS.File.writeAtomic(TEMP_CSS_FILE_PATH, "");
+});
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
@@ -17,16 +17,18 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
 });
 
 const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js");
 
 const BASE_PATH = "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
 const TEMP_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
+const TEMP_CSS_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`,
+                                        "test-tempfile.css");
 
 let cachedPackets = {};
 
 function getCleanedPacket(key, packet) {
   if(Object.keys(cachedPackets).includes(key)) {
     return cachedPackets[key];
   }
 
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
@@ -84,16 +84,31 @@ consoleApi.set("console.log(%cfoobar)", 
   keys: ["console.log(%cfoobar)"],
   code: `
 console.log(
   "%cfoo%cbar",
   "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
   "color:red;background:\\165rl('http://example.com/test')");
 `});
 
+// CSS messages
+const cssMessage = new Map();
+
+cssMessage.set("Unknown property", `
+p {
+  such-unknown-property: wow;
+}
+`);
+
+cssMessage.set("Invalid property value", `
+p {
+  padding-top: invalid value;
+}
+`);
+
 // Evaluation Result
 const evaluationResultCommands = [
   "new Date(0)",
   "asdf()",
   "1 + @"
 ];
 
 let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
@@ -137,12 +152,13 @@ pageError.set("Reference Error", `
     bar()
   }
 
   foo()
 `);
 
 module.exports = {
   consoleApi,
+  cssMessage,
   evaluationResult,
   networkEvent,
   pageError,
 };
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Stub generator</title>
+  </head>
+  <body>
+    <p>Stub generator</p>
+    <link rel="stylesheet" href="test-tempfile.css"/>
+  </body>
+</html>
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
@@ -0,0 +1,105 @@
+/* Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
+
+let stubPreparedMessages = new Map();
+let stubPackets = new Map();
+
+
+stubPreparedMessages.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "css",
+	"type": "log",
+	"level": "warn",
+	"messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
+	"parameters": null,
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+		"line": 3,
+		"column": 23
+	},
+	"groupId": null,
+	"userProvidedStyles": null
+}));
+
+stubPreparedMessages.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "css",
+	"type": "log",
+	"level": "warn",
+	"messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
+	"parameters": null,
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+		"line": 3,
+		"column": 15
+	},
+	"groupId": null,
+	"userProvidedStyles": null
+}));
+
+
+stubPackets.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", {
+	"from": "server1.conn0.child1/consoleActor2",
+	"type": "pageError",
+	"pageError": {
+		"errorMessage": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
+		"errorMessageName": "",
+		"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+		"lineText": "  such-unknown-property: wow;",
+		"lineNumber": 3,
+		"columnNumber": 23,
+		"category": "CSS Parser",
+		"timeStamp": 1478964550247,
+		"warning": true,
+		"error": false,
+		"exception": false,
+		"strict": false,
+		"info": false,
+		"private": false,
+		"stacktrace": null
+	}
+});
+
+stubPackets.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", {
+	"from": "server1.conn0.child1/consoleActor2",
+	"type": "pageError",
+	"pageError": {
+		"errorMessage": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
+		"errorMessageName": "",
+		"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+		"lineText": "  padding-top: invalid value;",
+		"lineNumber": 3,
+		"columnNumber": 15,
+		"category": "CSS Parser",
+		"timeStamp": 1478964550282,
+		"warning": true,
+		"error": false,
+		"exception": false,
+		"strict": false,
+		"info": false,
+		"private": false,
+		"stacktrace": null
+	}
+});
+
+
+module.exports = {
+  stubPreparedMessages,
+  stubPackets,
+}
\ No newline at end of file
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js
@@ -2,28 +2,31 @@
   http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 let maps = [];
 
 [
   "consoleApi",
+  "cssMessage",
   "evaluationResult",
   "networkEvent",
   "pageError",
 ].forEach((filename) => {
   maps[filename] = require(`./${filename}`);
 });
 
 // Combine all the maps into a single map.
 module.exports = {
-    stubPreparedMessages: new Map([
-      ...maps.consoleApi.stubPreparedMessages,
-      ...maps.evaluationResult.stubPreparedMessages,
-      ...maps.networkEvent.stubPreparedMessages,
-      ...maps.pageError.stubPreparedMessages, ]),
-    stubPackets: new Map([
-      ...maps.consoleApi.stubPackets,
-      ...maps.evaluationResult.stubPackets,
-      ...maps.networkEvent.stubPackets,
-      ...maps.pageError.stubPackets, ]),
+  stubPreparedMessages: new Map([
+    ...maps.consoleApi.stubPreparedMessages,
+    ...maps.cssMessage.stubPreparedMessages,
+    ...maps.evaluationResult.stubPreparedMessages,
+    ...maps.networkEvent.stubPreparedMessages,
+    ...maps.pageError.stubPreparedMessages, ]),
+  stubPackets: new Map([
+    ...maps.consoleApi.stubPackets,
+    ...maps.cssMessage.stubPackets,
+    ...maps.evaluationResult.stubPackets,
+    ...maps.networkEvent.stubPackets,
+    ...maps.pageError.stubPackets, ]),
 };
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
@@ -1,11 +1,12 @@
 # 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/.
 
 DevToolsModules(
   'consoleApi.js',
+  'cssMessage.js',
   'evaluationResult.js',
   'index.js',
   'networkEvent.js',
   'pageError.js',
 )
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -12,10 +12,11 @@ support-files =
 
 [browser_webconsole_batching.js]
 [browser_webconsole_console_group.js]
 [browser_webconsole_console_table.js]
 [browser_webconsole_filters.js]
 [browser_webconsole_init.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_keyboard_accessibility.js]
+[browser_webconsole_nodes_highlight.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_vview_close_on_esc_key.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_nodes_highlight.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Check hovering logged nodes highlight them in the content page.
+
+const HTML = `
+  <!DOCTYPE html>
+  <html>
+    <body>
+      <h1>Node Highlight  Test</h1>
+    </body>
+    <script>
+      function logNode(selector) {
+        console.log(document.querySelector(selector));
+      }
+    </script>
+  </html>
+`;
+const TEST_URI = "data:text/html;charset=utf-8," + encodeURI(HTML);
+
+add_task(function* () {
+  const hud = yield openNewTabAndConsole(TEST_URI);
+  const toolbox = gDevTools.getToolbox(hud.target);
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
+    content.wrappedJSObject.logNode("h1");
+  });
+
+  let msg = yield waitFor(() => findMessage(hud, "<h1>"));
+  let node = msg.querySelector(".objectBox-node");
+  ok(node !== null, "Node was logged as expected");
+  const view = node.ownerDocument.defaultView;
+
+  info("Highlight the node by moving the cursor on it");
+  let onNodeHighlight = toolbox.once("node-highlight");
+  EventUtils.synthesizeMouseAtCenter(node, {type: "mousemove"}, view);
+
+  let nodeFront = yield onNodeHighlight;
+  is(nodeFront.displayName, "h1", "The correct node was highlighted");
+
+  info("Unhighlight the node by moving away from the node");
+  let onNodeUnhighlight = toolbox.once("node-unhighlight");
+  let btn = toolbox.doc.querySelector(".toolbox-dock-button");
+  EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"}, view);
+
+  yield onNodeUnhighlight;
+  ok(true, "node-unhighlight event was fired when moving away from the node");
+});
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -55,16 +55,30 @@ describe("Filtering", () => {
 
     it("filters error messages", () => {
       store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
 
       let messages = getAllMessages(store.getState());
       expect(messages.size).toEqual(numMessages - 1);
     });
 
+    it("filters css messages", () => {
+      let message = stubPreparedMessages.get(
+        "Unknown property ‘such-unknown-property’.  Declaration dropped."
+      );
+      store.dispatch(messageAdd(message));
+
+      let messages = getAllMessages(store.getState());
+      expect(messages.size).toEqual(numMessages);
+
+      store.dispatch(actions.filterToggle("css"));
+      messages = getAllMessages(store.getState());
+      expect(messages.size).toEqual(numMessages + 1);
+    });
+
     it("filters xhr messages", () => {
       let message = stubPreparedMessages.get("XHR GET request");
       store.dispatch(messageAdd(message));
 
       let messages = getAllMessages(store.getState());
       expect(messages.size).toEqual(numMessages);
 
       store.dispatch(actions.filterToggle("netxhr"));
@@ -162,30 +176,32 @@ describe("Clear filters", () => {
 
     // Setup test case
     store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
     store.dispatch(actions.filterToggle("netxhr"));
     store.dispatch(actions.filterTextSet("foobar"));
 
     let filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
+      "css": true,
       "debug": true,
       "error": false,
       "info": true,
       "log": true,
       "net": false,
       "netxhr": true,
       "warn": true,
       "text": "foobar"
     });
 
     store.dispatch(actions.filtersClear());
 
     filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
+      "css": false,
       "debug": true,
       "error": true,
       "info": true,
       "log": true,
       "net": false,
       "netxhr": false,
       "warn": true,
       "text": ""
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -152,18 +152,21 @@ function transformPacket(packet) {
       }
 
       const frame = pageError.sourceName ? {
         source: pageError.sourceName,
         line: pageError.lineNumber,
         column: pageError.columnNumber
       } : null;
 
+      let matchesCSS = /^(?:CSS|Layout)\b/.test(pageError.category);
+      let messageSource = matchesCSS ? MESSAGE_SOURCE.CSS
+                                     : MESSAGE_SOURCE.JAVASCRIPT;
       return new ConsoleMessage({
-        source: MESSAGE_SOURCE.JAVASCRIPT,
+        source: messageSource,
         type: MESSAGE_TYPE.LOG,
         level,
         messageText: pageError.errorMessage,
         stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
         frame,
         exceptionDocURL: pageError.exceptionDocURL,
       });
     }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -3294,19 +3294,16 @@ WebConsoleConnectionProxy.prototype = {
       console.error("Web Console getCachedMessages error: invalid state.");
     }
 
     let messages =
       response.messages.concat(...this.webConsoleClient.getNetworkEvents());
     messages.sort((a, b) => a.timeStamp - b.timeStamp);
 
     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
-      // Filter out CSS page errors.
-      messages = messages.filter(message => !(message._type == "PageError"
-          && Utils.categoryForScriptError(message) === CATEGORY_CSS));
       this.dispatchMessagesAdd(messages);
     } else {
       this.webConsoleFrame.displayCachedMessages(messages);
       if (!this._hasNativeConsoleAPI) {
         this.webConsoleFrame.logWarningAboutReplacedAPI();
       }
     }
 
@@ -3322,20 +3319,17 @@ WebConsoleConnectionProxy.prototype = {
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onPageError: function (type, packet) {
     if (this.webConsoleFrame && packet.from == this._consoleActor) {
       if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
-        let category = Utils.categoryForScriptError(packet.pageError);
-        if (category !== CATEGORY_CSS) {
-          this.dispatchMessageAdd(packet);
-        }
+        this.dispatchMessageAdd(packet);
         return;
       }
       this.webConsoleFrame.handlePageError(packet.pageError);
     }
   },
 
   /**
    * The "logMessage" message type handler. We redirect any message to the UI
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8475,20 +8475,18 @@ nsContentUtils::SetFetchReferrerURIWithP
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
-  if (referrerPolicy == net::RP_Default) {
-    referrerPolicy = aDoc->GetReferrerPolicy();
-  }
+  net::ReferrerPolicy referrerPolicy = (aReferrerPolicy != net::RP_Unset) ?
+                                       aReferrerPolicy : net::RP_Default;
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
 net::ReferrerPolicy
 nsContentUtils::GetReferrerPolicyFromHeader(const nsAString& aHeader)
 {
   // Multiple headers could be concatenated into one comma-separated
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11838,17 +11838,30 @@ nsGlobalWindow::CloneStorageEvent(const 
   RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
   return event.forget();
 }
 
 void
 nsGlobalWindow::Suspend()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(IsInnerWindow());
+
+  // We can only safely suspend windows that are the current inner window.  If
+  // its not the current inner, then we are in one of two different cases.
+  // Either we are in the bfcache or we are doomed window that is going away.
+  // When a window becomes inactive we purposely avoid placing already suspended
+  // windows into the bfcache.  It only expects windows suspended due to the
+  // Freeze() method which occurs while the window is still the current inner.
+  // So we must not call Suspend() on bfcache windows at this point or this
+  // invariant will be broken.  If the window is doomed there is no point in
+  // suspending it since it will soon be gone.
+  if (!AsInner()->IsCurrentInnerWindow()) {
+    return;
+  }
 
   // All children are also suspended.  This ensure mSuspendDepth is
   // set properly and the timers are properly canceled for each child.
   CallOnChildren(&nsGlobalWindow::Suspend);
 
   mSuspendDepth += 1;
   if (mSuspendDepth != 1) {
     return;
@@ -11886,17 +11899,27 @@ nsGlobalWindow::Suspend()
     RefPtr<Promise> d = mAudioContexts[i]->Suspend(dummy);
   }
 }
 
 void
 nsGlobalWindow::Resume()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(IsInnerWindow());
+
+  // We can only safely resume a window if its the current inner window.  If
+  // its not the current inner, then we are in one of two different cases.
+  // Either we are in the bfcache or we are doomed window that is going away.
+  // If a window is suspended when it becomes inactive we purposely do not
+  // put it in the bfcache, so Resume should never be needed in that case.
+  // If the window is doomed then there is no point in resuming it.
+  if (!AsInner()->IsCurrentInnerWindow()) {
+    return;
+  }
 
   // Resume all children.  This restores timers recursively canceled
   // in Suspend() and ensures all children have the correct mSuspendDepth.
   CallOnChildren(&nsGlobalWindow::Resume);
 
   MOZ_ASSERT(mSuspendDepth != 0);
   mSuspendDepth -= 1;
   if (mSuspendDepth != 0) {
@@ -11983,26 +12006,27 @@ nsGlobalWindow::IsSuspended() const
   }
   return mSuspendDepth != 0;
 }
 
 void
 nsGlobalWindow::Freeze()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsInnerWindow());
   Suspend();
   FreezeInternal();
 }
 
 void
 nsGlobalWindow::FreezeInternal()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsSuspended());
+  MOZ_DIAGNOSTIC_ASSERT(IsInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(AsInner()->IsCurrentInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
 
   CallOnChildren(&nsGlobalWindow::FreezeInternal);
 
   mFreezeDepth += 1;
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 1) {
     return;
   }
@@ -12028,26 +12052,27 @@ nsGlobalWindow::FreezeInternal()
 
   NotifyDOMWindowFrozen(this);
 }
 
 void
 nsGlobalWindow::Thaw()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsInnerWindow());
   ThawInternal();
   Resume();
 }
 
 void
 nsGlobalWindow::ThawInternal()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsSuspended());
+  MOZ_DIAGNOSTIC_ASSERT(IsInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(AsInner()->IsCurrentInnerWindow());
+  MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
 
   CallOnChildren(&nsGlobalWindow::ThawInternal);
 
   MOZ_ASSERT(mFreezeDepth != 0);
   mFreezeDepth -= 1;
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 0) {
     return;
@@ -12094,16 +12119,17 @@ nsGlobalWindow::IsFrozen() const
 }
 
 void
 nsGlobalWindow::SyncStateFromParentWindow()
 {
   // This method should only be called on an inner window that has been
   // assigned to an outer window already.
   MOZ_ASSERT(IsInnerWindow());
+  MOZ_ASSERT(AsInner()->IsCurrentInnerWindow());
   nsPIDOMWindowOuter* outer = GetOuterWindow();
   MOZ_ASSERT(outer);
 
   // Attempt to find our parent windows.
   nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
   nsPIDOMWindowOuter* parentOuter = frame ? frame->OwnerDoc()->GetWindow()
                                           : nullptr;
   nsGlobalWindow* parentInner =
@@ -12137,16 +12163,18 @@ nsGlobalWindow::SyncStateFromParentWindo
   }
 }
 
 template<typename Method>
 void
 nsGlobalWindow::CallOnChildren(Method aMethod)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(IsInnerWindow());
+  MOZ_ASSERT(AsInner()->IsCurrentInnerWindow());
 
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (!docShell) {
     return;
   }
 
   int32_t childCount = 0;
   docShell->GetChildCount(&childCount);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1087,23 +1087,23 @@ DOMInterfaces = {
     'headerFile': 'WebGLBuffer.h'
 },
 
 'WEBGL_compressed_texture_atc': {
     'nativeType': 'mozilla::WebGLExtensionCompressedTextureATC',
     'headerFile': 'WebGLExtensions.h'
 },
 
-'WEBGL_compressed_texture_etc1': {
-    'nativeType': 'mozilla::WebGLExtensionCompressedTextureETC1',
+'WEBGL_compressed_texture_etc': {
+    'nativeType': 'mozilla::WebGLExtensionCompressedTextureES3',
     'headerFile': 'WebGLExtensions.h'
 },
 
-'WEBGL_compressed_texture_es3': {
-    'nativeType': 'mozilla::WebGLExtensionCompressedTextureES3',
+'WEBGL_compressed_texture_etc1': {
+    'nativeType': 'mozilla::WebGLExtensionCompressedTextureETC1',
     'headerFile': 'WebGLExtensions.h'
 },
 
 'WEBGL_compressed_texture_pvrtc': {
     'nativeType': 'mozilla::WebGLExtensionCompressedTexturePVRTC',
     'headerFile': 'WebGLExtensions.h'
 },
 
@@ -1275,21 +1275,16 @@ DOMInterfaces = {
     'headerFile': 'WebGLSync.h'
 },
 
 'WebGLTexture': {
     'nativeType': 'mozilla::WebGLTexture',
     'headerFile': 'WebGLTexture.h'
 },
 
-'WebGLTimerQueryEXT': {
-    'nativeType': 'mozilla::WebGLTimerQuery',
-    'headerFile': 'WebGLTimerQuery.h'
-},
-
 'WebGLTransformFeedback': {
     'nativeType': 'mozilla::WebGLTransformFeedback',
     'headerFile': 'WebGLTransformFeedback.h'
 },
 
 'WebGLUniformLocation': {
     'nativeType': 'mozilla::WebGLUniformLocation',
     'headerFile': 'WebGLUniformLocation.h'
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -192,17 +192,20 @@ static_assert(int(HeadersGuardEnum::None
               int(HeadersGuardEnum::EndGuard_) == 5,
               "HeadersGuardEnum values are as expected");
 static_assert(int(ReferrerPolicy::_empty) == 0 &&
               int(ReferrerPolicy::No_referrer) == 1 &&
               int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
               int(ReferrerPolicy::Origin) == 3 &&
               int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
               int(ReferrerPolicy::Unsafe_url) == 5 &&
-              int(ReferrerPolicy::EndGuard_) == 6,
+              int(ReferrerPolicy::Same_origin) == 6 &&
+              int(ReferrerPolicy::Strict_origin) == 7 &&
+              int(ReferrerPolicy::Strict_origin_when_cross_origin) == 8 &&
+              int(ReferrerPolicy::EndGuard_) == 9,
               "ReferrerPolicy values are as expected");
 static_assert(int(RequestMode::Same_origin) == 0 &&
               int(RequestMode::No_cors) == 1 &&
               int(RequestMode::Cors) == 2 &&
               int(RequestMode::Navigate) == 3 &&
               int(RequestMode::EndGuard_) == 4,
               "RequestMode values are as expected");
 static_assert(int(RequestCredentials::Omit) == 0 &&
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -33,22 +33,16 @@ WebGL1Context::CreateFormatUsage(gl::GLC
 }
 
 JSObject*
 WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLRenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
-bool
-WebGL1Context::ValidateQueryTarget(GLenum target, const char* info)
-{
-    return false;
-}
-
 } // namespace mozilla
 
 nsresult
 NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** out_result)
 {
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CANVAS_WEBGL_USED, 1);
 
     nsIDOMWebGLRenderingContext* ctx = mozilla::WebGL1Context::Create();
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -31,15 +31,14 @@ public:
 
     // nsWrapperCache
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 private:
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
                                            uint32_t* alignment,
                                            const char* info) override;
-    virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_1_CONTEXT_H_
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -298,28 +298,16 @@ public:
                         GLuint srcElemOffset)
     {
         ClearBufferuiv(buffer, drawBuffer, Uint32Arr::From(list), srcElemOffset);
     }
 
     void ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth, GLint stencil);
 
     // -------------------------------------------------------------------------
-    // Query Objects - WebGL2ContextQueries.cpp
-
-    already_AddRefed<WebGLQuery> CreateQuery();
-    void DeleteQuery(WebGLQuery* query);
-    bool IsQuery(WebGLQuery* query);
-    void BeginQuery(GLenum target, WebGLQuery* query);
-    void EndQuery(GLenum target);
-    already_AddRefed<WebGLQuery> GetQuery(GLenum target, GLenum pname);
-    void GetQueryParameter(JSContext*, WebGLQuery* query, GLenum pname, JS::MutableHandleValue retval);
-
-
-    // -------------------------------------------------------------------------
     // Sampler Objects - WebGL2ContextSamplers.cpp
 
     already_AddRefed<WebGLSampler> CreateSampler();
     void DeleteSampler(WebGLSampler* sampler);
     bool IsSampler(WebGLSampler* sampler);
     void BindSampler(GLuint unit, WebGLSampler* sampler);
     void SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
     void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Int32Array& param);
@@ -405,15 +393,14 @@ private:
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
     // CreateVertexArrayImpl is assumed to be infallible.
     virtual WebGLVertexArray* CreateVertexArrayImpl() override;
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
                                            uint32_t* alignment,
                                            const char* info) override;
-    virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL2ContextQueries.cpp
+++ b/dom/canvas/WebGL2ContextQueries.cpp
@@ -17,393 +17,234 @@ namespace mozilla {
  *
  * OpenGL ES 3.0 spec 4.1.6:
  *     If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an
  *     implementation may choose to use a less precise version of the test which
  *     can additionally set the samples-boolean state to TRUE in some other
  *     implementation-dependent cases.
  */
 
-static const char*
-GetQueryTargetEnumString(GLenum target)
+WebGLRefPtr<WebGLQuery>*
+WebGLContext::ValidateQuerySlotByTarget(const char* funcName, GLenum target)
 {
-    switch (target)
-    {
-    case LOCAL_GL_ANY_SAMPLES_PASSED:
-        return "ANY_SAMPLES_PASSED";
-    case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
-        return "ANY_SAMPLES_PASSED_CONSERVATIVE";
-    case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
-        return "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN";
-    default:
-        break;
+    if (IsWebGL2()) {
+        switch (target) {
+        case LOCAL_GL_ANY_SAMPLES_PASSED:
+        case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+            return &mQuerySlot_SamplesPassed;
+
+        case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+            return &mQuerySlot_TFPrimsWritten;
+
+        default:
+            break;
+        }
     }
 
-    MOZ_ASSERT(false, "Unknown query `target`.");
-    return "UNKNOWN_QUERY_TARGET";
-}
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+        switch (target) {
+        case LOCAL_GL_TIME_ELAPSED_EXT:
+            return &mQuerySlot_TimeElapsed;
 
-static inline GLenum
-SimulateOcclusionQueryTarget(const gl::GLContext* gl, GLenum target)
-{
-    MOZ_ASSERT(target == LOCAL_GL_ANY_SAMPLES_PASSED ||
-               target == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
-               "unknown occlusion query target");
-
-    if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) {
-        return target;
-    } else if (gl->IsSupported(gl::GLFeature::occlusion_query2)) {
-        return LOCAL_GL_ANY_SAMPLES_PASSED;
+        default:
+            break;
+        }
     }
 
-    return LOCAL_GL_SAMPLES_PASSED;
-}
-
-WebGLRefPtr<WebGLQuery>&
-WebGLContext::GetQuerySlotByTarget(GLenum target)
-{
-    /* This function assumes that target has been validated for either
-     * WebGL1 or WebGL2.
-     */
-    switch (target) {
-    case LOCAL_GL_ANY_SAMPLES_PASSED:
-    case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
-        return mActiveOcclusionQuery;
-
-    case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
-        return mActiveTransformFeedbackQuery;
-
-    default:
-        MOZ_CRASH("GFX: Should not get here.");
-    }
+    ErrorInvalidEnum("%s: Bad `target`.", funcName);
+    return nullptr;
 }
 
 
 // -------------------------------------------------------------------------
 // Query Objects
 
 already_AddRefed<WebGLQuery>
-WebGL2Context::CreateQuery()
+WebGLContext::CreateQuery(const char* funcName)
 {
+    if (!funcName) {
+        funcName = "createQuery";
+    }
+
     if (IsContextLost())
         return nullptr;
 
-    if (mActiveOcclusionQuery && !gl->IsGLES()) {
-        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
-         *
-         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
-         * any target is active causes an INVALID_OPERATION error to be
-         * generated.
-         */
-        GenerateWarning("createQuery: The WebGL 2 prototype might generate"
-                        " INVALID_OPERATION when creating a query object while"
-                        " one other is active.");
-        /*
-         * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a
-         * good mechanism to do this yet. See bug 898404.
-         */
-    }
-
     RefPtr<WebGLQuery> globj = new WebGLQuery(this);
-
     return globj.forget();
 }
 
 void
-WebGL2Context::DeleteQuery(WebGLQuery* query)
+WebGLContext::DeleteQuery(WebGLQuery* query, const char* funcName)
 {
+    if (!funcName) {
+        funcName = "deleteQuery";
+    }
+
     if (IsContextLost())
         return;
 
     if (!query)
         return;
 
-    if (query->IsDeleted())
+    if (!ValidateObjectAllowDeleted(funcName, query))
         return;
 
-    if (query->IsActive())
-        EndQuery(query->mType);
-
-    if (mActiveOcclusionQuery && !gl->IsGLES()) {
-        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
-         *
-         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
-         * any target is active causes an INVALID_OPERATION error to be
-         * generated.
-         */
-        GenerateWarning("deleteQuery: The WebGL 2 prototype might generate"
-                        " INVALID_OPERATION when deleting a query object while"
-                        " one other is active.");
-    }
-
-    query->RequestDelete();
+    query->DeleteQuery();
 }
 
 bool
-WebGL2Context::IsQuery(WebGLQuery* query)
+WebGLContext::IsQuery(const WebGLQuery* query, const char* funcName)
 {
+    if (!funcName) {
+        funcName = "isQuery";
+    }
+
     if (IsContextLost())
         return false;
 
     if (!query)
         return false;
 
-    return (ValidateObjectAllowDeleted("isQuery", query) &&
-            !query->IsDeleted() &&
-            query->HasEverBeenActive());
+    if (!ValidateObjectAllowDeleted("isQuery", query))
+        return false;
+
+    return query->IsQuery();
 }
 
 void
-WebGL2Context::BeginQuery(GLenum target, WebGLQuery* query)
+WebGLContext::BeginQuery(GLenum target, WebGLQuery* query, const char* funcName)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateQueryTarget(target, "beginQuery"))
-        return;
-
-    if (!query) {
-        /* From GLES's EXT_occlusion_query_boolean:
-         *     BeginQueryEXT sets the active query object name for the query
-         *     type given by <target> to <id>. If BeginQueryEXT is called with
-         *     an <id> of zero, if the active query object name for <target> is
-         *     non-zero (for the targets ANY_SAMPLES_PASSED_EXT and
-         *     ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the active query for
-         *     either target is non-zero), if <id> is the name of an existing
-         *     query object whose type does not match <target>, or if <id> is
-         *     the active query object name for any query type, the error
-         *     INVALID_OPERATION is generated.
-         */
-        ErrorInvalidOperation("beginQuery: Query should not be null.");
-        return;
+    if (!funcName) {
+        funcName = "beginQuery";
     }
 
-    if (query->IsDeleted()) {
-        /* From GLES's EXT_occlusion_query_boolean:
-         *     BeginQueryEXT fails and an INVALID_OPERATION error is generated
-         *     if <id> is not a name returned from a previous call to
-         *     GenQueriesEXT, or if such a name has since been deleted with
-         *     DeleteQueriesEXT.
-         */
-        ErrorInvalidOperation("beginQuery: Query has been deleted.");
-        return;
-    }
-
-    if (query->HasEverBeenActive() &&
-        query->mType != target)
-    {
-        ErrorInvalidOperation("beginQuery: Target doesn't match with the query"
-                              " type.");
-        return;
-    }
-
-    WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
-    WebGLQuery* activeQuery = querySlot.get();
-    if (activeQuery)
-        return ErrorInvalidOperation("beginQuery: An other query already active.");
-
-    if (!query->HasEverBeenActive())
-        query->mType = target;
-
-    MakeContextCurrent();
-
-    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) {
-        gl->fBeginQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
-                        query->mGLName);
-    } else {
-        gl->fBeginQuery(SimulateOcclusionQueryTarget(gl, target),
-                        query->mGLName);
-    }
-
-    UpdateBoundQuery(target, query);
-}
-
-void
-WebGL2Context::EndQuery(GLenum target)
-{
     if (IsContextLost())
         return;
 
-    if (!ValidateQueryTarget(target, "endQuery"))
+    if (!ValidateObjectAllowDeleted(funcName, query))
         return;
 
-    WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
-    WebGLQuery* activeQuery = querySlot.get();
+    if (query->IsDeleted())
+        return ErrorInvalidOperation("%s: Cannot begin a deleted query.", funcName);
 
-    if (!activeQuery || target != activeQuery->mType)
-    {
-        /* From GLES's EXT_occlusion_query_boolean:
-         *     marks the end of the sequence of commands to be tracked for the
-         *     query type given by <target>. The active query object for
-         *     <target> is updated to indicate that query results are not
-         *     available, and the active query object name for <target> is reset
-         *     to zero. When the commands issued prior to EndQueryEXT have
-         *     completed and a final query result is available, the query object
-         *     active when EndQueryEXT is called is updated by the GL. The query
-         *     object is updated to indicate that the query results are
-         *     available and to contain the query result. If the active query
-         *     object name for <target> is zero when EndQueryEXT is called, the
-         *     error INVALID_OPERATION is generated.
-         */
-        ErrorInvalidOperation("endQuery: There is no active query of type %s.",
-                              GetQueryTargetEnumString(target));
+    const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+    if (!slot)
         return;
-    }
-
-    MakeContextCurrent();
-
-    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) {
-        gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
-    } else {
-        gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target));
-    }
-
-    UpdateBoundQuery(target, nullptr);
-    NS_DispatchToCurrentThread(new WebGLQuery::AvailableRunnable(activeQuery));
-}
 
-already_AddRefed<WebGLQuery>
-WebGL2Context::GetQuery(GLenum target, GLenum pname)
-{
-    if (IsContextLost())
-        return nullptr;
-
-    if (!ValidateQueryTarget(target, "getQuery"))
-        return nullptr;
-
-    if (pname != LOCAL_GL_CURRENT_QUERY) {
-        /* OpenGL ES 3.0 spec 6.1.7:
-         *     pname must be CURRENT_QUERY.
-         */
-        ErrorInvalidEnum("getQuery: `pname` must be CURRENT_QUERY.");
-        return nullptr;
-    }
+    if (*slot)
+        return ErrorInvalidOperation("%s: Query target already active.", funcName);
 
-    WebGLRefPtr<WebGLQuery>& targetSlot = GetQuerySlotByTarget(target);
-    RefPtr<WebGLQuery> tmp = targetSlot.get();
-    if (tmp && tmp->mType != target) {
-        // Query in slot doesn't match target
-        return nullptr;
-    }
-
-    return tmp.forget();
-}
+    ////
 
-static bool
-ValidateQueryEnum(WebGLContext* webgl, GLenum pname, const char* info)
-{
-    switch (pname) {
-    case LOCAL_GL_QUERY_RESULT_AVAILABLE:
-    case LOCAL_GL_QUERY_RESULT:
-        return true;
-
-    default:
-        webgl->ErrorInvalidEnum("%s: invalid pname: %s", info, webgl->EnumName(pname));
-        return false;
-    }
+    query->BeginQuery(target, *slot);
 }
 
 void
-WebGL2Context::GetQueryParameter(JSContext*, WebGLQuery* query, GLenum pname,
-                                 JS::MutableHandleValue retval)
+WebGLContext::EndQuery(GLenum target, const char* funcName)
 {
-    retval.set(JS::NullValue());
+    if (!funcName) {
+        funcName = "endQuery";
+    }
 
     if (IsContextLost())
         return;
 
-    if (!ValidateQueryEnum(this, pname, "getQueryParameter"))
+    const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+    if (!slot)
         return;
 
-    if (!query) {
-        /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1):
-         *    If id is not the name of a query object, or if the query object
-         *    named by id is currently active, then an INVALID_OPERATION error
-         *    is generated. pname must be QUERY_RESULT or
-         *    QUERY_RESULT_AVAILABLE.
-         */
-        ErrorInvalidOperation("getQueryObject: `query` should not be null.");
-        return;
-    }
+    const auto& query = *slot;
+    if (!query)
+        return ErrorInvalidOperation("%s: Query target not active.", funcName);
+
+    query->EndQuery();
+}
 
-    if (query->IsDeleted()) {
-        // See (spec getQueryObject 1)
-        ErrorInvalidOperation("getQueryObject: `query` has been deleted.");
-        return;
-    }
-
-    if (query->IsActive()) {
-        // See (spec getQueryObject 1)
-        ErrorInvalidOperation("getQueryObject: `query` is active.");
-        return;
+void
+WebGLContext::GetQuery(JSContext* cx, GLenum target, GLenum pname,
+                       JS::MutableHandleValue retval, const char* funcName)
+{
+    if (!funcName) {
+        funcName = "getQuery";
     }
 
-    if (!query->HasEverBeenActive()) {
-        /* See (spec getQueryObject 1)
-         *     If this instance of WebGLQuery has never been active before, that
-         *     mean that query->mGLName is not a query object yet.
-         */
-        ErrorInvalidOperation("getQueryObject: `query` has never been active.");
+    retval.setNull();
+    if (IsContextLost())
         return;
-    }
 
-    // We must wait for an event loop before the query can be available
-    if (!query->mCanBeAvailable && !gfxPrefs::WebGLImmediateQueries()) {
-        if (pname == LOCAL_GL_QUERY_RESULT_AVAILABLE) {
-            retval.set(JS::BooleanValue(false));
+    switch (pname) {
+    case LOCAL_GL_CURRENT_QUERY_EXT:
+        {
+            if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query) &&
+                target == LOCAL_GL_TIMESTAMP)
+            {
+                // Doesn't seem illegal to ask about, but is always null.
+                // TIMESTAMP has no slot, so ValidateQuerySlotByTarget would generate
+                // INVALID_ENUM.
+                return;
+            }
+
+            const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+            if (!slot || !*slot)
+                return;
+
+            const auto& query = *slot;
+            if (target != query->Target())
+                return;
+
+            JS::Rooted<JS::Value> v(cx);
+            dom::GetOrCreateDOMReflector(cx, slot->get(), &v);
+            retval.set(v);
         }
         return;
-    }
+
+    case LOCAL_GL_QUERY_COUNTER_BITS_EXT:
+        if (!IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query))
+            break;
 
-    MakeContextCurrent();
-    GLuint returned = 0;
-    switch (pname) {
-    case LOCAL_GL_QUERY_RESULT_AVAILABLE:
-        gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
-        retval.set(JS::BooleanValue(returned != 0));
-        return;
-
-    case LOCAL_GL_QUERY_RESULT:
-        gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned);
-
-        if (query->mType == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) {
-            retval.set(JS::NumberValue(returned));
+        if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
+            target != LOCAL_GL_TIMESTAMP_EXT)
+        {
+            ErrorInvalidEnum("%s: Bad pname for target.", funcName);
             return;
         }
 
-        /*
-         * test (returned != 0) is important because ARB_occlusion_query on desktop drivers
-         * return the number of samples drawed when the OpenGL ES extension
-         * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed.
-         */
-        retval.set(JS::BooleanValue(returned != 0));
+        {
+            GLint bits = 0;
+            gl->fGetQueryiv(target, pname, &bits);
+
+            if (!Has64BitTimestamps() && bits > 32) {
+                bits = 32;
+            }
+            retval.set(JS::Int32Value(bits));
+        }
         return;
 
     default:
         break;
     }
 
-    ErrorInvalidEnum("getQueryObject: `pname` must be QUERY_RESULT{_AVAILABLE}.");
+    ErrorInvalidEnum("%s: Bad pname.", funcName);
+    return;
 }
 
 void
-WebGL2Context::UpdateBoundQuery(GLenum target, WebGLQuery* query)
-{
-    WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
-    querySlot = query;
-}
-
-bool
-WebGL2Context::ValidateQueryTarget(GLenum target, const char* info)
+WebGLContext::GetQueryParameter(JSContext*, const WebGLQuery* query, GLenum pname,
+                                JS::MutableHandleValue retval, const char* funcName)
 {
-    switch (target) {
-    case LOCAL_GL_ANY_SAMPLES_PASSED:
-    case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
-    case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
-        return true;
+    if (!funcName) {
+        funcName = "getQueryParameter";
+    }
+
+    retval.setNull();
+    if (IsContextLost())
+        return;
 
-    default:
-        ErrorInvalidEnumInfo(info, target);
-        return false;
-    }
+    if (!ValidateObjectAllowDeleted(funcName, query))
+        return;
+
+    if (query->IsDeleted())
+        return ErrorInvalidOperation("%s: Query must not be deleted.", funcName);
+
+    query->GetQueryParameter(pname, retval);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -66,17 +66,16 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLMemoryTracker.h"
 #include "WebGLObjectModel.h"
 #include "WebGLProgram.h"
 #include "WebGLQuery.h"
 #include "WebGLSampler.h"
 #include "WebGLShader.h"
 #include "WebGLSync.h"
-#include "WebGLTimerQuery.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #ifdef MOZ_WIDGET_COCOA
 #include "nsCocoaFeatures.h"
 #endif
 
@@ -247,37 +246,39 @@ WebGLContext::DestroyResourcesAndContext
     mBoundCopyWriteBuffer = nullptr;
     mBoundPixelPackBuffer = nullptr;
     mBoundPixelUnpackBuffer = nullptr;
     mBoundUniformBuffer = nullptr;
     mCurrentProgram = nullptr;
     mActiveProgramLinkInfo = nullptr;
     mBoundDrawFramebuffer = nullptr;
     mBoundReadFramebuffer = nullptr;
-    mActiveOcclusionQuery = nullptr;
     mBoundRenderbuffer = nullptr;
     mBoundVertexArray = nullptr;
     mDefaultVertexArray = nullptr;
     mBoundTransformFeedback = nullptr;
     mDefaultTransformFeedback = nullptr;
 
+    mQuerySlot_SamplesPassed = nullptr;
+    mQuerySlot_TFPrimsWritten = nullptr;
+    mQuerySlot_TimeElapsed = nullptr;
+
     mIndexedUniformBufferBindings.clear();
 
     //////
 
     ClearLinkedList(mBuffers);
     ClearLinkedList(mFramebuffers);
     ClearLinkedList(mPrograms);
     ClearLinkedList(mQueries);
     ClearLinkedList(mRenderbuffers);
     ClearLinkedList(mSamplers);
     ClearLinkedList(mShaders);
     ClearLinkedList(mSyncs);
     ClearLinkedList(mTextures);
-    ClearLinkedList(mTimerQueries);
     ClearLinkedList(mTransformFeedbacks);
     ClearLinkedList(mVertexArrays);
 
     //////
 
     mFakeBlack_2D_0000       = nullptr;
     mFakeBlack_2D_0001       = nullptr;
     mFakeBlack_CubeMap_0000  = nullptr;
@@ -1642,17 +1643,17 @@ WebGLContext::DummyReadFramebufferOperat
 
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         ErrorInvalidFramebufferOperation("%s: Framebuffer must be complete.",
                                          funcName);
     }
 }
 
 bool
-WebGLContext::HasTimestampBits() const
+WebGLContext::Has64BitTimestamps() const
 {
     // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
     return gl->IsSupported(GLFeature::sync);
 }
 
 static bool
 CheckContextLost(GLContext* gl, bool* const out_isGuilty)
 {
@@ -2508,28 +2509,37 @@ WebGLContext::StartVRPresentation()
             TextureFlags::ORIGIN_BOTTOM_LEFT);
 
     screen->Morph(Move(factory));
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+static inline size_t
+SizeOfViewElem(const dom::ArrayBufferView& view)
+{
+    const auto& elemType = view.Type();
+    if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
+        return 1;
+
+    return js::Scalar::byteSize(elemType);
+}
+
 bool
 WebGLContext::ValidateArrayBufferView(const char* funcName,
                                       const dom::ArrayBufferView& view, GLuint elemOffset,
                                       GLuint elemCountOverride, uint8_t** const out_bytes,
                                       size_t* const out_byteLen)
 {
     view.ComputeLengthAndData();
     uint8_t* const bytes = view.DataAllowShared();
     const size_t byteLen = view.LengthAllowShared();
 
-    const auto& elemType = view.Type();
-    const auto& elemSize = js::Scalar::byteSize(elemType);
+    const auto& elemSize = SizeOfViewElem(view);
 
     size_t elemCount = byteLen / elemSize;
     if (elemOffset > elemCount) {
         ErrorInvalidValue("%s: Invalid offset into ArrayBufferView.", funcName);
         return false;
     }
     elemCount -= elemOffset;
 
@@ -2587,18 +2597,19 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(We
   mBoundTransformFeedback,
   mBoundUniformBuffer,
   mCurrentProgram,
   mBoundDrawFramebuffer,
   mBoundReadFramebuffer,
   mBoundRenderbuffer,
   mBoundVertexArray,
   mDefaultVertexArray,
-  mActiveOcclusionQuery,
-  mActiveTransformFeedbackQuery)
+  mQuerySlot_SamplesPassed,
+  mQuerySlot_TFPrimsWritten,
+  mQuerySlot_TimeElapsed)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
     NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
     NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     // If the exact way we cast to nsISupports here ever changes, fix our
     // ToSupports() method.
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -98,17 +98,16 @@ class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLRenderbuffer;
 class WebGLSampler;
 class WebGLShader;
 class WebGLShaderPrecisionFormat;
 class WebGLSync;
 class WebGLTexture;
-class WebGLTimerQuery;
 class WebGLTransformFeedback;
 class WebGLUniformLocation;
 class WebGLVertexArray;
 
 namespace dom {
 class Element;
 class ImageData;
 class OwningHTMLCanvasElementOrOffscreenCanvas;
@@ -441,17 +440,17 @@ public:
 
     // Prepare the context for capture before compositing
     void BeginComposition();
     // Clean up the context after captured for compositing
     void EndComposition();
 
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
-    uint32_t Generation() { return mGeneration.value(); }
+    uint32_t Generation() const { return mGeneration.value(); }
 
     // This is similar to GLContext::ClearSafely, but tries to minimize the
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
     void ForceClearFramebufferWithDefaultValues(GLbitfield bufferBits, bool fakeNoAlpha);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
@@ -929,20 +928,34 @@ protected:
 
     WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTarget(GLenum target);
     WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTargetIndexed(GLenum target,
                                                            GLuint index);
 
 // -----------------------------------------------------------------------------
 // Queries (WebGL2ContextQueries.cpp)
 protected:
-    WebGLRefPtr<WebGLQuery>& GetQuerySlotByTarget(GLenum target);
+    WebGLRefPtr<WebGLQuery> mQuerySlot_SamplesPassed;
+    WebGLRefPtr<WebGLQuery> mQuerySlot_TFPrimsWritten;
+    WebGLRefPtr<WebGLQuery> mQuerySlot_TimeElapsed;
+
+    WebGLRefPtr<WebGLQuery>*
+    ValidateQuerySlotByTarget(const char* funcName, GLenum target);
 
-    WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
-    WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
+public:
+    already_AddRefed<WebGLQuery> CreateQuery(const char* funcName = nullptr);
+    void DeleteQuery(WebGLQuery* query, const char* funcName = nullptr);
+    bool IsQuery(const WebGLQuery* query, const char* funcName = nullptr);
+    void BeginQuery(GLenum target, WebGLQuery* query, const char* funcName = nullptr);
+    void EndQuery(GLenum target, const char* funcName = nullptr);
+    void GetQuery(JSContext* cx, GLenum target, GLenum pname,
+                  JS::MutableHandleValue retval, const char* funcName = nullptr);
+    void GetQueryParameter(JSContext* cx, const WebGLQuery* query, GLenum pname,
+                           JS::MutableHandleValue retval, const char* funcName = nullptr);
+
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 public:
     void Disable(GLenum cap);
     void Enable(GLenum cap);
     bool GetStencilBits(GLint* const out_stencilBits);
     bool GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val);
@@ -1605,45 +1618,44 @@ protected:
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     //////
 
     // Returns false if `object` is null or not valid.
     template<class ObjectType>
-    bool ValidateObject(const char* info, ObjectType* object);
+    bool ValidateObject(const char* info, const ObjectType* object);
 
     // Returns false if `object` is not valid.  Considers null to be valid.
     template<class ObjectType>
-    bool ValidateObjectAllowNull(const char* info, ObjectType* object);
+    bool ValidateObjectAllowNull(const char* info, const ObjectType* object);
 
     // Returns false if `object` is not valid, but considers deleted objects and
     // null objects valid.
     template<class ObjectType>
-    bool ValidateObjectAllowDeletedOrNull(const char* info, ObjectType* object);
+    bool ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object);
 
     // Returns false if `object` is null or not valid, but considers deleted
     // objects valid.
     template<class ObjectType>
-    bool ValidateObjectAllowDeleted(const char* info, ObjectType* object);
+    bool ValidateObjectAllowDeleted(const char* info, const ObjectType* object);
 
 private:
     // Like ValidateObject, but only for cases when `object` is known to not be
     // null already.
     template<class ObjectType>
-    bool ValidateObjectAssumeNonNull(const char* info, ObjectType* object);
+    bool ValidateObjectAssumeNonNull(const char* info, const ObjectType* object);
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual WebGLVertexArray* CreateVertexArrayImpl();
 
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
-    virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
 
 public:
     void ForceLoseContext(bool simulateLoss = false);
 
 protected:
     void ForceRestoreContext();
 
@@ -1670,17 +1682,16 @@ protected:
     LinkedList<WebGLFramebuffer> mFramebuffers;
     LinkedList<WebGLProgram> mPrograms;
     LinkedList<WebGLQuery> mQueries;
     LinkedList<WebGLRenderbuffer> mRenderbuffers;
     LinkedList<WebGLSampler> mSamplers;
     LinkedList<WebGLShader> mShaders;
     LinkedList<WebGLSync> mSyncs;
     LinkedList<WebGLTexture> mTextures;
-    LinkedList<WebGLTimerQuery> mTimerQueries;
     LinkedList<WebGLTransformFeedback> mTransformFeedbacks;
     LinkedList<WebGLVertexArray> mVertexArrays;
 
     WebGLRefPtr<WebGLTransformFeedback> mDefaultTransformFeedback;
     WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
 
     // PixelStore parameters
     uint32_t mPixelStore_UnpackImageHeight;
@@ -1785,17 +1796,17 @@ protected:
 
     uint64_t mLastUseIndex;
 
     bool mNeedsFakeNoAlpha;
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
     bool mNeedsEmulatedLoneDepthStencil;
 
-    bool HasTimestampBits() const;
+    bool Has64BitTimestamps() const;
 
     struct ScopedMaskWorkaround {
         WebGLContext& mWebGL;
         const bool mFakeNoAlpha;
         const bool mFakeNoDepth;
         const bool mFakeNoStencil;
 
         static bool ShouldFakeNoAlpha(WebGLContext& webgl) {
@@ -1893,17 +1904,16 @@ public:
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
     friend class WebGLSync;
-    friend class WebGLTimerQuery;
     friend class WebGLTransformFeedback;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
     friend class WebGLVertexArrayFake;
     friend class WebGLVertexArrayGL;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
@@ -1915,70 +1925,70 @@ ToSupports(WebGLContext* webgl)
 
 /**
  ** Template implementations
  **/
 
 template<class ObjectType>
 inline bool
 WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info,
-                                               ObjectType* object)
+                                               const ObjectType* object)
 {
     if (object && !object->IsCompatibleWithContext(this)) {
         ErrorInvalidOperation("%s: object from different WebGL context "
                               "(or older generation of this one) "
                               "passed as argument", info);
         return false;
     }
 
     return true;
 }
 
 template<class ObjectType>
 inline bool
-WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object)
+WebGLContext::ValidateObjectAssumeNonNull(const char* info, const ObjectType* object)
 {
     MOZ_ASSERT(object);
 
     if (!ValidateObjectAllowDeletedOrNull(info, object))
         return false;
 
     if (object->IsDeleted()) {
         ErrorInvalidValue("%s: Deleted object passed as argument.", info);
         return false;
     }
 
     return true;
 }
 
 template<class ObjectType>
 inline bool
-WebGLContext::ValidateObjectAllowNull(const char* info, ObjectType* object)
+WebGLContext::ValidateObjectAllowNull(const char* info, const ObjectType* object)
 {
     if (!object)
         return true;
 
     return ValidateObjectAssumeNonNull(info, object);
 }
 
 template<class ObjectType>
 inline bool
-WebGLContext::ValidateObjectAllowDeleted(const char* info, ObjectType* object)
+WebGLContext::ValidateObjectAllowDeleted(const char* info, const ObjectType* object)
 {
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAllowDeletedOrNull(info, object);
 }
 
 template<class ObjectType>
 inline bool
-WebGLContext::ValidateObject(const char* info, ObjectType* object)
+WebGLContext::ValidateObject(const char* info, const ObjectType* object)
 {
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAssumeNonNull(info, object);
 }
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -43,17 +43,17 @@ WebGLContext::GetExtensionString(WebGLEx
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_atc)
-        WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_es3_0)
+        WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc1)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_pvrtc)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_s3tc)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_renderer_info)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_shaders)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_depth_texture)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_draw_buffers)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_lose_context)
@@ -107,27 +107,29 @@ WebGLContext::IsExtensionSupported(WebGL
 {
     if (mDisableExtensions)
         return false;
 
     // Extensions for both WebGL 1 and 2.
     switch (ext) {
     // In alphabetical order
     // EXT_
+    case WebGLExtensionID::EXT_disjoint_timer_query:
+        return WebGLExtensionDisjointTimerQuery::IsSupported(this);
     case WebGLExtensionID::EXT_texture_filter_anisotropic:
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic);
 
     // OES_
     case WebGLExtensionID::OES_texture_float_linear:
         return gl->IsSupported(gl::GLFeature::texture_float_linear);
 
     // WEBGL_
     case WebGLExtensionID::WEBGL_compressed_texture_atc:
         return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture);
-    case WebGLExtensionID::WEBGL_compressed_texture_es3_0:
+    case WebGLExtensionID::WEBGL_compressed_texture_etc:
         return gl->IsSupported(gl::GLFeature::ES3_compatibility);
     case WebGLExtensionID::WEBGL_compressed_texture_etc1:
         return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture);
     case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
         return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
     case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
         if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
             return true;
@@ -206,24 +208,23 @@ WebGLContext::IsExtensionSupported(WebGL
         case WebGLExtensionID::WEBGL_draw_buffers:
             return WebGLExtensionDrawBuffers::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
 
         if (gfxPrefs::WebGLDraftExtensionsEnabled()) {
+            /*
             switch (ext) {
-            case WebGLExtensionID::EXT_disjoint_timer_query:
-                return WebGLExtensionDisjointTimerQuery::IsSupported(this);
-
             default:
                 // For warnings-as-errors.
                 break;
             }
+            */
         }
     }
 
     return false;
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char* other)
@@ -390,17 +391,17 @@ WebGLContext::EnableExtension(WebGLExten
 
     // WEBGL_
     case WebGLExtensionID::WEBGL_color_buffer_float:
         obj = new WebGLExtensionColorBufferFloat(this);
         break;
     case WebGLExtensionID::WEBGL_compressed_texture_atc:
         obj = new WebGLExtensionCompressedTextureATC(this);
         break;
-    case WebGLExtensionID::WEBGL_compressed_texture_es3_0:
+    case WebGLExtensionID::WEBGL_compressed_texture_etc:
         obj = new WebGLExtensionCompressedTextureES3(this);
         break;
     case WebGLExtensionID::WEBGL_compressed_texture_etc1:
         obj = new WebGLExtensionCompressedTextureETC1(this);
         break;
     case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
         obj = new WebGLExtensionCompressedTexturePVRTC(this);
         break;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -259,34 +259,42 @@ WebGLContext::GetParameter(JSContext* cx
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) {
             WebGLVertexArray* vao =
                 (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr;
             return WebGLObjectAsJSValue(cx, vao, rv);
         }
     }
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
-        if (pname == LOCAL_GL_TIMESTAMP_EXT) {
-            GLuint64 iv = 0;
-            if (HasTimestampBits()) {
-                gl->fGetInteger64v(pname, (GLint64*)&iv);
-            } else {
-                GenerateWarning("QUERY_COUNTER_BITS_EXT for TIMESTAMP_EXT is 0.");
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+        switch (pname) {
+        case LOCAL_GL_TIMESTAMP_EXT:
+            {
+                uint64_t val = 0;
+                if (Has64BitTimestamps()) {
+                    gl->fGetInteger64v(pname, (GLint64*)&val);
+                } else {
+                    gl->fGetIntegerv(pname, (GLint*)&val);
+                }
+                // TODO: JS doesn't support 64-bit integers. Be lossy and
+                // cast to double (53 bits)
+                return JS::NumberValue(val);
             }
-            // TODO: JS doesn't support 64-bit integers. Be lossy and
-            // cast to double (53 bits)
-            return JS::NumberValue(static_cast<double>(iv));
-        } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
-            // When disjoint isn't supported, leave as false.
-            realGLboolean disjoint = LOCAL_GL_FALSE;
-            if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
-                gl->fGetBooleanv(pname, &disjoint);
+
+        case LOCAL_GL_GPU_DISJOINT_EXT:
+            {
+                realGLboolean val = false; // Not disjoint by default.
+                if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
+                    gl->fGetBooleanv(pname, &val);
+                }
+                return JS::BooleanValue(val);
             }
-            return JS::BooleanValue(bool(disjoint));
+
+        default:
+            break;
         }
     }
 
     // Privileged string params exposed by WEBGL_debug_renderer_info.
     // The privilege check is done in WebGLContext::IsExtensionSupported.
     // So here we just have to check that the extension is enabled.
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) {
         switch (pname) {
--- a/dom/canvas/WebGLExtensionCompressedTextureES3.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureES3.cpp
@@ -58,11 +58,11 @@ WebGLExtensionCompressedTextureES3::WebG
 
 #undef FOO
 }
 
 WebGLExtensionCompressedTextureES3::~WebGLExtensionCompressedTextureES3()
 {
 }
 
-IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureES3, WEBGL_compressed_texture_es3)
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureES3, WEBGL_compressed_texture_etc)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -7,252 +7,123 @@
 #include "WebGLExtensions.h"
 
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "WebGLContext.h"
-#include "WebGLTimerQuery.h"
+#include "WebGLQuery.h"
 
 namespace mozilla {
 
 WebGLExtensionDisjointTimerQuery::WebGLExtensionDisjointTimerQuery(WebGLContext* webgl)
   : WebGLExtensionBase(webgl)
-  , mActiveQuery(nullptr)
 {
-  MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
+    MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 }
 
 WebGLExtensionDisjointTimerQuery::~WebGLExtensionDisjointTimerQuery()
 {
 }
 
-already_AddRefed<WebGLTimerQuery>
-WebGLExtensionDisjointTimerQuery::CreateQueryEXT()
+already_AddRefed<WebGLQuery>
+WebGLExtensionDisjointTimerQuery::CreateQueryEXT() const
 {
-  if (mIsLost)
-    return nullptr;
+    const char funcName[] = "createQueryEXT";
+    if (mIsLost)
+        return nullptr;
 
-  RefPtr<WebGLTimerQuery> query = WebGLTimerQuery::Create(mContext);
-  return query.forget();
+    return mContext->CreateQuery(funcName);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLTimerQuery* query)
+WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLQuery* query) const
 {
-  if (mIsLost)
-    return;
+    const char funcName[] = "deleteQueryEXT";
+    if (mIsLost)
+        return;
 
-  if (!mContext->ValidateObject("deleteQueryEXT", query))
-    return;
-
-  query->RequestDelete();
+    mContext->DeleteQuery(query, funcName);
 }
 
 bool
-WebGLExtensionDisjointTimerQuery::IsQueryEXT(WebGLTimerQuery* query)
+WebGLExtensionDisjointTimerQuery::IsQueryEXT(const WebGLQuery* query) const
 {
-  if (!query)
-    return false;
-
-  if (!mContext->ValidateObjectAllowDeleted("isQueryEXT", query))
-    return false;
-
-  if (query->IsDeleted())
-    return false;
-
-  return true;
-}
-
-void
-WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target,
-                                                WebGLTimerQuery* query)
-{
-  if (mIsLost)
-    return;
-
-  if (!mContext->ValidateObject("beginQueryEXT", query))
-    return;
+    const char funcName[] = "isQueryEXT";
+    if (mIsLost)
+        return false;
 
-  if (query->HasEverBeenBound() && query->Target() != target) {
-    mContext->ErrorInvalidOperation("beginQueryEXT: Query is already bound"
-                                    " to a different target.");
-    return;
-  }
-
-  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
-    mContext->ErrorInvalidEnumInfo("beginQueryEXT: Can only begin on target"
-                                   " TIME_ELAPSED_EXT.", target);
-    return;
-  }
-
-  if (mActiveQuery) {
-    mContext->ErrorInvalidOperation("beginQueryEXT: A query is already"
-                                    " active.");
-    return;
-  }
-
-  mContext->MakeContextCurrent();
-  gl::GLContext* gl = mContext->GL();
-  gl->fBeginQuery(target, query->mGLName);
-  query->mTarget = LOCAL_GL_TIME_ELAPSED_EXT;
-  mActiveQuery = query;
+    return mContext->IsQuery(query, funcName);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target)
+WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery* query) const
 {
-  if (mIsLost)
-    return;
+    const char funcName[] = "beginQueryEXT";
+    if (mIsLost)
+        return;
+
+    mContext->BeginQuery(target, query, funcName);
+}
 
-  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
-    mContext->ErrorInvalidEnumInfo("endQueryEXT: Can only end on"
-                                   " TIME_ELAPSED_EXT.", target);
-    return;
-  }
+void
+WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target) const
+{
+    const char funcName[] = "endQueryEXT";
+    if (mIsLost)
+        return;
 
-  if (!mActiveQuery) {
-    mContext->ErrorInvalidOperation("endQueryEXT: A query is not active.");
-    return;
-  }
-
-  mContext->MakeContextCurrent();
-  mContext->GL()->fEndQuery(target);
-  mActiveQuery->QueueAvailablity();
-  mActiveQuery = nullptr;
+    mContext->EndQuery(target, funcName);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLTimerQuery* query,
-                                                  GLenum target)
+WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery* query, GLenum target) const
 {
-  if (mIsLost)
-    return;
-
-  if (!mContext->ValidateObject("queryCounterEXT", query))
-    return;
+    const char funcName[] = "queryCounterEXT";
+    if (mIsLost)
+        return;
 
-  if (target != LOCAL_GL_TIMESTAMP_EXT) {
-    mContext->ErrorInvalidEnumInfo("queryCounterEXT: requires"
-                                   " TIMESTAMP_EXT.", target);
-    return;
-  }
+    if (!mContext->ValidateObject(funcName, query))
+        return;
 
-  mContext->MakeContextCurrent();
-  mContext->GL()->fQueryCounter(query->mGLName, target);
-  query->mTarget = LOCAL_GL_TIMESTAMP_EXT;
-  query->QueueAvailablity();
+    query->QueryCounter(funcName, target);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
-                                              GLenum pname,
-                                              JS::MutableHandle<JS::Value> retval)
+WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
+                                              JS::MutableHandleValue retval) const
 {
-  if (mIsLost)
-    return;
+    const char funcName[] = "getQueryEXT";
+    retval.setNull();
+    if (mIsLost)
+        return;
 
-  mContext->MakeContextCurrent();
-  switch (pname) {
-  case LOCAL_GL_CURRENT_QUERY_EXT: {
-    if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
-      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
-                                     target);
-      return;
-    }
-    if (mActiveQuery) {
-      JS::Rooted<JS::Value> v(cx);
-      dom::GetOrCreateDOMReflector(cx, mActiveQuery.get(), &v);
-      retval.set(v);
-    } else {
-      retval.set(JS::NullValue());
-    }
-    break;
-  }
-  case LOCAL_GL_QUERY_COUNTER_BITS_EXT: {
-    if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
-        target != LOCAL_GL_TIMESTAMP_EXT) {
-      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
-                                     target);
-      return;
-    }
-    GLint bits = 0;
-    if (mContext->HasTimestampBits()) {
-      mContext->GL()->fGetQueryiv(target, pname, &bits);
-    }
-    retval.set(JS::Int32Value(int32_t(bits)));
-    break;
-  }
-  default:
-    mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query property.",
-                                   pname);
-    break;
-  }
+    mContext->GetQuery(cx, target, pname, retval, funcName);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
-                                                    WebGLTimerQuery* query,
-                                                    GLenum pname,
-                                                    JS::MutableHandle<JS::Value> retval)
+                                                    const WebGLQuery* query, GLenum pname,
+                                                    JS::MutableHandleValue retval) const
 {
-  if (mIsLost)
-    return;
-
-  if (!mContext->ValidateObject("getQueryObjectEXT", query))
-    return;
-
-  if (query == mActiveQuery.get()) {
-    mContext->ErrorInvalidOperation("getQueryObjectEXT: Query must not be"
-                                    " active.");
-  }
+    const char funcName[] = "getQueryObjectEXT";
+    retval.setNull();
+    if (mIsLost)
+        return;
 
-  mContext->MakeContextCurrent();
-  // XXX: Note that the query result *may change* within the same task!
-  // This does not follow the specification, which states that all calls
-  // checking query results must return the same value until the event loop
-  // is empty.
-  switch (pname) {
-  case LOCAL_GL_QUERY_RESULT_EXT: {
-    GLuint64 result = 0;
-    mContext->GL()->fGetQueryObjectui64v(query->mGLName,
-                                         LOCAL_GL_QUERY_RESULT_EXT,
-                                         &result);
-    retval.set(JS::NumberValue(result));
-    break;
-  }
-  case LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT: {
-    GLuint avail = 0;
-    mContext->GL()->fGetQueryObjectuiv(query->mGLName,
-                                       LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT,
-                                       &avail);
-    bool canBeAvailable = query->CanBeAvailable() || gfxPrefs::WebGLImmediateQueries();
-    retval.set(JS::BooleanValue(bool(avail) && canBeAvailable));
-    break;
-  }
-  default:
-    mContext->ErrorInvalidEnumInfo("getQueryObjectEXT: Invalid query"
-                                   " property.", pname);
-    break;
-  }
+    mContext->GetQueryParameter(cx, query, pname, retval, funcName);
 }
 
 bool
 WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
 {
-  webgl->MakeContextCurrent();
-  gl::GLContext* gl = webgl->GL();
-  return gl->IsSupported(gl::GLFeature::query_objects) &&
-         gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
-         gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
-}
-
-void
-WebGLExtensionDisjointTimerQuery::OnMarkLost()
-{
-  mActiveQuery = nullptr;
+    webgl->MakeContextCurrent();
+    gl::GLContext* gl = webgl->GL();
+    return gl->IsSupported(gl::GLFeature::query_objects) &&
+           gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
+           gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDisjointTimerQuery, EXT_disjoint_timer_query)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -20,17 +20,16 @@ class Sequence;
 
 namespace webgl {
 class FormatUsageAuthority;
 } // namespace webgl
 
 class WebGLContext;
 class WebGLShader;
 class WebGLQuery;
-class WebGLTimerQuery;
 class WebGLVertexArray;
 
 class WebGLExtensionBase
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     explicit WebGLExtensionBase(WebGLContext* webgl);
@@ -365,36 +364,27 @@ public:
 
 class WebGLExtensionDisjointTimerQuery
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionDisjointTimerQuery(WebGLContext* webgl);
     virtual ~WebGLExtensionDisjointTimerQuery();
 
-    already_AddRefed<WebGLTimerQuery> CreateQueryEXT();
-    void DeleteQueryEXT(WebGLTimerQuery* query);
-    bool IsQueryEXT(WebGLTimerQuery* query);
-    void BeginQueryEXT(GLenum target, WebGLTimerQuery* query);
-    void EndQueryEXT(GLenum target);
-    void QueryCounterEXT(WebGLTimerQuery* query, GLenum target);
-    void GetQueryEXT(JSContext *cx, GLenum target, GLenum pname,
-                     JS::MutableHandle<JS::Value> retval);
-    void GetQueryObjectEXT(JSContext *cx, WebGLTimerQuery* query,
-                           GLenum pname,
-                           JS::MutableHandle<JS::Value> retval);
+    already_AddRefed<WebGLQuery> CreateQueryEXT() const;
+    void DeleteQueryEXT(WebGLQuery* query) const;
+    bool IsQueryEXT(const WebGLQuery* query) const;
+    void BeginQueryEXT(GLenum target, WebGLQuery* query) const;
+    void EndQueryEXT(GLenum target) const;
+    void QueryCounterEXT(WebGLQuery* query, GLenum target) const;
+    void GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
+                     JS::MutableHandleValue retval) const;
+    void GetQueryObjectEXT(JSContext* cx, const WebGLQuery* query,
+                           GLenum pname, JS::MutableHandleValue retval) const;
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
-
-private:
-    virtual void OnMarkLost() override;
-
-    /**
-     * An active TIME_ELAPSED query participating in a begin/end block.
-     */
-    WebGLRefPtr<WebGLTimerQuery> mActiveQuery;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_EXTENSIONS_H_
--- a/dom/canvas/WebGLObjectModel.cpp
+++ b/dom/canvas/WebGLObjectModel.cpp
@@ -11,15 +11,15 @@ namespace mozilla {
 
 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
     : mContext(webgl)
     , mContextGeneration(webgl->Generation())
 {
 }
 
 bool
-WebGLContextBoundObject::IsCompatibleWithContext(WebGLContext* other)
+WebGLContextBoundObject::IsCompatibleWithContext(const WebGLContext* other) const
 {
     return (mContext == other &&
             mContextGeneration == other->Generation());
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLObjectModel.h
+++ b/dom/canvas/WebGLObjectModel.h
@@ -262,17 +262,17 @@ protected:
 // This class is a mixin for objects that are tied to a specific
 // context (which is to say, all of them).  They provide initialization
 // as well as comparison with the current context.
 class WebGLContextBoundObject
 {
 public:
     explicit WebGLContextBoundObject(WebGLContext* webgl);
 
-    bool IsCompatibleWithContext(WebGLContext* other);
+    bool IsCompatibleWithContext(const WebGLContext* other) const;
 
     WebGLContext* const mContext;
 protected:
     const uint32_t mContextGeneration;
 };
 
 // this class is a mixin for GL objects that have dimensions
 // that we need to track.
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -7,51 +7,267 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "nsContentUtils.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
+class AvailableRunnable final : public Runnable
 {
-    return dom::WebGLQueryBinding::Wrap(cx, this, givenProto);
+    const RefPtr<WebGLQuery> mQuery;
+
+public:
+    explicit AvailableRunnable(WebGLQuery* query)
+        : mQuery(query)
+    { }
+
+    NS_IMETHOD Run() override {
+        mQuery->mCanBeAvailable = true;
+        return NS_OK;
+    }
+};
+
+////
+
+static GLuint
+GenQuery(gl::GLContext* gl)
+{
+    gl->MakeCurrent();
+
+    GLuint ret = 0;
+    gl->fGenQueries(1, &ret);
+    return ret;
 }
 
 WebGLQuery::WebGLQuery(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
+    , mGLName(GenQuery(mContext->gl))
+    , mTarget(0)
+    , mActiveSlot(nullptr)
     , mCanBeAvailable(false)
-    , mGLName(0)
-    , mType(0)
 {
     mContext->mQueries.insertBack(this);
-
-    mContext->MakeContextCurrent();
-    mContext->gl->fGenQueries(1, &mGLName);
 }
 
 void
 WebGLQuery::Delete()
 {
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteQueries(1, &mGLName);
     LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries);
 }
 
+////
+
+static GLenum
+TargetForDriver(const gl::GLContext* gl, GLenum target)
+{
+    switch (target) {
+    case LOCAL_GL_ANY_SAMPLES_PASSED:
+    case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+        break;
+
+    default:
+        return target;
+    }
+
+    if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
+        return target;
+
+    if (gl->IsSupported(gl::GLFeature::occlusion_query2))
+        return LOCAL_GL_ANY_SAMPLES_PASSED;
+
+    return LOCAL_GL_SAMPLES_PASSED;
+}
+
+void
+WebGLQuery::BeginQuery(GLenum target, WebGLRefPtr<WebGLQuery>& slot)
+{
+    const char funcName[] = "beginQuery";
+
+    if (mTarget && target != mTarget) {
+        mContext->ErrorInvalidOperation("%s: Queries cannot change targets.", funcName);
+        return;
+    }
+
+    ////
+
+    mTarget = target;
+    mActiveSlot = &slot;
+    *mActiveSlot = this;
+
+    ////
+
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+
+    const auto driverTarget = TargetForDriver(gl, mTarget);
+    gl->fBeginQuery(driverTarget, mGLName);
+}
+
+void
+WebGLQuery::EndQuery()
+{
+    *mActiveSlot = nullptr;
+    mActiveSlot = nullptr;
+    mCanBeAvailable = false;
+
+    ////
+
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+
+    const auto driverTarget = TargetForDriver(gl, mTarget);
+    gl->fEndQuery(driverTarget);
+
+    ////
+
+    NS_DispatchToCurrentThread(new AvailableRunnable(this));
+}
+
+void
+WebGLQuery::GetQueryParameter(GLenum pname, JS::MutableHandleValue retval) const
+{
+    const char funcName[] = "getQueryParameter";
+
+    switch (pname) {
+    case LOCAL_GL_QUERY_RESULT_AVAILABLE:
+    case LOCAL_GL_QUERY_RESULT:
+        break;
+
+    default:
+        mContext->ErrorInvalidEnum("%s: Invalid pname: %s", funcName,
+                                   mContext->EnumName(pname));
+        return;
+    }
+
+    if (!mTarget) {
+        mContext->ErrorInvalidOperation("%s: Query has never been active.", funcName);
+        return;
+    }
+
+    if (mActiveSlot)
+        return mContext->ErrorInvalidOperation("%s: Query is still active.", funcName);
+
+    // End of validation
+    ////
+
+    // We must usually wait for an event loop before the query can be available.
+    const bool canBeAvailable = (mCanBeAvailable || gfxPrefs::WebGLImmediateQueries());
+    if (!canBeAvailable) {
+        if (pname == LOCAL_GL_QUERY_RESULT_AVAILABLE) {
+            retval.set(JS::BooleanValue(false));
+        }
+        return;
+    }
+
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+
+    uint64_t val = 0;
+    switch (pname) {
+    case LOCAL_GL_QUERY_RESULT_AVAILABLE:
+        gl->fGetQueryObjectuiv(mGLName, pname, (GLuint*)&val);
+        retval.set(JS::BooleanValue(bool(val)));
+        return;
+
+    case LOCAL_GL_QUERY_RESULT:
+        switch (mTarget) {
+        case LOCAL_GL_TIME_ELAPSED_EXT:
+        case LOCAL_GL_TIMESTAMP_EXT:
+            if (mContext->Has64BitTimestamps()) {
+                gl->fGetQueryObjectui64v(mGLName, pname, &val);
+                break;
+            }
+            MOZ_FALLTHROUGH;
+
+        default:
+            gl->fGetQueryObjectuiv(mGLName, LOCAL_GL_QUERY_RESULT, (GLuint*)&val);
+            break;
+        }
+
+        switch (mTarget) {
+        case LOCAL_GL_ANY_SAMPLES_PASSED:
+        case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+            retval.set(JS::BooleanValue(bool(val)));
+            break;
+
+        case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+        case LOCAL_GL_TIME_ELAPSED_EXT:
+        case LOCAL_GL_TIMESTAMP_EXT:
+            retval.set(JS::NumberValue(val));
+            break;
+
+        default:
+            MOZ_CRASH("Bad `mTarget`.");
+        }
+        return;
+
+    default:
+        MOZ_CRASH("Bad `pname`.");
+    }
+}
+
 bool
-WebGLQuery::IsActive() const
+WebGLQuery::IsQuery() const
 {
-    if (!HasEverBeenActive())
+    if (IsDeleted())
         return false;
 
-    WebGLRefPtr<WebGLQuery>& targetSlot = mContext->GetQuerySlotByTarget(mType);
+    if (!mTarget)
+        return false;
+
+    return true;
+}
+
+void
+WebGLQuery::DeleteQuery()
+{
+    if (IsDeleted())
+        return;
+
+    if (mActiveSlot) {
+        EndQuery();
+    }
+
+    RequestDelete();
+}
 
-    return targetSlot.get() == this;
+void
+WebGLQuery::QueryCounter(const char* funcName, GLenum target)
+{
+    if (target != LOCAL_GL_TIMESTAMP_EXT) {
+        mContext->ErrorInvalidEnum("%s: `target` must be TIMESTAMP_EXT.", funcName,
+                                   target);
+        return;
+    }
+
+    if (mTarget && target != mTarget) {
+        mContext->ErrorInvalidOperation("%s: Queries cannot change targets.", funcName);
+        return;
+    }
+
+    mTarget = target;
+    mCanBeAvailable = false;
+
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+    gl->fQueryCounter(mGLName, mTarget);
+
+    NS_DispatchToCurrentThread(new AvailableRunnable(this));
+}
+
+////
+
+JSObject*
+WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
+{
+    return dom::WebGLQueryBinding::Wrap(cx, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQuery)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLQuery, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLQuery, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -15,61 +15,56 @@
 namespace mozilla {
 
 class WebGLQuery final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLQuery>
     , public LinkedListElement<WebGLQuery>
     , public WebGLContextBoundObject
 {
-public:
-    explicit WebGLQuery(WebGLContext* webgl);
-
-    class AvailableRunnable final : public Runnable
-    {
-    public:
-        explicit AvailableRunnable(WebGLQuery* query) : mQuery(query) { }
+    friend class AvailableRunnable;
+    friend class WebGLRefCountedObject<WebGLQuery>;
 
-        NS_IMETHOD Run() override {
-            mQuery->mCanBeAvailable = true;
-            return NS_OK;
-        }
-    private:
-        const RefPtr<WebGLQuery> mQuery;
-    };
+public:
+    const GLuint mGLName;
+private:
+    GLenum mTarget;
+    WebGLRefPtr<WebGLQuery>* mActiveSlot;
 
-    bool IsActive() const;
-
-    bool HasEverBeenActive() const {
-        return mType != 0;
-    }
+    bool mCanBeAvailable; // Track whether the event loop has spun
 
-    // WebGLRefCountedObject
-    void Delete();
+    ////
+public:
+    GLenum Target() const { return mTarget; }
+    bool IsActive() const { return bool(mActiveSlot); }
 
-    // nsWrapperCache
-    WebGLContext* GetParentObject() const {
-        return mContext;
-    }
-
-    // NS
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
+    ////
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLQuery)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLQuery)
 
-    // Track whether the event loop has spun
-    bool mCanBeAvailable;
+    explicit WebGLQuery(WebGLContext* webgl);
 
 private:
     ~WebGLQuery() {
         DeleteOnce();
     };
 
-    GLuint mGLName;
-    GLenum mType;
+    // WebGLRefCountedObject
+    void Delete();
+
+public:
+    WebGLContext* GetParentObject() const { return mContext; }
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
-    friend class WebGL2Context;
+    ////
+
+    void BeginQuery(GLenum target, WebGLRefPtr<WebGLQuery>& slot);
+    void DeleteQuery();
+    void EndQuery();
+    void GetQueryParameter(GLenum pname, JS::MutableHandleValue retval) const;
+    bool IsQuery() const;
+    void QueryCounter(const char* funcName, GLenum target);
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_QUERY_H_
deleted file mode 100644
--- a/dom/canvas/WebGLTimerQuery.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "WebGLTimerQuery.h"
-
-#include "GLContext.h"
-#include "mozilla/dom/WebGLRenderingContextBinding.h"
-#include "nsContentUtils.h"
-#include "WebGLContext.h"
-#include "nsThreadUtils.h"
-
-namespace mozilla {
-
-JSObject*
-WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
-{
-  return dom::WebGLTimerQueryEXTBinding::Wrap(cx, this, givenProto);
-}
-
-WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint name)
-  : WebGLContextBoundObject(webgl)
-  , mGLName(name)
-  , mTarget(LOCAL_GL_NONE)
-  , mCanBeAvailable(false)
-{
-  mContext->mTimerQueries.insertBack(this);
-}
-
-WebGLTimerQuery::~WebGLTimerQuery()
-{
-  DeleteOnce();
-}
-
-WebGLTimerQuery*
-WebGLTimerQuery::Create(WebGLContext* webgl)
-{
-  GLuint name = 0;
-  webgl->MakeContextCurrent();
-  webgl->gl->fGenQueries(1, &name);
-  return new WebGLTimerQuery(webgl, name);
-}
-
-void
-WebGLTimerQuery::Delete()
-{
-  gl::GLContext* gl = mContext->GL();
-
-  gl->MakeCurrent();
-  gl->fDeleteQueries(1, &mGLName);
-
-  LinkedListElement<WebGLTimerQuery>::removeFrom(mContext->mTimerQueries);
-}
-
-WebGLContext*
-WebGLTimerQuery::GetParentObject() const
-{
-  return mContext;
-}
-
-void
-WebGLTimerQuery::QueueAvailablity()
-{
-  RefPtr<WebGLTimerQuery> self = this;
-  NS_DispatchToCurrentThread(NS_NewRunnableFunction([self] { self->mCanBeAvailable = true; }));
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/canvas/WebGLTimerQuery.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef WEBGL_TIMER_QUERY_H_
-#define WEBGL_TIMER_QUERY_H_
-
-#include "GLConsts.h"
-#include "nsWrapperCache.h"
-#include "WebGLObjectModel.h"
-
-namespace mozilla {
-
-class WebGLTimerQuery final
-  : public nsWrapperCache
-  , public WebGLRefCountedObject<WebGLTimerQuery>
-  , public LinkedListElement<WebGLTimerQuery>
-  , public WebGLContextBoundObject
-{
-public:
-  static WebGLTimerQuery* Create(WebGLContext* webgl);
-
-  void Delete();
-
-  bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
-  bool CanBeAvailable() const { return mCanBeAvailable; }
-  void QueueAvailablity();
-  GLenum Target() const { return mTarget; }
-
-  WebGLContext* GetParentObject() const;
-
-  // NS
-  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
-
-  const GLenum mGLName;
-
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
-
-private:
-  WebGLTimerQuery(WebGLContext* webgl, GLuint name);
-  ~WebGLTimerQuery();
-
-  GLenum mTarget;
-  bool mCanBeAvailable;
-
-  friend class WebGLExtensionDisjointTimerQuery;
-};
-
-} // namespace mozilla
-
-#endif // WEBGL_TIMER_QUERY_H_
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -153,17 +153,17 @@ enum class WebGLExtensionID : uint8_t {
     OES_standard_derivatives,
     OES_texture_float,
     OES_texture_float_linear,
     OES_texture_half_float,
     OES_texture_half_float_linear,
     OES_vertex_array_object,
     WEBGL_color_buffer_float,
     WEBGL_compressed_texture_atc,
-    WEBGL_compressed_texture_es3_0,
+    WEBGL_compressed_texture_etc,
     WEBGL_compressed_texture_etc1,
     WEBGL_compressed_texture_pvrtc,
     WEBGL_compressed_texture_s3tc,
     WEBGL_debug_renderer_info,
     WEBGL_debug_shaders,
     WEBGL_depth_texture,
     WEBGL_draw_buffers,
     WEBGL_lose_context,
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -143,17 +143,16 @@ UNIFIED_SOURCES += [
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
     'WebGLShaderValidator.cpp',
     'WebGLSync.cpp',
     'WebGLTexelConversions.cpp',
     'WebGLTexture.cpp',
     'WebGLTextureUpload.cpp',
-    'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
     'WebGLVertexArrayObject.cpp',
     'WebGLVertexAttribData.cpp',
--- a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html
@@ -5,15 +5,13 @@
     <script src='/tests/SimpleTest/SimpleTest.js'></script>
     <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
     <script src='ensure-ext.js'></script>
   </head>
   <body>
     <script>
 
 'use strict';
-Lastly_WithDraftExtsEnabled(function() {
-    EnsureExtFor('webgl', 'EXT_disjoint_timer_query');
-});
+EnsureExt('EXT_disjoint_timer_query');
 
     </script>
   </body>
 </html>
--- a/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html
@@ -14,16 +14,17 @@
 var ENSURE = 'ENSURE'; // Works on all test machines.
 var FORBID = 'FORBID'; // Should not work on any test machine.
 var MACHINE_SPECIFIC = 'MACHINE_SPECIFIC';
 
 var defaultExts = [
     // Ratified
     ['ANGLE_instanced_arrays'        , [MACHINE_SPECIFIC, FORBID          ]],
     ['EXT_blend_minmax'              , [MACHINE_SPECIFIC, FORBID          ]],
+    ['EXT_disjoint_timer_query'      , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
     ['EXT_frag_depth'                , [MACHINE_SPECIFIC, FORBID          ]],
     ['EXT_shader_texture_lod'        , [MACHINE_SPECIFIC, FORBID          ]],
     ['EXT_texture_filter_anisotropic', [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
     ['OES_element_index_uint'        , [ENSURE          , FORBID          ]],
     ['OES_standard_derivatives'      , [MACHINE_SPECIFIC, FORBID          ]],
     ['OES_texture_float'             , [ENSURE          , FORBID          ]],
     ['OES_texture_float_linear'      , [ENSURE          , ENSURE          ]],
     ['OES_texture_half_float'        , [ENSURE          , FORBID          ]],
@@ -42,17 +43,16 @@ var defaultExts = [
     ['EXT_sRGB'                      , [MACHINE_SPECIFIC, FORBID          ]],
     ['WEBGL_color_buffer_float'      , [MACHINE_SPECIFIC, FORBID          ]],
     ['WEBGL_compressed_texture_atc'  , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
     ['WEBGL_compressed_texture_etc1' , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
     ['WEBGL_compressed_texture_pvrtc', [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
 ];
 
 var draftExts = [
-    ['EXT_disjoint_timer_query'    , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]], // TODO: Actually Community Approved now.
     ['WEBGL_compressed_texture_es3', [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
 ];
 
 var nonImplementedExts = [
     'OES_fbo_render_mipmap',
     'WEBGL_compressed_texture_astc',
     'WEBGL_security_sensitive_resources',
     'WEBGL_shared_resources',
--- a/dom/canvas/test/webgl-mochitest/mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest/mochitest.ini
@@ -77,17 +77,16 @@ skip-if = android_version == '18' #Andro
 [test_uninit_data.html]
 [test_webgl_available.html]
 #[test_webgl_color_buffer_float.html]
 # We haven't cleaned up the Try results yet, but let's get this on the books first.
 [test_webgl_conformance.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_compressed_texture_es3.html]
 [test_webgl_disjoint_timer_query.html]
-fail-if = (os == 'win' && (os_version == '6.1' || os_version == '6.2' || os_version == '10.0'))
 [test_webgl_force_enable.html]
 [test_webgl_request_context.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_request_mismatch.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl2_not_exposed.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl2_invalidate_framebuffer.html]
--- a/dom/canvas/test/webgl-mochitest/test_webgl_disjoint_timer_query.html
+++ b/dom/canvas/test/webgl-mochitest/test_webgl_disjoint_timer_query.html
@@ -26,20 +26,21 @@ function doTest() {
     return;
   }
 
   ok(!ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT),
      "No query is active initially.");
 
   var elapsedQuery = ext.createQueryEXT();
   ok(elapsedQuery, "Query creation works.");
-  ok(ext.isQueryEXT(elapsedQuery), "New query is valid after creation.");
+  ok(!ext.isQueryEXT(elapsedQuery), "isQuery fails after creation but before bind.");
 
   ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, elapsedQuery);
-  is(ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT), elapsedQuery,
+  ok(ext.isQueryEXT(elapsedQuery), "isQuery fails after bind.");
+  ok(ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT) == elapsedQuery,
      "Query is active after beginQueryEXT.");
   ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
   gl.flush();
 
   ok(!ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT),
      "Query is inactive after endQueryEXT.");
 
   defer(function() {
@@ -60,23 +61,22 @@ function doTest() {
         defer(function() {
           ok(ext.getQueryObjectEXT(timestampQuery, ext.QUERY_RESULT_AVAILABLE_EXT),
           "Timestamp query should be available immediately after flush and event loop tick.");
 
           ok(ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30,
           "Time elapsed must be at least 30 bits to hold at least 1 second of timing.");
 
           SimpleTest.finish();
-
-          });
+        });
     } else {
       SimpleTest.finish();
     }
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({"set": [['webgl.enable-draft-extensions', true]]}, doTest);
+doTest();
 
 </script>
 </body>
 </html>
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3999,17 +3999,17 @@ public:
           mTargets.AppendObject(current);
         }
         // mouseenter/leave is fired only on elements.
         current = current->GetParent();
       }
     }
   }
 
-  ~EnterLeaveDispatcher()
+  void Dispatch()
   {
     if (mEventMessage == eMouseEnter || mEventMessage == ePointerEnter) {
       for (int32_t i = mTargets.Count() - 1; i >= 0; --i) {
         mESM->DispatchMouseOrPointerEvent(mMouseEvent, mEventMessage,
                                           mTargets[i], mRelatedTarget);
       }
     } else {
       for (int32_t i = 0; i < mTargets.Count(); ++i) {
@@ -4084,16 +4084,17 @@ EventStateManager::NotifyMouseOut(Widget
 
   EnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement,
                                        movingInto, aMouseEvent,
                                        isPointer ? ePointerLeave : eMouseLeave);
 
   // Fire mouseout
   DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? ePointerOut : eMouseOut,
                               wrapper->mLastOverElement, aMovingInto);
+  leaveDispatcher.Dispatch();
 
   wrapper->mLastOverFrame = nullptr;
   wrapper->mLastOverElement = nullptr;
 
   // Turn recursion protection back off
   wrapper->mFirstOutEventElement = nullptr;
 }
 
@@ -4157,16 +4158,17 @@ EventStateManager::NotifyMouseOver(Widge
   }
 
   if (dispatch) {
     // Fire mouseover
     wrapper->mLastOverFrame = 
       DispatchMouseOrPointerEvent(aMouseEvent,
                                   isPointer ? ePointerOver : eMouseOver,
                                   aContent, lastOverElement);
+    enterDispatcher->Dispatch();
     wrapper->mLastOverElement = aContent;
   } else {
     wrapper->mLastOverFrame = nullptr;
     wrapper->mLastOverElement = nullptr;
   }
 
   // Turn recursion protection back off
   wrapper->mFirstOverEventElement = nullptr;
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -30,16 +30,17 @@
 #include "nsHttpChannel.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Unused.h"
 
 #include "Fetch.h"
+#include "FetchUtil.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
                   nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
@@ -255,72 +256,40 @@ FetchDriver::HttpFetch()
     nsAutoCString method;
     mRequest->GetMethod(method);
     rv = httpChan->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the same headers.
     SetRequestHeaders(httpChan);
 
-    // Step 2. Set the referrer.
-    nsAutoString referrer;
-    mRequest->GetReferrer(referrer);
+    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
+    // Step 6 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request's referrer policy is the empty string and request's client is
+    // non-null, then set request's referrer policy to request's client's
+    // associated referrer policy.
+    // Basically, "client" is not in our implementation, we use
+    // EnvironmentReferrerPolicy of the worker or document context
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+    }
+    // Step 7 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request’s referrer policy is the empty string,
+    // then set request’s referrer policy to "no-referrer-when-downgrade".
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net::RP_No_Referrer_When_Downgrade);
+    }
 
-    // The Referrer Policy in Request can be used to override a referrer policy
-    // associated with an environment settings object.
-    // If there's no Referrer Policy in the request, it should be inherited
-    // from environment.
-    ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
-    switch (referrerPolicy) {
-    case ReferrerPolicy::_empty:
-      break;
-    case ReferrerPolicy::No_referrer:
-      net_referrerPolicy = net::RP_No_Referrer;
-      break;
-    case ReferrerPolicy::No_referrer_when_downgrade:
-      net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
-      break;
-    case ReferrerPolicy::Origin:
-      net_referrerPolicy = net::RP_Origin;
-      break;
-    case ReferrerPolicy::Origin_when_cross_origin:
-      net_referrerPolicy = net::RP_Origin_When_Crossorigin;
-      break;
-    case ReferrerPolicy::Unsafe_url:
-      net_referrerPolicy = net::RP_Unsafe_URL;
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
-      break;
-    }
-    if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
-      rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
-                                                         mDocument,
-                                                         httpChan,
-                                                         net_referrerPolicy);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else if (referrer.IsEmpty()) {
-      rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else {
-      // From "Determine request's Referrer" step 3
-      // "If request's referrer is a URL, let referrerSource be request's
-      // referrer."
-      nsCOMPtr<nsIURI> referrerURI;
-      rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv =
-        httpChan->SetReferrerWithPolicy(referrerURI,
-                                        referrerPolicy == ReferrerPolicy::_empty ?
-                                          mRequest->GetEnvironmentReferrerPolicy() :
-                                          net_referrerPolicy);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                       mDocument,
+                                       httpChan,
+                                       mRequest);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     // Bug 1120722 - Authorization will be handled later.
     // Auth may require prompting, we don't support it yet.
     // The next patch in this same bug prevents this from aborting the request.
     // Credentials checks for CORS are handled by nsCORSListenerProxy,
 
     nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
 
@@ -818,39 +787,25 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   mRequest->AddURL(spec, fragment);
   NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
   // updates request’s associated referrer policy according to the
   // Referrer-Policy header (if any).
   if (!tRPHeaderValue.IsEmpty()) {
     net::ReferrerPolicy net_referrerPolicy =
       nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
     if (net_referrerPolicy != net::RP_Unset) {
-      ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-      switch (net_referrerPolicy) {
-        case net::RP_No_Referrer:
-          referrerPolicy = ReferrerPolicy::No_referrer;
-          break;
-        case net::RP_No_Referrer_When_Downgrade:
-          referrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
-          break;
-        case net::RP_Origin:
-          referrerPolicy = ReferrerPolicy::Origin;
-          break;
-        case net::RP_Origin_When_Crossorigin:
-          referrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
-          break;
-        case net::RP_Unsafe_URL:
-          referrerPolicy = ReferrerPolicy::Unsafe_url;
-          break;
-        default:
-          MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
-          break;
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+      // Should update channel's referrer policy
+      if (httpChannel) {
+        rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                           mDocument,
+                                           httpChannel,
+                                           mRequest);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      mRequest->SetReferrerPolicy(referrerPolicy);
     }
   }
 
   aCallback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -1,15 +1,17 @@
 #include "FetchUtil.h"
 
 #include "nsError.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsString.h"
+#include "nsIDocument.h"
 
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/InternalRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 nsresult
 FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
 {
@@ -105,10 +107,64 @@ FetchUtil::ExtractHeader(nsACString::con
   if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
     return false;
   }
   aHeaderValue.CompressWhitespace();
 
   return PushOverLine(aStart, aEnd);
 }
 
+// static
+nsresult
+FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal,
+                              nsIDocument* aDoc,
+                              nsIHttpChannel* aChannel,
+                              InternalRequest* aRequest) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoString referrer;
+  aRequest->GetReferrer(referrer);
+  net::ReferrerPolicy policy = aRequest->GetReferrerPolicy();
+
+  nsresult rv = NS_OK;
+  if (referrer.IsEmpty()) {
+    // This is the case request’s referrer is "no-referrer"
+    rv = aChannel->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
+    rv = nsContentUtils::SetFetchReferrerURIWithPolicy(aPrincipal,
+                                                       aDoc,
+                                                       aChannel,
+                                                       policy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // From "Determine request's Referrer" step 3
+    // "If request's referrer is a URL, let referrerSource be request's
+    // referrer."
+    nsCOMPtr<nsIURI> referrerURI;
+    rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aChannel->SetReferrerWithPolicy(referrerURI, policy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIURI> referrerURI;
+  aChannel->GetReferrer(getter_AddRefs(referrerURI));
+
+  // Step 8 https://fetch.spec.whatwg.org/#main-fetch
+  // If request’s referrer is not "no-referrer", set request’s referrer to
+  // the result of invoking determine request’s referrer.
+  if (referrerURI) {
+    nsAutoCString spec;
+    rv = referrerURI->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aRequest->SetReferrer(NS_ConvertUTF8toUTF16(spec));
+  } else {
+    aRequest->SetReferrer(EmptyString());
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/FetchUtil.h
+++ b/dom/fetch/FetchUtil.h
@@ -3,19 +3,25 @@
 
 #include "nsString.h"
 #include "nsError.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 
+class nsIPrincipal;
+class nsIDocument;
+class nsIHttpChannel;
+
 namespace mozilla {
 namespace dom {
 
+class InternalRequest;
+
 class FetchUtil final
 {
 private:
   FetchUtil() = delete;
 
 public:
   /**
   * Sets outMethod to a valid HTTP request method string based on an input method.
@@ -30,13 +36,20 @@ public:
    * Extracts an HTTP header from a substring range.
    */
   static bool
   ExtractHeader(nsACString::const_iterator& aStart,
                 nsACString::const_iterator& aEnd,
                 nsCString& aHeaderName,
                 nsCString& aHeaderValue,
                 bool* aWasEmptyHeader);
+
+  static nsresult
+  SetRequestReferrer(nsIPrincipal* aPrincipal,
+                     nsIDocument* aDoc,
+                     nsIHttpChannel* aChannel,
+                     InternalRequest* aRequest);
+
 };
 
 } // namespace dom
 } // namespace mozilla
 #endif
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -228,16 +228,82 @@ public:
   }
 
   void
   SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
   {
     mReferrerPolicy = aReferrerPolicy;
   }
 
+  void
+  SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
+  {
+    switch (aReferrerPolicy) {
+      case net::RP_Unset:
+        mReferrerPolicy = ReferrerPolicy::_empty;
+        break;
+      case net::RP_No_Referrer:
+        mReferrerPolicy = ReferrerPolicy::No_referrer;
+        break;
+      case net::RP_No_Referrer_When_Downgrade:
+        mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+        break;
+      case net::RP_Origin:
+        mReferrerPolicy = ReferrerPolicy::Origin;
+        break;
+      case net::RP_Origin_When_Crossorigin:
+        mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+        break;
+      case net::RP_Unsafe_URL:
+        mReferrerPolicy = ReferrerPolicy::Unsafe_url;
+        break;
+      case net::RP_Same_Origin:
+        mReferrerPolicy = ReferrerPolicy::Same_origin;
+        break;
+      case net::RP_Strict_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin;
+        break;
+      case net::RP_Strict_Origin_When_Cross_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
+        break;
+    }
+  }
+
+  net::ReferrerPolicy
+  GetReferrerPolicy()
+  {
+    switch (mReferrerPolicy) {
+      case ReferrerPolicy::_empty:
+        return net::RP_Unset;
+      case ReferrerPolicy::No_referrer:
+        return net::RP_No_Referrer;
+      case ReferrerPolicy::No_referrer_when_downgrade:
+        return net::RP_No_Referrer_When_Downgrade;
+      case ReferrerPolicy::Origin:
+        return net::RP_Origin;
+      case ReferrerPolicy::Origin_when_cross_origin:
+        return net::RP_Origin_When_Crossorigin;
+      case ReferrerPolicy::Unsafe_url:
+        return net::RP_Unsafe_URL;
+      case ReferrerPolicy::Strict_origin:
+        return net::RP_Strict_Origin;
+      case ReferrerPolicy::Same_origin:
+        return net::RP_Same_Origin;
+      case ReferrerPolicy::Strict_origin_when_cross_origin:
+        return net::RP_Strict_Origin_When_Cross_Origin;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
+        break;
+    }
+    return net::RP_Unset;
+  }
+
   net::ReferrerPolicy
   GetEnvironmentReferrerPolicy() const
   {
     return mEnvironmentReferrerPolicy;
   }
 
   void
   SetEnvironmentReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -619,28 +619,31 @@ HTMLCanvasElement::ParseAttribute(int32_
     return aResult.ParseNonNegativeIntValue(aValue);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 
-// HTMLCanvasElement::toDataURL
 
-NS_IMETHODIMP
-HTMLCanvasElement::ToDataURL(const nsAString& aType, JS::Handle<JS::Value> aParams,
-                             JSContext* aCx, nsAString& aDataURL)
+void
+HTMLCanvasElement::ToDataURL(JSContext* aCx, const nsAString& aType,
+                             JS::Handle<JS::Value> aParams,
+                             nsAString& aDataURL,
+                             CallerType aCallerType,
+                             ErrorResult& aRv)
 {
   // do a trust check if this is a write-only canvas
-  if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  if (mWriteOnly && aCallerType != CallerType::System) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
-  return ToDataURLImpl(aCx, aType, aParams, aDataURL);
+  aRv = ToDataURLImpl(aCx, aType, aParams, aDataURL);
 }
 
 void
 HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback)
 {
   mPrintCallback = aCallback;
 }
 
@@ -804,20 +807,21 @@ HTMLCanvasElement::ToDataURLImpl(JSConte
   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
 }
 
 void
 HTMLCanvasElement::ToBlob(JSContext* aCx,
                           BlobCallback& aCallback,
                           const nsAString& aType,
                           JS::Handle<JS::Value> aParams,
+                          CallerType aCallerType,
                           ErrorResult& aRv)
 {
   // do a trust check if this is a write-only canvas
-  if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
+  if (mWriteOnly && aCallerType != CallerType::System) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
 
   CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
@@ -858,50 +862,40 @@ HTMLCanvasElement::TransferControlToOffs
   }
 
   return mOffscreenCanvas;
 }
 
 already_AddRefed<File>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
+                                CallerType aCallerType,
                                 ErrorResult& aRv)
 {
-  nsCOMPtr<nsISupports> file;
-  aRv = MozGetAsFile(aName, aType, getter_AddRefs(file));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(file);
-  RefPtr<Blob> domBlob = static_cast<Blob*>(blob.get());
-  MOZ_ASSERT(domBlob->IsFile());
-  return domBlob->ToFile();
-}
-
-NS_IMETHODIMP
-HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
-                                const nsAString& aType,
-                                nsISupports** aResult)
-{
   OwnerDoc()->WarnOnceAbout(nsIDocument::eMozGetAsFile);
 
   // do a trust check if this is a write-only canvas
-  if ((mWriteOnly) &&
-      !nsContentUtils::IsCallerChrome()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  if (mWriteOnly && aCallerType != CallerType::System) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
   }
 
-  return MozGetAsBlobImpl(aName, aType, aResult);
+
+  RefPtr<File> file;
+  aRv = MozGetAsFileImpl(aName, aType, getter_AddRefs(file));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+  return file.forget();
 }
 
 nsresult
-HTMLCanvasElement::MozGetAsBlobImpl(const nsAString& aName,
+HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                     const nsAString& aType,
-                                    nsISupports** aResult)
+                                    File** aResult)
 {
   nsCOMPtr<nsIInputStream> stream;
   nsAutoString type(aType);
   nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t imgSize;
   rv = stream->Available(&imgSize);
@@ -915,17 +909,17 @@ HTMLCanvasElement::MozGetAsBlobImpl(cons
   JSContext* cx = nsContentUtils::GetCurrentJSContext();
   if (cx) {
     JS_updateMallocCounter(cx, imgSize);
   }
 
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(OwnerDoc()->GetScopeObject());
 
   // The File takes ownership of the buffer
-  nsCOMPtr<nsIDOMBlob> file =
+  RefPtr<File> file =
     File::CreateMemoryFile(win, imgData, (uint32_t)imgSize, aName, type,
                            PR_Now());
 
   file.forget(aResult);
   return NS_OK;
 }
 
 nsresult
@@ -947,56 +941,60 @@ HTMLCanvasElement::GetContext(JSContext*
     return nullptr;
   }
 
   return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
     aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue,
     aRv);
 }
 
-NS_IMETHODIMP
+already_AddRefed<nsISupports>
 HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
-                                    nsISupports **aContext)
+                                    ErrorResult& aRv)
 {
-  if(!nsContentUtils::IsCallerChrome()) {
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
+  // Note that we're a [ChromeOnly] method, so from JS we can only be called by
+  // system code.
 
   // We only support 2d shmem contexts for now.
-  if (!aContextId.EqualsLiteral("2d"))
-    return NS_ERROR_INVALID_ARG;
+  if (!aContextId.EqualsLiteral("2d")) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return nullptr;
+  }
 
   CanvasContextType contextType = CanvasContextType::Canvas2D;
 
   if (!mCurrentContext) {
     // This canvas doesn't have a context yet.
 
     RefPtr<nsICanvasRenderingContextInternal> context;
     context = CreateContext(contextType);
     if (!context) {
-      *aContext = nullptr;
-      return NS_OK;
+      return nullptr;
     }
 
     mCurrentContext = context;
     mCurrentContext->SetIsIPC(true);
     mCurrentContextType = contextType;
 
     ErrorResult dummy;
     nsresult rv = UpdateContext(nullptr, JS::NullHandleValue, dummy);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.Throw(rv);
+      return nullptr;
+    }
   } else {
     // We already have a context of some type.
-    if (contextType != mCurrentContextType)
-      return NS_ERROR_INVALID_ARG;
+    if (contextType != mCurrentContextType) {
+      aRv.Throw(NS_ERROR_INVALID_ARG);
+      return nullptr;
+    }
   }
 
-  NS_ADDREF (*aContext = mCurrentContext);
-  return NS_OK;
+  nsCOMPtr<nsISupports> context(mCurrentContext);
+  return context.forget();
 }
 
 
 nsIntSize
 HTMLCanvasElement::GetSize()
 {
   return GetWidthHeight();
 }
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -11,16 +11,17 @@
 #include "nsIDOMEventListener.h"
 #include "nsIDOMHTMLCanvasElement.h"
 #include "nsIObserver.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CanvasRenderingContextHelper.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/layers/LayersTypes.h"
 
 class nsICanvasRenderingContextInternal;
 class nsITimerCallback;
 
 namespace mozilla {
@@ -171,25 +172,24 @@ public:
 
   virtual already_AddRefed<nsISupports>
   GetContext(JSContext* aCx, const nsAString& aContextId,
              JS::Handle<JS::Value> aContextOptions,
              ErrorResult& aRv) override;
 
   void ToDataURL(JSContext* aCx, const nsAString& aType,
                  JS::Handle<JS::Value> aParams,
-                 nsAString& aDataURL, ErrorResult& aRv)
-  {
-    aRv = ToDataURL(aType, aParams, aCx, aDataURL);
-  }
+                 nsAString& aDataURL, CallerType aCallerType,
+                 ErrorResult& aRv);
 
   void ToBlob(JSContext* aCx,
               BlobCallback& aCallback,
               const nsAString& aType,
               JS::Handle<JS::Value> aParams,
+              CallerType aCallerType,
               ErrorResult& aRv);
 
   OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
 
   bool MozOpaque() const
   {
     return GetBoolAttr(nsGkAtoms::moz_opaque);
   }
@@ -199,24 +199,20 @@ public:
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
 
     SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
   }
   already_AddRefed<File> MozGetAsFile(const nsAString& aName,
                                       const nsAString& aType,
+                                      CallerType aCallerType,
                                       ErrorResult& aRv);
   already_AddRefed<nsISupports> MozGetIPCContext(const nsAString& aContextId,
-                                                 ErrorResult& aRv)
-  {
-    nsCOMPtr<nsISupports> context;
-    aRv = MozGetIPCContext(aContextId, getter_AddRefs(context));
-    return context.forget();
-  }
+                                                 ErrorResult& aRv);
   PrintCallback* GetMozPrintCallback() const;
   void SetMozPrintCallback(PrintCallback* aCallback);
 
   already_AddRefed<CanvasCaptureMediaStream>
   CaptureStream(const Optional<double>& aFrameRate, ErrorResult& aRv);
 
   /**
    * Get the size in pixels of this canvas element
@@ -366,19 +362,19 @@ protected:
 
   nsresult ExtractData(nsAString& aType,
                        const nsAString& aOptions,
                        nsIInputStream** aStream);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
-  nsresult MozGetAsBlobImpl(const nsAString& aName,
+  nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
-                            nsISupports** aResult);
+                            File** aResult);
   void CallPrintCallback();
 
   AsyncCanvasRenderer* GetAsyncCanvasRenderer();
 
   bool mResetLayer;
   RefPtr<HTMLCanvasElement> mOriginalCanvas;
   RefPtr<PrintCallback> mPrintCallback;
   RefPtr<HTMLCanvasPrintState> mPrintState;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3749,17 +3749,17 @@ HTMLInputElement::Select()
 
 bool
 HTMLInputElement::DispatchSelectEvent(nsPresContext* aPresContext)
 {
   nsEventStatus status = nsEventStatus_eIgnore;
 
   // If already handling select event, don't dispatch a second.
   if (!mHandlingSelectEvent) {
-    WidgetEvent event(nsContentUtils::LegacyIsCallerChromeOrNativeCode(), eFormSelect);
+    WidgetEvent event(true, eFormSelect);
 
     mHandlingSelectEvent = true;
     EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
                               aPresContext, &event, nullptr, &status);
     mHandlingSelectEvent = false;
   }
 
   // If the DOM event was not canceled (e.g. by a JS event handler
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/MediaEncryptedEvent.h"
+#include "mozilla/EMEUtils.h"
 
 #include "base/basictypes.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "TimeRanges.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsPresContext.h"
@@ -5287,35 +5288,41 @@ void HTMLMediaElement::SuspendOrResumeEl
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
       ReportTelemetry();
       ReportEMETelemetry();
 
-      // For EME content, force destruction of the CDM client (and CDM
+      // For EME content, we may force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
-        mMediaKeys->Shutdown();
-        mMediaKeys = nullptr;
-        if (mDecoder) {
-          ShutdownDecoder();
+        nsAutoString keySystem;
+        mMediaKeys->GetKeySystem(keySystem);
+        // If we're using Primetime we need to shutdown the key system and
+        // decoder to preserve secure stop like behavior, other CDMs don't
+        // implement this so we don't need to worry with them on a suspend.
+        if (IsPrimetimeKeySystem(keySystem)) {
+          mMediaKeys->Shutdown();
+          mMediaKeys = nullptr;
+          if (mDecoder) {
+            ShutdownDecoder();
+          }
         }
       }
       if (mDecoder) {
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
-      MOZ_ASSERT(!mMediaKeys);
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
@@ -5349,16 +5356,25 @@ void HTMLMediaElement::NotifyOwnerDocume
 
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->NotifyOwnerActivityChanged(visible);
   }
 
   bool pauseElement = ShouldElementBePaused();
   SuspendOrResumeElement(pauseElement, !IsActive());
 
+  // If the owning document has become inactive we should shutdown the CDM.
+  if (!OwnerDoc()->IsCurrentActiveDocument() && mMediaKeys) {
+      mMediaKeys->Shutdown();
+      mMediaKeys = nullptr;
+      if (mDecoder) {
+        ShutdownDecoder();
+      }
+    }
+
   AddRemoveSelfReference();
 }
 
 void HTMLMediaElement::AddRemoveSelfReference()
 {
   // XXX we could release earlier here in many situations if we examined
   // which event listeners are attached. Right now we assume there is a
   // potential listener for every event. We would also have to keep the
--- a/dom/html/HTMLMenuElement.cpp
+++ b/dom/html/HTMLMenuElement.cpp
@@ -53,87 +53,69 @@ HTMLMenuElement::HTMLMenuElement(already
 {
 }
 
 HTMLMenuElement::~HTMLMenuElement()
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(HTMLMenuElement, nsGenericHTMLElement,
-                            nsIDOMHTMLMenuElement, nsIHTMLMenu)
+                            nsIDOMHTMLMenuElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLMenuElement)
 
 NS_IMPL_BOOL_ATTR(HTMLMenuElement, Compact, compact)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMenuElement, Type, type,
                                 kMenuDefaultType->tag)
 NS_IMPL_STRING_ATTR(HTMLMenuElement, Label, label)
 
 
-NS_IMETHODIMP
+void
 HTMLMenuElement::SendShowEvent()
 {
-  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
-
   nsCOMPtr<nsIDocument> document = GetComposedDoc();
   if (!document) {
-    return NS_ERROR_FAILURE;
+    return;
   }
 
   WidgetEvent event(true, eShow);
   event.mFlags.mBubbles = false;
   event.mFlags.mCancelable = false;
 
   nsCOMPtr<nsIPresShell> shell = document->GetShell();
   if (!shell) {
-    return NS_ERROR_FAILURE;
+    return;
   }
  
   RefPtr<nsPresContext> presContext = shell->GetPresContext();
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
                             &event, nullptr, &status);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLMenuElement::CreateBuilder(nsIMenuBuilder** _retval)
-{
-  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
-
-  nsCOMPtr<nsIMenuBuilder> builder = CreateBuilder();
-  builder.swap(*_retval);
-  return NS_OK;
 }
 
 already_AddRefed<nsIMenuBuilder>
 HTMLMenuElement::CreateBuilder()
 {
   if (mType != MENU_TYPE_CONTEXT) {
     return nullptr;
   }
 
   nsCOMPtr<nsIMenuBuilder> builder = do_CreateInstance(HTMLMENUBUILDER_CONTRACTID);
   NS_WARNING_ASSERTION(builder, "No builder available");
   return builder.forget();
 }
 
-NS_IMETHODIMP
+void
 HTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
 {
-  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
-
   if (!aBuilder) {
-    return NS_OK;
+    return;
   }
 
   BuildSubmenu(EmptyString(), this, aBuilder);
-
-  return NS_OK;
 }
 
 
 bool
 HTMLMenuElement::ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
--- a/dom/html/HTMLMenuElement.h
+++ b/dom/html/HTMLMenuElement.h
@@ -4,40 +4,37 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLMenuElement_h
 #define mozilla_dom_HTMLMenuElement_h
 
 #include "mozilla/Attributes.h"
 #include "nsIDOMHTMLMenuElement.h"
-#include "nsIHTMLMenu.h"
 #include "nsGenericHTMLElement.h"
 
+class nsIMenuBuilder;
+
 namespace mozilla {
 namespace dom {
 
 class HTMLMenuElement final : public nsGenericHTMLElement,
-                              public nsIDOMHTMLMenuElement,
-                              public nsIHTMLMenu
+                              public nsIDOMHTMLMenuElement
 {
 public:
   explicit HTMLMenuElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLMenuElement, menu)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMHTMLMenuElement
   NS_DECL_NSIDOMHTMLMENUELEMENT
 
-  // nsIHTMLMenu
-  NS_DECL_NSIHTMLMENU
-
   virtual bool ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult) override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
   uint8_t GetType() const { return mType; }
@@ -60,21 +57,21 @@ public:
   {
     return GetBoolAttr(nsGkAtoms::compact);
   }
   void SetCompact(bool aCompact, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::compact, aCompact, aError);
   }
 
-  // The XPCOM SendShowEvent is OK for us
+  void SendShowEvent();
 
   already_AddRefed<nsIMenuBuilder> CreateBuilder();
 
-  // The XPCOM Build is OK for us
+  void Build(nsIMenuBuilder* aBuilder);
 
 protected:
   virtual ~HTMLMenuElement();
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 
 protected:
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -358,19 +358,16 @@ HTMLTextAreaElement::SetValue(const nsAS
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
-  if (!nsContentUtils::IsCallerChrome()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
   return SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
 {
   bool previousValue = mValueChanged;
 
--- a/dom/html/moz.build
+++ b/dom/html/moz.build
@@ -15,17 +15,16 @@ MOCHITEST_CHROME_MANIFESTS += [
     'test/forms/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 XPIDL_SOURCES += [
     'nsIDateTimeInputArea.idl',
     'nsIFormSubmitObserver.idl',
-    'nsIHTMLMenu.idl',
     'nsIImageDocument.idl',
     'nsIMenuBuilder.idl',
     'nsIPhonetic.idl',
 ]
 
 XPIDL_MODULE = 'content_html'
 
 EXPORTS += [
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2408,17 +2408,17 @@ nsGenericHTMLFormElement::IsLabelable() 
          type == NS_FORM_OUTPUT ||
          type == NS_FORM_SELECT ||
          type == NS_FORM_TEXTAREA;
 }
 
 //----------------------------------------------------------------------
 
 void
-nsGenericHTMLElement::Click()
+nsGenericHTMLElement::Click(CallerType aCallerType)
 {
   if (HandlingClick())
     return;
 
   // Strong in case the event kills it
   nsCOMPtr<nsIDocument> doc = GetComposedDoc();
 
   nsCOMPtr<nsIPresShell> shell;
@@ -2427,20 +2427,18 @@ nsGenericHTMLElement::Click()
     shell = doc->GetShell();
     if (shell) {
       context = shell->GetPresContext();
     }
   }
 
   SetHandlingClick();
 
-  // Click() is never called from native code, but it may be
-  // called from chrome JS. Mark this event trusted if Click()
-  // is called from chrome code.
-  WidgetMouseEvent event(nsContentUtils::IsCallerChrome(),
+  // Mark this event trusted if Click() is called from system code.
+  WidgetMouseEvent event(aCallerType == CallerType::System,
                          eMouseClick, nullptr, WidgetMouseEvent::eReal);
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
 
   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context, &event);
 
   ClearHandlingClick();
 }
 
@@ -2562,23 +2560,16 @@ nsGenericHTMLElement::DispatchSimulatedC
                          WidgetMouseEvent::eReal);
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
   return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event);
 }
 
 nsresult
 nsGenericHTMLElement::GetEditor(nsIEditor** aEditor)
 {
-  *aEditor = nullptr;
-
-  // See also HTMLTextFieldAccessible::GetEditor.
-  if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
   NS_IF_ADDREF(*aEditor = GetEditorInternal());
   return NS_OK;
 }
 
 already_AddRefed<nsIEditor>
 nsGenericHTMLElement::GetAssociatedEditor()
 {
   // If contenteditable is ever implemented, it might need to do something different here?
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -11,16 +11,17 @@
 #include "nsMappedAttributeElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsNameSpaceManager.h"  // for kNameSpaceID_None
 #include "nsIFormControl.h"
 #include "nsGkAtoms.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIDOMHTMLMenuElement.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/ValidityState.h"
 #include "mozilla/dom/ElementInlines.h"
 
 class nsDOMTokenList;
 class nsIDOMHTMLMenuElement;
 class nsIEditor;
 class nsIFormControlFrame;
@@ -98,17 +99,17 @@ public:
   bool Hidden() const
   {
     return GetBoolAttr(nsGkAtoms::hidden);
   }
   void SetHidden(bool aHidden, mozilla::ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::hidden, aHidden, aError);
   }
-  virtual void Click();
+  void Click(mozilla::dom::CallerType aCallerType);
   void GetAccessKey(nsString& aAccessKey)
   {
     GetHTMLAttr(nsGkAtoms::accesskey, aAccessKey);
   }
   void SetAccessKey(const nsAString& aAccessKey, mozilla::ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::accesskey, aAccessKey, aError);
   }
@@ -423,20 +424,16 @@ public:
   NS_IMETHOD GetOffsetWidth(int32_t* aOffsetWidth) final override {
     *aOffsetWidth = OffsetWidth();
     return NS_OK;
   }
   NS_IMETHOD GetOffsetHeight(int32_t* aOffsetHeight) final override {
     *aOffsetHeight = OffsetHeight();
     return NS_OK;
   }
-  NS_IMETHOD DOMClick() final override {
-    Click();
-    return NS_OK;
-  }
   NS_IMETHOD GetTabIndex(int32_t* aTabIndex) final override {
     *aTabIndex = TabIndex();
     return NS_OK;
   }
   NS_IMETHOD SetTabIndex(int32_t aTabIndex) final override {
     mozilla::ErrorResult rv;
     SetTabIndex(aTabIndex, rv);
     return rv.StealNSResult();
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -111,16 +111,17 @@
 #include "nsIStringBundle.h"
 #include "nsDOMClassInfo.h"
 #include "nsFocusManager.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
+#include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
 
 #include "prtime.h"
 
@@ -2790,27 +2791,31 @@ nsHTMLDocument::EditingStateChanged()
       return NS_OK;
     }
   }
 
   if (makeWindowEditable) {
     // Set the editor to not insert br's on return when in p
     // elements by default.
     // XXX Do we only want to do this for designMode?
-    bool unused;
-    rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), false,
-                     NS_LITERAL_STRING("false"), &unused);
-
-    if (NS_FAILED(rv)) {
+    // Note that it doesn't matter what CallerType we pass, because the callee
+    // doesn't use it for this command.  Play it safe and pass the more
+    // restricted one.
+    ErrorResult errorResult;
+    Unused << ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), false,
+                          NS_LITERAL_STRING("false"), CallerType::NonSystem,
+                          errorResult);
+
+    if (errorResult.Failed()) {
       // Editor setup failed. Editing is not on after all.
       // XXX Should we reset the editable flag on nodes?
       editSession->TearDownEditorOnWindow(window);
       mEditingState = eOff;
 
-      return rv;
+      return errorResult.StealNSResult();
     }
   }
 
   if (updateState) {
     nsAutoScriptBlocker scriptBlocker;
     NotifyEditableStateChange(this, this);
   }
 
@@ -3116,32 +3121,21 @@ ConvertToMidasInternalCommand(const nsAS
   nsAutoCString dummyCString;
   nsAutoString dummyString;
   bool dummyBool;
   return ConvertToMidasInternalCommandInner(inCommandID, dummyString,
                                             outCommandID, dummyCString,
                                             dummyBool, dummyBool, true);
 }
 
-/* TODO: don't let this call do anything if the page is not done loading */
-NS_IMETHODIMP
-nsHTMLDocument::ExecCommand(const nsAString& commandID,
-                            bool doShowUI,
-                            const nsAString& value,
-                            bool* _retval)
-{
-  ErrorResult rv;
-  *_retval = ExecCommand(commandID, doShowUI, value, rv);
-  return rv.StealNSResult();
-}
-
 bool
 nsHTMLDocument::ExecCommand(const nsAString& commandID,
                             bool doShowUI,
                             const nsAString& value,
+                            CallerType aCallerType,
                             ErrorResult& rv)
 {
   //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
   //  this might add some ugly JS dependencies?
 
   nsAutoCString cmdToDispatch, paramStr;
   bool isBool, boolVal;
   if (!ConvertToMidasInternalCommand(commandID, value,
@@ -3191,17 +3185,17 @@ nsHTMLDocument::ExecCommand(const nsAStr
   }
 
   if (commandID.LowerCaseEqualsLiteral("gethtml")) {
     rv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   bool restricted = commandID.LowerCaseEqualsLiteral("paste");
-  if (restricted && !nsContentUtils::IsCallerChrome()) {
+  if (restricted && aCallerType != CallerType::System) {
     return false;
   }
 
   // get command manager and dispatch command to our window if it's acceptable
   nsCOMPtr<nsICommandManager> cmdMgr;
   GetMidasCommandManager(getter_AddRefs(cmdMgr));
   if (!cmdMgr) {
     rv.Throw(NS_ERROR_FAILURE);
@@ -3255,43 +3249,36 @@ nsHTMLDocument::ExecCommand(const nsAStr
       return false;
     }
     rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
   }
 
   return !rv.Failed();
 }
 
-NS_IMETHODIMP
+bool
 nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID,
-                                    bool* _retval)
-{
-  ErrorResult rv;
-  *_retval = QueryCommandEnabled(commandID, rv);
-  return rv.StealNSResult();
-}
-
-bool
-nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID, ErrorResult& rv)
+                                    CallerType aCallerType,
+                                    ErrorResult& rv)
 {
   nsAutoCString cmdToDispatch;
   if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
     return false;
   }
 
   // cut & copy are always allowed
   bool isCutCopy = commandID.LowerCaseEqualsLiteral("cut") ||
                    commandID.LowerCaseEqualsLiteral("copy");
   if (isCutCopy) {
     return nsContentUtils::IsCutCopyAllowed();
   }
 
   // Report false for restricted commands
   bool restricted = commandID.LowerCaseEqualsLiteral("paste");
-  if (restricted && !nsContentUtils::IsCallerChrome()) {
+  if (restricted && aCallerType != CallerType::System) {
     return false;
   }
 
   // if editing is not on, bail
   if (!IsEditingOnAfterFlush()) {
     return false;
   }
 
@@ -3449,34 +3436,27 @@ nsHTMLDocument::QueryCommandState(const 
   // If command does not have a state_all value, this call fails and sets
   // retval to false.  This is fine -- we want to return false in that case
   // anyway (bug 738385), so we just succeed and return false regardless.
   bool retval = false;
   cmdParams->GetBooleanValue("state_all", &retval);
   return retval;
 }
 
-NS_IMETHODIMP
-nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
-                                      bool *_retval)
-{
-  *_retval = QueryCommandSupported(commandID);
-  return NS_OK;
-}
-
 bool
-nsHTMLDocument::QueryCommandSupported(const nsAString& commandID)
+nsHTMLDocument::QueryCommandSupported(const nsAString& commandID,
+                                      CallerType aCallerType)
 {
   // Gecko technically supports all the clipboard commands including
   // cut/copy/paste, but non-privileged content will be unable to call
   // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
   // may also be disallowed to be called from non-privileged content.
   // For that reason, we report the support status of corresponding
   // command accordingly.
-  if (!nsContentUtils::IsCallerChrome()) {
+  if (aCallerType != CallerType::System) {
     if (commandID.LowerCaseEqualsLiteral("paste")) {
       return false;
     }
     if (nsContentUtils::IsCutCopyRestricted()) {
       if (commandID.LowerCaseEqualsLiteral("cut") ||
           commandID.LowerCaseEqualsLiteral("copy")) {
         return false;
       }
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -15,16 +15,17 @@
 #include "nsTArray.h"
 
 #include "PLDHashTable.h"
 #include "nsIHttpChannel.h"
 #include "nsHTMLStyleSheet.h"
 
 #include "nsICommandManager.h"
 #include "mozilla/dom/HTMLSharedElement.h"
+#include "mozilla/dom/BindingDeclarations.h"
 
 class nsIEditor;
 class nsIURI;
 class nsIDocShell;
 class nsICachingChannel;
 class nsIWyciwygChannel;
 class nsILoadGroup;
 
@@ -217,23 +218,27 @@ public:
   }
   void SetDesignMode(const nsAString& aDesignMode,
                      nsIPrincipal& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   void SetDesignMode(const nsAString& aDesignMode,
                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   bool ExecCommand(const nsAString& aCommandID, bool aDoShowUI,
-                   const nsAString& aValue, mozilla::ErrorResult& rv);
+                   const nsAString& aValue,
+                   mozilla::dom::CallerType aCallerType,
+                   mozilla::ErrorResult& rv);
   bool QueryCommandEnabled(const nsAString& aCommandID,
+                           mozilla::dom::CallerType aCallerType,
                            mozilla::ErrorResult& rv);
   bool QueryCommandIndeterm(const nsAString& aCommandID,
                             mozilla::ErrorResult& rv);
   bool QueryCommandState(const nsAString& aCommandID, mozilla::ErrorResult& rv);
-  bool QueryCommandSupported(const nsAString& aCommandID);
+  bool QueryCommandSupported(const nsAString& aCommandID,
+                             mozilla::dom::CallerType aCallerType);
   void QueryCommandValue(const nsAString& aCommandID, nsAString& aValue,
                          mozilla::ErrorResult& rv);
   // The XPCOM Get/SetFgColor work OK for us, since they never throw.
   // The XPCOM Get/SetLinkColor work OK for us, since they never throw.
   // The XPCOM Get/SetVLinkColor work OK for us, since they never throw.
   // The XPCOM Get/SetALinkColor work OK for us, since they never throw.
   // The XPCOM Get/SetBgColor work OK for us, since they never throw.
   nsIHTMLCollection* Anchors();
deleted file mode 100644
--- a/dom/html/nsIHTMLMenu.idl
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIMenuBuilder;
-
-/**
- * A private interface.
- * All methods throw NS_ERROR_DOM_SECURITY_ERR if the caller is not chrome.
- */
-
-[scriptable, uuid(d3d068d8-e223-4228-ba39-4d6df21ba616)]
-interface nsIHTMLMenu : nsISupports
-{
-  /**
-   * Creates and dispatches a trusted event named "show".
-   * The event is not cancelable and does not bubble.
-   * See http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus
-   */
-  void sendShowEvent();
-
-  /**
-   * Creates a native menu builder. The builder type is dependent on menu type.
-   * Currently, it returns nsXULContextMenuBuilder for context menus.
-   * Toolbar menus are not yet supported (the method returns null).
-   */
-  nsIMenuBuilder createBuilder();
-
-  /*
-   * Builds a menu by iterating over menu children.
-   * See http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#building-menus-and-toolbars
-   * The caller can use a native builder by calling createBuilder() or provide
-   * a custom builder that implements the nsIMenuBuilder interface.
-   * A custom builder can be used for example to build native context menus
-   * that are not defined using <menupopup>.
-   */
-  void build(in nsIMenuBuilder aBuilder);
-
-};
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
@@ -20,28 +20,10 @@ interface nsIVariant;
 interface nsIInputStreamCallback;
 
 [uuid(4e8f1316-b601-471d-8f44-3c650d91ee9b)]
 interface nsIDOMHTMLCanvasElement : nsISupports
 {
   attribute unsigned long width;
   attribute unsigned long height;
   attribute boolean mozOpaque;
-
-  // Valid calls are:
-  //  toDataURL();              -- defaults to image/png
-  //  toDataURL(type);          -- uses given type
-  //  toDataURL(type, params);  -- uses given type, and any valid parameters
-  [implicit_jscontext]
-  DOMString toDataURL([optional] in DOMString type,
-                      [optional] in jsval params);
-
-  // Valid calls are
-  // mozGetAsFile(name);              -- defaults to image/png
-  // mozGetAsFile(name, type);        -- uses given type
-  // The return value is a File object.
-  nsISupports mozGetAsFile(in DOMString name, [optional] in DOMString type);
-
-  // A Mozilla-only extension to get a canvas context backed by double-buffered
-  // shared memory. Only privileged callers can call this.
-  nsISupports MozGetIPCContext(in DOMString contextId);
 };
 
--- a/dom/interfaces/html/nsIDOMHTMLDocument.idl
+++ b/dom/interfaces/html/nsIDOMHTMLDocument.idl
@@ -47,32 +47,22 @@ interface nsIDOMHTMLDocument : nsIDOMDoc
   [implicit_jscontext]
   void                      writeln([optional, Null(Stringify)] in DOMString text);
 
   /**
    * Midas additions
    */
   attribute DOMString       designMode;
 
-  boolean                   execCommand(in DOMString commandID,
-                                        [optional] in boolean doShowUI,
-                                        [optional] in DOMString value);
-
-  // returns true if the command is enabled (false otherwise)
-  boolean                   queryCommandEnabled(in DOMString commandID);
-  
   // returns true if the command is in a indeterminate state (false otherwise)
   boolean                   queryCommandIndeterm(in DOMString commandID);
 
   // returns true if the command has been executed (false otherwise)
   boolean                   queryCommandState(in DOMString commandID);
 
-  // returns true if the command is supported on the current range
-  boolean                   queryCommandSupported(in DOMString commandID);
-
   // returns the current value of the document or current selection for command
   DOMString                 queryCommandValue(in DOMString commandID);
 
 
   // Obsolete APIs
            attribute DOMString            fgColor;
            attribute DOMString            bgColor;
            attribute DOMString            linkColor;
--- a/dom/interfaces/html/nsIDOMHTMLElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLElement.idl
@@ -30,18 +30,16 @@ interface nsIDOMHTMLElement : nsIDOMElem
 
   // user interaction
   /**
    * Indicates that the element is not yet, or is no longer, relevant.
    *
    * See <http://www.whatwg.org/html5/#the-hidden-attribute>.
    */
            attribute boolean          hidden;
-  [binaryname(DOMClick)]
-  void click();
            attribute long             tabIndex;
   void focus();
   [binaryname(DOMBlur)]
   void blur();
            attribute DOMString        accessKey;
   readonly attribute DOMString        accessKeyLabel;
            attribute boolean          draggable;
            attribute DOMString        contentEditable;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1451,18 +1451,21 @@ ContentChild::RecvSetProcessSandbox(cons
 
 bool
 ContentChild::RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId)
 {
   if (!CompositorBridgeChild::Get()->IPCOpen()) {
     return true;
   }
 
+  // Note: sending the constructor could fail, but we do not propagate the
+  // error back since the GPU process is fallible.
   APZChild* apz = ContentProcessController::Create(aTabId);
-  return CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId);
+  CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId);
+  return true;
 }
 
 bool
 ContentChild::RecvSpeakerManagerNotify()
 {
 #ifdef MOZ_WIDGET_GONK
   // Only notify the process which has the SpeakerManager instance.
   RefPtr<SpeakerManagerService> service =
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1341,23 +1341,17 @@ TabChild::RecvShow(const ScreenIntSize& 
     }
 
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
     if (!baseWindow) {
         NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow");
         return false;
     }
 
-    if (!InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame)) {
-        // We can fail to initialize our widget if the <browser
-        // remote> has already been destroyed, and we couldn't hook
-        // into the parent-process's layer system.  That's not a fatal
-        // error.
-        return true;
-    }
+    InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame);
 
     baseWindow->SetVisibility(true);
 
     bool res = InitTabChildGlobal();
     ApplyShowInfo(aInfo);
     RecvParentActivated(aParentIsActive);
 
     return res;
@@ -2560,39 +2554,37 @@ TabChild::InitTabChildGlobal(FrameScript
     if (IsMozBrowserOrApp()) {
       RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
     }
   }
 
   return true;
 }
 
-bool
+void
 TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                              const uint64_t& aLayersId,
                              PRenderFrameChild* aRenderFrame)
 {
     mPuppetWidget->InitIMEState();
 
-    RenderFrameChild* remoteFrame = static_cast<RenderFrameChild*>(aRenderFrame);
-    if (!remoteFrame) {
-        NS_WARNING("failed to construct RenderFrame");
-        return false;
+    if (!aRenderFrame) {
+      NS_WARNING("failed to construct RenderFrame");
+      return;
     }
 
     MOZ_ASSERT(aLayersId != 0);
     mTextureFactoryIdentifier = aTextureFactoryIdentifier;
 
     // Pushing layers transactions directly to a separate
     // compositor context.
     PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
     if (!compositorChild) {
       NS_WARNING("failed to get CompositorBridgeChild instance");
-      PRenderFrameChild::Send__delete__(remoteFrame);
-      return false;
+      return;
     }
 
     ShadowLayerForwarder* lf =
         mPuppetWidget->GetLayerManager(
             nullptr, mTextureFactoryIdentifier.mParentBackend)
                 ->AsShadowForwarder();
     // As long as we are creating a ClientLayerManager for the puppet widget,
     // lf must be non-null here.
@@ -2600,36 +2592,25 @@ TabChild::InitRenderingState(const Textu
 
     if (lf) {
       nsTArray<LayersBackend> backends;
       backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
       bool success;
       PLayerTransactionChild* shadowManager =
           compositorChild->SendPLayerTransactionConstructor(backends,
                                                             aLayersId, &mTextureFactoryIdentifier, &success);
-      if (!success) {
-        NS_WARNING("failed to properly allocate layer transaction");
-        PRenderFrameChild::Send__delete__(remoteFrame);
-        return false;
+      if (shadowManager && success) {
+        lf->SetShadowManager(shadowManager);
+        lf->IdentifyTextureHost(mTextureFactoryIdentifier);
+        ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
+        gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
       }
-
-      if (!shadowManager) {
-        NS_WARNING("failed to construct LayersChild");
-        // This results in |remoteFrame| being deleted.
-        PRenderFrameChild::Send__delete__(remoteFrame);
-        return false;
-      }
-
-      lf->SetShadowManager(shadowManager);
-      lf->IdentifyTextureHost(mTextureFactoryIdentifier);
-      ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
-      gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
     }
 
-    mRemoteFrame = remoteFrame;
+    mRemoteFrame = static_cast<RenderFrameChild*>(aRenderFrame);
     if (aLayersId != 0) {
       if (!sTabChildren) {
         sTabChildren = new TabChildMap;
       }
       MOZ_ASSERT(!sTabChildren->Get(aLayersId));
       sTabChildren->Put(aLayersId, this);
       mLayersId = aLayersId;
     }
@@ -2639,17 +2620,16 @@ TabChild::InitRenderingState(const Textu
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
 
     if (observerService) {
         observerService->AddObserver(this,
                                      BEFORE_FIRST_PAINT,
                                      false);
     }
-    return true;
 }
 
 void
 TabChild::GetDPI(float* aDPI)
 {
     *aDPI = -1.0;
     if (!mRemoteFrame) {
         return;
@@ -2998,18 +2978,16 @@ TabChild::ReinitRendering()
 
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
 
 void
 TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
 {
-  gfxPlatform::GetPlatform()->CompositorUpdated();
-
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
   ClientLayerManager* clm = lm->AsClientLayerManager();
   MOZ_ASSERT(clm);
 
   mTextureFactoryIdentifier = aNewIdentifier;
   clm->UpdateTextureFactoryIdentifier(aNewIdentifier);
   FrameLayerBuilder::InvalidateAllLayers(clm);
 }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -723,17 +723,17 @@ private:
   void UpdateFrameType();
 
   void ActorDestroy(ActorDestroyReason why) override;
 
   enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
 
   bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
 
-  bool InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+  void InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                           const uint64_t& aLayersId,
                           PRenderFrameChild* aRenderFrame);
 
   void DestroyWindow();
 
   void SetProcessNameToAppName();
 
   void ApplyShowInfo(const ShowInfo& aInfo);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -632,17 +632,16 @@ TabParent::Show(const ScreenIntSize& siz
     if (IsInitedByParent()) {
         // If TabParent is initialized by parent side then the RenderFrame must also
         // be created here. If TabParent is initialized by child side,
         // child side will create RenderFrame.
         MOZ_ASSERT(!GetRenderFrame());
         RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
         if (frameLoader) {
           renderFrame = new RenderFrameParent(frameLoader, &success);
-          MOZ_ASSERT(success);
           layersId = renderFrame->GetLayersId();
           renderFrame->GetTextureFactoryIdentifier(&textureFactoryIdentifier);
           AddTabParentToTable(layersId, this);
           Unused << SendPRenderFrameConstructor(renderFrame);
         }
     } else {
       // Otherwise, the child should have constructed the RenderFrame,
       // and we should already know about it.
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -93,16 +93,18 @@ public:
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   virtual void Init(PromiseId aPromiseId,
                     const nsAString& aOrigin,
                     const nsAString& aTopLevelOrigin,
                     const nsAString& aName,
                     bool aInPrivateBrowsing) = 0;
 
+  virtual void OnSetDecryptorId(uint32_t aId) {}
+
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              MediaKeySessionType aSessionType,
                              PromiseId aPromiseId,
                              const nsAString& aInitDataType,
@@ -214,16 +216,18 @@ public:
 
   virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                      nsTArray<nsCString>& aSessionIds) = 0;
 
 #ifdef DEBUG
   virtual bool IsOnOwnerThread() = 0;
 #endif
 
+  virtual uint32_t GetDecryptorId() { return 0; }
+
 protected:
   virtual ~CDMProxy() {}
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
     explicit MainThreadOnlyRawPtr(Type* aPtr)
--- a/dom/media/eme/DecryptorProxyCallback.h
+++ b/dom/media/eme/DecryptorProxyCallback.h
@@ -10,16 +10,18 @@
 #include "mozilla/dom/MediaKeyMessageEventBinding.h" // For MediaKeyMessageType
 #include "mozilla/CDMProxy.h"
 
 class DecryptorProxyCallback {
 public:
 
   virtual ~DecryptorProxyCallback() {}
 
+  virtual void SetDecryptorId(uint32_t aId) = 0;
+
   virtual void SetSessionId(uint32_t aCreateSessionId,
                             const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) = 0;
 
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
--- a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 
 namespace mozilla {
 class CDMProxy;
 // Proxies call backs from the MediaDrmProxy -> MediaDrmProxySupport back to the MediaKeys
 // object on the main thread.
 // We used annotation calledFrom = "gecko" to ensure running on main thread.
 class MediaDrmCDMCallbackProxy : public DecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override {}
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMCallbackProxy.cpp
+++ b/dom/media/gmp/GMPCDMCallbackProxy.cpp
@@ -17,16 +17,29 @@
 
 namespace mozilla {
 
 GMPCDMCallbackProxy::GMPCDMCallbackProxy(CDMProxy* aProxy)
   : mProxy(aProxy)
 {}
 
 void
+GMPCDMCallbackProxy::SetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
+
+  RefPtr<CDMProxy> proxy = mProxy;
+  NS_DispatchToMainThread(
+    NS_NewRunnableFunction([proxy, aId] ()
+    {
+      proxy->OnSetDecryptorId(aId);
+    })
+  );}
+
+void
 GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
                                   const nsCString& aSessionId)
 {
   MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   RefPtr<CDMProxy> proxy = mProxy;
   auto sid = NS_ConvertUTF8toUTF16(aSessionId);
   NS_DispatchToMainThread(
--- a/dom/media/gmp/GMPCDMCallbackProxy.h
+++ b/dom/media/gmp/GMPCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 #include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 
 // Proxies call backs from the CDM on the GMP thread back to the MediaKeys
 // object on the main thread.
 class GMPCDMCallbackProxy : public GMPDecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override;
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -33,16 +33,18 @@ GMPCDMProxy::GMPCDMProxy(dom::MediaKeys*
   : CDMProxy(aKeys,
              aKeySystem,
              aDistinctiveIdentifierRequired,
              aPersistentStateRequired)
   , mCrashHelper(aCrashHelper)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
   , mShutdownCalled(false)
+  , mDecryptorId(0)
+  , mCreatePromiseId(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(GMPCDMProxy);
 }
 
 GMPCDMProxy::~GMPCDMProxy()
 {
   MOZ_COUNT_DTOR(GMPCDMProxy);
@@ -126,20 +128,29 @@ GMPCDMProxy::gmp_InitDone(GMPDecryptorPr
     return;
   }
 
   mCDM = aCDM;
   mCallback = new GMPCDMCallbackProxy(this);
   mCDM->Init(mCallback,
              mDistinctiveIdentifierRequired,
              mPersistentStateRequired);
+
+  // Await the OnSetDecryptorId callback.
+  mCreatePromiseId = aData->mPromiseId;
+}
+
+void GMPCDMProxy::OnSetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mCreatePromiseId);
+  mDecryptorId = aId;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<uint32_t>(this,
                                 &GMPCDMProxy::OnCDMCreated,
-                                aData->mPromiseId));
+                                mCreatePromiseId));
   NS_DispatchToMainThread(task);
 }
 
 class gmp_InitDoneCallback : public GetGMPDecryptorCallback
 {
 public:
   gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
                        nsAutoPtr<GMPCDMProxy::InitData>&& aData)
@@ -763,14 +774,26 @@ GMPCDMProxy::GetSessionIdsForKeyId(const
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
 GMPCDMProxy::Terminated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
+  if (mCreatePromiseId) {
+    RejectPromise(mCreatePromiseId,
+                  NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                  NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
+    mCreatePromiseId = 0;
+  }
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
 
+uint32_t
+GMPCDMProxy::GetDecryptorId()
+{
+  return mDecryptorId;
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -29,16 +29,18 @@ public:
               bool aPersistentStateRequired);
 
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             const nsAString& aGMPName,
             bool aInPrivateBrowsing) override;
 
+  void OnSetDecryptorId(uint32_t aId) override;
+
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
                    const nsAString& aSessionId) override;
@@ -104,16 +106,18 @@ public:
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
 #ifdef DEBUG
   bool IsOnOwnerThread() override;
 #endif
 
+  uint32_t GetDecryptorId() override;
+
 private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsString mOrigin;
     nsString mTopLevelOrigin;
@@ -245,14 +249,18 @@ private:
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
 
   // True if GMPCDMProxy::gmp_Shutdown was called.
   // GMP thread only.
   bool mShutdownCalled;
+
+  uint32_t mDecryptorId;
+
+  PromiseId mCreatePromiseId;
 };
 
 
 } // namespace mozilla
 
 #endif // GMPCDMProxy_h_
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -272,22 +272,25 @@ GMPChild::RecvSetNodeId(const nsCString&
   // Store the per origin salt for the node id. Note: we do this in a
   // separate message than RecvStartPlugin() so that the string is not
   // sitting in a string on the IPC code's call stack.
   mNodeId = aNodeId;
   return true;
 }
 
 GMPErr
-GMPChild::GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI)
+GMPChild::GetAPI(const char* aAPIName,
+                 void* aHostAPI,
+                 void** aPluginAPI,
+                 uint32_t aDecryptorId)
 {
   if (!mGMPLoader) {
     return GMPGenericErr;
   }
-  return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI);
+  return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
 }
 
 bool
 GMPChild::RecvPreloadLibs(const nsCString& aLibs)
 {
 #ifdef XP_WIN
   // Pre-load DLLs that need to be used by the EME plugin but that can't be
   // loaded after the sandbox has started
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -71,17 +71,17 @@ private:
 
   bool RecvCrashPluginNow() override;
   bool RecvBeginAsyncShutdown() override;
   bool RecvCloseActive() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
-  GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
+  GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, uint32_t aDecryptorId = 0);
 
   nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
 
   GMPAsyncShutdown* mAsyncShutdown;
   RefPtr<GMPTimerChild> mTimerChild;
   RefPtr<GMPStorageChild> mStorage;
 
   MessageLoop* mGMPMessageLoop;
--- a/dom/media/gmp/GMPContentChild.cpp
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -75,17 +75,17 @@ GMPContentChild::AllocPGMPDecryptorChild
 bool
 GMPContentChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
 {
   static_cast<GMPDecryptorChild*>(aActor)->Release();
   return true;
 }
 
 PGMPVideoDecoderChild*
-GMPContentChild::AllocPGMPVideoDecoderChild()
+GMPContentChild::AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId)
 {
   GMPVideoDecoderChild* actor = new GMPVideoDecoderChild(this);
   actor->AddRef();
   return actor;
 }
 
 bool
 GMPContentChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
@@ -201,17 +201,17 @@ private:
 
 bool
 GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
 {
   GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
   GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
 
   void* ptr = nullptr;
-  GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &ptr);
+  GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &ptr, aActor->Id());
   GMPDecryptor* decryptor = nullptr;
   if (GMP_SUCCEEDED(err) && ptr) {
     decryptor = static_cast<GMPDecryptor*>(ptr);
   } else if (err != GMPNoErr) {
     // We Adapt the previous GMPDecryptor version to the current, so that
     // Gecko thinks it's only talking to the current version. v7 differs
     // from v9 in its Init() function arguments, and v9 has extra enumeration
     // members at the end of the key status enumerations.
@@ -239,22 +239,23 @@ GMPContentChild::RecvPGMPAudioDecoderCon
   }
 
   vdc->Init(static_cast<GMPAudioDecoder*>(vd));
 
   return true;
 }
 
 bool
-GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
+GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor,
+                                                 const uint32_t& aDecryptorId)
 {
   auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
 
   void* vd = nullptr;
-  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd);
+  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd, aDecryptorId);
   if (err != GMPNoErr || !vd) {
     NS_WARNING("GMPGetAPI call failed trying to construct decoder.");
     return false;
   }
 
   vdc->Init(static_cast<GMPVideoDecoder*>(vd));
 
   return true;
--- a/dom/media/gmp/GMPContentChild.h
+++ b/dom/media/gmp/GMPContentChild.h
@@ -20,26 +20,26 @@ class GMPContentChild : public PGMPConte
 public:
   explicit GMPContentChild(GMPChild* aChild);
   virtual ~GMPContentChild();
 
   MessageLoop* GMPMessageLoop();
 
   bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) override;
   bool RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
-  bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) override;
+  bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor, const uint32_t& aDecryptorId) override;
   bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
 
   PGMPAudioDecoderChild* AllocPGMPAudioDecoderChild() override;
   bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) override;
 
   PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
   bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
 
-  PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() override;
+  PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId) override;
   bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) override;
 
   PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() override;
   bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/media/gmp/GMPContentParent.cpp
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -189,20 +189,21 @@ GMPContentParent::GetGMPAudioDecoder(GMP
   NS_ADDREF(vap);
   *aGMPAD = vap;
   mAudioDecoders.AppendElement(vap);
 
   return NS_OK;
 }
 
 nsresult
-GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
+GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD,
+                                     uint32_t aDecryptorId)
 {
   // returned with one anonymous AddRef that locks it until Destroy
-  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
+  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor(aDecryptorId);
   if (!pvdp) {
     return NS_ERROR_FAILURE;
   }
   GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
   // This addref corresponds to the Proxy pointer the consumer is returned.
   // It's dropped by calling Close() on the interface.
   NS_ADDREF(vdp);
   *aGMPVD = vdp;
@@ -225,17 +226,17 @@ GMPContentParent::GetGMPVideoEncoder(GMP
   NS_ADDREF(vep);
   *aGMPVE = vep;
   mVideoEncoders.AppendElement(vep);
 
   return NS_OK;
 }
 
 PGMPVideoDecoderParent*
-GMPContentParent::AllocPGMPVideoDecoderParent()
+GMPContentParent::AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId)
 {
   GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
   NS_ADDREF(vdp);
   return vdp;
 }
 
 bool
 GMPContentParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
--- a/dom/media/gmp/GMPContentParent.h
+++ b/dom/media/gmp/GMPContentParent.h
@@ -22,17 +22,18 @@ class GMPVideoEncoderParent;
 class GMPContentParent final : public PGMPContentParent,
                                public GMPSharedMem
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPContentParent)
 
   explicit GMPContentParent(GMPParent* aParent = nullptr);
 
-  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
+  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD,
+                              uint32_t aDecryptorId);
   void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
 
   nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
   void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
 
   nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
   void DecryptorDestroyed(GMPDecryptorParent* aSession);
 
@@ -61,17 +62,17 @@ public:
     return mPluginId;
   }
 
 private:
   ~GMPContentParent();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() override;
+  PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId) override;
   bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) override;
 
   PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() override;
   bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) override;
 
   PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
   bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
 
--- a/dom/media/gmp/GMPCrashHelperHolder.h
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -48,22 +48,22 @@ namespace mozilla {
 // In the crashing case, the GMPCrashHelpers are deallocated when the crash
 // report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
 //
 // It's a bit yuck that we have to have two paths for disconnecting the crash
 // helpers, but there aren't really any better options.
 class GMPCrashHelperHolder
 {
 public:
-  
+
   void SetCrashHelper(GMPCrashHelper* aHelper)
   {
     mCrashHelper = aHelper;
   }
-  
+
   GMPCrashHelper* GetCrashHelper()
   {
     return mCrashHelper;
   }
 
   void MaybeDisconnect(bool aAbnormalShutdown)
   {
     if (!aAbnormalShutdown) {
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -68,16 +68,21 @@ GMPDecryptorChild::CallOnGMPThread(Metho
   }
 }
 
 void
 GMPDecryptorChild::Init(GMPDecryptor* aSession)
 {
   MOZ_ASSERT(aSession);
   mSession = aSession;
+  // The ID of this decryptor is the IPDL actor ID. Note it's unique inside
+  // the child process, but not necessarily across all gecko processes. However,
+  // since GMPDecryptors are segregated by node ID/origin, we shouldn't end up
+  // with clashes in the content process.
+  SendSetDecryptorId(Id());
 }
 
 void
 GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
                                 const char* aSessionId,
                                 uint32_t aSessionIdLength)
 {
   CALL_ON_GMP_THREAD(SendSetSessionId,
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -35,16 +35,27 @@ GMPDecryptorParent::GMPDecryptorParent(G
 {
   MOZ_ASSERT(mPlugin && mGMPThread);
 }
 
 GMPDecryptorParent::~GMPDecryptorParent()
 {
 }
 
+bool
+GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId)
+{
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use a dead GMP decrypter!");
+    return false;
+  }
+  mCallback->SetDecryptorId(aId);
+  return true;
+}
+
 nsresult
 GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback,
                          bool aDistinctiveIdentifierRequired,
                          bool aPersistentStateRequired)
 {
   LOGD(("GMPDecryptorParent[%p]::Init()", this));
 
   if (mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -67,16 +67,18 @@ public:
 
   void Shutdown();
 
 private:
   ~GMPDecryptorParent();
 
   // PGMPDecryptorParent
 
+  bool RecvSetDecryptorId(const uint32_t& aId) override;
+
   bool RecvSetSessionId(const uint32_t& aCreateSessionToken,
                         const nsCString& aSessionId) override;
 
   bool RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
                                      const bool& aSuccess) override;
 
   bool RecvResolvePromise(const uint32_t& aPromiseId) override;
 
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -34,17 +34,18 @@ public:
             uint32_t aUTF8LibPathLen,
             char* aOriginSalt,
             uint32_t aOriginSaltLen,
             const GMPPlatformAPI* aPlatformAPI,
             GMPAdapter* aAdapter) override;
 
   GMPErr GetAPI(const char* aAPIName,
                 void* aHostAPI,
-                void** aPluginAPI) override;
+                void** aPluginAPI,
+                uint32_t aDecryptorId) override;
 
   void Shutdown() override;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) override;
 #endif
 
 private:
@@ -75,17 +76,20 @@ public:
     }
     GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
     if (!initFunc) {
       return GMPNotImplementedErr;
     }
     return initFunc(aPlatformAPI);
   }
 
-  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override
+  GMPErr GMPGetAPI(const char* aAPIName,
+                   void* aHostAPI,
+                   void** aPluginAPI,
+                   uint32_t aDecryptorId) override
   {
     if (!mLib) {
       return GMPGenericErr;
     }
     GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
     if (!getapiFunc) {
       return GMPNotImplementedErr;
     }
@@ -185,19 +189,20 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
   mAdapter->GMPSetNodeId(nodeId.c_str(), nodeId.size());
 
   return true;
 }
 
 GMPErr
 GMPLoaderImpl::GetAPI(const char* aAPIName,
                       void* aHostAPI,
-                      void** aPluginAPI)
+                      void** aPluginAPI,
+                      uint32_t aDecryptorId)
 {
-  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI);
+  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
 }
 
 void
 GMPLoaderImpl::Shutdown()
 {
   if (mAdapter) {
     mAdapter->GMPShutdown();
   }
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -37,17 +37,20 @@ public:
   virtual ~GMPAdapter() {}
   // Sets the adapted to plugin library module.
   // Note: the GMPAdapter is responsible for calling PR_UnloadLibrary on aLib
   // when it's finished with it.
   virtual void SetAdaptee(PRLibrary* aLib) = 0;
 
   // These are called in place of the corresponding GMP API functions.
   virtual GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) = 0;
-  virtual GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual GMPErr GMPGetAPI(const char* aAPIName,
+                           void* aHostAPI,
+                           void** aPluginAPI,
+                           uint32_t aDecryptorId) = 0;
   virtual void GMPShutdown() = 0;
   virtual void GMPSetNodeId(const char* aNodeId, uint32_t aLength) = 0;
 };
 
 // Encapsulates generating the device-bound node id, activating the sandbox,
 // loading the GMP, and passing the node id to the GMP (in that order).
 //
 // In Desktop Gecko, the implementation of this lives in plugin-container,
@@ -78,17 +81,20 @@ public:
   virtual bool Load(const char* aUTF8LibPath,
                     uint32_t aLibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
                     const GMPPlatformAPI* aPlatformAPI,
                     GMPAdapter* aAdapter = nullptr) = 0;
 
   // Retrieves an interface pointer from the GMP.
-  virtual GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual GMPErr GetAPI(const char* aAPIName,
+                        void* aHostAPI,
+                        void** aPluginAPI,
+                        uint32_t aDecryptorId) = 0;
 
   // Calls the GMPShutdown function exported by the GMP lib, and unloads the
   // plugin library.
   virtual void Shutdown() = 0;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -344,54 +344,58 @@ GeckoMediaPluginService::GetGMPAudioDeco
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
 {
 public:
   explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
-                                                  GMPCrashHelper* aHelper)
+                                                  GMPCrashHelper* aHelper,
+                                                  uint32_t aDecryptorId)
    : mCallback(Move(aCallback))
    , mHelper(aHelper)
+   , mDecryptorId(aDecryptorId)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoDecoderParent* gmpVDP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP))) {
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP, mDecryptorId))) {
       videoHost = &gmpVDP->Host();
       gmpVDP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVDP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoDecoderCallback> mCallback;
   RefPtr<GMPCrashHelper> mHelper;
+  const uint32_t mDecryptorId;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoDecoder(GMPCrashHelper* aHelper,
-                                            nsTArray<nsCString>* aTags,
-                                            const nsACString& aNodeId,
-                                            UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+GeckoMediaPluginService::GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                                      nsTArray<nsCString>* aTags,
+                                                      const nsACString& aNodeId,
+                                                      UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                                      uint32_t aDecryptorId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper));
+    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper, aDecryptorId));
   if (!GetContentParentFrom(aHelper,
                             aNodeId,
                             NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
                             *aTags,
                             Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -64,20 +64,21 @@ public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
   virtual nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetThread(nsIThread** aThread) override;
-  NS_IMETHOD GetGMPVideoDecoder(GMPCrashHelper* aHelper,
-                                nsTArray<nsCString>* aTags,
-                                const nsACString& aNodeId,
-                                UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+  NS_IMETHOD GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                          nsTArray<nsCString>* aTags,
+                                          const nsACString& aNodeId,
+                                          UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                          uint32_t aDecryptorId)
     override;
   NS_IMETHOD GetGMPVideoEncoder(GMPCrashHelper* aHelper,
                                 nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
     override;
   NS_IMETHOD GetGMPAudioDecoder(GMPCrashHelper* aHelper,
                                 nsTArray<nsCString>* aTags,
@@ -85,16 +86,26 @@ public:
                                 UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
     override;
   NS_IMETHOD GetGMPDecryptor(GMPCrashHelper* aHelper,
                              nsTArray<nsCString>* aTags,
                              const nsACString& aNodeId,
                              UniquePtr<GetGMPDecryptorCallback>&& aCallback)
     override;
 
+  // Helper for backwards compatibility with WebRTC/tests.
+  NS_IMETHOD
+  GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                     nsTArray<nsCString>* aTags,
+                     const nsACString& aNodeId,
+                     UniquePtr<GetGMPVideoDecoderCallback>&& aCallback) override
+  {
+    return GetDecryptingGMPVideoDecoder(aHelper, aTags, aNodeId, Move(aCallback), 0);
+  }
+
   int32_t AsyncShutdownTimeoutMs();
 
   NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
                                      const nsACString& aPluginName) override;
 
   RefPtr<AbstractThread> GetAbstractGMPThread();
 
   void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1383,25 +1383,22 @@ GeckoMediaPluginServiceParent::GetNodeId
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
 
   nsresult rv;
 
-  if (aGMPName.EqualsLiteral("gmp-widevinecdm") ||
-      aOrigin.EqualsLiteral("null") ||
+  if (aOrigin.EqualsLiteral("null") ||
       aOrigin.IsEmpty() ||
       aTopLevelOrigin.EqualsLiteral("null") ||
       aTopLevelOrigin.IsEmpty()) {
-    // This is for the Google Widevine CDM, which doesn't have persistent
-    // storage and which can't handle being used by more than one origin at
-    // once in the same plugin instance, or at least one of the
-    // (origin, topLevelOrigin) is null or empty; probably a local file.
+    // (origin, topLevelOrigin) is null or empty; this is for an anonymous
+    // origin, probably a local file, for which we don't provide persistent storage.
     // Generate a random node id, and don't store it so that the GMP's storage
     // is temporary and the process for this GMP is not shared with GMP
     // instances that have the same nodeId.
     nsAutoCString salt;
     rv = GenerateRandomPathName(salt, NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
--- a/dom/media/gmp/PGMPContent.ipdl
+++ b/dom/media/gmp/PGMPContent.ipdl
@@ -20,14 +20,14 @@ intr protocol PGMPContent
   manages PGMPAudioDecoder;
   manages PGMPDecryptor;
   manages PGMPVideoDecoder;
   manages PGMPVideoEncoder;
 
 child:
   async PGMPAudioDecoder();
   async PGMPDecryptor();
-  async PGMPVideoDecoder();
+  async PGMPVideoDecoder(uint32_t aDecryptorId);
   async PGMPVideoEncoder();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -48,16 +48,18 @@ child:
                 uint8_t[] aBuffer,
                 GMPDecryptionData aMetadata);
 
   async DecryptingComplete();
 
 parent:
   async __delete__();
 
+  async SetDecryptorId(uint32_t aId);
+
   async SetSessionId(uint32_t aCreateSessionToken,
                      nsCString aSessionId);
 
   async ResolveLoadSessionPromise(uint32_t aPromiseId,
                                   bool aSuccess);
 
   async ResolvePromise(uint32_t aPromiseId);
 
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -89,16 +89,29 @@ interface mozIGeckoMediaPluginService : 
    */
   [noscript]
   void getGMPVideoDecoder(in GMPCrashHelperPtr helper,
                           in TagArray tags,
                           [optional] in ACString nodeId,
                           in GetGMPVideoDecoderCallback callback);
 
   /**
+   * Gets a video decoder as per getGMPVideoDecoder, except it is linked to
+   * with a corresponding GMPDecryptor via the decryptor's ID.
+   * This is a temporary measure, until we can implement a Chromium CDM
+   * GMP protocol which does both decryption and decoding.
+   */
+  [noscript]
+  void getDecryptingGMPVideoDecoder(in GMPCrashHelperPtr helper,
+                                    in TagArray tags,
+                                    in ACString nodeId,
+                                    in GetGMPVideoDecoderCallback callback,
+                                    in uint32_t decryptorId);
+
+  /**
    * Get a video encoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags.
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -8,24 +8,21 @@
 #include "VideoUtils.h"
 #include "WidevineDecryptor.h"
 #include "WidevineUtils.h"
 #include "WidevineVideoDecoder.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
-#include "mozilla/StaticPtr.h"
 
 static const GMPPlatformAPI* sPlatform = nullptr;
 
 namespace mozilla {
 
-StaticRefPtr<CDMWrapper> sCDMWrapper;
-
 GMPErr GMPGetCurrentTime(GMPTimestamp* aOutTime) {
   return sPlatform->getcurrenttime(aOutTime);
 }
 
 // Call on main thread only.
 GMPErr GMPSetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) {
   return sPlatform->settimer(aTask, aTimeoutMS);
 }
@@ -83,61 +80,62 @@ WidevineAdapter::GMPInit(const GMPPlatfo
   init();
 
   return GMPNoErr;
 }
 
 GMPErr
 WidevineAdapter::GMPGetAPI(const char* aAPIName,
                            void* aHostAPI,
-                           void** aPluginAPI)
+                           void** aPluginAPI,
+                           uint32_t aDecryptorId)
 {
-  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p",
-      aAPIName, aHostAPI, aPluginAPI, this);
+  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
+      aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
   if (!strcmp(aAPIName, GMP_API_DECRYPTOR)) {
-    if (sCDMWrapper) {
-      // We only support one CDM instance per GMP process. Fail!
-      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per process! FAIL!");
+    if (WidevineDecryptor::GetInstance(aDecryptorId)) {
+      // We only support one CDM instance per PGMPDecryptor. Fail!
+      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per IPDL actor! FAIL!");
       return GMPQuotaExceededErr;
     }
     auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
       PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
     if (!create) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p FAILED to find CreateCdmInstance",
-        aAPIName, aHostAPI, aPluginAPI, this);
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to find CreateCdmInstance",
+        aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
       return GMPGenericErr;
     }
 
     WidevineDecryptor* decryptor = new WidevineDecryptor();
 
     auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
       create(cdm::ContentDecryptionModule::kVersion,
              kEMEKeySystemWidevine.get(),
              kEMEKeySystemWidevine.Length(),
              &GetCdmHost,
              decryptor));
     if (!cdm) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p FAILED to create cdm",
-          aAPIName, aHostAPI, aPluginAPI, this);
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
+          aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
       return GMPGenericErr;
     }
     Log("cdm: 0x%x", cdm);
-    sCDMWrapper = new CDMWrapper(cdm);
-    decryptor->SetCDM(RefPtr<CDMWrapper>(sCDMWrapper));
+    RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm));
+    decryptor->SetCDM(wrapper, aDecryptorId);
     *aPluginAPI = decryptor;
 
   } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
-    if (!sCDMWrapper) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p No cdm for video decoder",
-          aAPIName, aHostAPI, aPluginAPI, this);
+    RefPtr<CDMWrapper> wrapper = WidevineDecryptor::GetInstance(aDecryptorId);
+    if (!wrapper) {
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder",
+          aAPIName, aHostAPI, aPluginAPI, thiss, aDecryptorId);
       return GMPGenericErr;
     }
     *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI),
-                                           RefPtr<CDMWrapper>(sCDMWrapper));
-
+                                           wrapper);
   }
   return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
 }
 
 void
 WidevineAdapter::GMPShutdown()
 {
   Log("WidevineAdapter::GMPShutdown()");
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.h
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.h
@@ -16,17 +16,20 @@ namespace mozilla {
 
 class WidevineAdapter : public gmp::GMPAdapter {
 public:
 
   void SetAdaptee(PRLibrary* aLib) override;
 
   // These are called in place of the corresponding GMP API functions.
   GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override;
-  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override;
+  GMPErr GMPGetAPI(const char* aAPIName,
+                   void* aHostAPI,
+                   void** aPluginAPI,
+                   uint32_t aDecryptorId) override;
   void GMPShutdown() override;
   void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override;
 
   static bool Supports(int32_t aModuleVersion,
                        int32_t aInterfaceVersion,
                        int32_t aHostVersion);
 
 private:
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -11,33 +11,48 @@
 #include <mozilla/SizePrintfMacros.h>
 #include <stdarg.h>
 
 using namespace cdm;
 using namespace std;
 
 namespace mozilla {
 
+static map<uint32_t, RefPtr<CDMWrapper>> sDecryptors;
+
+/* static */
+RefPtr<CDMWrapper>
+WidevineDecryptor::GetInstance(uint32_t aInstanceId)
+{
+  auto itr = sDecryptors.find(aInstanceId);
+  if (itr != sDecryptors.end()) {
+    return itr->second;
+  }
+  return nullptr;
+}
+
 
 WidevineDecryptor::WidevineDecryptor()
   : mCallback(nullptr)
 {
   Log("WidevineDecryptor created this=%p", this);
   AddRef(); // Released in DecryptingComplete().
 }
 
 WidevineDecryptor::~WidevineDecryptor()
 {
   Log("WidevineDecryptor destroyed this=%p", this);
 }
 
 void
-WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM)
+WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
 {
   mCDM = aCDM;
+  mInstanceId = aInstanceId;
+  sDecryptors[mInstanceId] = aCDM;
 }
 
 void
 WidevineDecryptor::Init(GMPDecryptorCallback* aCallback,
                         bool aDistinctiveIdentifierRequired,
                         bool aPersistentStateRequired)
 {
   Log("WidevineDecryptor::Init() this=%p distinctiveId=%d persistentState=%d",
@@ -205,17 +220,22 @@ WidevineDecryptor::Decrypt(GMPBuffer* aB
   }
   mCallback->Decrypted(aBuffer, ToGMPErr(rv));
 }
 
 void
 WidevineDecryptor::DecryptingComplete()
 {
   Log("WidevineDecryptor::DecryptingComplete() this=%p", this);
+  // Drop our references to the CDMWrapper. When any other references
+  // held elsewhere are dropped (for example references held by a
+  // WidevineVideoDecoder, or a runnable), the CDMWrapper destroys
+  // the CDM.
   mCDM = nullptr;
+  sDecryptors.erase(mInstanceId);
   mCallback = nullptr;
   Release();
 }
 
 class WidevineBuffer : public cdm::Buffer {
 public:
   explicit WidevineBuffer(size_t aSize) {
     Log("WidevineBuffer(size=" PRIuSIZE ") created", aSize);
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.h
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.h
@@ -19,17 +19,19 @@ class WidevineDecryptor : public GMPDecr
                         , public cdm::Host_8
 {
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WidevineDecryptor)
 
   WidevineDecryptor();
 
-  void SetCDM(RefPtr<CDMWrapper> aCDM);
+  void SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aDecryptorId);
+
+  static RefPtr<CDMWrapper> GetInstance(uint32_t aDecryptorId);
 
   // GMPDecryptor
   void Init(GMPDecryptorCallback* aCallback,
             bool aDistinctiveIdentifierRequired,
             bool aPersistentStateRequired) override;
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aPromiseId,
@@ -119,13 +121,14 @@ private:
   ~WidevineDecryptor();
   RefPtr<CDMWrapper> mCDM;
   cdm::ContentDecryptionModule_8* CDM() { return mCDM->GetCDM(); }
 
   GMPDecryptorCallback* mCallback;
   std::map<uint32_t, uint32_t> mPromiseIdToNewSessionTokens;
   bool mDistinctiveIdentifierRequired = false;
   bool mPersistentStateRequired = false;
+  uint32_t mInstanceId = 0;
 };
 
 } // namespace mozilla
 
 #endif // WidevineDecryptor_h_
--- a/dom/media/gtest/TestAudioSegment.cpp
+++ b/dom/media/gtest/TestAudioSegment.cpp
@@ -197,17 +197,17 @@ void TestUpmixStereo()
 
   AudioChannelsUpMix(&channelsptr, 2, SilentChannel<T>());
 
   for (size_t channel = 0; channel < 2; channel++) {
     for (size_t i = 0; i < arraySize; i++) {
       ASSERT_TRUE(channelsptr[channel][i] == GetHighValue<T>());
     }
   }
-  delete channels[0];
+  delete [] channels[0];
 }
 
 template<typename T>
 void TestDownmixStereo()
 {
   const size_t arraySize = 1024;
   nsTArray<const T*> inputptr;
   nsTArray<T*> input;
@@ -229,18 +229,18 @@ void TestDownmixStereo()
 
   AudioChannelsDownMix(inputptr, output, 1, arraySize);
 
   for (size_t i = 0; i < arraySize; i++) {
     ASSERT_TRUE(output[0][i] == GetSilentValue<T>());
     ASSERT_TRUE(output[0][i] == GetSilentValue<T>());
   }
 
-  delete output[0];
-  delete output;
+  delete [] output[0];
+  delete [] output;
 }
 
 TEST(AudioSegment, Test)
 {
   TestInterleaveAndConvert<float, float>();
   TestInterleaveAndConvert<float, int16_t>();
   TestInterleaveAndConvert<int16_t, float>();
   TestInterleaveAndConvert<int16_t, int16_t>();
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -591,37 +591,33 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
   class CreateDecryptorDone : public GetGMPDecryptorCallback
   {
   public:
-    CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
-      : mRunner(aRunner),
-        mContinuation(aContinuation)
+    explicit CreateDecryptorDone(GMPStorageTest* aRunner)
+      : mRunner(aRunner)
     {
     }
 
     void Done(GMPDecryptorProxy* aDecryptor) override
     {
       mRunner->mDecryptor = aDecryptor;
       EXPECT_TRUE(!!mRunner->mDecryptor);
 
       if (mRunner->mDecryptor) {
         mRunner->mDecryptor->Init(mRunner, false, true);
       }
-      nsCOMPtr<nsIThread> thread(GetGMPThread());
-      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
     }
 
   private:
     RefPtr<GMPStorageTest> mRunner;
-    nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   void CreateDecryptor(const nsCString& aNodeId,
                        const nsCString& aUpdate)
   {
     nsTArray<nsCString> updates;
     updates.AppendElement(aUpdate);
     nsCOMPtr<nsIRunnable> continuation(new Updates(this, Move(updates)));
@@ -674,17 +670,23 @@ class GMPStorageTest : public GMPDecrypt
 
     mNodeId = aNodeId;
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     UniquePtr<GetGMPDecryptorCallback> callback(
-      new CreateDecryptorDone(this, aContinuation));
+      new CreateDecryptorDone(this));
+
+    // Continue after the OnSetDecryptorId message, so that we don't
+    // get warnings in the async shutdown tests due to receiving the
+    // SetDecryptorId message after we've started shutdown.
+    mSetDecryptorIdContinuation = aContinuation;
+
     nsresult rv =
       service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
@@ -1368,16 +1370,26 @@ class GMPStorageTest : public GMPDecrypt
       nsCOMPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
       mExpected.RemoveElementAt(0);
       if (continuation) {
         NS_DispatchToCurrentThread(continuation);
       }
     }
   }
 
+  void SetDecryptorId(uint32_t aId) override
+  {
+    if (!mSetDecryptorIdContinuation) {
+      return;
+    }
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(mSetDecryptorIdContinuation, NS_DISPATCH_NORMAL);
+    mSetDecryptorIdContinuation = nullptr;
+  }
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override { }
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override {}
   void ResolvePromise(uint32_t aPromiseId) override {}
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aException,
                      const nsCString& aSessionId) override { }
@@ -1411,16 +1423,18 @@ private:
       , mContinuation(aContinuation)
     {}
     nsCString mMessage;
     nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   nsTArray<ExpectedMessage> mExpected;
 
+  RefPtr<nsIRunnable> mSetDecryptorIdContinuation;
+
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
   nsCString mNodeId;
 };
 
 void
 GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
--- a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp
@@ -27,16 +27,17 @@ EMEVideoCallbackAdapter::Error(GMPErr aE
 
 EMEVideoDecoder::EMEVideoDecoder(CDMProxy* aProxy,
                                  const GMPVideoDecoderParams& aParams)
   : GMPVideoDecoder(GMPVideoDecoderParams(aParams).WithAdapter(
                     new EMEVideoCallbackAdapter(aParams.mCallback,
                                                 VideoInfo(aParams.mConfig.mDisplay),
                                                 aParams.mImageContainer)))
   , mProxy(aProxy)
+  , mDecryptorId(aProxy->GetDecryptorId())
 {}
 
 void
 EMEVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
 {
   VideoInfo config = GetConfig();
   if (MP4Decoder::IsH264(config.mMimeType)) {
     aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
--- a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
+++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
@@ -28,16 +28,18 @@ public:
 
 class EMEVideoDecoder : public GMPVideoDecoder {
 public:
   EMEVideoDecoder(CDMProxy* aProxy, const GMPVideoDecoderParams& aParams);
 
 private:
   void InitTags(nsTArray<nsCString>& aTags) override;
   nsCString GetNodeId() override;
+  uint32_t DecryptorId() const override { return mDecryptorId; }
   GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample) override;
 
   RefPtr<CDMProxy> mProxy;
+  uint32_t mDecryptorId;
 };
 
 } // namespace mozilla
 
 #endif // EMEVideoDecoder_h_
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -306,17 +306,21 @@ GMPVideoDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPVideoDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetDecryptingGMPVideoDecoder(mCrashHelper,
+                                                   &tags,
+                                                   GetNodeId(),
+                                                   Move(callback),
+                                                   DecryptorId()))) {
     mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
   }
 
   return promise;
 }
 
 void
 GMPVideoDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -77,16 +77,17 @@ public:
   const char* GetDescriptionName() const override
   {
     return "GMP video decoder";
   }
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
+  virtual uint32_t DecryptorId() const { return 0; }
   virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample);
   virtual const VideoInfo& GetConfig() const;
 
 private:
 
   class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
   {
   public:
--- a/dom/presentation/provider/AndroidCastDeviceProvider.js
+++ b/dom/presentation/provider/AndroidCastDeviceProvider.js
@@ -403,17 +403,17 @@ AndroidCastDeviceProvider.prototype = {
     if (!this._listener) {
       // remove observer
       Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED);
       Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED);
       return;
     }
 
     // Sync all device already found by Android.
-    Services.obs.notifyObservers(null, TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE, "");
+    Messaging.sendRequest({ type: TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE });
     // Observer registration
     Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED, false);
     Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED, false);
   },
 
   get listener() {
     return this._listener;
   },
--- a/dom/security/test/csp/test_referrerdirective.html
+++ b/dom/security/test/csp/test_referrerdirective.html
@@ -110,17 +110,20 @@ var referrerDirectiveTests = {
     SimpleTest.finish();
   }
 };
 
 SimpleTest.waitForExplicitFinish();
 // have to disable mixed content blocking to test https->http referrers.
 SpecialPowers.pushPrefEnv({
     'set': [['security.mixed_content.block_active_content',   false],
-            ['security.mixed_content.block_display_content',  false]]
+            ['security.mixed_content.block_display_content',  false],
+            ['security.mixed_content.send_hsts_priming',  false],
+            ['security.mixed_content.use_hsts',  false],
+    ]
     },
     function() {
       // each of the iframes we create will call us back when its contents are loaded.
       window.addEventListener("message", referrerDirectiveTests.onIframeComplete.bind(window), false);
 
       // one iframe created for each test case
       for (var id in testData) {
         var elt = document.createElement("iframe");
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1247,16 +1247,18 @@ var interfaceNamesInGlobalScope =
     "WebGLTexture",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WebGLTransformFeedback",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WebGLUniformLocation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WebGLVertexArrayObject",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "WebGLVertexArrayObjectOES",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "WebKitCSSMatrix",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WebSocket",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WheelEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Window",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/url/URL.cpp
+++ b/dom/url/URL.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/URLBinding.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "nsContentUtils.h"
 #include "nsEscape.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsIIOService.h"
+#include "nsIURIWithQuery.h"
 #include "nsIURL.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
@@ -516,65 +517,67 @@ URLMainThread::SetPort(const nsAString& 
   mURI->SetPort(port);
 }
 
 void
 URLMainThread::GetPathname(nsAString& aPathname, ErrorResult& aRv) const
 {
   aPathname.Truncate();
 
-  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
-  if (!url) {
-    nsAutoCString path;
-    nsresult rv = mURI->GetPath(path);
-    if (NS_FAILED(rv)){
-      // Do not throw!  Not having a valid URI or URL should result in an empty
-      // string.
-      return;
+  // Do not throw!  Not having a valid URI or URL should result in an empty
+  // string.
+
+  nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+  if (url) {
+    nsAutoCString file;
+    nsresult rv = url->GetFilePath(file);
+    if (NS_SUCCEEDED(rv)) {
+      CopyUTF8toUTF16(file, aPathname);
     }
 
-    CopyUTF8toUTF16(path, aPathname);
     return;
   }
 
-  nsAutoCString file;
-  nsresult rv = url->GetFilePath(file);
+  nsAutoCString path;
+  nsresult rv = mURI->GetPath(path);
   if (NS_SUCCEEDED(rv)) {
-    CopyUTF8toUTF16(file, aPathname);
+    CopyUTF8toUTF16(path, aPathname);
   }
 }
 
 void
 URLMainThread::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
-  if (!url) {
-    // Ignore failures to be compatible with NS4.
+  // Do not throw!
+
+  nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+  if (url) {
+    url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
     return;
   }
-
-  url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
 }
 
 void
 URLMainThread::GetSearch(nsAString& aSearch, ErrorResult& aRv) const
 {
   aSearch.Truncate();
 
-  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
-  if (!url) {
-    // Do not throw!  Not having a valid URI or URL should result in an empty
-    // string.
-    return;
-  }
+  // Do not throw!  Not having a valid URI or URL should result in an empty
+  // string.
 
   nsAutoCString search;
-  nsresult rv = url->GetQuery(search);
-  if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
-    CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
+  nsresult rv;
+
+  nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+  if (url) {
+    rv = url->GetQuery(search);
+    if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
+      CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
+    }
+    return;
   }
 }
 
 void
 URLMainThread::GetHash(nsAString& aHash, ErrorResult& aRv) const
 {
   aHash.Truncate();
 
@@ -593,23 +596,23 @@ void
 URLMainThread::SetHash(const nsAString& aHash, ErrorResult& aRv)
 {
   mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
 }
 
 void
 URLMainThread::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
-  if (!url) {
-    // Ignore failures to be compatible with NS4.
+  // Ignore failures to be compatible with NS4.
+
+  nsCOMPtr<nsIURIWithQuery> uriWithQuery(do_QueryInterface(mURI));
+  if (uriWithQuery) {
+    uriWithQuery->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
     return;
   }
-
-  url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
 }
 
 } // anonymous namespace
 
 ///////////////////////////////////////////////////////////////////////////////
 // URL for Workers
 ///////////////////////////////////////////////////////////////////////////////
 
--- a/dom/url/tests/test_url.html
+++ b/dom/url/tests/test_url.html
@@ -212,16 +212,26 @@
 
     { url: 'about:blank',
       base: undefined,
       error: false,
       protocol: 'about:',
       pathname: 'blank',
       skip_setters: false,
     },
+
+    { url: 'foo:bar?what#yeah',
+      base: undefined,
+      error: false,
+      protocol: 'foo:',
+      pathname: 'bar',
+      search: '?what',
+      hash: '#yeah',
+      skip_setters: false,
+    },
   ];
 
   while(tests.length) {
     var test = tests.shift();
 
     var error = false;
     var url;
     try {
@@ -382,10 +392,45 @@
     is(url.href, "ftp://tmp/test");
 
     url = new URL("ftp:\\\\tmp\\test", base);
     is(url.href, "ftp://tmp/test");
 
     url = new URL("scheme://tmp\\test", base);
     is(url.href, "scheme://tmp\\test");
   </script>
+
+  <script>
+    var url = new URL("scheme:path/to/file?query#hash");
+    is(url.href, "scheme:path/to/file?query#hash");
+    is(url.pathname, "path/to/file");
+    is(url.search, "?query");
+    is(url.hash, "#hash");
+
+    // pathname cannot be overwritten.
+    url.pathname = "new/path?newquery#newhash";
+    is(url.href, "scheme:path/to/file?query#hash");
+
+    url.search = "?newquery#newhash";
+    is(url.href, "scheme:path/to/file?newquery%23newhash#hash");
+
+    // nulls get encoded, whitespace gets stripped
+    url = new URL("scheme:pa\0\nth/to/fi\0\nle?qu\0\nery#ha\0\nsh");
+    is(url.href, "scheme:pa%00th/to/fi%00le?qu%00ery#ha%00sh");
+
+    url.search = "new\0\nquery";
+    is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#ha%00sh");
+    url.hash = "new\0\nhash";
+    is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#new%00%0Ahash");
+
+    url = new URL("scheme:path#hash");
+    is(url.href, "scheme:path#hash");
+    url.search = "query";
+    is(url.href, "scheme:path?query#hash");
+    url.hash = "";
+    is(url.href, "scheme:path?query");
+    url.hash = "newhash";
+    is(url.href, "scheme:path?query#newhash");
+    url.search = "";
+    is(url.href, "scheme:path#newhash");
+  </script>
 </body>
 </html>
--- a/dom/webidl/HTMLCanvasElement.webidl
+++ b/dom/webidl/HTMLCanvasElement.webidl
@@ -17,33 +17,36 @@ interface HTMLCanvasElement : HTMLElemen
   [Pure, SetterThrows]
            attribute unsigned long width;
   [Pure, SetterThrows]
            attribute unsigned long height;
 
   [Throws]
   nsISupports? getContext(DOMString contextId, optional any contextOptions = null);
 
-  [Throws]
+  [Throws, NeedsCallerType]
   DOMString toDataURL(optional DOMString type = "",
                       optional any encoderOptions);
-  [Throws]
+  [Throws, NeedsCallerType]
   void toBlob(BlobCallback _callback,
               optional DOMString type = "",
               optional any encoderOptions);
 };
 
 // Mozilla specific bits
 partial interface HTMLCanvasElement {
   [Pure, SetterThrows]
            attribute boolean mozOpaque;
-  [Throws]
+  [Throws, NeedsCallerType]
   File mozGetAsFile(DOMString name, optional DOMString? type = null);
+  // A Mozilla-only extension to get a canvas context backed by double-buffered
+  // shared memory. Only privileged callers can call this.
   [ChromeOnly, Throws]
   nsISupports? MozGetIPCContext(DOMString contextId);
+
            attribute PrintCallback? mozPrintCallback;
 
   [Throws, UnsafeInPrerendering, Pref="canvas.capturestream.enabled"]
   CanvasCaptureMediaStream captureStream(optional double frameRate);
 };
 
 // For OffscreenCanvas
 // Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -40,25 +40,26 @@ interface HTMLDocument : Document {
   void close();
   [Throws]
   void write(DOMString... text);
   [Throws]
   void writeln(DOMString... text);
 
   [SetterThrows, NeedsSubjectPrincipal]
            attribute DOMString designMode;
-  [Throws]
+  [Throws, NeedsCallerType]
   boolean execCommand(DOMString commandId, optional boolean showUI = false,
                       optional DOMString value = "");
-  [Throws]
+  [Throws, NeedsCallerType]
   boolean queryCommandEnabled(DOMString commandId);
   [Throws]
   boolean queryCommandIndeterm(DOMString commandId);
   [Throws]
   boolean queryCommandState(DOMString commandId);
+  [NeedsCallerType]
   boolean queryCommandSupported(DOMString commandId);
   [Throws]
   DOMString queryCommandValue(DOMString commandId);
 
   [TreatNullAs=EmptyString] attribute DOMString fgColor;
   [TreatNullAs=EmptyString] attribute DOMString linkColor;
   [TreatNullAs=EmptyString] attribute DOMString vlinkColor;
   [TreatNullAs=EmptyString] attribute DOMString alinkColor;
--- a/dom/webidl/HTMLElement.webidl
+++ b/dom/webidl/HTMLElement.webidl
@@ -23,16 +23,17 @@ interface HTMLElement : Element {
   readonly attribute DOMStringMap dataset;
 
   [GetterThrows, Pure, TreatNullAs=EmptyString]
            attribute DOMString innerText;
 
   // user interaction
   [SetterThrows, Pure]
            attribute boolean hidden;
+  [NeedsCallerType]
   void click();
   [SetterThrows, Pure]
            attribute long tabIndex;
   [Throws]
   void focus();
   [Throws]
   void blur();
   [SetterThrows, Pure]
--- a/dom/webidl/MimeTypeArray.webidl
+++ b/dom/webidl/MimeTypeArray.webidl
@@ -1,15 +1,13 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-// [LegacyUnenumerableNamedProperties]
-// Named properties enumerable for now; see
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1270364
+[LegacyUnenumerableNamedProperties]
 interface MimeTypeArray {
   readonly attribute unsigned long length;
 
   getter MimeType? item(unsigned long index);
   getter MimeType? namedItem(DOMString name);
 };
--- a/dom/webidl/Plugin.webidl
+++ b/dom/webidl/Plugin.webidl
@@ -1,17 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-// [LegacyUnenumerableNamedProperties]
-// Named properties enumerable for now; see
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1270366
+[LegacyUnenumerableNamedProperties]
 interface Plugin {
   readonly attribute DOMString description;
   readonly attribute DOMString filename;
   readonly attribute DOMString version;
   readonly attribute DOMString name;
 
   readonly attribute unsigned long length;
   getter MimeType? item(unsigned long index);
--- a/dom/webidl/PluginArray.webidl
+++ b/dom/webidl/PluginArray.webidl
@@ -1,17 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-// [LegacyUnenumerableNamedProperties]
-// Named properties enumerable for now; see
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1270366
+[LegacyUnenumerableNamedProperties]
 interface PluginArray {
   readonly attribute unsigned long length;
 
   getter Plugin? item(unsigned long index);
   getter Plugin? namedItem(DOMString name);
 
   void refresh(optional boolean reloadDocuments = false);
 };
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -58,9 +58,13 @@ enum RequestContext {
   "sharedworker", "subresource", "style", "track", "video", "worker", "xmlhttprequest",
   "xslt"
 };
 
 enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
 enum RequestCredentials { "omit", "same-origin", "include" };
 enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" };
 enum RequestRedirect { "follow", "error", "manual" };
-enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
+enum ReferrerPolicy {
+  "", "no-referrer", "no-referrer-when-downgrade", "origin",
+  "origin-when-cross-origin", "unsafe-url", "same-origin", "strict-origin",
+  "strict-origin-when-cross-origin"
+};
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -6,20 +6,16 @@
  * The source for this IDL is found at https://www.khronos.org/registry/webgl/specs/latest/2.0
  * This IDL depends on WebGLRenderingContext.webidl
  */
 
 typedef long long GLint64;
 typedef unsigned long long GLuint64;
 
 [Pref="webgl.enable-webgl2"]
-interface WebGLQuery {
-};
-
-[Pref="webgl.enable-webgl2"]
 interface WebGLSampler {
 };
 
 [Pref="webgl.enable-webgl2"]
 interface WebGLSync {
 };
 
 [Pref="webgl.enable-webgl2"]
@@ -620,17 +616,17 @@ interface WebGL2RenderingContext : WebGL
     void clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
 
     /* Query Objects */
     WebGLQuery? createQuery();
     void deleteQuery(WebGLQuery? query);
     [WebGLHandlesContextLoss] GLboolean isQuery(WebGLQuery? query);
     void beginQuery(GLenum target, WebGLQuery? query);
     void endQuery(GLenum target);
-    WebGLQuery? getQuery(GLenum target, GLenum pname);
+    any getQuery(GLenum target, GLenum pname);
     any getQueryParameter(WebGLQuery? query, GLenum pname);
 
     /* Sampler Objects */
     WebGLSampler? createSampler();
     void deleteSampler(WebGLSampler? sampler);
     [WebGLHandlesContextLoss] GLboolean isSampler(WebGLSampler? sampler);
     void bindSampler(GLuint unit, WebGLSampler? sampler);
     void samplerParameteri(WebGLSampler? sampler, GLenum pname, GLint param);
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -75,17 +75,16 @@ interface WebGLShader {
 interface WebGLTexture {
 };
 
 [Exposed=(Window,Worker),
  Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLUniformLocation {
 };
 
-[NoInterfaceObject]
 interface WebGLVertexArrayObjectOES {
 };
 
 [Exposed=(Window,Worker),
  Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLActiveInfo {
     readonly attribute GLint size;
     readonly attribute GLenum type;
@@ -802,17 +801,17 @@ interface WEBGL_compressed_texture_s3tc
 interface WEBGL_compressed_texture_atc
 {
     const GLenum COMPRESSED_RGB_ATC_WEBGL                     = 0x8C92;
     const GLenum COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL     = 0x8C93;
     const GLenum COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE;
 };
 
 [NoInterfaceObject]
-interface WEBGL_compressed_texture_es3
+interface WEBGL_compressed_texture_etc
 {
     const GLenum COMPRESSED_R11_EAC                                 = 0x9270;
     const GLenum COMPRESSED_SIGNED_R11_EAC                          = 0x9271;
     const GLenum COMPRESSED_RG11_EAC                                = 0x9272;
     const GLenum COMPRESSED_SIGNED_RG11_EAC                         = 0x9273;
     const GLenum COMPRESSED_RGB8_ETC2                               = 0x9274;
     const GLenum COMPRESSED_SRGB8_ETC2                              = 0x9275;
     const GLenum COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2           = 0x9276;
@@ -998,31 +997,30 @@ interface ANGLE_instanced_arrays {
 };
 
 [NoInterfaceObject]
 interface EXT_blend_minmax {
     const GLenum MIN_EXT = 0x8007;
     const GLenum MAX_EXT = 0x8008;
 };
 
-[NoInterfaceObject]
-interface WebGLTimerQueryEXT {
+interface WebGLQuery {
 };
 
 [NoInterfaceObject]
 interface EXT_disjoint_timer_query {
     const GLenum QUERY_COUNTER_BITS_EXT = 0x8864;
     const GLenum CURRENT_QUERY_EXT = 0x8865;
     const GLenum QUERY_RESULT_EXT = 0x8866;
     const GLenum QUERY_RESULT_AVAILABLE_EXT = 0x8867;
     const GLenum TIME_ELAPSED_EXT = 0x88BF;
     const GLenum TIMESTAMP_EXT = 0x8E28;
     const GLenum GPU_DISJOINT_EXT = 0x8FBB;
 
-    WebGLTimerQueryEXT? createQueryEXT();
-    void deleteQueryEXT(WebGLTimerQueryEXT? query);
-    [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLTimerQueryEXT? query);
-    void beginQueryEXT(GLenum target, WebGLTimerQueryEXT? query);
+    WebGLQuery? createQueryEXT();
+    void deleteQueryEXT(WebGLQuery? query);
+    [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLQuery? query);
+    void beginQueryEXT(GLenum target, WebGLQuery? query);
     void endQueryEXT(GLenum target);
-    void queryCounterEXT(WebGLTimerQueryEXT? query, GLenum target);
+    void queryCounterEXT(WebGLQuery? query, GLenum target);
     any getQueryEXT(GLenum target, GLenum pname);
-    any getQueryObjectEXT(WebGLTimerQueryEXT? query, GLenum pname);
+    any getQueryObjectEXT(WebGLQuery? query, GLenum pname);
 };
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -200,18 +200,20 @@ ChannelFromScriptURL(nsIPrincipal* princ
                        nullptr, // aCallbacks
                        aLoadFlags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
+    mozilla::net::ReferrerPolicy referrerPolicy = parentDoc ?
+      parentDoc->GetReferrerPolicy() : mozilla::net::RP_Default;
     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
-                                                       httpChannel, mozilla::net::RP_Default);
+                                                       httpChannel, referrerPolicy);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   channel.forget(aChannel);
   return rv;
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1336,37 +1336,53 @@ public:
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
     MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
 
     nsAutoCString referrer;
     // Ignore the return value since the Referer header may not exist.
     httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
     if (!referrer.IsEmpty()) {
       mReferrer = referrer;
+    } else {
+      // If there's no referrer Header, means the header was omitted for
+      // security/privacy reason.
+      mReferrer = EmptyCString();
     }
 
     uint32_t referrerPolicy = 0;
     rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
     NS_ENSURE_SUCCESS(rv, rv);
     switch (referrerPolicy) {
+      case nsIHttpChannel::REFERRER_POLICY_UNSET:
+      mReferrerPolicy = ReferrerPolicy::_empty;
+      break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
       mReferrerPolicy = ReferrerPolicy::No_referrer;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
       mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
       mReferrerPolicy = ReferrerPolicy::Unsafe_url;
       break;
+    case nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Same_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin;
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
       break;
     }
 
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2536,19 +2536,20 @@ XMLHttpRequestMainThread::InitiateFetch(
       mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
     }
 
     mAuthorRequestHeaders.ApplyToChannel(httpChannel);
 
     if (!IsSystemXHR()) {
       nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
       nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
+      mozilla::net::ReferrerPolicy referrerPolicy = doc ?
+        doc->GetReferrerPolicy() : mozilla::net::RP_Default;
       nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
-                                                    httpChannel,
-                                                    mozilla::net::RP_Default);
+                                                    httpChannel, referrerPolicy);
     }
 
     // Some extensions override the http protocol handler and provide their own
     // implementation. The channels returned from that implementation don't
     // always seem to implement the nsIUploadChannel2 interface, presumably
     // because it's a new interface. Eventually we should remove this and simply
     // require that http channels implement the new interface (see bug 529041).
     nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/file_sync_xhr_document_write_with_iframe.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<body>
+<script>
+function syncXHR() {
+  let xhr = new XMLHttpRequest();
+  xhr.open("GET", window.location, false);
+  xhr.send(null);
+}
+
+addEventListener('load', evt => {
+  syncXHR();
+  document.open();
+  document.write(
+    '<body>' +
+    '<iframe src="about:blank"></iframe>' +
+    '<script>window.opener.postMessage("DONE", "*");</' + 'script>' +
+    '</body>');
+  document.close();
+}, { once: true });
+</script>
+</body>
--- a/dom/xhr/tests/mochitest.ini
+++ b/dom/xhr/tests/mochitest.ini
@@ -58,16 +58,17 @@ support-files =
   subdir/relativeLoad_sub_worker2.js
   subdir/relativeLoad_sub_import.js
   common_temporaryFileBlob.js
   worker_temporaryFileBlob.js
   worker_bug1300552.js
   sync_xhr_unload.sjs
   iframe_sync_xhr_unload.html
   empty.html
+  file_sync_xhr_document_write_with_iframe.html
 
 [test_bug1300552.html]
 [test_html_in_xhr.html]
 [test_relativeLoad.html]
 skip-if = buildapp == 'b2g' # b2g(Failed to load script: relativeLoad_import.js) b2g-debug(Failed to load script: relativeLoad_import.js) b2g-desktop(Failed to load script: relativeLoad_import.js)
 [test_sync_xhr_timer.xhtml]
 skip-if = toolkit == 'android'
 [test_sync_xhr_unload.html]
@@ -106,8 +107,9 @@ skip-if = toolkit == 'android'
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(12 total, 2 failing - .mozSystem == true - got false, expected true + ) b2g-desktop(12 total, 2 failing - .mozSystem == true - got false, expected true + )
 [test_XHR_timeout.html]
 skip-if = buildapp == 'b2g' || (android_version == '18' && debug) # b2g(flaky on B2G, bug 960743) b2g-debug(flaky on B2G, bug 960743) b2g-desktop(flaky on B2G, bug 960743)
 support-files = test_XHR_timeout.js
 [test_xhr_withCredentials.html]
 [test_XHRDocURI.html]
 [test_XHRResponseURL.html]
 [test_XHRSendData.html]
+[test_sync_xhr_document_write_with_iframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/test_sync_xhr_document_write_with_iframe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug </title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>