Merge m-c to autoland. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 25 Aug 2016 08:14:36 -0400
changeset 352423 54c1dcc180bf043c881ee3ca92b34d016ad917af
parent 352422 dda28eb3aff3659291721355ae9e64c470385e92 (current diff)
parent 352343 9467a563a01b466fdca07c0495de7bcfd0a66215 (diff)
child 352424 6d94720c8e959c6f3c6f60439310cca84e5f9720
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland. a=merge
accessible/tests/browser/browser_caching_attributes.js
accessible/tests/browser/browser_caching_description.js
accessible/tests/browser/browser_caching_name.js
accessible/tests/browser/browser_caching_relations.js
accessible/tests/browser/browser_caching_states.js
accessible/tests/browser/browser_caching_value.js
accessible/tests/browser/browser_events_caretmove.js
accessible/tests/browser/browser_events_hide.js
accessible/tests/browser/browser_events_show.js
accessible/tests/browser/browser_events_statechange.js
accessible/tests/browser/browser_events_textchange.js
accessible/tests/browser/browser_treeupdate_ariadialog.js
accessible/tests/browser/browser_treeupdate_ariaowns.js
accessible/tests/browser/browser_treeupdate_canvas.js
accessible/tests/browser/browser_treeupdate_cssoverflow.js
accessible/tests/browser/browser_treeupdate_doc.js
accessible/tests/browser/browser_treeupdate_gencontent.js
accessible/tests/browser/browser_treeupdate_hidden.js
accessible/tests/browser/browser_treeupdate_imagemap.js
accessible/tests/browser/browser_treeupdate_list.js
accessible/tests/browser/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/browser_treeupdate_listener.js
accessible/tests/browser/browser_treeupdate_optgroup.js
accessible/tests/browser/browser_treeupdate_removal.js
accessible/tests/browser/browser_treeupdate_table.js
accessible/tests/browser/browser_treeupdate_textleaf.js
accessible/tests/browser/browser_treeupdate_visibility.js
accessible/tests/browser/browser_treeupdate_whitespace.js
accessible/tests/browser/doc_treeupdate_ariadialog.html
accessible/tests/browser/doc_treeupdate_ariaowns.html
accessible/tests/browser/doc_treeupdate_imagemap.html
accessible/tests/browser/doc_treeupdate_removal.xhtml
accessible/tests/browser/doc_treeupdate_visibility.html
accessible/tests/browser/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/browser_caching_attributes.js
accessible/tests/browser/e10s/browser_caching_description.js
accessible/tests/browser/e10s/browser_caching_name.js
accessible/tests/browser/e10s/browser_caching_relations.js
accessible/tests/browser/e10s/browser_caching_states.js
accessible/tests/browser/e10s/browser_caching_value.js
accessible/tests/browser/e10s/browser_events_caretmove.js
accessible/tests/browser/e10s/browser_events_hide.js
accessible/tests/browser/e10s/browser_events_show.js
accessible/tests/browser/e10s/browser_events_statechange.js
accessible/tests/browser/e10s/browser_events_textchange.js
accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
accessible/tests/browser/e10s/browser_treeupdate_canvas.js
accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
accessible/tests/browser/e10s/browser_treeupdate_doc.js
accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
accessible/tests/browser/e10s/browser_treeupdate_hidden.js
accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
accessible/tests/browser/e10s/browser_treeupdate_list.js
accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/e10s/browser_treeupdate_listener.js
accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
accessible/tests/browser/e10s/browser_treeupdate_removal.js
accessible/tests/browser/e10s/browser_treeupdate_table.js
accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
accessible/tests/browser/e10s/browser_treeupdate_visibility.js
accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
accessible/tests/browser/e10s/doc_treeupdate_visibility.html
accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/events.js
accessible/tests/browser/events.js
browser/components/extensions/test/browser/browser_ext_pageAction_title.js
browser/components/extensions/test/browser/head_pageAction.js
devtools/client/inspector/inspector-panel.js
dom/base/nsDOMWindowUtils.cpp
dom/canvas/WebGLContextReporter.cpp
dom/events/test/pointerevents/pointerevent_element_haspointercapture-manual.html
dom/events/test/pointerevents/pointerevent_element_haspointercapture.html
dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html
dom/events/test/pointerevents/test_pointerevent_element_haspointercapture.html
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/TrackUnionStream.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/thebes/DeviceManagerD3D11.cpp
gfx/thebes/DeviceManagerD3D11.h
gfx/thebes/DeviceManagerDx.cpp
gfx/thebes/DeviceManagerDx.h
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/reftests/svg/text/reftest.list
layout/reftests/text-overflow/reftest.list
media/webrtc/trunk/tools/gyp/MANIFEST
media/webrtc/trunk/tools/gyp/gyp_dummy.c
media/webrtc/trunk/tools/gyp/pylib/gyp/SCons.py
media/webrtc/trunk/tools/gyp/pylib/gyp/generator/android.py
media/webrtc/trunk/tools/gyp/pylib/gyp/generator/make.py.new
media/webrtc/trunk/tools/gyp/pylib/gyp/generator/scons.py
media/webrtc/trunk/tools/gyp/pylib/gyp/sun_tool.py
media/webrtc/trunk/tools/gyp/pylib/gyp/win_tool.py
media/webrtc/trunk/tools/gyp/pylintrc
media/webrtc/trunk/tools/gyp/test/compiler-override/compiler-exe.gyp
media/webrtc/trunk/tools/gyp/test/compiler-override/compiler.gyp
media/webrtc/trunk/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
media/webrtc/trunk/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
media/webrtc/trunk/tools/gyp/test/mac/gyptest-postbuild-static-library.gyp
media/webrtc/trunk/tools/gyp/test/mac/gyptest-postbuild-static-library.py
media/webrtc/trunk/tools/gyp/test/mac/gyptest-sourceless-module.gyp
media/webrtc/trunk/tools/gyp/test/rules/src/subdir4/asm-function.asm
media/webrtc/trunk/tools/gyp/test/rules/src/subdir4/asm-function.assem
media/webrtc/trunk/tools/gyp/test/same-source-file-name/gyptest-fail.py
media/webrtc/trunk/tools/gyp/test/same-source-file-name/src/double.gyp
media/webrtc/trunk/tools/gyp/test/scons_tools/gyptest-tools.py
media/webrtc/trunk/tools/gyp/test/scons_tools/site_scons/site_tools/this_tool.py
media/webrtc/trunk/tools/gyp/test/scons_tools/tools.c
media/webrtc/trunk/tools/gyp/test/scons_tools/tools.gyp
media/webrtc/trunk/tools/gyp/test/variants/gyptest-variants.py
media/webrtc/trunk/tools/gyp/test/variants/src/variants.c
media/webrtc/trunk/tools/gyp/test/variants/src/variants.gyp
media/webrtc/trunk/tools/gyp/test/win/linker-flags/default-libs.cc
media/webrtc/trunk/tools/gyp/test/win/linker-flags/default-libs.gyp
mfbt/Unused.h
mfbt/unused.cpp
mfbt/unused.h
modules/libpref/init/all.js
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture-manual.html
testing/web-platform/tests/pointerevents/pointerevent_element_haspointercapture.html
toolkit/components/telemetry/Histograms.json
--- 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 1294803's backout needed a clobber to fix SM builds
+Bug 1297276 - renaming a file with a different case needed a clobber on case insensitive filesystem
new file mode 100644
--- /dev/null
+++ b/accessible/aom/AccessibleNode.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AccessibleNode.h"
+#include "mozilla/dom/AccessibleNodeBinding.h"
+#include "mozilla/dom/BindingDeclarations.h"
+
+#include "Accessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "DocAccessible.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+using namespace mozilla::dom;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(AccessibleNode)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AccessibleNode)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AccessibleNode)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AccessibleNode)
+
+AccessibleNode::AccessibleNode(nsINode* aNode) : mDOMNode(aNode)
+{
+  DocAccessible* doc =
+    GetOrCreateAccService()->GetDocAccessible(mDOMNode->OwnerDoc());
+  if (doc) {
+    mIntl = doc->GetAccessible(mDOMNode);
+  }
+}
+
+AccessibleNode::~AccessibleNode()
+{
+}
+
+/* virtual */ JSObject*
+AccessibleNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return AccessibleNodeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+/* virtual */ ParentObject
+AccessibleNode::GetParentObject() const
+{
+  return mDOMNode->GetParentObject();
+}
+
+void
+AccessibleNode::GetRole(nsAString& aRole)
+{
+  if (mIntl) {
+    GetOrCreateAccService()->GetStringRole(mIntl->Role(), aRole);
+    return;
+  }
+
+  aRole.AssignLiteral("unknown");
+}
+
+nsINode*
+AccessibleNode::GetDOMNode()
+{
+  return mDOMNode;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/aom/AccessibleNode.h
@@ -0,0 +1,53 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef A11Y_AOM_ACCESSIBLENODE_H
+#define A11Y_AOM_ACCESSIBLENODE_H
+
+#include "nsWrapperCache.h"
+#include "mozilla/RefPtr.h"
+
+class nsINode;
+
+namespace mozilla {
+
+namespace a11y {
+  class Accessible;
+}
+
+namespace dom {
+
+struct ParentObject;
+
+class AccessibleNode : public nsISupports,
+                       public nsWrapperCache
+{
+public:
+  explicit AccessibleNode(nsINode* aNode);
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AccessibleNode);
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
+  virtual dom::ParentObject GetParentObject() const final;
+
+  void GetRole(nsAString& aRole);
+  nsINode* GetDOMNode();
+
+protected:
+  AccessibleNode(const AccessibleNode& aCopy) = delete;
+  AccessibleNode& operator=(const AccessibleNode& aCopy) = delete;
+  virtual ~AccessibleNode();
+
+  RefPtr<a11y::Accessible> mIntl;
+  RefPtr<nsINode> mDOMNode;
+};
+
+} // dom
+} // mozilla
+
+
+#endif // A11Y_JSAPI_ACCESSIBLENODE
new file mode 100644
--- /dev/null
+++ b/accessible/aom/moz.build
@@ -0,0 +1,38 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.dom += [
+    'AccessibleNode.h',
+]
+
+UNIFIED_SOURCES += [
+    'AccessibleNode.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/accessible/base',
+    '/accessible/generic',
+]
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+    LOCAL_INCLUDES += [
+        '/accessible/atk',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    LOCAL_INCLUDES += [
+        '/accessible/windows/ia2',
+        '/accessible/windows/msaa',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    LOCAL_INCLUDES += [
+        '/accessible/mac',
+    ]
+else:
+    LOCAL_INCLUDES += [
+        '/accessible/other',
+    ]
+
+FINAL_LIBRARY = 'xul'
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -100,26 +100,28 @@ static void
 LogDocShellState(nsIDocument* aDocumentNode)
 {
   printf("docshell busy: ");
 
   nsAutoCString docShellBusy;
   nsCOMPtr<nsIDocShell> docShell = aDocumentNode->GetDocShell();
   uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
   docShell->GetBusyFlags(&busyFlags);
-  if (busyFlags == nsIDocShell::BUSY_FLAGS_NONE)
+  if (busyFlags == nsIDocShell::BUSY_FLAGS_NONE) {
     printf("'none'");
-  if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY)
+  }
+  if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY) {
     printf("'busy'");
-  if (busyFlags & nsIDocShell::BUSY_FLAGS_BEFORE_PAGE_LOAD)
+  }
+  if (busyFlags & nsIDocShell::BUSY_FLAGS_BEFORE_PAGE_LOAD) {
     printf(", 'before page load'");
-  if (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)
+  }
+  if (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) {
     printf(", 'page loading'");
-
-    printf("[failed]");
+  }
 }
 
 static void
 LogDocType(nsIDocument* aDocumentNode)
 {
   if (aDocumentNode->IsActive()) {
     bool isContent = nsCoreUtils::IsContentDocument(aDocumentNode);
     printf("%s document", (isContent ? "content" : "chrome"));
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -73,17 +73,17 @@
 #include "nsIDOMCharacterData.h"
 #endif
 
 #include "mozilla/Assertions.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MouseEvents.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/dom/TreeWalker.h"
 
 using namespace mozilla;
--- a/accessible/ipc/ProxyAccessibleBase.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -8,17 +8,17 @@
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/a11y/Platform.h"
 #include "mozilla/a11y/ProxyAccessibleBase.h"
 #include "mozilla/a11y/ProxyAccessible.h"
 #include "mozilla/a11y/Role.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "RelationType.h"
 #include "xpcAccessibleDocument.h"
 
 namespace mozilla {
 namespace a11y {
 
 template <class Derived>
 void
--- a/accessible/ipc/other/ProxyAccessible.cpp
+++ b/accessible/ipc/other/ProxyAccessible.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProxyAccessible.h"
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
 namespace mozilla {
 namespace a11y {
 
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -6,17 +6,17 @@
 
 #include "Accessible2.h"
 #include "ProxyAccessible.h"
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
 #include <comutil.h>
 
 namespace mozilla {
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -10,16 +10,27 @@ if 'gtk' in toolkit:
     DIRS += ['atk']
 elif toolkit == 'windows':
     DIRS += ['windows']
 elif toolkit == 'cocoa':
     DIRS += ['mac']
 else:
     DIRS += ['other']
 
-DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
+DIRS += [ 'aom',
+          'base',
+          'generic',
+          'html',
+          'interfaces',
+          'ipc',
+          'jsat',
+          'xpcom'
+]
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
 TEST_DIRS += ['tests/mochitest']
 
-BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
+BROWSER_CHROME_MANIFESTS += [
+  'tests/browser/browser.ini',
+  'tests/browser/e10s/browser.ini'
+]
--- a/accessible/tests/browser/.eslintrc
+++ b/accessible/tests/browser/.eslintrc
@@ -2,46 +2,56 @@
   "extends": [
     "../../../testing/mochitest/browser.eslintrc"
   ],
   // All globals made available in the test environment.
   "globals": {
     // Content scripts have global 'content' object
     "content": true,
 
+    "add_task": true,
+
     // Defined in accessible/tests/mochitest/ common.js, name.js, states.js
     "prettyName": true,
     "statesToString": true,
     "eventTypeToString": true,
     "testAttrs": true,
     "testAbsentAttrs": true,
     "testName": true,
     "testDescr": true,
     "testStates": true,
     "testRelation": true,
     "testValue": true,
     "testAccessibleTree": true,
     "isAccessible": true,
     "getAccessibleDOMNodeID": true,
 
-    // Defined for all accessibility browser tests.
+    // Defined for all top level accessibility browser tests.
+    "setE10sPrefs": true,
+    "unsetE10sPrefs": true,
+    "initPromise": true,
+    "shutdownPromise": true,
+    "forceGC": true,
+
+    // Defined for all e10s accessibility browser tests.
     "addAccessibleTask": true,
     "BrowserTestUtils": true,
     "ContentTask": true,
     "gBrowser": true,
     "isDefunct": true,
     "loadScripts": true,
+    "loadFrameScripts": true,
     "Logger": true,
     "MOCHITESTS_DIR": true,
     "waitForEvent": true,
     "waitForMultipleEvents": true,
     "invokeSetAttribute": true,
     "invokeSetStyle": true,
     "invokeFocus": true,
-    "findAccessibleChildByID": true
+    "findAccessibleChildByID": true,
   },
   "rules": {
     "mozilla/mark-test-function-used": 1,
     "mozilla/no-aArgs": 1,
     "mozilla/no-cpows-in-tests": 1,
     "mozilla/reject-importGlobalProperties": 1,
     "mozilla/var-only-at-top-level": 1,
 
@@ -63,17 +73,17 @@
     "func-style": 0,
     "generator-star": 0,
     "global-strict": 0,
     "handle-callback-err": [2, "er"],
     "indent": [2, 2, {"SwitchCase": 1}],
     "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
     "linebreak-style": 0,
     "max-depth": 0,
-    "max-nested-callbacks": [2, 3],
+    "max-nested-callbacks": [2, 4],
     "max-params": 0,
     "max-statements": 0,
     "new-cap": [2, {"capIsNew": false}],
     "new-parens": 2,
     "no-array-constructor": 2,
     "no-bitwise": 0,
     "no-caller": 2,
     "no-catch-shadow": 2,
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -1,52 +1,9 @@
 [DEFAULT]
-skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
-support-files =
-  events.js
-  head.js
-  doc_treeupdate_ariadialog.html
-  doc_treeupdate_ariaowns.html
-  doc_treeupdate_imagemap.html
-  doc_treeupdate_removal.xhtml
-  doc_treeupdate_visibility.html
-  doc_treeupdate_whitespace.html
-  !/accessible/tests/mochitest/*.js
-  !/accessible/tests/mochitest/letters.gif
-  !/accessible/tests/mochitest/moz.png
-
-# Caching tests
-[browser_caching_attributes.js]
-[browser_caching_description.js]
-[browser_caching_name.js]
-skip-if = e10s
-[browser_caching_relations.js]
-[browser_caching_states.js]
-[browser_caching_value.js]
 
-# Events tests
-[browser_events_caretmove.js]
-[browser_events_hide.js]
-[browser_events_show.js]
-[browser_events_statechange.js]
-[browser_events_textchange.js]
+support-files =
+  head.js
+  shared-head.js
 
-# Tree update tests
-[browser_treeupdate_ariadialog.js]
-[browser_treeupdate_ariaowns.js]
-skip-if = e10s
-[browser_treeupdate_canvas.js]
-[browser_treeupdate_cssoverflow.js]
-[browser_treeupdate_doc.js]
-[browser_treeupdate_gencontent.js]
-[browser_treeupdate_hidden.js]
-[browser_treeupdate_imagemap.js]
-skip-if = e10s
-[browser_treeupdate_list.js]
-[browser_treeupdate_list_editabledoc.js]
-[browser_treeupdate_listener.js]
-[browser_treeupdate_optgroup.js]
-[browser_treeupdate_removal.js]
-[browser_treeupdate_table.js]
-[browser_treeupdate_textleaf.js]
-[browser_treeupdate_visibility.js]
-[browser_treeupdate_whitespace.js]
-skip-if = true # Failing due to incorrect index of test container children on document load.
+[browser_shutdown_multi_reference.js]
+[browser_shutdown_scope_lifecycle.js]
+[browser_shutdown_start_restart.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_multi_reference.js
@@ -0,0 +1,48 @@
+/* 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';
+
+add_task(function* () {
+  info('Creating a service');
+  // Create a11y service.
+  let a11yInit = initPromise();
+  let accService1 = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService1, 'Service initialized');
+
+  // Add another reference to a11y service. This will not trigger
+  // 'a11y-init-or-shutdown' event
+  let accService2 = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  ok(accService2, 'Service initialized');
+
+  info('Removing all service references');
+  let canShutdown = false;
+  // This promise will resolve only if canShutdonw flag is set to true. If
+  // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut
+  // down, the promise will reject.
+  let a11yShutdown = new Promise((resolve, reject) =>
+    shutdownPromise().then(flag => canShutdown ?
+      resolve() : reject('Accessible service was shut down incorrectly')));
+  // Remove first a11y service reference.
+  accService1 = null;
+  ok(!accService1, 'Service is removed');
+  // Force garbage collection that should not trigger shutdown because there is
+  // another reference.
+  forceGC();
+
+  // Have some breathing room when removing a11y service references.
+  yield new Promise(resolve => executeSoon(resolve));
+
+  // Now allow a11y service to shutdown.
+  canShutdown = true;
+  // Remove last a11y service reference.
+  accService2 = null;
+  ok(!accService2, 'Service is removed');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js
@@ -0,0 +1,21 @@
+/* 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';
+
+add_task(function* testScopeLifecycle() {
+  // Create a11y service inside of the function scope. Its reference should be
+  // released once the anonimous function is called.
+  let a11yInitThenShutdown = initPromise().then(shutdownPromise);
+
+  (function() {
+    let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+      Ci.nsIAccessibilityService);
+    ok(accService, 'Service initialized');
+  })();
+
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yInitThenShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_start_restart.js
@@ -0,0 +1,41 @@
+/* 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';
+
+add_task(function* testStartAndRestart() {
+  info('Creating a service');
+  // Create a11y service.
+  let a11yInit = initPromise();
+  let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService, 'Service initialized');
+
+  info('Removing a service');
+  // Remove the only reference to an a11y service.
+  let a11yShutdown = shutdownPromise();
+  accService = null;
+  ok(!accService, 'Service is removed');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+
+  info('Recreating a service');
+  // Re-create a11y service.
+  a11yInit = initPromise();
+  accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService, 'Service initialized again');
+
+  info('Removing a service again');
+  // Remove the only reference to an a11y service again.
+  a11yShutdown = shutdownPromise();
+  accService = null;
+  ok(!accService, 'Service is removed again');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -0,0 +1,53 @@
+[DEFAULT]
+skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
+support-files =
+  events.js
+  head.js
+  doc_treeupdate_ariadialog.html
+  doc_treeupdate_ariaowns.html
+  doc_treeupdate_imagemap.html
+  doc_treeupdate_removal.xhtml
+  doc_treeupdate_visibility.html
+  doc_treeupdate_whitespace.html
+  !/accessible/tests/browser/shared-head.js
+  !/accessible/tests/mochitest/*.js
+  !/accessible/tests/mochitest/letters.gif
+  !/accessible/tests/mochitest/moz.png
+
+# Caching tests
+[browser_caching_attributes.js]
+[browser_caching_description.js]
+[browser_caching_name.js]
+skip-if = e10s
+[browser_caching_relations.js]
+[browser_caching_states.js]
+[browser_caching_value.js]
+
+# Events tests
+[browser_events_caretmove.js]
+[browser_events_hide.js]
+[browser_events_show.js]
+[browser_events_statechange.js]
+[browser_events_textchange.js]
+
+# Tree update tests
+[browser_treeupdate_ariadialog.js]
+[browser_treeupdate_ariaowns.js]
+skip-if = e10s
+[browser_treeupdate_canvas.js]
+[browser_treeupdate_cssoverflow.js]
+[browser_treeupdate_doc.js]
+[browser_treeupdate_gencontent.js]
+[browser_treeupdate_hidden.js]
+[browser_treeupdate_imagemap.js]
+skip-if = e10s
+[browser_treeupdate_list.js]
+[browser_treeupdate_list_editabledoc.js]
+[browser_treeupdate_listener.js]
+[browser_treeupdate_optgroup.js]
+[browser_treeupdate_removal.js]
+[browser_treeupdate_table.js]
+[browser_treeupdate_textleaf.js]
+[browser_treeupdate_visibility.js]
+[browser_treeupdate_whitespace.js]
+skip-if = true # Failing due to incorrect index of test container children on document load.
rename from accessible/tests/browser/browser_caching_attributes.js
rename to accessible/tests/browser/e10s/browser_caching_attributes.js
rename from accessible/tests/browser/browser_caching_description.js
rename to accessible/tests/browser/e10s/browser_caching_description.js
rename from accessible/tests/browser/browser_caching_name.js
rename to accessible/tests/browser/e10s/browser_caching_name.js
rename from accessible/tests/browser/browser_caching_relations.js
rename to accessible/tests/browser/e10s/browser_caching_relations.js
rename from accessible/tests/browser/browser_caching_states.js
rename to accessible/tests/browser/e10s/browser_caching_states.js
rename from accessible/tests/browser/browser_caching_value.js
rename to accessible/tests/browser/e10s/browser_caching_value.js
rename from accessible/tests/browser/browser_events_caretmove.js
rename to accessible/tests/browser/e10s/browser_events_caretmove.js
rename from accessible/tests/browser/browser_events_hide.js
rename to accessible/tests/browser/e10s/browser_events_hide.js
rename from accessible/tests/browser/browser_events_show.js
rename to accessible/tests/browser/e10s/browser_events_show.js
rename from accessible/tests/browser/browser_events_statechange.js
rename to accessible/tests/browser/e10s/browser_events_statechange.js
rename from accessible/tests/browser/browser_events_textchange.js
rename to accessible/tests/browser/e10s/browser_events_textchange.js
rename from accessible/tests/browser/browser_treeupdate_ariadialog.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
rename from accessible/tests/browser/browser_treeupdate_ariaowns.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
rename from accessible/tests/browser/browser_treeupdate_canvas.js
rename to accessible/tests/browser/e10s/browser_treeupdate_canvas.js
rename from accessible/tests/browser/browser_treeupdate_cssoverflow.js
rename to accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
rename from accessible/tests/browser/browser_treeupdate_doc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_doc.js
rename from accessible/tests/browser/browser_treeupdate_gencontent.js
rename to accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
rename from accessible/tests/browser/browser_treeupdate_hidden.js
rename to accessible/tests/browser/e10s/browser_treeupdate_hidden.js
rename from accessible/tests/browser/browser_treeupdate_imagemap.js
rename to accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
rename from accessible/tests/browser/browser_treeupdate_list.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list.js
rename from accessible/tests/browser/browser_treeupdate_list_editabledoc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
rename from accessible/tests/browser/browser_treeupdate_listener.js
rename to accessible/tests/browser/e10s/browser_treeupdate_listener.js
rename from accessible/tests/browser/browser_treeupdate_optgroup.js
rename to accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
rename from accessible/tests/browser/browser_treeupdate_removal.js
rename to accessible/tests/browser/e10s/browser_treeupdate_removal.js
rename from accessible/tests/browser/browser_treeupdate_table.js
rename to accessible/tests/browser/e10s/browser_treeupdate_table.js
rename from accessible/tests/browser/browser_treeupdate_textleaf.js
rename to accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
rename from accessible/tests/browser/browser_treeupdate_visibility.js
rename to accessible/tests/browser/e10s/browser_treeupdate_visibility.js
rename from accessible/tests/browser/browser_treeupdate_whitespace.js
rename to accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
rename from accessible/tests/browser/doc_treeupdate_ariadialog.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
rename from accessible/tests/browser/doc_treeupdate_ariaowns.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
rename from accessible/tests/browser/doc_treeupdate_imagemap.html
rename to accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
rename from accessible/tests/browser/doc_treeupdate_removal.xhtml
rename to accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
rename from accessible/tests/browser/doc_treeupdate_visibility.html
rename to accessible/tests/browser/e10s/doc_treeupdate_visibility.html
rename from accessible/tests/browser/doc_treeupdate_whitespace.html
rename to accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
rename from accessible/tests/browser/events.js
rename to accessible/tests/browser/e10s/events.js
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/e10s/head.js
@@ -0,0 +1,84 @@
+/* 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';
+
+/* global EVENT_DOCUMENT_LOAD_COMPLETE, CURRENT_CONTENT_DIR, loadFrameScripts */
+
+/* exported addAccessibleTask */
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param  {String}             doc    URL (relative to current directory) or
+ *                                     data URL or markup snippet that is used
+ *                                     to test content with
+ * @param  {Function|Function*} task   a generator or a function with tests to
+ *                                     run
+ */
+function addAccessibleTask(doc, task) {
+  add_task(function*() {
+    let url;
+    if (doc.includes('doc_')) {
+      url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+    } else {
+      // Assume it's a markup snippet.
+      url = `data:text/html,
+        <html>
+          <head>
+            <meta charset="utf-8"/>
+            <title>Accessibility Test</title>
+          </head>
+          <body id="body">${doc}</body>
+        </html>`;
+    }
+
+    registerCleanupFunction(() => {
+      let observers = Services.obs.enumerateObservers('accessible-event');
+      while (observers.hasMoreElements()) {
+        Services.obs.removeObserver(
+          observers.getNext().QueryInterface(Ci.nsIObserver),
+          'accessible-event');
+      }
+    });
+
+    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+    yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: url
+    }, function*(browser) {
+      registerCleanupFunction(() => {
+        if (browser) {
+          let tab = gBrowser.getTabForBrowser(browser);
+          if (tab && !tab.closing && tab.linkedBrowser) {
+            gBrowser.removeTab(tab);
+          }
+        }
+      });
+
+      yield SimpleTest.promiseFocus(browser);
+
+      loadFrameScripts(browser,
+        'let { document, window, navigator } = content;',
+        { name: 'common.js', dir: MOCHITESTS_DIR });
+
+      Logger.log(
+        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+      let event = yield onDocLoad;
+      yield task(browser, event.accessible);
+    });
+  });
+}
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/head.js
@@ -1,303 +1,116 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
-/* global EVENT_DOCUMENT_LOAD_COMPLETE */
-
-/* exported Logger, MOCHITESTS_DIR, isDefunct, addAccessibleTask,
-            invokeSetAttribute, invokeFocus, invokeSetStyle,
-            findAccessibleChildByID, getAccessibleDOMNodeID */
-
-const { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import('resource://gre/modules/Services.jsm');
-
-/**
- * Current browser test directory path used to load subscripts.
- */
-const CURRENT_DIR =
-  'chrome://mochitests/content/browser/accessible/tests/browser/';
-/**
- * A11y mochitest directory where we find common files used in both browser and
- * plain tests.
- */
-const MOCHITESTS_DIR =
-  'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
-/**
- * A base URL for test files used in content.
- */
-const CURRENT_CONTENT_DIR =
-  'http://example.com/browser/accessible/tests/browser/';
+/* exported initPromise, shutdownPromise,
+            setE10sPrefs, unsetE10sPrefs, forceGC */
 
 /**
- * Used to dump debug information.
+ * Set e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are set.
  */
-let Logger = {
-  /**
-   * Set up this variable to dump log messages into console.
-   */
-  dumpToConsole: false,
-
-  /**
-   * Set up this variable to dump log messages into error console.
-   */
-  dumpToAppConsole: false,
-
-  /**
-   * Return true if dump is enabled.
-   */
-  get enabled() {
-    return this.dumpToConsole || this.dumpToAppConsole;
-  },
-
-  /**
-   * Dump information into console if applicable.
-   */
-  log(msg) {
-    if (this.enabled) {
-      this.logToConsole(msg);
-      this.logToAppConsole(msg);
-    }
-  },
-
-  /**
-   * Log message to console.
-   */
-  logToConsole(msg) {
-    if (this.dumpToConsole) {
-      dump(`\n${msg}\n`);
-    }
-  },
-
-  /**
-   * Log message to error console.
-   */
-  logToAppConsole(msg) {
-    if (this.dumpToAppConsole) {
-      Services.console.logStringMessage(`${msg}`);
-    }
-  }
-};
-
-/**
- * Check if an accessible object has a defunct test.
- * @param  {nsIAccessible}  accessible object to test defunct state for
- * @return {Boolean}        flag indicating defunct state
- */
-function isDefunct(accessible) {
-  let defunct = false;
-  try {
-    let extState = {};
-    accessible.getState({}, extState);
-    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
-  } catch (x) {
-    defunct = true;
-  } finally {
-    if (defunct) {
-      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
-    }
-  }
-  return defunct;
+function setE10sPrefs() {
+  return new Promise(resolve =>
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ['browser.tabs.remote.autostart', true],
+        ['browser.tabs.remote.force-enable', true],
+        ['extensions.e10sBlocksEnabling', false]
+      ]
+    }, resolve));
 }
 
 /**
- * Asynchronously set or remove content element's attribute (in content process
- * if e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  attr     attribute name
- * @param  {String?} value    optional attribute value, if not present, remove
- *                            attribute
- * @return {Promise}          promise indicating that attribute is set/removed
+ * Unset e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are unset.
  */
-function invokeSetAttribute(browser, id, attr, value) {
-  if (value) {
-    Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, attr, value },
-    ({ id, attr, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.setAttribute(attr, value);
-      } else {
-        elm.removeAttribute(attr);
-      }
-    });
+function unsetE10sPrefs() {
+  return new Promise(resolve => {
+    SpecialPowers.popPrefEnv(resolve);
+  });
 }
 
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
 /**
- * Asynchronously set or remove content element's style (in content process if
- * e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  aStyle   style property name
- * @param  {String?} aValue   optional style property value, if not present,
- *                            remove style
- * @return {Promise}          promise indicating that style is set/removed
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired.
+ * @return {Promise} event promise evaluating to event's data
  */
-function invokeSetStyle(browser, id, style, value) {
-  if (value) {
-    Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${style} style from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, style, value },
-    ({ id, style, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.style[style] = value;
-      } else {
-        delete elm.style[style];
-      }
-    });
-}
-
-/**
- * Asynchronously set focus on a content element (in content process if e10s is
- * enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @return {Promise} promise  indicating that focus is set
- */
-function invokeFocus(browser, id) {
-  Logger.log(`Setting focus on a node with id: ${id}`);
-  return ContentTask.spawn(browser, id, id => {
-    let elm = content.document.getElementById(id);
-    if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
-        elm instanceof Ci.nsIDOMXULTextBoxElement) {
-      elm.selectionStart = elm.selectionEnd = elm.value.length;
-    }
-    elm.focus();
+function a11yInitOrShutdownPromise() {
+  return new Promise(resolve => {
+    let observe = (subject, topic, data) => {
+      Services.obs.removeObserver(observe, 'a11y-init-or-shutdown');
+      resolve(data);
+    };
+    Services.obs.addObserver(observe, 'a11y-init-or-shutdown', false);
   });
 }
 
 /**
- * Traverses the accessible tree starting from a given accessible as a root and
- * looks for an accessible that matches based on its DOMNode id.
- * @param  {nsIAccessible}  accessible root accessible
- * @param  {String}         id         id to look up accessible for
- * @return {nsIAccessible?}            found accessible if any
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired
+ * in content.
+ * @param  {Object}   browser  current "tabbrowser" element
+ * @return {Promise}  event    promise evaluating to event's data
  */
-function findAccessibleChildByID(accessible, id) {
-  if (getAccessibleDOMNodeID(accessible) === id) {
-    return accessible;
-  }
-  for (let i = 0; i < accessible.children.length; ++i) {
-    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
-    if (found) {
-      return found;
-    }
-  }
+function contentA11yInitOrShutdownPromise(browser) {
+  return ContentTask.spawn(browser, {}, a11yInitOrShutdownPromise);
 }
 
 /**
- * Load a list of scripts into the test
- * @param {Array} scripts  a list of scripts to load
- */
-function loadScripts(...scripts) {
-  for (let script of scripts) {
-    let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
-      `${script.dir}${script.name}`;
-    Services.scriptloader.loadSubScript(path, this);
-  }
-}
-
-/**
- * Load a list of frame scripts into test's content.
- * @param {Object} browser   browser element that content belongs to
- * @param {Array}  scripts   a list of scripts to load into content
+ * A helper function that maps 'a11y-init-or-shutdown' event to a promise that
+ * resovles or rejects depending on whether accessibility service is expected to
+ * be initialized or shut down.
  */
-function loadFrameScripts(browser, ...scripts) {
-  let mm = browser.messageManager;
-  for (let script of scripts) {
-    let frameScript;
-    if (typeof script === 'string') {
-      if (script.includes('.js')) {
-        // If script string includes a .js extention, assume it is a script
-        // path.
-        frameScript = `${CURRENT_DIR}${script}`;
-      } else {
-        // Otherwise it is a serealized script.
-        frameScript = `data:,${script}`;
-      }
-    } else {
-      // Script is a object that has { dir, name } format.
-      frameScript = `${script.dir}${script.name}`;
-    }
-    mm.loadFrameScript(frameScript, false, true);
-  }
+function promiseOK(promise, expected) {
+  return promise.then(flag =>
+    flag === expected ? Promise.resolve() : Promise.reject());
 }
 
 /**
- * A wrapper around browser test add_task that triggers an accessible test task
- * as a new browser test task with given document, data URL or markup snippet.
- * @param  {String}             doc    URL (relative to current directory) or
- *                                     data URL or markup snippet that is used
- *                                     to test content with
- * @param  {Function|Function*} task   a generator or a function with tests to
- *                                     run
+ * Checks and returns a promise that resolves when accessibility service is
+ * initialized with the correct flag.
+ * @param  {?Object} contentBrowser optinal remove browser object that indicates
+ *                                  that accessibility service is expected to be
+ *                                  initialized in content process.
+ * @return {Promise}                promise that resolves when the accessibility
+ *                                  service initialized correctly.
  */
-function addAccessibleTask(doc, task) {
-  add_task(function*() {
-    let url;
-    if (doc.includes('doc_')) {
-      url = `${CURRENT_CONTENT_DIR}${doc}`;
-    } else {
-      // Assume it's a markup snippet.
-      url = `data:text/html,
-        <html>
-          <head>
-            <meta charset="utf-8"/>
-            <title>Accessibility Test</title>
-          </head>
-          <body id="body">${doc}</body>
-        </html>`;
-    }
-
-    registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers('accessible-event');
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          'accessible-event');
-      }
-    });
-
-    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
-
-    yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: url
-    }, function*(browser) {
-      registerCleanupFunction(() => {
-        if (browser) {
-          let tab = gBrowser.getTabForBrowser(browser);
-          if (tab && !tab.closing && tab.linkedBrowser) {
-            gBrowser.removeTab(tab);
-          }
-        }
-      });
-
-      yield SimpleTest.promiseFocus(browser);
-
-      loadFrameScripts(browser,
-        'let { document, window, navigator } = content;',
-        { name: 'common.js', dir: MOCHITESTS_DIR });
-
-      Logger.log(
-        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
-      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
-
-      let event = yield onDocLoad;
-      yield task(browser, event.accessible);
-    });
-  });
+function initPromise(contentBrowser) {
+  let a11yInitPromise = contentBrowser ?
+    contentA11yInitOrShutdownPromise(contentBrowser) :
+    a11yInitOrShutdownPromise();
+  return promiseOK(a11yInitPromise, '1').then(
+    () => ok(true, 'Service initialized correctly'),
+    () => ok(false, 'Service shutdown incorrectly'));
 }
 
-// Loading and common.js from accessible/tests/mochitest/ for all tests, as well
-// as events.js.
-loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * shut down with the correct flag.
+ * @param  {?Object} contentBrowser optinal remove browser object that indicates
+ *                                  that accessibility service is expected to be
+ *                                  shut down in content process.
+ * @return {Promise}                promise that resolves when the accessibility
+ *                                  service shuts down correctly.
+ */
+function shutdownPromise(contentBrowser) {
+  let a11yShutdownPromise = contentBrowser ?
+    contentA11yInitOrShutdownPromise(contentBrowser) :
+    a11yInitOrShutdownPromise();
+  return promiseOK(a11yShutdownPromise, '0').then(
+    () => ok(true, 'Service shutdown correctly'),
+    () => ok(false, 'Service initialized incorrectly'));
+}
+
+/**
+ * Force garbage collection.
+ */
+function forceGC() {
+  Cu.forceCC();
+  Cu.forceGC();
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/shared-head.js
@@ -0,0 +1,229 @@
+/* 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';
+
+/* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
+            invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
+            CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
+
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+/**
+ * Current browser test directory path used to load subscripts.
+ */
+const CURRENT_DIR =
+  'chrome://mochitests/content/browser/accessible/tests/browser/';
+/**
+ * A11y mochitest directory where we find common files used in both browser and
+ * plain tests.
+ */
+const MOCHITESTS_DIR =
+  'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
+/**
+ * A base URL for test files used in content.
+ */
+const CURRENT_CONTENT_DIR =
+  'http://example.com/browser/accessible/tests/browser/';
+
+/**
+ * Used to dump debug information.
+ */
+let Logger = {
+  /**
+   * Set up this variable to dump log messages into console.
+   */
+  dumpToConsole: false,
+
+  /**
+   * Set up this variable to dump log messages into error console.
+   */
+  dumpToAppConsole: false,
+
+  /**
+   * Return true if dump is enabled.
+   */
+  get enabled() {
+    return this.dumpToConsole || this.dumpToAppConsole;
+  },
+
+  /**
+   * Dump information into console if applicable.
+   */
+  log(msg) {
+    if (this.enabled) {
+      this.logToConsole(msg);
+      this.logToAppConsole(msg);
+    }
+  },
+
+  /**
+   * Log message to console.
+   */
+  logToConsole(msg) {
+    if (this.dumpToConsole) {
+      dump(`\n${msg}\n`);
+    }
+  },
+
+  /**
+   * Log message to error console.
+   */
+  logToAppConsole(msg) {
+    if (this.dumpToAppConsole) {
+      Services.console.logStringMessage(`${msg}`);
+    }
+  }
+};
+
+/**
+ * Check if an accessible object has a defunct test.
+ * @param  {nsIAccessible}  accessible object to test defunct state for
+ * @return {Boolean}        flag indicating defunct state
+ */
+function isDefunct(accessible) {
+  let defunct = false;
+  try {
+    let extState = {};
+    accessible.getState({}, extState);
+    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
+  } catch (x) {
+    defunct = true;
+  } finally {
+    if (defunct) {
+      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
+    }
+  }
+  return defunct;
+}
+
+/**
+ * Asynchronously set or remove content element's attribute (in content process
+ * if e10s is enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @param  {String}  attr     attribute name
+ * @param  {String?} value    optional attribute value, if not present, remove
+ *                            attribute
+ * @return {Promise}          promise indicating that attribute is set/removed
+ */
+function invokeSetAttribute(browser, id, attr, value) {
+  if (value) {
+    Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
+  } else {
+    Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
+  }
+  return ContentTask.spawn(browser, { id, attr, value },
+    ({ id, attr, value }) => {
+      let elm = content.document.getElementById(id);
+      if (value) {
+        elm.setAttribute(attr, value);
+      } else {
+        elm.removeAttribute(attr);
+      }
+    });
+}
+
+/**
+ * Asynchronously set or remove content element's style (in content process if
+ * e10s is enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @param  {String}  aStyle   style property name
+ * @param  {String?} aValue   optional style property value, if not present,
+ *                            remove style
+ * @return {Promise}          promise indicating that style is set/removed
+ */
+function invokeSetStyle(browser, id, style, value) {
+  if (value) {
+    Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
+  } else {
+    Logger.log(`Removing ${style} style from node with id: ${id}`);
+  }
+  return ContentTask.spawn(browser, { id, style, value },
+    ({ id, style, value }) => {
+      let elm = content.document.getElementById(id);
+      if (value) {
+        elm.style[style] = value;
+      } else {
+        delete elm.style[style];
+      }
+    });
+}
+
+/**
+ * Asynchronously set focus on a content element (in content process if e10s is
+ * enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @return {Promise} promise  indicating that focus is set
+ */
+function invokeFocus(browser, id) {
+  Logger.log(`Setting focus on a node with id: ${id}`);
+  return ContentTask.spawn(browser, id, id => {
+    let elm = content.document.getElementById(id);
+    if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
+        elm instanceof Ci.nsIDOMXULTextBoxElement) {
+      elm.selectionStart = elm.selectionEnd = elm.value.length;
+    }
+    elm.focus();
+  });
+}
+
+/**
+ * Traverses the accessible tree starting from a given accessible as a root and
+ * looks for an accessible that matches based on its DOMNode id.
+ * @param  {nsIAccessible}  accessible root accessible
+ * @param  {String}         id         id to look up accessible for
+ * @return {nsIAccessible?}            found accessible if any
+ */
+function findAccessibleChildByID(accessible, id) {
+  if (getAccessibleDOMNodeID(accessible) === id) {
+    return accessible;
+  }
+  for (let i = 0; i < accessible.children.length; ++i) {
+    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
+    if (found) {
+      return found;
+    }
+  }
+}
+
+/**
+ * Load a list of scripts into the test
+ * @param {Array} scripts  a list of scripts to load
+ */
+function loadScripts(...scripts) {
+  for (let script of scripts) {
+    let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
+      `${script.dir}${script.name}`;
+    Services.scriptloader.loadSubScript(path, this);
+  }
+}
+
+/**
+ * Load a list of frame scripts into test's content.
+ * @param {Object} browser   browser element that content belongs to
+ * @param {Array}  scripts   a list of scripts to load into content
+ */
+function loadFrameScripts(browser, ...scripts) {
+  let mm = browser.messageManager;
+  for (let script of scripts) {
+    let frameScript;
+    if (typeof script === 'string') {
+      if (script.includes('.js')) {
+        // If script string includes a .js extention, assume it is a script
+        // path.
+        frameScript = `${CURRENT_DIR}${script}`;
+      } else {
+        // Otherwise it is a serealized script.
+        frameScript = `data:,${script}`;
+      }
+    } else {
+      // Script is a object that has { dir, name } format.
+      frameScript = `${script.dir}${script.name}`;
+    }
+    mm.loadFrameScript(frameScript, false, true);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/aom/a11y.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_general.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Accessibility API: generic</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script>
+  'use strict';
+
+  SimpleTest.waitForExplicitFinish();
+  const finish = SimpleTest.finish.bind(SimpleTest);
+  enablePref()
+    .then(createIframe)
+    .then(checkImplementation)
+    .catch(err => {
+      dump(`${err}: ${err.stack}`);
+      finish();
+    });
+
+  function enablePref() {
+    const ops = {
+      "set": [
+        [ "accessibility.AOM.enabled", true ],
+      ],
+    };
+    return SpecialPowers.pushPrefEnv(ops);
+  }
+
+  // WebIDL conditional annotations for an interface are evaluated once per
+  // global, so we need to create an iframe to see the effects of calling
+  // enablePref().
+  function createIframe() {
+    return new Promise((resolve) => {
+      let iframe = document.createElement("iframe");
+      iframe.src = "about:blank";
+      iframe.onload = () => resolve(iframe.contentDocument);
+      document.body.appendChild(iframe);
+    });
+  }
+
+  // Check that the WebIDL is as expected.
+  function checkImplementation(ifrDoc) {
+    let anode = ifrDoc.accessibleNode;
+    ok(anode, "DOM document has accessible node");
+
+    is(anode.role, 'document', 'correct role of a document accessible node');
+    is(anode.DOMNode, ifrDoc, 'correct DOM Node of a document accessible node');
+
+    finish();
+  }
+  </script>
+</head>
--- a/accessible/tests/mochitest/moz.build
+++ b/accessible/tests/mochitest/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 A11Y_MANIFESTS += [
     'a11y.ini',
     'actions/a11y.ini',
+    'aom/a11y.ini',
     'attributes/a11y.ini',
     'bounds/a11y.ini',
     'editabletext/a11y.ini',
     'elm/a11y.ini',
     'events/a11y.ini',
     'focus/a11y.ini',
     'hittest/a11y.ini',
     'hyperlink/a11y.ini',
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -613,17 +613,16 @@ pref("javascript.options.mem.gc_high_fre
 
 pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
 pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 0);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
 pref("javascript.options.mem.high_water_mark", 6);
 pref("javascript.options.mem.gc_allocation_threshold_mb", 1);
-pref("javascript.options.mem.gc_decommit_threshold_mb", 1);
 pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
 pref("javascript.options.mem.gc_max_empty_chunk_count", 2);
 
 // Show/Hide scrollbars when active/inactive
 pref("ui.showHideScrollbars", 1);
 pref("ui.useOverlayScrollbars", 1);
 pref("ui.scrollbarFadeBeginDelay", 450);
 pref("ui.scrollbarFadeDuration", 0);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -757,16 +757,17 @@ html|*#fullscreen-exit-button {
 
 
 /* notification anchors should only be visible when their associated
    notifications are */
 .notification-anchor-icon {
   -moz-user-focus: normal;
 }
 
+.blocked-permission-icon:not([showing]),
 .notification-anchor-icon:not([showing]) {
   display: none;
 }
 
 #invalid-form-popup > description {
   max-width: 280px;
 }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -687,29 +687,29 @@
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
                 <image id="identity-icon"
                        consumeanchor="identity-box"
                        onclick="PageProxyClickHandler(event);"/>
                 <image id="sharing-icon" mousethrough="always"/>
                 <box id="blocked-permissions-container" align="center" tooltiptext="">
-                  <image data-permission-id="geo" class="notification-anchor-icon geo-icon blocked" role="button"
+                  <image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
                          aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
-                  <image data-permission-id="desktop-notification" class="notification-anchor-icon desktop-notification-icon blocked" role="button"
+                  <image data-permission-id="desktop-notification" class="blocked-permission-icon desktop-notification-icon" role="button"
                          aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
-                  <image data-permission-id="camera" class="notification-anchor-icon camera-icon blocked" role="button"
+                  <image data-permission-id="camera" class="blocked-permission-icon camera-icon" role="button"
                          aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
-                  <image data-permission-id="indexedDB" class="notification-anchor-icon indexedDB-icon blocked" role="button"
+                  <image data-permission-id="indexedDB" class="blocked-permission-icon indexedDB-icon" role="button"
                          aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
-                  <image data-permission-id="microphone" class="notification-anchor-icon microphone-icon blocked" role="button"
+                  <image data-permission-id="microphone" class="blocked-permission-icon microphone-icon" role="button"
                          aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
-                  <image data-permission-id="screen" class="notification-anchor-icon screen-icon blocked" role="button"
+                  <image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
                          aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
-                  <image data-permission-id="pointerLock" class="notification-anchor-icon pointerLock-icon blocked" role="button"
+                  <image data-permission-id="pointerLock" class="blocked-permission-icon pointerLock-icon" role="button"
                          aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
                 </box>
                 <box id="notification-popup-box"
                      hidden="true"
                      tooltiptext=""
                      onmouseover="document.getElementById('identity-icon').classList.add('no-hover');"
                      onmouseout="document.getElementById('identity-icon').classList.remove('no-hover');"
                      align="center">
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -14,17 +14,17 @@
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-arrowscrollbox");
 }
 
 .tab-close-button {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-close-tab-button");
 }
 
 .tab-close-button[pinned],
-.tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([visuallyselected="true"]),
+.tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([selected="true"]),
 .tab-icon-image:not([src]):not([pinned]):not([crashed])[selected],
 .tab-icon-image:not([src]):not([pinned]):not([crashed]):not([sharing]),
 .tab-icon-image[busy],
 .tab-throbber:not([busy]),
 .tab-icon-sound:not([soundplaying]):not([muted]),
 .tab-icon-sound[pinned],
 .tab-sharing-icon-overlay,
 .tab-icon-overlay {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6240,54 +6240,54 @@
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content context="tabContextMenu">
       <xul:stack class="tab-stack" flex="1">
-        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,fadein"
+        <xul:hbox xbl:inherits="pinned,selected=visuallyselected,fadein"
                   class="tab-background">
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
+          <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-start"/>
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
+          <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-middle"/>
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
+          <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-end"/>
         </xul:hbox>
-        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,attention"
+        <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
-          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected,visuallyselected"
+          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
-          <xul:image xbl:inherits="src=image,fadein,pinned,selected,visuallyselected,busy,crashed,sharing"
+          <xul:image xbl:inherits="src=image,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
-          <xul:image xbl:inherits="sharing,selected"
+          <xul:image xbl:inherits="sharing,selected=visuallyselected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,visuallyselected"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,selected=visuallyselected"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
-                     xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
+                     xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected=visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
-          <xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
+          <xul:image xbl:inherits="soundplaying,pinned,muted,selected=visuallyselected"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
-                             xbl:inherits="fadein,pinned,selected,visuallyselected"
+                             xbl:inherits="fadein,pinned,selected=visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
       <constructor><![CDATA[
         if (!("_lastAccessed" in this)) {
--- a/browser/base/content/test/general/browser_permissions.js
+++ b/browser/base/content/test/general/browser_permissions.js
@@ -181,18 +181,16 @@ add_task(function* testPermissionIcons()
   yield promiseTabLoadEvent(tab, PERMISSIONS_PAGE);
 
   SitePermissions.set(gBrowser.currentURI, "camera", SitePermissions.ALLOW);
   SitePermissions.set(gBrowser.currentURI, "geo", SitePermissions.BLOCK);
   SitePermissions.set(gBrowser.currentURI, "microphone", SitePermissions.SESSION);
 
   let geoIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='geo']");
   ok(geoIcon.hasAttribute("showing"), "blocked permission icon is shown");
-  ok(geoIcon.classList.contains("blocked"),
-    "blocked permission icon is shown as blocked");
 
   let cameraIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='camera']");
   ok(!cameraIcon.hasAttribute("showing"),
     "allowed permission icon is not shown");
 
   let microphoneIcon  = gIdentityHandler._identityBox.querySelector("[data-permission-id='microphone']");
   ok(!microphoneIcon.hasAttribute("showing"),
     "allowed permission icon is not shown");
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
@@ -37,26 +37,26 @@ var gTests = [
          "the tab has the attribute to show the " + aSharing + " icon");
       let icon =
         document.getAnonymousElementByAttribute(tab, "anonid", "sharing-icon");
       is(window.getComputedStyle(icon).display, "none",
          "the animated sharing icon of the tab is hidden");
 
       // After selecting a new tab, check the attribute is still there,
       // and the icon is now visible.
-      gBrowser.selectedTab = gBrowser.addTab();
+      yield BrowserTestUtils.switchTab(gBrowser, gBrowser.addTab());
       is(gBrowser.selectedTab.getAttribute("sharing"), "",
          "the new tab doesn't have the 'sharing' attribute");
       is(tab.getAttribute("sharing"), aSharing,
          "the tab still has the 'sharing' attribute");
       isnot(window.getComputedStyle(icon).display, "none",
             "the animated sharing icon of the tab is now visible");
 
       // Ensure the icon disappears when selecting the tab.
-      gBrowser.removeCurrentTab();
+      yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
       ok(tab.selected, "the tab with ongoing sharing is selected again");
       is(window.getComputedStyle(icon).display, "none",
          "the animated sharing icon is gone after selecting the tab again");
 
       // And finally verify the attribute is removed when closing the stream.
       yield closeStream();
       is(tab.getAttribute("sharing"), "",
          "the tab no longer has the 'sharing' attribute after closing the stream");
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -20,8 +20,9 @@ skip-if = os == "mac" || os == "win" # I
 tags = openwindow
 [browser_windowOpen.js]
 tags = openwindow
 [browser_serviceworkers.js]
 [browser_broadcastchannel.js]
 [browser_blobUrl.js]
 [browser_middleClick.js]
 [browser_imageCache.js]
+[browser_count_and_remove.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/ContextualIdentityService.jsm");
+
+function openTabInUserContext(userContextId) {
+  let tab = gBrowser.addTab("about:blank", {userContextId});
+  gBrowser.selectedTab = tab;
+}
+
+add_task(function* setup() {
+  // make sure userContext is enabled.
+  yield SpecialPowers.pushPrefEnv({"set": [
+          ["privacy.userContext.enabled", true]
+        ]});
+});
+
+add_task(function* test() {
+  is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs by default.");
+
+  openTabInUserContext(1);
+  is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
+
+  openTabInUserContext(1);
+  is(ContextualIdentityService.countContainerTabs(), 2, "2 container tab created");
+
+  openTabInUserContext(2);
+  is(ContextualIdentityService.countContainerTabs(), 3, "3 container tab created");
+
+  ContextualIdentityService.closeAllContainerTabs();
+  is(ContextualIdentityService.countContainerTabs(), 0, "0 container tab at the end.");
+});
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 support-files =
   head.js
+  head_pageAction.js
   context.html
   ctxmenu-image.png
   context_tabs_onUpdated_page.html
   context_tabs_onUpdated_iframe.html
   file_popup_api_injection_a.html
   file_popup_api_injection_b.html
   file_iframe_document.html
   file_iframe_document.sjs
@@ -39,16 +40,17 @@ tags = webextensions
 [browser_ext_getViews.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_optionsPage_privileges.js]
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_pageAction_popup_resize.js]
 [browser_ext_pageAction_simple.js]
+[browser_ext_pageAction_title.js]
 [browser_ext_popup_api_injection.js]
 [browser_ext_popup_background.js]
 [browser_ext_popup_corners.js]
 [browser_ext_runtime_openOptionsPage.js]
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_simple.js]
 [browser_ext_tab_runtimeConnect.js]
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
@@ -1,166 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-function* runTests(options) {
-  function background(getTests) {
-    let tabs;
-    let tests;
-
-    // Gets the current details of the page action, and returns a
-    // promise that resolves to an object containing them.
-    function getDetails() {
-      return new Promise(resolve => {
-        return browser.tabs.query({active: true, currentWindow: true}, resolve);
-      }).then(([tab]) => {
-        let tabId = tab.id;
-        browser.test.log(`Get details: tab={id: ${tabId}, url: ${JSON.stringify(tab.url)}}`);
-        return Promise.all([
-          browser.pageAction.getTitle({tabId}),
-          browser.pageAction.getPopup({tabId})]);
-      }).then(details => {
-        return Promise.resolve({title: details[0],
-                                popup: details[1]});
-      });
-    }
-
-
-    // Runs the next test in the `tests` array, checks the results,
-    // and passes control back to the outer test scope.
-    function nextTest() {
-      let test = tests.shift();
-
-      test(expecting => {
-        function finish() {
-          // Check that the actual icon has the expected values, then
-          // run the next test.
-          browser.test.sendMessage("nextTest", expecting, tests.length);
-        }
-
-        if (expecting) {
-          // Check that the API returns the expected values, and then
-          // run the next test.
-          getDetails().then(details => {
-            browser.test.assertEq(expecting.title, details.title,
-                                  "expected value from getTitle");
-
-            browser.test.assertEq(expecting.popup, details.popup,
-                                  "expected value from getPopup");
-
-            finish();
-          });
-        } else {
-          finish();
-        }
-      });
-    }
-
-    function runTests() {
-      tabs = [];
-      tests = getTests(tabs);
-
-      browser.tabs.query({active: true, currentWindow: true}, resultTabs => {
-        tabs[0] = resultTabs[0].id;
-
-        nextTest();
-      });
-    }
-
-    browser.test.onMessage.addListener((msg) => {
-      if (msg == "runTests") {
-        runTests();
-      } else if (msg == "runNextTest") {
-        nextTest();
-      } else {
-        browser.test.fail(`Unexpected message: ${msg}`);
-      }
-    });
-
-    runTests();
-  }
+/* global runTests */
 
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: options.manifest,
-
-    files: options.files || {},
-
-    background: `(${background})(${options.getTests})`,
-  });
-
-  let pageActionId;
-  let currentWindow = window;
-  let windows = [];
-
-  function checkDetails(details) {
-    let image = currentWindow.document.getElementById(pageActionId);
-    if (details == null) {
-      ok(image == null || image.hidden, "image is hidden");
-    } else {
-      ok(image, "image exists");
-
-      is(getListStyleImage(image), details.icon, "icon URL is correct");
-
-      let title = details.title || options.manifest.name;
-      is(image.getAttribute("tooltiptext"), title, "image title is correct");
-      is(image.getAttribute("aria-label"), title, "image aria-label is correct");
-      // TODO: Popup URL.
-    }
-  }
-
-  let testNewWindows = 1;
-
-  let awaitFinish = new Promise(resolve => {
-    extension.onMessage("nextTest", (expecting, testsRemaining) => {
-      if (!pageActionId) {
-        pageActionId = `${makeWidgetId(extension.id)}-page-action`;
-      }
-
-      checkDetails(expecting);
-
-      if (testsRemaining) {
-        extension.sendMessage("runNextTest");
-      } else if (testNewWindows) {
-        testNewWindows--;
-
-        BrowserTestUtils.openNewBrowserWindow().then(window => {
-          windows.push(window);
-          currentWindow = window;
-          return focusWindow(window);
-        }).then(() => {
-          extension.sendMessage("runTests");
-        });
-      } else {
-        resolve();
-      }
-    });
-  });
-
-  yield SpecialPowers.pushPrefEnv({set: [["general.useragent.locale", "es-ES"]]});
-
-  yield extension.startup();
-
-  yield awaitFinish;
-
-  yield extension.unload();
-
-  yield SpecialPowers.popPrefEnv();
-
-  let node = document.getElementById(pageActionId);
-  is(node, null, "pageAction image removed from document");
-
-  currentWindow = null;
-  for (let win of windows.splice(0)) {
-    node = win.document.getElementById(pageActionId);
-    is(node, null, "pageAction image removed from second document");
-
-    yield BrowserTestUtils.closeWindow(win);
-  }
-}
+Services.scriptloader.loadSubScript(new URL("head_pageAction.js", gTestPath).href,
+                                    this);
 
 add_task(function* testTabSwitchContext() {
   yield runTests({
     manifest: {
       "name": "Foo Extension",
 
       "page_action": {
         "default_icon": "default.png",
@@ -324,60 +174,8 @@ add_task(function* testTabSwitchContext(
           browser.pageAction.hide(tabs[0]).then(() => {
             expect(null);
           });
         },
       ];
     },
   });
 });
-
-add_task(function* testDefaultTitle() {
-  yield runTests({
-    manifest: {
-      "name": "Foo Extension",
-
-      "page_action": {
-        "default_icon": "icon.png",
-      },
-
-      "permissions": ["tabs"],
-    },
-
-    files: {
-      "icon.png": imageBuffer,
-    },
-
-    getTests(tabs) {
-      let details = [
-        {"title": "Foo Extension",
-         "popup": "",
-         "icon": browser.runtime.getURL("icon.png")},
-        {"title": "Foo Title",
-         "popup": "",
-         "icon": browser.runtime.getURL("icon.png")},
-      ];
-
-      return [
-        expect => {
-          browser.test.log("Initial state. No icon visible.");
-          expect(null);
-        },
-        expect => {
-          browser.test.log("Show the icon on the first tab, expect extension title as default title.");
-          browser.pageAction.show(tabs[0]).then(() => {
-            expect(details[0]);
-          });
-        },
-        expect => {
-          browser.test.log("Change the title. Expect new title.");
-          browser.pageAction.setTitle({tabId: tabs[0], title: "Foo Title"});
-          expect(details[1]);
-        },
-        expect => {
-          browser.test.log("Clear the title. Expect extension title.");
-          browser.pageAction.setTitle({tabId: tabs[0], title: ""});
-          expect(details[0]);
-        },
-      ];
-    },
-  });
-});
copy from browser/components/extensions/test/browser/browser_ext_pageAction_context.js
copy to browser/components/extensions/test/browser/browser_ext_pageAction_title.js
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_title.js
@@ -1,166 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-function* runTests(options) {
-  function background(getTests) {
-    let tabs;
-    let tests;
-
-    // Gets the current details of the page action, and returns a
-    // promise that resolves to an object containing them.
-    function getDetails() {
-      return new Promise(resolve => {
-        return browser.tabs.query({active: true, currentWindow: true}, resolve);
-      }).then(([tab]) => {
-        let tabId = tab.id;
-        browser.test.log(`Get details: tab={id: ${tabId}, url: ${JSON.stringify(tab.url)}}`);
-        return Promise.all([
-          browser.pageAction.getTitle({tabId}),
-          browser.pageAction.getPopup({tabId})]);
-      }).then(details => {
-        return Promise.resolve({title: details[0],
-                                popup: details[1]});
-      });
-    }
-
-
-    // Runs the next test in the `tests` array, checks the results,
-    // and passes control back to the outer test scope.
-    function nextTest() {
-      let test = tests.shift();
-
-      test(expecting => {
-        function finish() {
-          // Check that the actual icon has the expected values, then
-          // run the next test.
-          browser.test.sendMessage("nextTest", expecting, tests.length);
-        }
-
-        if (expecting) {
-          // Check that the API returns the expected values, and then
-          // run the next test.
-          getDetails().then(details => {
-            browser.test.assertEq(expecting.title, details.title,
-                                  "expected value from getTitle");
-
-            browser.test.assertEq(expecting.popup, details.popup,
-                                  "expected value from getPopup");
-
-            finish();
-          });
-        } else {
-          finish();
-        }
-      });
-    }
-
-    function runTests() {
-      tabs = [];
-      tests = getTests(tabs);
-
-      browser.tabs.query({active: true, currentWindow: true}, resultTabs => {
-        tabs[0] = resultTabs[0].id;
-
-        nextTest();
-      });
-    }
-
-    browser.test.onMessage.addListener((msg) => {
-      if (msg == "runTests") {
-        runTests();
-      } else if (msg == "runNextTest") {
-        nextTest();
-      } else {
-        browser.test.fail(`Unexpected message: ${msg}`);
-      }
-    });
-
-    runTests();
-  }
+/* global runTests */
 
-  let extension = ExtensionTestUtils.loadExtension({
-    manifest: options.manifest,
-
-    files: options.files || {},
-
-    background: `(${background})(${options.getTests})`,
-  });
-
-  let pageActionId;
-  let currentWindow = window;
-  let windows = [];
-
-  function checkDetails(details) {
-    let image = currentWindow.document.getElementById(pageActionId);
-    if (details == null) {
-      ok(image == null || image.hidden, "image is hidden");
-    } else {
-      ok(image, "image exists");
-
-      is(getListStyleImage(image), details.icon, "icon URL is correct");
-
-      let title = details.title || options.manifest.name;
-      is(image.getAttribute("tooltiptext"), title, "image title is correct");
-      is(image.getAttribute("aria-label"), title, "image aria-label is correct");
-      // TODO: Popup URL.
-    }
-  }
-
-  let testNewWindows = 1;
-
-  let awaitFinish = new Promise(resolve => {
-    extension.onMessage("nextTest", (expecting, testsRemaining) => {
-      if (!pageActionId) {
-        pageActionId = `${makeWidgetId(extension.id)}-page-action`;
-      }
-
-      checkDetails(expecting);
-
-      if (testsRemaining) {
-        extension.sendMessage("runNextTest");
-      } else if (testNewWindows) {
-        testNewWindows--;
-
-        BrowserTestUtils.openNewBrowserWindow().then(window => {
-          windows.push(window);
-          currentWindow = window;
-          return focusWindow(window);
-        }).then(() => {
-          extension.sendMessage("runTests");
-        });
-      } else {
-        resolve();
-      }
-    });
-  });
-
-  yield SpecialPowers.pushPrefEnv({set: [["general.useragent.locale", "es-ES"]]});
-
-  yield extension.startup();
-
-  yield awaitFinish;
-
-  yield extension.unload();
-
-  yield SpecialPowers.popPrefEnv();
-
-  let node = document.getElementById(pageActionId);
-  is(node, null, "pageAction image removed from document");
-
-  currentWindow = null;
-  for (let win of windows.splice(0)) {
-    node = win.document.getElementById(pageActionId);
-    is(node, null, "pageAction image removed from second document");
-
-    yield BrowserTestUtils.closeWindow(win);
-  }
-}
+Services.scriptloader.loadSubScript(new URL("head_pageAction.js", gTestPath).href,
+                                    this);
 
 add_task(function* testTabSwitchContext() {
   yield runTests({
     manifest: {
       "name": "Foo Extension",
 
       "page_action": {
         "default_icon": "default.png",
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
@@ -26,28 +26,30 @@ add_task(function* testExecuteScript() {
     const MAX_TRIES = 10;
     let tries = 0;
 
     function again() {
       if (tries++ == MAX_TRIES) {
         return Promise.reject(new Error("Max tries exceeded"));
       }
 
+      let url = `${URL}?r=${Math.random()}`;
+
       let loadingPromise = new Promise(resolve => {
         browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab_) {
-          if (tabId == tab.id && changed.status == "loading" && tab_.url == URL) {
+          if (tabId == tab.id && changed.status == "loading" && tab_.url == url) {
             browser.tabs.onUpdated.removeListener(listener);
             resolve();
           }
         });
       });
 
       // TODO: Test allFrames and frameId.
 
-      return browser.tabs.update({url: URL}).then(() => {
+      return browser.tabs.update({url}).then(() => {
         return loadingPromise;
       }).then(() => {
         return Promise.all([
           // Send the executeScript requests in the reverse order that we expect
           // them to execute in, to avoid them passing only because of timing
           // races.
           browser.tabs.executeScript({
             code: "document.readyState",
copy from browser/components/extensions/test/browser/browser_ext_pageAction_context.js
copy to browser/components/extensions/test/browser/head_pageAction.js
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/head_pageAction.js
@@ -1,12 +1,15 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+/* exported runTests */
+/* globals getListStyleImage */
+
 function* runTests(options) {
   function background(getTests) {
     let tabs;
     let tests;
 
     // Gets the current details of the page action, and returns a
     // promise that resolves to an object containing them.
     function getDetails() {
@@ -152,232 +155,8 @@ function* runTests(options) {
   for (let win of windows.splice(0)) {
     node = win.document.getElementById(pageActionId);
     is(node, null, "pageAction image removed from second document");
 
     yield BrowserTestUtils.closeWindow(win);
   }
 }
 
-add_task(function* testTabSwitchContext() {
-  yield runTests({
-    manifest: {
-      "name": "Foo Extension",
-
-      "page_action": {
-        "default_icon": "default.png",
-        "default_popup": "__MSG_popup__",
-        "default_title": "Default __MSG_title__ \u263a",
-      },
-
-      "default_locale": "en",
-
-      "permissions": ["tabs"],
-    },
-
-    "files": {
-      "_locales/en/messages.json": {
-        "popup": {
-          "message": "default.html",
-          "description": "Popup",
-        },
-
-        "title": {
-          "message": "Title",
-          "description": "Title",
-        },
-      },
-
-      "_locales/es_ES/messages.json": {
-        "popup": {
-          "message": "default.html",
-          "description": "Popup",
-        },
-
-        "title": {
-          "message": "T\u00edtulo",
-          "description": "Title",
-        },
-      },
-
-      "default.png": imageBuffer,
-      "1.png": imageBuffer,
-      "2.png": imageBuffer,
-    },
-
-    getTests(tabs) {
-      let details = [
-        {"icon": browser.runtime.getURL("default.png"),
-         "popup": browser.runtime.getURL("default.html"),
-         "title": "Default T\u00edtulo \u263a"},
-        {"icon": browser.runtime.getURL("1.png"),
-         "popup": browser.runtime.getURL("default.html"),
-         "title": "Default T\u00edtulo \u263a"},
-        {"icon": browser.runtime.getURL("2.png"),
-         "popup": browser.runtime.getURL("2.html"),
-         "title": "Title 2"},
-        {"icon": browser.runtime.getURL("2.png"),
-         "popup": browser.runtime.getURL("2.html"),
-         "title": "Default T\u00edtulo \u263a"},
-      ];
-
-      let promiseTabLoad = details => {
-        return new Promise(resolve => {
-          browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
-            if (tabId == details.id && changed.url == details.url) {
-              browser.tabs.onUpdated.removeListener(listener);
-              resolve();
-            }
-          });
-        });
-      };
-      return [
-        expect => {
-          browser.test.log("Initial state. No icon visible.");
-          expect(null);
-        },
-        expect => {
-          browser.test.log("Show the icon on the first tab, expect default properties.");
-          browser.pageAction.show(tabs[0]).then(() => {
-            expect(details[0]);
-          });
-        },
-        expect => {
-          browser.test.log("Change the icon. Expect default properties excluding the icon.");
-          browser.pageAction.setIcon({tabId: tabs[0], path: "1.png"});
-          expect(details[1]);
-        },
-        expect => {
-          browser.test.log("Create a new tab. No icon visible.");
-          browser.tabs.create({active: true, url: "about:blank?0"}, tab => {
-            tabs.push(tab.id);
-            expect(null);
-          });
-        },
-        expect => {
-          browser.test.log("Await tab load. No icon visible.");
-          expect(null);
-        },
-        expect => {
-          browser.test.log("Change properties. Expect new properties.");
-          let tabId = tabs[1];
-          browser.pageAction.show(tabId).then(() => {
-            browser.pageAction.setIcon({tabId, path: "2.png"});
-            browser.pageAction.setPopup({tabId, popup: "2.html"});
-            browser.pageAction.setTitle({tabId, title: "Title 2"});
-
-            expect(details[2]);
-          });
-        },
-        expect => {
-          browser.test.log("Change the hash. Expect same properties.");
-
-          promiseTabLoad({id: tabs[1], url: "about:blank?0#ref"}).then(() => {
-            expect(details[2]);
-          });
-
-          browser.tabs.update(tabs[1], {url: "about:blank?0#ref"});
-        },
-        expect => {
-          browser.test.log("Clear the title. Expect default title.");
-          browser.pageAction.setTitle({tabId: tabs[1], title: ""});
-
-          expect(details[3]);
-        },
-        expect => {
-          browser.test.log("Navigate to a new page. Expect icon hidden.");
-
-          // TODO: This listener should not be necessary, but the |tabs.update|
-          // callback currently fires too early in e10s windows.
-          promiseTabLoad({id: tabs[1], url: "about:blank?1"}).then(() => {
-            expect(null);
-          });
-
-          browser.tabs.update(tabs[1], {url: "about:blank?1"});
-        },
-        expect => {
-          browser.test.log("Show the icon. Expect default properties again.");
-          browser.pageAction.show(tabs[1]).then(() => {
-            expect(details[0]);
-          });
-        },
-        expect => {
-          browser.test.log("Switch back to the first tab. Expect previously set properties.");
-          browser.tabs.update(tabs[0], {active: true}, () => {
-            expect(details[1]);
-          });
-        },
-        expect => {
-          browser.test.log("Hide the icon on tab 2. Switch back, expect hidden.");
-          browser.pageAction.hide(tabs[1]).then(() => {
-            browser.tabs.update(tabs[1], {active: true}, () => {
-              expect(null);
-            });
-          });
-        },
-        expect => {
-          browser.test.log("Switch back to tab 1. Expect previous results again.");
-          browser.tabs.remove(tabs[1], () => {
-            expect(details[1]);
-          });
-        },
-        expect => {
-          browser.test.log("Hide the icon. Expect hidden.");
-          browser.pageAction.hide(tabs[0]).then(() => {
-            expect(null);
-          });
-        },
-      ];
-    },
-  });
-});
-
-add_task(function* testDefaultTitle() {
-  yield runTests({
-    manifest: {
-      "name": "Foo Extension",
-
-      "page_action": {
-        "default_icon": "icon.png",
-      },
-
-      "permissions": ["tabs"],
-    },
-
-    files: {
-      "icon.png": imageBuffer,
-    },
-
-    getTests(tabs) {
-      let details = [
-        {"title": "Foo Extension",
-         "popup": "",
-         "icon": browser.runtime.getURL("icon.png")},
-        {"title": "Foo Title",
-         "popup": "",
-         "icon": browser.runtime.getURL("icon.png")},
-      ];
-
-      return [
-        expect => {
-          browser.test.log("Initial state. No icon visible.");
-          expect(null);
-        },
-        expect => {
-          browser.test.log("Show the icon on the first tab, expect extension title as default title.");
-          browser.pageAction.show(tabs[0]).then(() => {
-            expect(details[0]);
-          });
-        },
-        expect => {
-          browser.test.log("Change the title. Expect new title.");
-          browser.pageAction.setTitle({tabId: tabs[0], title: "Foo Title"});
-          expect(details[1]);
-        },
-        expect => {
-          browser.test.log("Clear the title. Expect extension title.");
-          browser.pageAction.setTitle({tabId: tabs[0], title: ""});
-          expect(details[0]);
-        },
-      ];
-    },
-  });
-});
--- a/browser/components/migration/AutoMigrate.jsm
+++ b/browser/components/migration/AutoMigrate.jsm
@@ -18,16 +18,18 @@ const kAutoMigrateLastUndoPromptDateMsPr
 const kAutoMigrateDaysToOfferUndoPref = "browser.migrate.automigrate.daysToOfferUndo";
 
 const kPasswordManagerTopic = "passwordmgr-storage-changed";
 const kPasswordManagerTopicTypes = new Set([
   "addLogin",
   "modifyLogin",
 ]);
 
+const kSyncTopic = "fxaccounts:onlogin";
+
 const kNotificationId = "abouthome-automigration-undo";
 
 Cu.import("resource:///modules/MigrationUtils.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -41,34 +43,38 @@ const AutoMigrate = {
   init() {
     this.enabled = Preferences.get(kAutoMigrateEnabledPref, false);
     if (this.enabled) {
       this.maybeInitUndoObserver();
     }
   },
 
   maybeInitUndoObserver() {
-    // Check synchronously (NB: canUndo is async) if we really need
-    // to do this:
-    if (!this.getUndoRange()) {
+    if (!this.canUndo()) {
       return;
     }
-    // Now register places and password observers:
+    // Now register places, password and sync observers:
     this.onItemAdded = this.onItemMoved = this.onItemChanged =
-      this.removeUndoOption;
+      this.removeUndoOption.bind(this, this.UNDO_REMOVED_REASON_BOOKMARK_CHANGE);
     PlacesUtils.addLazyBookmarkObserver(this, true);
-    Services.obs.addObserver(this, kPasswordManagerTopic, true);
+    for (let topic of [kSyncTopic, kPasswordManagerTopic]) {
+      Services.obs.addObserver(this, topic, true);
+    }
   },
 
   observe(subject, topic, data) {
-    // As soon as any login gets added or modified, disable undo:
-    // (Note that this ignores logins being removed as that doesn't
-    //  impair the 'undo' functionality of the import.)
-    if (kPasswordManagerTopicTypes.has(data)) {
-      this.removeUndoOption();
+    if (topic == kPasswordManagerTopic) {
+      // As soon as any login gets added or modified, disable undo:
+      // (Note that this ignores logins being removed as that doesn't
+      //  impair the 'undo' functionality of the import.)
+      if (kPasswordManagerTopicTypes.has(data)) {
+        this.removeUndoOption(this.UNDO_REMOVED_REASON_PASSWORD_CHANGE);
+      }
+    } else if (topic == kSyncTopic) {
+      this.removeUndoOption(this.UNDO_REMOVED_REASON_SYNC_SIGNIN);
     }
   },
 
   /**
    * Automatically pick a migrator and resources to migrate,
    * then migrate those and start up.
    *
    * @throws if automatically deciding on migrators/data
@@ -185,34 +191,23 @@ const AutoMigrate = {
     }
     if (!finish || !start) {
       return null;
     }
     return [new Date(start), new Date(finish)];
   },
 
   canUndo() {
-    if (!this.getUndoRange()) {
-      return Promise.resolve(false);
-    }
-    // Return a promise resolving to false if we're signed into sync, resolve
-    // to true otherwise.
-    let {fxAccounts} = Cu.import("resource://gre/modules/FxAccounts.jsm", {});
-    return fxAccounts.getSignedInUser().then(user => {
-      if (user) {
-        Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_CANT_UNDO_BECAUSE_SYNC").add(true);
-      }
-      return !user;
-    }, () => Promise.resolve(true));
+    return !!this.getUndoRange();
   },
 
   undo: Task.async(function* () {
     let histogram = Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_UNDO");
     histogram.add(0);
-    if (!(yield this.canUndo())) {
+    if (!this.canUndo()) {
       histogram.add(5);
       throw new Error("Can't undo!");
     }
 
     histogram.add(10);
 
     yield PlacesUtils.bookmarks.eraseEverything();
     histogram.add(15);
@@ -233,29 +228,37 @@ const AutoMigrate = {
     histogram.add(20);
 
     try {
       Services.logins.removeAllLogins();
     } catch (ex) {
       // ignore failure.
     }
     histogram.add(25);
-    this.removeUndoOption();
+    this.removeUndoOption(this.UNDO_REMOVED_REASON_UNDO_USED);
     histogram.add(30);
   }),
 
-  removeUndoOption() {
+  removeUndoOption(reason) {
     // Remove observers, and ensure that exceptions doing so don't break
     // removing the pref.
-    try {
-      Services.obs.removeObserver(this, kPasswordManagerTopic);
-    } catch (ex) {}
+    for (let topic of [kSyncTopic, kPasswordManagerTopic]) {
+      try {
+        Services.obs.removeObserver(this, topic);
+      } catch (ex) {
+        Cu.reportError("Error removing observer for " + topic + ": " + ex);
+      }
+    }
     try {
       PlacesUtils.removeLazyBookmarkObserver(this);
-    } catch (ex) {}
+    } catch (ex) {
+      Cu.reportError("Error removing lazy bookmark observer: " + ex);
+    }
+
+    let migrationBrowser = Preferences.get(kAutoMigrateBrowserPref, "unknown");
     Services.prefs.clearUserPref(kAutoMigrateStartedPref);
     Services.prefs.clearUserPref(kAutoMigrateFinishedPref);
     Services.prefs.clearUserPref(kAutoMigrateBrowserPref);
 
     let browserWindows = Services.wm.getEnumerator("navigator:browser");
     while (browserWindows.hasMoreElements()) {
       let win = browserWindows.getNext();
       if (!win.closed) {
@@ -263,75 +266,77 @@ const AutoMigrate = {
           let nb = win.gBrowser.getNotificationBox(browser);
           let notification = nb.getNotificationWithValue(kNotificationId);
           if (notification) {
             nb.removeNotification(notification);
           }
         }
       }
     }
+    let histogram =
+      Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_UNDO_REASON");
+    histogram.add(migrationBrowser, reason);
   },
 
   getBrowserUsedForMigration() {
     let browserId = Services.prefs.getCharPref(kAutoMigrateBrowserPref);
     if (browserId) {
       return MigrationUtils.getBrowserName(browserId);
     }
     return null;
   },
 
   maybeShowUndoNotification(target) {
-    this.canUndo().then(canUndo => {
-      // The tab might have navigated since we requested the undo state:
-      if (!canUndo || target.currentURI.spec != "about:home") {
-        return;
-      }
-      let win = target.ownerGlobal;
-      let notificationBox = win.gBrowser.getNotificationBox(target);
-      if (!notificationBox || notificationBox.getNotificationWithValue("abouthome-automigration-undo")) {
-        return;
-      }
+    // The tab might have navigated since we requested the undo state:
+    if (!this.canUndo() || target.currentURI.spec != "about:home") {
+      return;
+    }
 
-      // At this stage we're committed to show the prompt - unless we shouldn't,
-      // in which case we remove the undo prefs (which will cause canUndo() to
-      // return false from now on.):
-      if (!this.shouldStillShowUndoPrompt()) {
-        this.removeUndoOption();
-        return;
-      }
+    let win = target.ownerGlobal;
+    let notificationBox = win.gBrowser.getNotificationBox(target);
+    if (!notificationBox || notificationBox.getNotificationWithValue("abouthome-automigration-undo")) {
+      return;
+    }
+
+    // At this stage we're committed to show the prompt - unless we shouldn't,
+    // in which case we remove the undo prefs (which will cause canUndo() to
+    // return false from now on.):
+    if (!this.shouldStillShowUndoPrompt()) {
+      this.removeUndoOption(this.UNDO_REMOVED_REASON_OFFER_EXPIRED);
+      return;
+    }
 
-      let browserName = this.getBrowserUsedForMigration();
-      let message;
-      if (browserName) {
-        message = MigrationUtils.getLocalizedString("automigration.undo.message",
-                                                    [browserName]);
-      } else {
-        message = MigrationUtils.getLocalizedString("automigration.undo.unknownBrowserMessage");
-      }
+    let browserName = this.getBrowserUsedForMigration();
+    let message;
+    if (browserName) {
+      message = MigrationUtils.getLocalizedString("automigration.undo.message",
+                                                  [browserName]);
+    } else {
+      message = MigrationUtils.getLocalizedString("automigration.undo.unknownBrowserMessage");
+    }
 
-      let buttons = [
-        {
-          label: MigrationUtils.getLocalizedString("automigration.undo.keep.label"),
-          accessKey: MigrationUtils.getLocalizedString("automigration.undo.keep.accesskey"),
-          callback: () => {
-            this.removeUndoOption();
-          },
+    let buttons = [
+      {
+        label: MigrationUtils.getLocalizedString("automigration.undo.keep.label"),
+        accessKey: MigrationUtils.getLocalizedString("automigration.undo.keep.accesskey"),
+        callback: () => {
+          this.removeUndoOption(this.UNDO_REMOVED_REASON_OFFER_REJECTED);
         },
-        {
-          label: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.label"),
-          accessKey: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.accesskey"),
-          callback: () => {
-            this.undo();
-          },
+      },
+      {
+        label: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.label"),
+        accessKey: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.accesskey"),
+        callback: () => {
+          this.undo();
         },
-      ];
-      notificationBox.appendNotification(
-        message, kNotificationId, null, notificationBox.PRIORITY_INFO_HIGH, buttons
-      );
-    });
+      },
+    ];
+    notificationBox.appendNotification(
+      message, kNotificationId, null, notificationBox.PRIORITY_INFO_HIGH, buttons
+    );
   },
 
   shouldStillShowUndoPrompt() {
     let today = new Date();
     // Round down to midnight:
     today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
     // We store the unix timestamp corresponding to midnight on the last day
     // on which we prompted. Fetch that and compare it to today's date.
@@ -345,14 +350,21 @@ const AutoMigrate = {
       Preferences.set(kAutoMigrateLastUndoPromptDateMsPref, today.valueOf().toString());
       if (remainingDays <= 0) {
         return false;
       }
     }
     return true;
   },
 
+  UNDO_REMOVED_REASON_UNDO_USED: 0,
+  UNDO_REMOVED_REASON_SYNC_SIGNIN: 1,
+  UNDO_REMOVED_REASON_PASSWORD_CHANGE: 2,
+  UNDO_REMOVED_REASON_BOOKMARK_CHANGE: 3,
+  UNDO_REMOVED_REASON_OFFER_EXPIRED: 4,
+  UNDO_REMOVED_REASON_OFFER_REJECTED: 5,
+
   QueryInterface: XPCOMUtils.generateQI(
     [Ci.nsIObserver, Ci.nsINavBookmarkObserver, Ci.nsISupportsWeakReference]
   ),
 };
 
 AutoMigrate.init();
--- a/browser/components/migration/tests/unit/test_automigration.js
+++ b/browser/components/migration/tests/unit/test_automigration.js
@@ -160,17 +160,17 @@ add_task(function* checkUndoPrecondition
   Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
                    "migrate called with 'null' as a profile");
 
   yield migrationFinishedPromise;
   Assert.ok(Preferences.has("browser.migrate.automigrate.started"),
             "Should have set start time pref");
   Assert.ok(Preferences.has("browser.migrate.automigrate.finished"),
             "Should have set finish time pref");
-  Assert.ok((yield AutoMigrate.canUndo()), "Should be able to undo migration");
+  Assert.ok(AutoMigrate.canUndo(), "Should be able to undo migration");
 
   let [beginRange, endRange] = AutoMigrate.getUndoRange();
   let stringRange = `beginRange: ${beginRange}; endRange: ${endRange}`;
   Assert.ok(beginRange <= endRange,
             "Migration should have started before or when it ended " + stringRange);
 
   yield AutoMigrate.undo();
   Assert.ok(true, "Should be able to finish an undo cycle.");
@@ -221,17 +221,17 @@ add_task(function* checkUndoRemoval() {
   visits.root.containerOpen = false;
 
   // Now set finished pref:
   let endTime = "" + Date.now();
   Preferences.set("browser.migrate.automigrate.started", startTime);
   Preferences.set("browser.migrate.automigrate.finished", endTime);
 
   // Verify that we can undo, then undo:
-  Assert.ok(yield AutoMigrate.canUndo(), "Should be possible to undo migration");
+  Assert.ok(AutoMigrate.canUndo(), "Should be possible to undo migration");
   yield AutoMigrate.undo();
 
   // Check that the undo removed the history visits:
   visits = PlacesUtils.history.executeQuery(query, opts);
   visits.root.containerOpen = true;
   Assert.equal(visits.root.childCount, 0, "Should have no more visits");
   visits.root.containerOpen = false;
 
@@ -254,35 +254,35 @@ add_task(function* checkUndoRemoval() {
 add_task(function* checkUndoDisablingByBookmarksAndPasswords() {
   let startTime = "" + Date.now();
   Services.prefs.setCharPref("browser.migrate.automigrate.started", startTime);
   // Now set finished pref:
   let endTime = "" + (Date.now() + 1000);
   Services.prefs.setCharPref("browser.migrate.automigrate.finished", endTime);
   AutoMigrate.maybeInitUndoObserver();
 
-  ok((yield AutoMigrate.canUndo()), "Should be able to undo.");
+  ok(AutoMigrate.canUndo(), "Should be able to undo.");
 
   // Insert a login and check that that disabled undo.
   let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
   login.init("www.mozilla.org", "http://www.mozilla.org", null, "user", "pass", "userEl", "passEl");
   Services.logins.addLogin(login);
 
-  ok(!(yield AutoMigrate.canUndo()), "Should no longer be able to undo.");
+  ok(!AutoMigrate.canUndo(), "Should no longer be able to undo.");
   Services.prefs.setCharPref("browser.migrate.automigrate.started", startTime);
   Services.prefs.setCharPref("browser.migrate.automigrate.finished", endTime);
-  ok((yield AutoMigrate.canUndo()), "Should be able to undo.");
+  ok(AutoMigrate.canUndo(), "Should be able to undo.");
   AutoMigrate.maybeInitUndoObserver();
 
   // Insert a bookmark and check that that disabled undo.
   yield PlacesUtils.bookmarks.insert({
     parentGuid: PlacesUtils.bookmarks.toolbarGuid,
     url: "http://www.example.org/",
     title: "Some example bookmark",
   });
-  ok(!(yield AutoMigrate.canUndo()), "Should no longer be able to undo.");
+  ok(!AutoMigrate.canUndo(), "Should no longer be able to undo.");
 
   try {
     Services.logins.removeAllLogins();
   } catch (ex) {}
   yield PlacesUtils.bookmarks.eraseEverything();
 });
 
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -1,13 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource://gre/modules/PluralForm.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
+                                  "resource://gre/modules/ContextualIdentityService.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
 
 var gPrivacyPane = {
 
   /**
    * Whether the use has selected the auto-start private browsing mode in the UI.
    */
   _autoStartPrivateBrowsing: false,
 
@@ -60,16 +66,55 @@ var gPrivacyPane = {
     if (!Services.prefs.getBoolPref("privacy.userContext.ui.enabled")) {
       return;
     }
 
     let link = document.getElementById("browserContainersLearnMore");
     link.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "containers";
 
     document.getElementById("browserContainersbox").hidden = false;
+
+    document.getElementById("browserContainersCheckbox").checked =
+      Services.prefs.getBoolPref("privacy.userContext.enabled");
+  },
+
+  _checkBrowserContainers: function(event) {
+    let checkbox = document.getElementById("browserContainersCheckbox");
+    if (checkbox.checked) {
+      Services.prefs.setBoolPref("privacy.userContext.enabled", true);
+      return;
+    }
+
+    let count = ContextualIdentityService.countContainerTabs();
+    if (count == 0) {
+      Services.prefs.setBoolPref("privacy.userContext.enabled", false);
+      return;
+    }
+
+    let bundlePreferences = document.getElementById("bundlePreferences");
+
+    let title = bundlePreferences.getString("disableContainersAlertTitle");
+    let message = PluralForm.get(count, bundlePreferences.getString("disableContainersMsg"))
+                            .replace("#S", count)
+    let okButton = PluralForm.get(count, bundlePreferences.getString("disableContainersOkButton"))
+                             .replace("#S", count)
+    let cancelButton = bundlePreferences.getString("disableContainersButton2");
+
+    let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+                      (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
+
+    let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
+                                       okButton, cancelButton, null, null, {});
+    if (rv == 0) {
+      ContextualIdentityService.closeAllContainerTabs();
+      Services.prefs.setBoolPref("privacy.userContext.enabled", false);
+      return;
+    }
+
+    checkbox.checked = true;
   },
 
   /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
@@ -126,16 +171,18 @@ var gPrivacyPane = {
     setEventListener("trackingProtectionRadioGroup", "command",
                      gPrivacyPane.trackingProtectionWritePrefs);
     setEventListener("trackingProtectionExceptions", "command",
                      gPrivacyPane.showTrackingProtectionExceptions);
     setEventListener("changeBlockList", "command",
                      gPrivacyPane.showBlockLists);
     setEventListener("changeBlockListPBM", "command",
                      gPrivacyPane.showBlockLists);
+    setEventListener("browserContainersCheckbox", "command",
+                     gPrivacyPane._checkBrowserContainers);
   },
 
   // TRACKING PROTECTION MODE
 
   /**
    * Selects the right item of the Tracking Protection radiogroup.
    */
   trackingProtectionReadPrefs() {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -68,22 +68,16 @@
               type="bool"/>
   <preference id="privacy.sanitize.timeSpan"
               name="privacy.sanitize.timeSpan"
               type="int"/>
   <!-- Private Browsing -->
   <preference id="browser.privatebrowsing.autostart"
               name="browser.privatebrowsing.autostart"
               type="bool"/>
-
-  <!-- Containers -->
-  <preference id="privacy.userContext.enabled"
-              name="privacy.userContext.enabled"
-              type="bool"/>
-
 </preferences>
 
 <hbox id="header-privacy"
       class="header"
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&panePrivacy.title;</label>
   <button class="help-button"
@@ -293,12 +287,11 @@
 <groupbox id="browserContainersGroup" data-category="panePrivacy" hidden="true">
   <vbox id="browserContainersbox" hidden="true">
     <caption><label>&browserContainersHeader.label;
       <label id="browserContainersLearnMore" class="text-link"
              value="&browserContainersLearnMore.label;"/>
     </label></caption>
     <checkbox id="browserContainersCheckbox"
               label="&browserContainersEnabled.label;"
-              accesskey="&browserContainersEnabled.accesskey;"
-              preference="privacy.userContext.enabled"/>
+              accesskey="&browserContainersEnabled.accesskey;" />
   </vbox>
 </groupbox>
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -170,8 +170,13 @@ syncUnlinkConfirm.label=Unlink
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
 shouldRestartTitle=Restart %S
 okToRestartButton=Restart %S now
 revertNoRestartButton=Revert
 
 restartNow=Restart Now
 restartLater=Restart Later
+
+disableContainersAlertTitle=Close All Container Tabs?
+disableContainersMsg=If you disable Container Tabs now, #S container tab will be closed. Are you sure you want to disable Container Tabs?;If you disable Containers Tabs now, #S container tabs will be closed. Are you sure you want to disable Containers Tabs?
+disableContainersOkButton=Close #S Container Tab;Close #S Container Tabs
+disableContainersButton2=Keep enabled
--- a/browser/themes/linux/browser-lightweightTheme.css
+++ b/browser/themes/linux/browser-lightweightTheme.css
@@ -6,26 +6,26 @@
 %filter substitution
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1587,22 +1587,22 @@ html|span.ac-emphasize-text-url {
 /* Tab drag and drop */
 .tab-drop-indicator {
   list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
   margin-bottom: -9px;
   z-index: 3;
 }
 
 /* Tab close button */
-.tab-close-button:not([visuallyselected]):not(:hover) {
+.tab-close-button:not([selected]):not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 64, 16, 48);
 }
 
 .tab-close-button:not(:hover):-moz-lwtheme-brighttext,
-#TabsToolbar[brighttext] .tab-close-button:not([visuallyselected]):not(:hover) {
+#TabsToolbar[brighttext] .tab-close-button:not([selected]):not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
 }
 
 .tab-close-button:not(:hover):-moz-lwtheme-darktext {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 96, 16, 80);
 }
 
 /* Tabstrip new tab button */
--- a/browser/themes/linux/devedition.css
+++ b/browser/themes/linux/devedition.css
@@ -16,17 +16,17 @@
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
   --urlbar-dropmarker-2x-region: rect(0px, 11px, 14px, 0px);
   --urlbar-dropmarker-hover-2x-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-active-2x-region: rect(0px, 33px, 14px, 22px);
 }
 
 :root[devtoolstheme="dark"] .findbar-closebutton:not(:hover),
 :root[devtoolstheme="dark"] #sidebar-header > .close-icon:not(:hover),
-.tab-close-button[visuallyselected]:not(:hover) {
+.tab-close-button[selected]:not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
 }
 
 /* The menubar and tabs toolbar should match the devedition theme */
 #TabsToolbar,
 #toolbar-menubar {
   -moz-appearance: none !important;
 }
--- a/browser/themes/osx/browser-lightweightTheme.css
+++ b/browser/themes/osx/browser-lightweightTheme.css
@@ -5,34 +5,34 @@
 %include shared.inc
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
 
 @media (min-resolution: 2dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTextureLWT@;/*,
                       lwtHeader;*/
   }
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2475,40 +2475,40 @@ toolbarbutton.chevron > .toolbarbutton-m
       url(chrome://browser/skin/tabbrowser/tab-stroke-end.png),
       url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
       url(chrome://browser/skin/tabbrowser/tab-stroke-start.png),
       url(chrome://browser/skin/tabbrowser/tab-background-end.png),
       url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
       url(chrome://browser/skin/tabbrowser/tab-background-start.png);
   }
 
-  .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
+  .tab-background-middle[selected=true]:-moz-window-inactive {
     background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive.png),
                       @fgTabTextureYosemiteInactive@,
                       none;
   }
 
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+  .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+  .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive.png);
   }
 
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+  .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+  .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive.png);
   }
 
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+  .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+  .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
     background-image: url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg);
     background-size: 100% 100%;
   }
 
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+  .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+  .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
     background-image: url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg);
     background-size: 100% 100%;
   }
 
   @media (min-resolution: 2dppx) {
     /* image preloading hack from shared/tabs.inc.css */
     #tabbrowser-tabs::before {
       background-image:
@@ -2522,47 +2522,47 @@ toolbarbutton.chevron > .toolbarbutton-m
         url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png),
         url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
         url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png),
         url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png),
         url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
         url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png);
     }
 
-    .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
+    .tab-background-middle[selected=true]:-moz-window-inactive {
       background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive@2x.png),
                         @fgTabTextureYosemiteInactive@,
                         none;
     }
 
-    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+    .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+    .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
       background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive@2x.png);
     }
 
-    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+    .tab-background-end[selected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+    .tab-background-start[selected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
       background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive@2x.png);
     }
   }
 }
 
-.tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([visuallyselected="true"]) {
+.tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {
   opacity: .9;
 }
 
 /*
  * Force the overlay to create a new stacking context so it always appears on
  * top of the icon.
  */
 .tab-icon-overlay {
   opacity: 0.9999;
 }
 
-.tab-label:not([visuallyselected="true"]) {
+.tab-label:not([selected="true"]) {
   opacity: .7;
 }
 
 .tabbrowser-tab,
 .tabs-newtab-button {
   font: message-box;
   border: none;
 }
@@ -2644,22 +2644,22 @@ toolbarbutton.chevron > .toolbarbutton-m
 
 .tab-close-button {
   -moz-appearance: none;
   border: none !important;
   background: none;
   cursor: default;
 }
 
-#TabsToolbar[brighttext] .tab-close-button.close-icon:not([visuallyselected=true]):not(:hover) {
+#TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 
 @media (min-resolution: 2dppx) {
-  #TabsToolbar[brighttext] .tab-close-button.close-icon:not([visuallyselected=true]):not(:hover) {
+  #TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-image-region: rect(0, 13px, 20px, 0);
   margin: 0 0 var(--tab-toolbar-navbar-overlap);
--- a/browser/themes/osx/devedition.css
+++ b/browser/themes/osx/devedition.css
@@ -103,22 +103,22 @@
   margin-bottom: -1px !important;
   padding-top: 3px !important;
   padding-bottom: 3px !important;
 }
 
 :root[devtoolstheme="dark"] .findbar-closebutton:not(:hover),
 /* Tab styling - make sure to use an inverted icon for the selected tab
    (brighttext only covers the unselected tabs) */
-.tab-close-button[visuallyselected=true]:not(:hover) {
+.tab-close-button[selected=true]:not(:hover) {
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 @media (min-resolution: 2dppx) {
   :root[devtoolstheme="dark"] .findbar-closebutton:not(:hover),
-  .tab-close-button[visuallyselected=true]:not(:hover) {
+  .tab-close-button[selected=true]:not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .ac-type-icon {
   /* Left-align the type icon in awesomebar popup results with the icon in the
      urlbar. */
   margin-inline-start: 14px;
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -280,17 +280,17 @@
   margin-top: 1em;
   background-color: hsla(210,4%,10%,.07);
 }
 
 #identity-popup-securityView-footer > button {
   -moz-appearance: none;
   margin: 0;
   border: none;
-  border-top: 1px solid #ccc;
+  border-top: 1px solid var(--panel-separator-color);
   padding: 8px 20px;
   color: ButtonText;
   background-color: transparent;
 }
 
 #identity-popup-securityView-footer > button:hover,
 #identity-popup-securityView-footer > button:focus {
   background-color: hsla(210,4%,10%,.07);
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -113,27 +113,27 @@
   margin-inline-start: 0;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
   padding-inline-end: 0;
   padding-inline-start: 0;
 }
 
-.tab-background-start[visuallyselected=true]::after,
-.tab-background-start[visuallyselected=true]::before,
+.tab-background-start[selected=true]::after,
+.tab-background-start[selected=true]::before,
 .tab-background-start,
 .tab-background-end,
-.tab-background-end[visuallyselected=true]::after,
-.tab-background-end[visuallyselected=true]::before {
+.tab-background-end[selected=true]::after,
+.tab-background-end[selected=true]::before {
   width: 0;
 }
 
-.tab-background-start[visuallyselected=true]::after,
-.tab-background-end[visuallyselected=true]::after {
+.tab-background-start[selected=true]::after,
+.tab-background-end[selected=true]::after {
   margin-inline-start: 0;
 }
 /* End override @tabCurveHalfWidth@ and @tabCurveWidth@ */
 
 #urlbar ::-moz-selection,
 #navigator-toolbox .searchbar-textbox ::-moz-selection,
 .browserContainer > findbar ::-moz-selection {
   background-color: var(--chrome-selection-background-color);
@@ -251,24 +251,24 @@ window:not([chromehidden~="toolbar"]) #u
 }
 
 .tabbrowser-tab {
   /* We normally rely on other tab elements for pointer events, but this
      theme hides those so we need it set here instead */
   pointer-events: auto;
 }
 
-.tabbrowser-tab:-moz-any([image], [pinned]) > .tab-stack > .tab-content[attention]:not([visuallyselected="true"]),
-.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([visuallyselected="true"]) {
+.tabbrowser-tab:-moz-any([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected="true"]),
+.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected="true"]) {
   background-image: var(--pinned-tab-glow);
   background-position: center;
   background-size: 100%;
 }
 
-.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([visuallyselected="true"]) {
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]) {
   background-position: left bottom var(--tab-toolbar-navbar-overlap);
   background-size: 34px 100%;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
 .tabbrowser-tab:hover {
   background-color: var(--tab-hover-background-color);
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -6,23 +6,27 @@
 
 #notification-popup-box {
   padding: 5px 0px;
   margin: -5px 0px;
   margin-inline-end: -5px;
   padding-inline-end: 5px;
 }
 
+.notification-anchor-icon,
+.blocked-permission-icon {
+  width: 16px;
+  height: 16px;
+  margin-inline-start: 2px;
+}
+
 /* This class can be used alone or in combination with the class defining the
    type of icon displayed. This rule must be defined before the others in order
    for its list-style-image to be overridden. */
 .notification-anchor-icon {
-  width: 16px;
-  height: 16px;
-  margin-inline-start: 2px;
 %ifdef MOZ_WIDGET_GTK
   list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16);
 %else
   list-style-image: url(chrome://global/skin/icons/information-16.png);
 %endif
 }
 
 @media (min-resolution: 1.1dppx) {
@@ -71,31 +75,31 @@
 
 /* INDIVIDUAL NOTIFICATIONS */
 
 .popup-notification-icon[popupid="web-notifications"],
 .desktop-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
 }
 
-.desktop-notification-icon.blocked {
+.desktop-notification-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification-blocked);
 }
 
 .geo-icon {
 %ifdef XP_MACOSX
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx);
 %elif defined(MOZ_WIDGET_GTK)
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux);
 %else
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows);
 %endif
 }
 
-.geo-icon.blocked {
+.geo-icon.blocked-permission-icon {
 %ifdef XP_MACOSX
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx-blocked);
 %elif defined(MOZ_WIDGET_GTK)
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux-blocked);
 %else
   list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows-blocked);
 %endif
 }
@@ -110,17 +114,17 @@
 %endif
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB);
 }
 
-.indexedDB-icon.blocked {
+.indexedDB-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB-blocked);
 }
 
 .login-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#login);
 }
 
 .popup-notification-icon[popupid="password"] {
@@ -132,47 +136,47 @@
   transform: scaleX(-1);
 }
 
 .camera-icon,
 .popup-notification-icon[popupid="webRTC-shareDevices"] {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
 }
 
-.camera-icon.blocked {
+.camera-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-blocked);
 }
 
 .microphone-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
 }
 
-.microphone-icon.blocked {
+.microphone-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-blocked);
 }
 
 .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-detailed);
 }
 
 .popup-notification-icon[popupid="webRTC-shareScreen"],
 .screen-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen);
 }
 
-.screen-icon.blocked {
+.screen-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
 }
 
 .popup-notification-icon[popupid="pointerLock"],
 .pointerLock-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock);
 }
 
-.pointerLock-icon.blocked {
+.pointerLock-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock-blocked);
 }
 
 /* This icon has a block sign in it, so we don't need a blocked version. */
 .popup-icon {
   list-style-image: url("chrome://browser/skin/notification-icons.svg#popup");
 }
 
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -156,23 +156,23 @@
 .tab-icon-overlay[soundplaying] {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
 }
 
 .tab-icon-overlay[muted]:not([crashed]) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not([visuallyselected]):not(:hover),
-.tab-icon-overlay[soundplaying][visuallyselected]:-moz-lwtheme-brighttext:not(:hover) {
+#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not([selected]):not(:hover),
+.tab-icon-overlay[soundplaying][selected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not([visuallyselected]):not(:hover),
-.tab-icon-overlay[muted][visuallyselected]:-moz-lwtheme-brighttext:not(:hover) {
+#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not([selected]):not(:hover),
+.tab-icon-overlay[muted][selected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted");
 }
 
 .tab-throbber[busy] {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
@@ -267,22 +267,22 @@
   opacity: 0;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator,
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator {
   transition: opacity 150ms ease;
 }
 
-.tab-background-start[visuallyselected=true]::after,
-.tab-background-start[visuallyselected=true]::before,
+.tab-background-start[selected=true]::after,
+.tab-background-start[selected=true]::before,
 .tab-background-start,
 .tab-background-end,
-.tab-background-end[visuallyselected=true]::after,
-.tab-background-end[visuallyselected=true]::before {
+.tab-background-end[selected=true]::after,
+.tab-background-end[selected=true]::before {
   min-height: var(--tab-min-height);
   width: @tabCurveWidth@;
 }
 
 .tabbrowser-tab:not([visuallyselected=true]),
 .tabbrowser-tab:-moz-lwtheme {
   color: inherit;
 }
@@ -293,106 +293,106 @@
  Tab background pseudo-elements which are positioned above .tab-background-start/end:
    - ::before - provides the fill of the tab curve and is clipped to the tab shape. This is where
                 pointer events go for the curve.
    - ::after  - provides the border/stroke of the tab curve and is overlayed above ::before.  Pointer
                 events go through to ::before to get the proper shape.
  */
 
 
-.tab-background-start[visuallyselected=true]::after,
-.tab-background-end[visuallyselected=true]::after {
+.tab-background-start[selected=true]::after,
+.tab-background-end[selected=true]::after {
   /* position ::after on top of its parent */
   margin-inline-start: -@tabCurveWidth@;
   background-size: 100% 100%;
   content: "";
   display: -moz-box;
   position: relative;
 }
 
-.tab-background-start[visuallyselected=true]::before,
-.tab-background-end[visuallyselected=true]::before {
+.tab-background-start[selected=true]::before,
+.tab-background-end[selected=true]::before {
   /* all ::before pseudo elements */
   content: "";
   display: -moz-box;
 }
 
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+.tab-background-start[selected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+.tab-background-end[selected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
   background-image: url(chrome://browser/skin/tabbrowser/tab-selected-start.svg);
   background-size: 100% 100%;
 }
 
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+.tab-background-end[selected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+.tab-background-start[selected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
   background-image: url(chrome://browser/skin/tabbrowser/tab-selected-end.svg);
   background-size: 100% 100%;
 }
 
 /* For lightweight themes, clip the header image on start, middle, and end. */
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
+.tab-background-start[selected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
+.tab-background-end[selected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
   clip-path: url(chrome://browser/content/browser.xul#tab-curve-clip-path-start);
 }
 
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
+.tab-background-end[selected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
+.tab-background-start[selected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
   clip-path: url(chrome://browser/content/browser.xul#tab-curve-clip-path-end);
 }
 
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr)::after,
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
+.tab-background-start[selected=true]:-moz-locale-dir(ltr)::after,
+.tab-background-end[selected=true]:-moz-locale-dir(rtl)::after {
   background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start.png);
 }
 
-.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
-.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
+.tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
+.tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
   background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end.png);
 }
 
-.tab-background-middle[visuallyselected=true] {
+.tab-background-middle[selected=true] {
   background-clip: padding-box, padding-box, content-box;
   background-color: @fgTabBackgroundColor@;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTexture@,
                     none;
   background-repeat: repeat-x;
   background-size: var(--tab-stroke-background-size), auto 100%;
   /* The padding-top combined with background-clip: content-box (the bottom-most) ensure the
      background-color doesn't extend above the top border. */
   padding-top: 2px;
 }
 
 /* Selected tab lightweight theme styles.
    See browser-lightweightTheme.css for information about run-time changes to LWT styles. */
-.tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+.tab-background-middle[selected=true]:-moz-lwtheme {
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   /* Don't stretch the LWT header images */
   background-size: var(--tab-stroke-background-size), auto 100%, auto auto;
 }
 
 /* These LWT styles are normally overridden by browser-lightweightTheme.css */
-.tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
-.tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
+.tab-background-start[selected=true]:-moz-lwtheme::before,
+.tab-background-end[selected=true]:-moz-lwtheme::before {
   background-image: @fgTabTextureLWT@;
 }
 
-.tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
-.tab-background-end[visuallyselected=true]:-moz-lwtheme::before,
-.tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+.tab-background-start[selected=true]:-moz-lwtheme::before,
+.tab-background-end[selected=true]:-moz-lwtheme::before,
+.tab-background-middle[selected=true]:-moz-lwtheme {
   background-color: transparent;
 }
 
 /* End selected tab */
 
 /* new tab button border and gradient on hover */
-.tabbrowser-tab:hover > .tab-stack > .tab-background:not([visuallyselected=true]),
+.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
 .tabs-newtab-button:hover {
   background-image: url(chrome://browser/skin/tabbrowser/tab-background-start.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-end.png);
   background-position: left bottom, @tabCurveWidth@ bottom, right bottom;
   background-repeat: no-repeat;
   background-size: @tabCurveWidth@ 100%, calc(100% - (2 * @tabCurveWidth@)) 100%, @tabCurveWidth@ 100%;
 }
@@ -414,30 +414,30 @@
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
-.tabbrowser-tab:-moz-any([image], [pinned]) > .tab-stack > .tab-content[attention]:not([visuallyselected="true"]),
-.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([visuallyselected="true"]) {
+.tabbrowser-tab:-moz-any([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected="true"]),
+.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected="true"]) {
   background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
   background-position: center bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
-.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([visuallyselected="true"]) {
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]) {
   background-position: left bottom var(--tab-toolbar-navbar-overlap);
   background-size: 34px 100%;
 }
 
-.tab-label[attention]:not([visuallyselected="true"]) {
+.tab-label[attention]:not([selected="true"]) {
   font-weight: bold;
 }
 
 /* Tab separators */
 
 .tabbrowser-tab::after,
 .tabbrowser-tab::before {
   width: 1px;
@@ -451,18 +451,18 @@
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-tab::before,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-tab::after {
   opacity: 0.4;
 }
 
 /* Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
-.tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
-#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
+.tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
+#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
   content: "";
   display: -moz-box;
 }
 
 /* New tab button */
 
 .tabs-newtab-button {
   width: calc(36px + @tabCurveWidth@);
@@ -472,36 +472,36 @@
   /* image preloading hack from like lowdpi styles */
   #tabbrowser-tabs::before {
     background-image:
       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png);
   }
 
-  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([visuallyselected=true]),
+  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
   .tabs-newtab-button:hover {
     background-image: url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png);
   }
 
-  .tab-background-middle[visuallyselected=true] {
+  .tab-background-middle[selected=true] {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTexture@,
                       none;
   }
 
-  .tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-start[selected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-end[selected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
-  .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
 
   .tab-throbber[busy] {
     list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
   }
 
   .tab-icon-image {
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -59,17 +59,17 @@
     }
 
     #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
     #browser-bottombox:not(:-moz-lwtheme),
     .browserContainer > findbar {
       background-color: @customToolbarColor@;
     }
 
-    .tab-background-middle[visuallyselected=true]:not(:-moz-lwtheme) {
+    .tab-background-middle[selected=true]:not(:-moz-lwtheme) {
       background-color: @customToolbarColor@;
     }
   }
 }
 
 @media (-moz-windows-compositor) {
   #main-window {
     -moz-appearance: -moz-win-glass;
--- a/browser/themes/windows/browser-lightweightTheme.css
+++ b/browser/themes/windows/browser-lightweightTheme.css
@@ -6,34 +6,34 @@
 %filter substitution
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
 
 @media (min-resolution: 1.25dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTextureLWT@;/*,
                       lwtHeader;*/
   }
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2058,18 +2058,18 @@ html|span.ac-emphasize-text-url {
 %include ../shared/tabs.inc.css
 
 /* Remove border between tab strip and navigation toolbar on Windows 10+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       @media not all and (-moz-os-version: windows-win8) {
         @media (-moz-windows-default-theme) {
-          .tab-background-end[visuallyselected=true]::after,
-          .tab-background-start[visuallyselected=true]::after {
+          .tab-background-end[selected=true]::after,
+          .tab-background-start[selected=true]::after {
             content: none;
           }
 
           #TabsToolbar {
             --tab-stroke-background-size: 0 0;
           }
 
           :root {
@@ -2084,24 +2084,24 @@ html|span.ac-emphasize-text-url {
       }
     }
   }
 }
 
 /* Invert the unhovered close tab icons on bright-text tabs */
 @media not all and (min-resolution: 1.1dppx) {
   .tab-close-button:-moz-lwtheme-brighttext,
-  #TabsToolbar[brighttext] .tab-close-button:not([visuallyselected="true"]) {
+  #TabsToolbar[brighttext] .tab-close-button:not([selected="true"]) {
     list-style-image: url("chrome://global/skin/icons/close-inverted.png");
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   .tab-close-button:-moz-lwtheme-brighttext,
-  #TabsToolbar[brighttext] .tab-close-button:not([visuallyselected="true"]) {
+  #TabsToolbar[brighttext] .tab-close-button:not([selected="true"]) {
     list-style-image: url("chrome://global/skin/icons/close-inverted@2x.png");
   }
 }
 
 /* tabbrowser-tab focus ring */
 .tabbrowser-tab:focus > .tab-stack > .tab-content {
   outline: 1px dotted;
   outline-offset: -6px;
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -99,24 +99,24 @@
 #toolbar-menubar {
   text-shadow: none !important;
 }
 
 :root[devtoolstheme="dark"] .findbar-closebutton,
 :root[devtoolstheme="dark"] #sidebar-header > .close-icon,
 /* Tab styling - make sure to use an inverted icon for the selected tab
    (brighttext only covers the unselected tabs) */
-.tab-close-button[visuallyselected=true] {
+.tab-close-button[selected=true] {
   list-style-image: url("chrome://global/skin/icons/close-inverted.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   :root[devtoolstheme="dark"] .findbar-closebutton,
   :root[devtoolstheme="dark"] #sidebar-header > .close-icon,
-  .tab-close-button[visuallyselected=true] {
+  .tab-close-button[selected=true] {
     list-style-image: url("chrome://global/skin/icons/close-inverted@2x.png");
   }
 }
 
 @media (-moz-os-version: windows-xp),
        (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7),
        (-moz-os-version: windows-win8) {
--- a/build/autoconf/ffi.m4
+++ b/build/autoconf/ffi.m4
@@ -1,32 +1,12 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-AC_DEFUN([MOZ_CONFIG_FFI], [
-
-MOZ_ARG_ENABLE_BOOL(system-ffi,
-[  --enable-system-ffi       Use system libffi (located with pkgconfig)],
-    MOZ_SYSTEM_FFI=1 )
-
-if test -n "$MOZ_SYSTEM_FFI"; then
-    # Vanilla libffi 3.0.9 needs a few patches from upcoming version 3.0.10
-    # for non-GCC compilers.
-    if test -z "$GNU_CC"; then
-        PKG_CHECK_MODULES(MOZ_FFI, libffi > 3.0.9)
-    else
-        PKG_CHECK_MODULES(MOZ_FFI, libffi >= 3.0.9)
-    fi
-fi
-
-AC_SUBST(MOZ_SYSTEM_FFI)
-
-])
-
 AC_DEFUN([MOZ_SUBCONFIGURE_FFI], [
 if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then
 
   if test "$BUILD_CTYPES" -a -z "$MOZ_SYSTEM_FFI"; then
     # Run the libffi 'configure' script.
     ac_configure_args="--disable-shared --enable-static --disable-raw-api"
     if test "$MOZ_DEBUG"; then
       ac_configure_args="$ac_configure_args --enable-debug"
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/ffi.configure
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+js_option('--with-system-ffi',
+          help='Use system libffi (located with pkgconfig)')
+
+use_system_ffi = depends_if('--with-system-ffi')(lambda _: True)
+
+system_ffi = pkg_check_modules('MOZ_FFI', 'libffi > 3.0.9',
+                               when=use_system_ffi)
+
+set_config('MOZ_SYSTEM_FFI', system_ffi)
+add_old_configure_assignment('MOZ_SYSTEM_FFI', system_ffi)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -164,17 +164,16 @@ def old_configure_options(*options):
     '--enable-b2g-camera',
     '--enable-b2g-ril',
     '--enable-bundled-fonts',
     '--enable-clang-plugin',
     '--enable-content-sandbox',
     '--enable-cookies',
     '--enable-cpp-rtti',
     '--enable-crashreporter',
-    '--enable-ctypes',
     '--enable-dbus',
     '--enable-debug-js-modules',
     '--enable-directshow',
     '--enable-dtrace',
     '--enable-dump-painting',
     '--enable-elf-hack',
     '--enable-extensions',
     '--enable-faststripe',
@@ -227,17 +226,16 @@ def old_configure_options(*options):
     '--enable-small-chunk-size',
     '--enable-startup-notification',
     '--enable-startupcache',
     '--enable-stdcxx-compat',
     '--enable-strip',
     '--enable-synth-pico',
     '--enable-system-cairo',
     '--enable-system-extension-dirs',
-    '--enable-system-ffi',
     '--enable-system-pixman',
     '--enable-system-sqlite',
     '--enable-tasktracer',
     '--enable-thread-sanitizer',
     '--enable-trace-logging',
     '--enable-tree-freetype',
     '--enable-ui-locale',
     '--enable-universalchardet',
--- a/caps/DomainPolicy.cpp
+++ b/caps/DomainPolicy.cpp
@@ -2,17 +2,17 @@
 /* vim: set ts=4 et sw=4 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DomainPolicy.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/URIUtils.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIMessageManager.h"
 #include "nsScriptSecurityManager.h"
 
 namespace mozilla {
 
 using namespace ipc;
 using namespace dom;
 
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -20,17 +20,17 @@
 #include "nsComponentManager.h"
 #include "nsEnumeratorUtils.h"
 #include "nsNetUtil.h"
 #include "nsStringEnumerator.h"
 #include "nsTextFormatter.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "mozilla/LookAndFeel.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "nsICommandLine.h"
 #include "nsILocaleService.h"
 #include "nsIObserverService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "mozilla/Preferences.h"
 #include "nsIResProtocolHandler.h"
--- a/configure.py
+++ b/configure.py
@@ -1,26 +1,28 @@
 # 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/.
 
 from __future__ import print_function, unicode_literals
 
 import codecs
-import json
 import os
 import subprocess
 import sys
-
-from collections import Iterable
+import textwrap
 
 
 base_dir = os.path.abspath(os.path.dirname(__file__))
 sys.path.insert(0, os.path.join(base_dir, 'python', 'mozbuild'))
 from mozbuild.configure import ConfigureSandbox
+from mozbuild.util import (
+    indented_repr,
+    encode,
+)
 
 
 def main(argv):
     config = {}
     sandbox = ConfigureSandbox(config, os.environ, argv)
     sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))
 
     if sandbox._help:
@@ -55,64 +57,51 @@ def config_status(config):
     sanitized_config['mozconfig'] = config.get('MOZCONFIG')
 
     # Create config.status. Eventually, we'll want to just do the work it does
     # here, when we're able to skip configure tests/use cached results/not rely
     # on autoconf.
     print("Creating config.status", file=sys.stderr)
     encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
     with codecs.open('config.status', 'w', encoding) as fh:
-        fh.write('#!%s\n' % config['PYTHON'])
-        fh.write('# coding=%s\n' % encoding)
-        # Because we're serializing as JSON but reading as python, the values
-        # for True, False and None are true, false and null, which don't exist.
-        # Define them.
-        fh.write('true, false, null = True, False, None\n')
+        fh.write(textwrap.dedent('''\
+            #!%(python)s
+            # coding=%(encoding)s
+            from __future__ import unicode_literals
+            from mozbuild.util import encode
+            encoding = '%(encoding)s'
+        ''') % {'python': config['PYTHON'], 'encoding': encoding})
+        # A lot of the build backend code is currently expecting byte
+        # strings and breaks in subtle ways with unicode strings. (bug 1296508)
         for k, v in sanitized_config.iteritems():
-            fh.write('%s = ' % k)
-            json.dump(v, fh, sort_keys=True, indent=4, ensure_ascii=False)
-            fh.write('\n')
+            fh.write('%s = encode(%s, encoding)\n' % (k, indented_repr(v)))
         fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', "
                  "'non_global_defines', 'substs', 'mozconfig']")
 
         if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'):
-            fh.write('''
-if __name__ == '__main__':
-    args = dict([(name, globals()[name]) for name in __all__])
-    from mozbuild.config_status import config_status
-    config_status(**args)
-''')
-
-    # Running config.status standalone uses byte literals for all the config,
-    # instead of the unicode literals we have in sanitized_config right now.
-    # Some values in sanitized_config also have more complex types, such as
-    # EnumString, which using when calling config_status would currently break
-    # the build, as well as making it inconsistent with re-running
-    # config.status. Fortunately, EnumString derives from unicode, so it's
-    # covered by converting unicode strings.
-    # Moreover, a lot of the build backend code is currently expecting byte
-    # strings and breaks in subtle ways with unicode strings.
-    def encode(v):
-        if isinstance(v, dict):
-            return {
-                encode(k): encode(val)
-                for k, val in v.iteritems()
-            }
-        if isinstance(v, str):
-            return v
-        if isinstance(v, unicode):
-            return v.encode(encoding)
-        if isinstance(v, Iterable):
-            return [encode(i) for i in v]
-        return v
+            fh.write(textwrap.dedent('''
+                if __name__ == '__main__':
+                    from mozbuild.config_status import config_status
+                    args = dict([(name, globals()[name]) for name in __all__])
+                    config_status(**args)
+            '''))
 
     # Other things than us are going to run this file, so we need to give it
     # executable permissions.
     os.chmod('config.status', 0o755)
     if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'):
         os.environ[b'WRITE_MOZINFO'] = b'1'
         from mozbuild.config_status import config_status
-        return config_status(args=[], **encode(sanitized_config))
+
+        # Some values in sanitized_config also have more complex types, such as
+        # EnumString, which using when calling config_status would currently
+        # break the build, as well as making it inconsistent with re-running
+        # config.status. Fortunately, EnumString derives from unicode, so it's
+        # covered by converting unicode strings.
+
+        # A lot of the build backend code is currently expecting byte strings
+        # and breaks in subtle ways with unicode strings.
+        return config_status(args=[], **encode(sanitized_config, encoding))
     return 0
 
 
 if __name__ == '__main__':
     sys.exit(main(sys.argv))
--- a/devtools/client/canvasdebugger/snapshotslist.js
+++ b/devtools/client/canvasdebugger/snapshotslist.js
@@ -357,19 +357,17 @@ var SnapshotsListView = Heritage.extend(
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");
 
     if (fp.show() != Ci.nsIFilePicker.returnOK) {
       return;
     }
 
     let channel = NetUtil.newChannel({
-      uri: NetUtil.newURI(fp.file),
-      loadingNode: window.document,
-      contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER});
+      uri: NetUtil.newURI(fp.file), loadUsingSystemPrincipal: true});
     channel.contentType = "text/plain";
 
     NetUtil.asyncFetch(channel, (inputStream, status) => {
       if (!Components.isSuccessCode(status)) {
         console.error("Could not import recorded animation frame snapshot file.");
         return;
       }
       try {
--- a/devtools/client/framework/source-map-service.js
+++ b/devtools/client/framework/source-map-service.js
@@ -29,23 +29,26 @@ function SourceMapService(target) {
   this.subscribe = this.subscribe.bind(this);
   this.unsubscribe = this.unsubscribe.bind(this);
   this.reset = this.reset.bind(this);
   this.destroy = this.destroy.bind(this);
 
   target.on("source-updated", this._onSourceUpdated);
   target.on("navigate", this.reset);
   target.on("will-navigate", this.reset);
-  target.on("close", this.destroy);
 }
 
 /**
  * Clears the store containing the cached promised locations
  */
 SourceMapService.prototype.reset = function () {
+  // Guard to prevent clearing the store when it is not initialized yet.
+  if (!this._locationStore) {
+    return;
+  }
   this._locationStore.clear();
   this._isNotSourceMapped.clear();
 };
 
 SourceMapService.prototype.destroy = function () {
   this.reset();
   this._target.off("source-updated", this._onSourceUpdated);
   this._target.off("navigate", this.reset);
@@ -79,19 +82,20 @@ SourceMapService.prototype.subscribe = f
  * @param location
  * @param callback
  */
 SourceMapService.prototype.unsubscribe = function (location, callback) {
   this.off(serialize(location), callback);
   // Check to see if the store exists before attempting to clear a location
   // Sometimes un-subscribe happens during the destruction cascades and this
   // condition is to protect against that. Could be looked into in the future.
-  if (this._locationStore) {
-    this._locationStore.clearByURL(location.url);
+  if (!this._locationStore) {
+    return;
   }
+  this._locationStore.clearByURL(location.url);
 };
 
 /**
  * Tries to resolve the location and if successful,
  * emits the resolved location
  * @param location
  * @private
  */
--- a/devtools/client/inspector/inspector-commands.js
+++ b/devtools/client/inspector/inspector-commands.js
@@ -6,16 +6,18 @@
 
 const l10n = require("gcli/l10n");
 const {gDevTools} = require("devtools/client/framework/devtools");
 /* eslint-disable mozilla/reject-some-requires */
 const {EyeDropper, HighlighterEnvironment} = require("devtools/server/actors/highlighters");
 /* eslint-enable mozilla/reject-some-requires */
 const Telemetry = require("devtools/client/shared/telemetry");
 
+const windowEyeDroppers = new WeakMap();
+
 exports.items = [{
   item: "command",
   runAt: "client",
   name: "inspect",
   description: l10n.lookup("inspectDesc"),
   manual: l10n.lookup("inspectManual"),
   params: [
     {
@@ -43,33 +45,70 @@ exports.items = [{
     // This hidden parameter is only set to true when the eyedropper browser menu item is
     // used. It is useful to log a different telemetry event whether the tool was used
     // from the menu, or from the gcli command line.
     group: "hiddengroup",
     params: [{
       name: "frommenu",
       type: "boolean",
       hidden: true
+    }, {
+      name: "hide",
+      type: "boolean",
+      hidden: true
     }]
   }],
-  exec: function (args, context) {
+  exec: function* (args, context) {
+    if (args.hide) {
+      context.updateExec("eyedropper_server_hide").catch(e => console.error(e));
+      return;
+    }
+
+    // If the inspector is already picking a color from the page, cancel it.
+    let target = context.environment.target;
+    let toolbox = gDevTools.getToolbox(target);
+    if (toolbox) {
+      let inspector = toolbox.getPanel("inspector");
+      if (inspector) {
+        yield inspector.hideEyeDropper();
+      }
+    }
+
     let telemetry = new Telemetry();
     telemetry.toolOpened(args.frommenu ? "menueyedropper" : "eyedropper");
     context.updateExec("eyedropper_server").catch(e => console.error(e));
   }
 }, {
   item: "command",
   runAt: "server",
   name: "eyedropper_server",
   hidden: true,
   exec: function (args, {environment}) {
-    let env = new HighlighterEnvironment();
-    env.initFromWindow(environment.window);
-    let eyeDropper = new EyeDropper(env);
+    let eyeDropper = windowEyeDroppers.get(environment.window);
+
+    if (!eyeDropper) {
+      let env = new HighlighterEnvironment();
+      env.initFromWindow(environment.window);
+
+      eyeDropper = new EyeDropper(env);
+      eyeDropper.once("hidden", () => {
+        eyeDropper.destroy();
+        env.destroy();
+        windowEyeDroppers.delete(environment.window);
+      });
+
+      windowEyeDroppers.set(environment.window, eyeDropper);
+    }
 
     eyeDropper.show(environment.document.documentElement, {copyOnSelect: true});
-
-    eyeDropper.once("hidden", () => {
-      eyeDropper.destroy();
-      env.destroy();
-    });
+  }
+}, {
+  item: "command",
+  runAt: "server",
+  name: "eyedropper_server_hide",
+  hidden: true,
+  exec: function (args, {environment}) {
+    let eyeDropper = windowEyeDroppers.get(environment.window);
+    if (eyeDropper) {
+      eyeDropper.hide();
+    }
   }
 }];
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -1299,17 +1299,17 @@ InspectorPanel.prototype = {
   /**
    * Show the eyedropper on the page.
    * @return {Promise} resolves when the eyedropper is visible.
    */
   showEyeDropper: function () {
     this.telemetry.toolOpened("toolbareyedropper");
     this.eyeDropperButton.setAttribute("checked", "true");
     this.startEyeDropperListeners();
-    return this.inspector.pickColorFromPage({copyOnSelect: true})
+    return this.inspector.pickColorFromPage(this.toolbox, {copyOnSelect: true})
                          .catch(e => console.error(e));
   },
 
   /**
    * Hide the eyedropper.
    * @return {Promise} resolves when the eyedropper is hidden.
    */
   hideEyeDropper: function () {
--- a/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
+++ b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
@@ -38,16 +38,17 @@ function test() {
           col2: "Column 2",
           col3: "Column 3",
           col4: "Column 4"
         },
         uniqueId: "col1",
         emptyText: "This is dummy empty text",
         highlightUpdated: true,
         removableColumns: true,
+        wrapTextInElements: true,
       });
       startTests();
     });
   });
 }
 
 function endTests() {
   table.destroy();
@@ -138,44 +139,58 @@ function click(node, button = 0) {
 /**
  * Tests if clicking the table items does the expected behavior
  */
 var testMouseInteraction = Task.async(function* () {
   info("Testing mouse interaction with the table");
   ok(!table.selectedRow, "Nothing should be selected beforehand");
 
   let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
-  let node = table.tbody.firstChild.firstChild.children[1];
+  let firstColumnFirstRowCell = table.tbody.firstChild.firstChild.children[1];
   info("clicking on the first row");
-  ok(!node.classList.contains("theme-selected"),
+  ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
      "Node should not have selected class before clicking");
-  click(node);
+  click(firstColumnFirstRowCell);
   let id = yield event;
-  ok(node.classList.contains("theme-selected"),
+  ok(firstColumnFirstRowCell.classList.contains("theme-selected"),
      "Node has selected class after click");
   is(id, "id1", "Correct row was selected");
 
-  info("clicking on third row to select it");
+  info("clicking on second row to select it");
   event = table.once(TableWidget.EVENTS.ROW_SELECTED);
-  let node2 = table.tbody.firstChild.firstChild.children[3];
+  let firstColumnSecondRowCell = table.tbody.firstChild.firstChild.children[2];
   // node should not have selected class
-  ok(!node2.classList.contains("theme-selected"),
+  ok(!firstColumnSecondRowCell.classList.contains("theme-selected"),
      "New node should not have selected class before clicking");
-  click(node2);
+  click(firstColumnSecondRowCell);
   id = yield event;
-  ok(node2.classList.contains("theme-selected"),
+  ok(firstColumnSecondRowCell.classList.contains("theme-selected"),
      "New node has selected class after clicking");
+  is(id, "id2", "Correct table path is emitted for new node");
+  isnot(firstColumnFirstRowCell, firstColumnSecondRowCell,
+    "Old and new node are different");
+  ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
+     "Old node should not have selected class after the click on new node");
+
+  info("clicking on the third row cell content to select third row");
+  event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+  let firstColumnThirdRowCell = table.tbody.firstChild.firstChild.children[3];
+  let firstColumnThirdRowCellInnerNode = firstColumnThirdRowCell.querySelector("span");
+  // node should not have selected class
+  ok(!firstColumnThirdRowCell.classList.contains("theme-selected"),
+     "New node should not have selected class before clicking");
+  click(firstColumnThirdRowCellInnerNode);
+  id = yield event;
+  ok(firstColumnThirdRowCell.classList.contains("theme-selected"),
+     "New node has selected class after clicking the cell content");
   is(id, "id3", "Correct table path is emitted for new node");
-  isnot(node, node2, "Old and new node are different");
-  ok(!node.classList.contains("theme-selected"),
-     "Old node should not have selected class after the click on new node");
 
   // clicking on table header to sort by it
   event = table.once(TableWidget.EVENTS.COLUMN_SORTED);
-  node = table.tbody.children[6].firstChild.children[0];
+  let node = table.tbody.children[6].firstChild.children[0];
   info("clicking on the 4th coulmn header to sort the table by it");
   ok(!node.hasAttribute("sorted"),
      "Node should not have sorted attribute before clicking");
   ok(doc.querySelector("[sorted]"),
      "Although, something else should be sorted on");
   isnot(doc.querySelector("[sorted]"), node, "Which is not equal to this node");
   click(node);
   id = yield event;
--- a/devtools/client/shared/widgets/FilterWidget.js
+++ b/devtools/client/shared/widgets/FilterWidget.js
@@ -683,19 +683,21 @@ CSSFilterEditorWidget.prototype = {
 
       this.filtersList.appendChild(el);
     }
 
     let lastInput =
         this.filtersList.querySelector(".filter:last-of-type input");
     if (lastInput) {
       lastInput.focus();
-      // move cursor to end of input
-      const end = lastInput.value.length;
-      lastInput.setSelectionRange(end, end);
+      if (lastInput.type == "text") {
+        // move cursor to end of input
+        const end = lastInput.value.length;
+        lastInput.setSelectionRange(end, end);
+      }
     }
 
     this.emit("render");
   },
 
   renderPresets: function () {
     this.getPresets().then(presets => {
       // getPresets is async and the widget may be destroyed in between.
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -1423,17 +1423,17 @@ Column.prototype = {
       return;
     }
     if (event.button == 0) {
       let closest = target.closest("[data-id]");
       if (!closest) {
         return;
       }
 
-      let dataid = target.getAttribute("data-id");
+      let dataid = closest.getAttribute("data-id");
       this.table.emit(EVENTS.ROW_SELECTED, dataid);
     }
   },
 };
 
 /**
  * A single cell in a column
  *
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -771,34 +771,34 @@ Heritage.extend(SwatchBasedEditorTooltip
         this.commit();
       }
     }
   },
 
   _openEyeDropper: function () {
     let {inspector, toolbox, telemetry} = this.inspector;
     telemetry.toolOpened("pickereyedropper");
-    inspector.pickColorFromPage({copyOnSelect: false}).catch(e => console.error(e));
+    inspector.pickColorFromPage(toolbox, {copyOnSelect: false}).then(() => {
+      this.eyedropperOpen = true;
+
+      // close the colorpicker tooltip so that only the eyedropper is open.
+      this.hide();
+
+      this.tooltip.emit("eyedropper-opened");
+    }, e => console.error(e));
 
     inspector.once("color-picked", color => {
       toolbox.win.focus();
       this._selectColor(color);
       this._onEyeDropperDone();
     });
 
     inspector.once("color-pick-canceled", () => {
       this._onEyeDropperDone();
     });
-
-    this.eyedropperOpen = true;
-
-    // close the colorpicker tooltip so that only the eyedropper is open.
-    this.hide();
-
-    this.tooltip.emit("eyedropper-opened");
   },
 
   _onEyeDropperDone: function () {
     this.eyedropperOpen = false;
     this.activeSwatch = null;
   },
 
   _colorToRgba: function (color) {
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -227,8 +227,476 @@ textbox :-moz-focusring {
 
 /* Form fields should already have box-shadow hightlight */
 select:-moz-focusring,
 input[type="radio"]:-moz-focusring,
 input[type="checkbox"]:-moz-focusring,
 checkbox:-moz-focusring {
   outline: none;
 }
+
+/* Toolbar buttons */
+.devtools-menulist,
+.devtools-toolbarbutton,
+.devtools-button {
+  -moz-appearance: none;
+  background: transparent;
+  min-height: 18px;
+  text-shadow: none;
+  border: none;
+  border-radius: 0;
+  color: var(--theme-body-color);
+  transition: background 0.05s ease-in-out;
+}
+
+.devtools-menulist,
+.devtools-toolbarbutton {
+  -moz-box-align: center;
+  min-width: 78px;
+  padding: 1px;
+  margin: 2px 1px;
+}
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
+.devtools-button::before {
+  width: 16px;
+  height: 16px;
+  transition: opacity 0.05s ease-in-out;
+}
+
+/* HTML buttons */
+.devtools-button {
+  margin: 2px 1px;
+  padding: 1px;
+  min-width: 32px;
+  /* The icon is absolutely positioned in the button using ::before */
+  position: relative;
+}
+
+.devtools-button::before {
+  content: "";
+  display: block;
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  margin: -8px 0 0 -8px;
+  background-size: cover;
+  background-repeat: no-repeat;
+  transition: opacity 0.05s ease-in-out;
+}
+
+.devtools-button:-moz-focusring {
+  outline: none;
+}
+
+/* Standalone buttons */
+.devtools-button[standalone],
+.devtools-button[data-standalone],
+.devtools-toolbarbutton[standalone],
+.devtools-toolbarbutton[data-standalone] {
+  border-width: 1px;
+  border-style: solid;
+  min-height: 32px;
+  background-color: var(--theme-toolbar-background);
+}
+
+.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
+  margin-inline-end: 5px;
+}
+
+.devtools-toolbarbutton[label][standalone] {
+  min-height: 2em;
+}
+
+.devtools-menulist,
+.devtools-toolbarbutton,
+.devtools-button {
+  border-color: var(--toolbar-button-border-color);
+}
+
+/* Icon button styles */
+.devtools-toolbarbutton:not([label]),
+.devtools-toolbarbutton[text-as-image] {
+  min-width: 32px;
+}
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
+  display: none;
+}
+
+.devtools-toolbarbutton > .toolbarbutton-icon {
+  margin: 0;
+}
+
+/* Menu button styles (eg. web console filters) */
+.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
+  -moz-appearance: none;
+  color: inherit;
+  border-width: 0;
+  -moz-box-orient: horizontal;
+  padding: 0;
+}
+
+.devtools-toolbarbutton[type=menu-button] {
+  padding: 0 1px;
+  -moz-box-align: stretch;
+}
+
+.devtools-toolbarbutton > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+  margin-inline-end: 4px;
+}
+
+.devtools-menulist > .menulist-dropmarker {
+  -moz-appearance: none;
+  display: -moz-box;
+  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
+  -moz-box-align: center;
+  min-width: 16px;
+}
+
+.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
+.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
+  -moz-appearance: none !important;
+  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
+  -moz-box-align: center;
+  padding: 0 3px;
+}
+
+/* Icon-only buttons */
+.devtools-button:empty::before,
+.devtools-toolbarbutton:not([label]):not([disabled]) > image {
+  opacity: 0.8;
+}
+
+.devtools-button:hover:empty::before,
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-toolbarbutton:not([label]):hover > image,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+  opacity: 1;
+}
+
+.devtools-button:disabled,
+.devtools-button[disabled],
+.devtools-toolbarbutton[disabled] {
+  opacity: 0.5 !important;
+}
+
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-button.checked::before,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+  filter: var(--checked-icon-filter);
+}
+
+/* Icon-and-text buttons */
+.devtools-toolbarbutton.icon-and-text .toolbarbutton-text {
+  margin-inline-start: .5em !important;
+  font-weight: 600;
+}
+
+/* Text-only buttons */
+.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-light .devtools-toolbarbutton[data-text-only] {
+  background-color: var(--toolbar-tab-hover);
+}
+.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-dark .devtools-toolbarbutton[data-text-only] {
+  background-color: rgba(0, 0, 0, .2); /* Splitter */
+}
+
+/* Text-only button states */
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
+  background: rgba(0, 0, 0, .3); /* Splitters */
+}
+.theme-light .devtools-button:not(:empty):not([disabled]):hover,
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
+  background: rgba(170, 170, 170, .3); /* Splitters */
+}
+
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
+  background: rgba(0, 0, 0, .4); /* Splitters */
+}
+.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
+  background: var(--toolbar-tab-hover-active);
+}
+
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-dark .devtools-button:not(:empty)[checked=true] {
+  background: var(--theme-selection-background-semitransparent);
+  color: var(--theme-selection-color);
+}
+.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-light .devtools-button:not(:empty)[checked=true] {
+  background: rgba(76, 158, 217, .3); /* Select highlight blue */
+}
+
+:root {
+  --clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
+}
+
+.devtools-button.devtools-clear-icon::before {
+  background-image: var(--clear-icon-url);
+}
+
+.devtools-button.devtools-filter-icon::before {
+  background-image: var(--filter-image);
+}
+
+.devtools-toolbarbutton.devtools-clear-icon {
+  list-style-image: var(--clear-icon-url);
+}
+
+.devtools-option-toolbarbutton {
+  list-style-image: var(--tool-options-image);
+}
+
+.devtools-toolbarbutton-group > .devtools-toolbarbutton:last-child {
+  margin-inline-end: 0;
+}
+
+.devtools-toolbarbutton-group + .devtools-toolbarbutton {
+  margin-inline-start: 3px;
+}
+
+.devtools-separator + .devtools-toolbarbutton {
+  margin-inline-start: 1px;
+}
+
+/*
+ * Filter buttons
+ * @TODO : Fix when https://bugzilla.mozilla.org/show_bug.cgi?id=1255116 lands
+ */
+.menu-filter-button {
+  -moz-appearance: none;
+  background: rgba(128,128,128,0.1);
+  border: none;
+  border-radius: 2px;
+  min-width: 0;
+  padding: 0 5px;
+  margin: 2px;
+  color: var(--theme-body-color);
+}
+
+.menu-filter-button:hover {
+  background: rgba(128,128,128,0.2);
+}
+
+.menu-filter-button:hover:active {
+  background-color: var(--theme-selection-background-semitransparent);
+}
+
+.menu-filter-button:not(:active).checked {
+  background-color: var(--theme-selection-background);
+  color: var(--theme-selection-color);
+}
+
+/* Text input */
+
+.devtools-textinput,
+.devtools-searchinput,
+.devtools-filterinput {
+  -moz-appearance: none;
+  margin: 1px 3px;
+  border: 1px solid;
+  border-radius: 2px;
+  padding: 4px 6px;
+  border-color: var(--theme-splitter-color);
+  font: message-box;
+}
+
+:root[platform="mac"] .devtools-textinput,
+:root[platform="mac"] .devtools-searchinput,
+:root[platform="mac"] .devtools-filterinput {
+  border-radius: 20px;
+}
+
+.devtools-searchinput,
+.devtools-filterinput {
+  padding: 0;
+  padding-inline-start: 22px;
+  padding-inline-end: 4px;
+  background-position: 8px center;
+  background-size: 11px 11px;
+  background-repeat: no-repeat;
+  font-size: inherit;
+}
+
+.devtools-searchinput {
+  background-image: var(--magnifying-glass-image);
+}
+
+.devtools-filterinput {
+  background-image: url(images/filter.svg#filterinput);
+}
+
+.devtools-searchinput:-moz-locale-dir(rtl),
+.devtools-searchinput:dir(rtl),
+.devtools-filterinput:-moz-locale-dir(rtl),
+.devtools-filterinput:dir(rtl) {
+  background-position: calc(100% - 8px) center;
+}
+
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
+  visibility: hidden;
+}
+
+.devtools-searchinput .textbox-input::-moz-placeholder,
+.devtools-filterinput .textbox-input::-moz-placeholder {
+  font-style: normal;
+}
+
+.devtools-plaininput {
+  border-color: transparent;
+  background-color: transparent;
+}
+
+.theme-dark .devtools-plaininput {
+  color: var(--theme-highlight-gray);
+}
+
+/* Searchbox is a div container element for a search input element */
+.devtools-searchbox {
+  display: flex;
+  flex: 1;
+  height: 23px;
+  position: relative;
+  padding: 0 3px;
+}
+
+/* The spacing is accomplished with a padding on the searchbox */
+.devtools-searchbox > .devtools-textinput,
+.devtools-searchbox > .devtools-searchinput,
+.devtools-searchbox > .devtools-filterinput {
+  margin-left: 0;
+  margin-right: 0;
+}
+
+.devtools-searchbox > .devtools-textinput:-moz-focusring,
+.devtools-searchbox > .devtools-searchinput:-moz-focusring,
+.devtools-searchbox > .devtools-filterinput:-moz-focusring {
+  border-color: var(--theme-focus-border-color-textbox);
+  box-shadow: var(--theme-focus-box-shadow-textbox);
+  transition: all 0.2s ease-in-out;
+  outline: none;
+}
+
+/* Don't add 'double spacing' for inputs that are at beginning / end
+   of a toolbar (since the toolbar has it's own spacing). */
+.devtools-toolbar > .devtools-textinput:first-child,
+.devtools-toolbar > .devtools-searchinput:first-child,
+.devtools-toolbar > .devtools-filterinput:first-child {
+  margin-inline-start: 0;
+}
+.devtools-toolbar > .devtools-textinput:last-child,
+.devtools-toolbar > .devtools-searchinput:last-child,
+.devtools-toolbar > .devtools-filterinput:last-child {
+  margin-inline-end: 0;
+}
+.devtools-toolbar > .devtools-searchbox:first-child {
+  padding-inline-start: 0;
+}
+.devtools-toolbar > .devtools-searchbox:last-child {
+  padding-inline-end: 0;
+}
+
+.devtools-rule-searchbox {
+  -moz-box-flex: 1;
+  width: 100%;
+}
+
+.devtools-rule-searchbox[filled] {
+  background-color: var(--searchbox-background-color);
+  border-color: var(--searchbox-border-color);
+  padding-inline-end: 23px;
+}
+
+.devtools-style-searchbox-no-match {
+  background-color: var(--searcbox-no-match-background-color) !important;
+  border-color: var(--searcbox-no-match-border-color) !important;
+}
+
+.devtools-searchinput-clear {
+  position: absolute;
+  top: 3.5px;
+  offset-inline-end: 7px;
+  padding: 0;
+  border: 0;
+  width: 16px;
+  height: 16px;
+  background-position: 0 0;
+  background-repeat: no-repeat;
+  background-color: transparent;
+}
+
+.devtools-searchinput-clear:dir(rtl) {
+  right: unset;
+  left: 7px;
+}
+
+.theme-dark .devtools-searchinput-clear {
+  background-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
+}
+
+.theme-light .devtools-searchinput-clear {
+  background-image: url("chrome://devtools/skin/images/search-clear-light.svg");
+}
+
+.devtools-style-searchbox-no-match + .devtools-searchinput-clear {
+  background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
+}
+
+.devtools-searchinput-clear:hover {
+  background-position: -16px 0;
+}
+
+.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+  list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+  list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+  margin-bottom: 0;
+}
+
+.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
+.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+/* Throbbers */
+.devtools-throbber::before {
+  content: "";
+  display: inline-block;
+  vertical-align: bottom;
+  margin-inline-end: 0.5em;
+  width: 1em;
+  height: 1em;
+  border: 2px solid currentColor;
+  border-right-color: transparent;
+  border-radius: 50%;
+  animation: 1.1s linear throbber-spin infinite;
+}
+
+@keyframes throbber-spin {
+  from {
+    transform: none;
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -60,466 +60,41 @@
   padding: 0 3px;
 }
 
 .devtools-toolbar checkbox {
   margin: 0 2px;
   padding: 0;
   line-height: -moz-block-height;
 }
+
 .devtools-toolbar checkbox .checkbox-check {
   margin: 0;
   padding: 0;
   vertical-align: bottom;
 }
+
 .devtools-toolbar checkbox .checkbox-label-box {
   border: none !important; /* overrides .checkbox-label-box from checkbox.css */
 }
+
 .devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
   margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
   padding: 0;
 }
 
 .devtools-separator {
   margin: 0 2px;
   width: 2px;
   background-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%);
   background-size: 1px 100%;
   background-repeat: no-repeat;
   background-position: 0, 1px, 2px;
 }
 
-/* Toolbar buttons */
-
-.devtools-menulist,
-.devtools-toolbarbutton,
-.devtools-button {
-  -moz-appearance: none;
-  background: transparent;
-  min-height: 18px;
-  text-shadow: none;
-  border: none;
-  border-radius: 0;
-  color: var(--theme-body-color);
-  transition: background 0.05s ease-in-out;
-}
-
-.devtools-menulist,
-.devtools-toolbarbutton {
-  -moz-box-align: center;
-  min-width: 78px;
-  padding: 1px;
-  margin: 2px 1px;
-}
-
-.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
-.devtools-button::before {
-  width: 16px;
-  height: 16px;
-  transition: opacity 0.05s ease-in-out;
-}
-
-/* HTML buttons */
-.devtools-button {
-  margin: 2px 1px;
-  padding: 1px;
-  min-width: 32px;
-  /* The icon is absolutely positioned in the button using ::before */
-  position: relative;
-}
-
-.devtools-button::before {
-  content: "";
-  display: block;
-  position: absolute;
-  left: 50%;
-  top: 50%;
-  margin: -8px 0 0 -8px;
-  background-size: cover;
-  background-repeat: no-repeat;
-  transition: opacity 0.05s ease-in-out;
-}
-
-.devtools-button:-moz-focusring {
-  outline: none;
-}
-
-/* Standalone buttons */
-.devtools-button[standalone],
-.devtools-button[data-standalone],
-.devtools-toolbarbutton[standalone],
-.devtools-toolbarbutton[data-standalone] {
-  border-width: 1px;
-  border-style: solid;
-  min-height: 32px;
-  background-color: var(--theme-toolbar-background);
-}
-
-.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
-  margin-inline-end: 5px;
-}
-
-.devtools-toolbarbutton[label][standalone] {
-  min-height: 2em;
-}
-
-.devtools-menulist,
-.devtools-toolbarbutton,
-.devtools-button {
-  border-color: var(--toolbar-button-border-color);
-}
-
-/* Icon button styles */
-.devtools-toolbarbutton:not([label]),
-.devtools-toolbarbutton[text-as-image] {
-  min-width: 32px;
-}
-
-/* Set flex attribute to Toolbox buttons and Picker container so,
-   they don't overlapp with the tab bar */
-#toolbox-buttons {
-  display: flex;
-}
-
-#toolbox-picker-container {
-  display: flex;
-}
-
-.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
-  display: none;
-}
-
-.devtools-toolbarbutton > .toolbarbutton-icon {
-  margin: 0;
-}
-
-/* Menu button styles (eg. web console filters) */
-.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
-  -moz-appearance: none;
-  color: inherit;
-  border-width: 0;
-  -moz-box-orient: horizontal;
-  padding: 0;
-}
-
-.devtools-toolbarbutton[type=menu-button] {
-  padding: 0 1px;
-  -moz-box-align: stretch;
-}
-
-.devtools-toolbarbutton > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
-  margin-inline-end: 4px;
-}
-
-.devtools-menulist > .menulist-dropmarker {
-  -moz-appearance: none;
-  display: -moz-box;
-  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
-  -moz-box-align: center;
-  min-width: 16px;
-}
-
-.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
-.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
-  -moz-appearance: none !important;
-  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
-  -moz-box-align: center;
-  padding: 0 3px;
-}
-
-/* Icon-only buttons */
-.devtools-button:empty::before,
-.devtools-toolbarbutton:not([label]):not([disabled]) > image {
-  opacity: 0.8;
-}
-
-.devtools-button:hover:empty::before,
-.devtools-button[checked]:empty::before,
-.devtools-button[open]:empty::before,
-.devtools-toolbarbutton:not([label]):hover > image,
-.devtools-toolbarbutton:not([label])[checked=true] > image,
-.devtools-toolbarbutton:not([label])[open=true] > image {
-  opacity: 1;
-}
-
-.devtools-button:disabled,
-.devtools-button[disabled],
-.devtools-toolbarbutton[disabled] {
-  opacity: 0.5 !important;
-}
-
-.devtools-button[checked]:empty::before,
-.devtools-button[open]:empty::before,
-.devtools-button.checked::before,
-.devtools-toolbarbutton:not([label])[checked=true] > image,
-.devtools-toolbarbutton:not([label])[open=true] > image {
-  filter: var(--checked-icon-filter);
-}
-
-/* Icon-and-text buttons */
-.devtools-toolbarbutton.icon-and-text .toolbarbutton-text {
-  margin-inline-start: .5em !important;
-  font-weight: 600;
-}
-
-/* Text-only buttons */
-.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
-.theme-light .devtools-toolbarbutton[data-text-only] {
-  background-color: var(--toolbar-tab-hover);
-}
-.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
-.theme-dark .devtools-toolbarbutton[data-text-only] {
-  background-color: rgba(0, 0, 0, .2); /* Splitter */
-}
-
-/* Text-only button states */
-.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
-.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
-  background: rgba(0, 0, 0, .3); /* Splitters */
-}
-.theme-light .devtools-button:not(:empty):not([disabled]):hover,
-.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
-  background: rgba(170, 170, 170, .3); /* Splitters */
-}
-
-.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
-.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
-  background: rgba(0, 0, 0, .4); /* Splitters */
-}
-.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
-.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
-  background: var(--toolbar-tab-hover-active);
-}
-
-.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
-.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
-.theme-dark .devtools-button:not(:empty)[checked=true] {
-  background: var(--theme-selection-background-semitransparent);
-  color: var(--theme-selection-color);
-}
-.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
-.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
-.theme-light .devtools-button:not(:empty)[checked=true] {
-  background: rgba(76, 158, 217, .3); /* Select highlight blue */
-}
-
-:root {
-  --clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
-}
-
-.devtools-button.devtools-clear-icon::before {
-  background-image: var(--clear-icon-url);
-}
-
-.devtools-button.devtools-filter-icon::before {
-  background-image: var(--filter-image);
-}
-
-.devtools-toolbarbutton.devtools-clear-icon {
-  list-style-image: var(--clear-icon-url);
-}
-
-.devtools-option-toolbarbutton {
-  list-style-image: var(--tool-options-image);
-}
-
-.devtools-toolbarbutton-group > .devtools-toolbarbutton:last-child {
-  margin-inline-end: 0;
-}
-
-.devtools-toolbarbutton-group + .devtools-toolbarbutton {
-  margin-inline-start: 3px;
-}
-
-.devtools-separator + .devtools-toolbarbutton {
-  margin-inline-start: 1px;
-}
-
-/* Text input */
-
-.devtools-textinput,
-.devtools-searchinput,
-.devtools-filterinput {
-  -moz-appearance: none;
-  margin: 1px 3px;
-  border: 1px solid;
-  border-radius: 2px;
-  padding: 4px 6px;
-  border-color: var(--theme-splitter-color);
-  font: message-box;
-}
-
-:root[platform="mac"] .devtools-textinput,
-:root[platform="mac"] .devtools-searchinput,
-:root[platform="mac"] .devtools-filterinput {
-  border-radius: 20px;
-}
-
-.devtools-searchinput,
-.devtools-filterinput {
-  padding: 0;
-  padding-inline-start: 22px;
-  padding-inline-end: 4px;
-  background-position: 8px center;
-  background-size: 11px 11px;
-  background-repeat: no-repeat;
-  font-size: inherit;
-}
-
-.devtools-searchinput {
-  background-image: var(--magnifying-glass-image);
-}
-
-.devtools-filterinput {
-  background-image: url(images/filter.svg#filterinput);
-}
-
-.devtools-searchinput:-moz-locale-dir(rtl),
-.devtools-searchinput:dir(rtl),
-.devtools-filterinput:-moz-locale-dir(rtl),
-.devtools-filterinput:dir(rtl) {
-  background-position: calc(100% - 8px) center;
-}
-
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
-.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
-  visibility: hidden;
-}
-
-.devtools-searchinput .textbox-input::-moz-placeholder,
-.devtools-filterinput .textbox-input::-moz-placeholder {
-  font-style: normal;
-}
-
-.devtools-plaininput {
-  border-color: transparent;
-  background-color: transparent;
-}
-
-.theme-dark .devtools-plaininput {
-  color: var(--theme-highlight-gray);
-}
-
-/* Searchbox is a div container element for a search input element */
-.devtools-searchbox {
-  display: flex;
-  flex: 1;
-  height: 23px;
-  position: relative;
-  padding: 0 3px;
-}
-
-/* The spacing is accomplished with a padding on the searchbox */
-.devtools-searchbox > .devtools-textinput,
-.devtools-searchbox > .devtools-searchinput,
-.devtools-searchbox > .devtools-filterinput {
-  margin-left: 0;
-  margin-right: 0;
-}
-
-.devtools-searchbox > .devtools-textinput:-moz-focusring,
-.devtools-searchbox > .devtools-searchinput:-moz-focusring,
-.devtools-searchbox > .devtools-filterinput:-moz-focusring {
-  border-color: var(--theme-focus-border-color-textbox);
-  box-shadow: var(--theme-focus-box-shadow-textbox);
-  transition: all 0.2s ease-in-out;
-  outline: none;
-}
-
-/* Don't add 'double spacing' for inputs that are at beginning / end
-   of a toolbar (since the toolbar has it's own spacing). */
-.devtools-toolbar > .devtools-textinput:first-child,
-.devtools-toolbar > .devtools-searchinput:first-child,
-.devtools-toolbar > .devtools-filterinput:first-child {
-  margin-inline-start: 0;
-}
-.devtools-toolbar > .devtools-textinput:last-child,
-.devtools-toolbar > .devtools-searchinput:last-child,
-.devtools-toolbar > .devtools-filterinput:last-child {
-  margin-inline-end: 0;
-}
-.devtools-toolbar > .devtools-searchbox:first-child {
-  padding-inline-start: 0;
-}
-.devtools-toolbar > .devtools-searchbox:last-child {
-  padding-inline-end: 0;
-}
-
-.devtools-rule-searchbox {
-  -moz-box-flex: 1;
-  width: 100%;
-}
-
-.devtools-rule-searchbox[filled] {
-  background-color: var(--searchbox-background-color);
-  border-color: var(--searchbox-border-color);
-  padding-inline-end: 23px;
-}
-
-.devtools-style-searchbox-no-match {
-  background-color: var(--searcbox-no-match-background-color) !important;
-  border-color: var(--searcbox-no-match-border-color) !important;
-}
-
-.devtools-searchinput-clear {
-  position: absolute;
-  top: 3.5px;
-  offset-inline-end: 7px;
-  padding: 0;
-  border: 0;
-  width: 16px;
-  height: 16px;
-  background-position: 0 0;
-  background-repeat: no-repeat;
-  background-color: transparent;
-}
-
-.devtools-searchinput-clear:dir(rtl) {
-  right: unset;
-  left: 7px;
-}
-
-.theme-dark .devtools-searchinput-clear {
-  background-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
-}
-
-.theme-light .devtools-searchinput-clear {
-  background-image: url("chrome://devtools/skin/images/search-clear-light.svg");
-}
-
-.devtools-style-searchbox-no-match + .devtools-searchinput-clear {
-  background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
-}
-
-.devtools-searchinput-clear:hover {
-  background-position: -16px 0;
-}
-
-.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
-.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
-  list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
-.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
-  list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
-.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
-  margin-bottom: 0;
-}
-
-.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
-.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
 /* In-tools sidebar */
 .devtools-sidebar-tabs {
   -moz-appearance: none;
   margin: 0;
   height: 100%;
 }
 
 .devtools-sidebar-tabs > tabpanels {
@@ -635,59 +210,8 @@
   border-color: transparent;
   background-color: transparent;
 }
 
 .devtools-horizontal-splitter,
 .devtools-side-splitter {
   background-color: var(--theme-splitter-color);
 }
-
-/* Throbbers */
-.devtools-throbber::before {
-  content: "";
-  display: inline-block;
-  vertical-align: bottom;
-  margin-inline-end: 0.5em;
-  width: 1em;
-  height: 1em;
-  border: 2px solid currentColor;
-  border-right-color: transparent;
-  border-radius: 50%;
-  animation: 1.1s linear throbber-spin infinite;
-}
-
-@keyframes throbber-spin {
-  from {
-    transform: none;
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}
-
-/*
- * Filter buttons
- * @TODO : Fix when https://bugzilla.mozilla.org/show_bug.cgi?id=1255116 lands
- */
-.menu-filter-button {
-  -moz-appearance: none;
-  background: rgba(128,128,128,0.1);
-  border: none;
-  border-radius: 2px;
-  min-width: 0;
-  padding: 0 5px;
-  margin: 2px;
-  color: var(--theme-body-color);
-}
-
-.menu-filter-button:hover {
-  background: rgba(128,128,128,0.2);
-}
-
-.menu-filter-button:hover:active {
-  background-color: var(--theme-selection-background-semitransparent);
-}
-
-.menu-filter-button:not(:active).checked {
-  background-color: var(--theme-selection-background);
-  color: var(--theme-selection-color);
-}
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -50,16 +50,26 @@
   background: var(--theme-tab-toolbar-background);
   border-bottom-color: var(--theme-splitter-color);
 }
 
 #toolbox-tabs {
   margin: 0;
 }
 
+/* Set flex attribute to Toolbox buttons and Picker container so,
+   they don't overlapp with the tab bar */
+#toolbox-buttons {
+  display: flex;
+}
+
+#toolbox-picker-container {
+  display: flex;
+}
+
 /* Toolbox tabs */
 
 .devtools-tab {
   -moz-appearance: none;
   -moz-binding: url("chrome://global/content/bindings/general.xml#control-item");
   -moz-box-align: center;
   min-width: 32px;
   min-height: 24px;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -37,17 +37,19 @@ const GRID_LINES_PROPERTIES = {
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
  * Available Options:
- * - infiniteLines {Boolean}
+ * - showGridLineNumbers {Boolean}
+ *   Displays the grid line numbers
+ * - showInfiniteLines {Boolean}
  *   Displays an infinite line to represent the grid lines
  */
 function CssGridHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
   this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
     this._buildMarkup.bind(this));
 }
@@ -238,27 +240,31 @@ CssGridHighlighter.prototype = extend(Au
    * @param  {Number} endPos
    *         The end position of the cross side of the grid dimension.
    */
   renderLines(gridDimension, {bounds}, dimensionType, mainSide, crossSide,
               mainSize, startPos, endPos) {
     let lineStartPos = (bounds[crossSide] / getCurrentZoom(this.win)) + startPos;
     let lineEndPos = (bounds[crossSide] / getCurrentZoom(this.win)) + endPos;
 
-    if (this.options.infiniteLines) {
+    if (this.options.showInfiniteLines) {
       lineStartPos = 0;
       lineEndPos = parseInt(this.canvas.getAttribute(mainSize), 10);
     }
 
     let lastEdgeLineIndex = this.getLastEdgeLineIndex(gridDimension.tracks);
 
     for (let i = 0; i < gridDimension.lines.length; i++) {
       let line = gridDimension.lines[i];
       let linePos = (bounds[mainSide] / getCurrentZoom(this.win)) + line.start;
 
+      if (this.options.showGridLineNumbers) {
+        this.renderGridLineNumber(line.number, linePos, lineStartPos, dimensionType);
+      }
+
       if (i == 0 || i == lastEdgeLineIndex) {
         this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType, "edge");
       } else {
         this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType,
                         gridDimension.tracks[i - 1].type);
       }
 
       // Render a second line to illustrate the gutter for non-zero breadth.
@@ -286,29 +292,51 @@ CssGridHighlighter.prototype = extend(Au
    *         The grid line type - "edge", "explicit", or "implicit".
    */
   renderLine(linePos, startPos, endPos, dimensionType, lineType) {
     this.ctx.save();
     this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
     this.ctx.beginPath();
     this.ctx.translate(.5, .5);
 
-    if (dimensionType == COLUMNS) {
+    if (dimensionType === COLUMNS) {
       this.ctx.moveTo(linePos, startPos);
       this.ctx.lineTo(linePos, endPos);
     } else {
       this.ctx.moveTo(startPos, linePos);
       this.ctx.lineTo(endPos, linePos);
     }
 
     this.ctx.strokeStyle = GRID_LINES_PROPERTIES[lineType].strokeStyle;
     this.ctx.stroke();
     this.ctx.restore();
   },
 
+  /**
+   * Render the grid line number on the css grid highlighter canvas.
+   *
+   * @param  {Number} lineNumber
+   *         The grid line number.
+   * @param  {Number} linePos
+   *         The line position along the x-axis for a column grid line and
+   *         y-axis for a row grid line.
+   * @param  {Number} startPos
+   *         The start position of the cross side of the grid line.
+   * @param  {String} dimensionType
+   *         The grid dimension type which is either the constant COLUMNS or ROWS.
+   */
+  renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) {
+    if (dimensionType === COLUMNS) {
+      this.ctx.fillText(lineNumber, linePos, startPos);
+    } else {
+      let textWidth = this.ctx.measureText(lineNumber).width;
+      this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
+    }
+  },
+
   _hide() {
     setIgnoreLayoutChanges(true);
     this._hideGrid();
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   _hideGrid() {
     this.getElement("canvas").setAttribute("hidden", "true");
--- a/devtools/server/actors/highlighters/eye-dropper.js
+++ b/devtools/server/actors/highlighters/eye-dropper.js
@@ -173,16 +173,18 @@ EyeDropper.prototype = {
     pageListenerTarget.removeEventListener("mousemove", this);
     pageListenerTarget.removeEventListener("click", this);
     pageListenerTarget.removeEventListener("keydown", this);
     pageListenerTarget.removeEventListener("DOMMouseScroll", this);
     pageListenerTarget.removeEventListener("FullZoomChange", this);
 
     this.getElement("root").setAttribute("hidden", "true");
     this.getElement("root").removeAttribute("drawn");
+
+    this.emit("hidden");
   },
 
   prepareImageCapture() {
     // Get the image data from the content window.
     let imageData = getWindowAsImageData(this.win);
 
     // We need to transform imageData to something drawWindow will consume. An ImageBitmap
     // works well. We could have used an Image, but doing so results in errors if the page
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -21,16 +21,18 @@ const {
 } = require("devtools/shared/specs/inspector");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 const { Class } = require("sdk/core/heritage");
 const events = require("sdk/event/core");
 const object = require("sdk/util/object");
 const nodeConstants = require("devtools/shared/dom-node-constants.js");
+loader.lazyRequireGetter(this, "CommandUtils",
+  "devtools/client/shared/developer-toolbar", true);
 
 const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 
 /**
  * Convenience API for building a list of attribute modifications
  * for the `modifyAttributes` request.
  */
 const AttributeModificationList = Class({
@@ -972,12 +974,28 @@ var InspectorFront = FrontClassWithSpec(
         return pageStyle;
       }
       return this.getWalker().then(() => {
         return pageStyle;
       });
     });
   }, {
     impl: "_getPageStyle"
+  }),
+
+  pickColorFromPage: custom(Task.async(function* (toolbox, options) {
+    if (toolbox) {
+      // If the eyedropper was already started using the gcli command, hide it so we don't
+      // end up with 2 instances of the eyedropper on the page.
+      let {target} = toolbox;
+      let requisition = yield CommandUtils.createRequisition(target, {
+        environment: CommandUtils.createEnvironment({target})
+      });
+      yield requisition.updateExec("eyedropper --hide");
+    }
+
+    yield this._pickColorFromPage(options);
+  }), {
+    impl: "_pickColorFromPage"
   })
 });
 
 exports.InspectorFront = InspectorFront;
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -1030,16 +1030,62 @@ struct TwoByteString::HashPolicy {
     return k.match(eq);
   }
 
   static void rekey(TwoByteString& k, TwoByteString&& newKey) {
     k = Move(newKey);
   }
 };
 
+// Returns whether `edge` should be included in a heap snapshot of
+// `compartments`. The optional `policy` out-param is set to INCLUDE_EDGES
+// if we want to include the referent's edges, or EXCLUDE_EDGES if we don't
+// want to include them.
+static bool
+ShouldIncludeEdge(JS::CompartmentSet* compartments,
+                  const ubi::Node& origin, const ubi::Edge& edge,
+                  CoreDumpWriter::EdgePolicy* policy = nullptr)
+{
+  if (policy) {
+    *policy = CoreDumpWriter::INCLUDE_EDGES;
+  }
+
+  if (!compartments) {
+    // We aren't targeting a particular set of compartments, so serialize all the
+    // things!
+    return true;
+  }
+
+  // We are targeting a particular set of compartments. If this node is in our target
+  // set, serialize it and all of its edges. If this node is _not_ in our
+  // target set, we also serialize under the assumption that it is a shared
+  // resource being used by something in our target compartments since we reached it
+  // by traversing the heap graph. However, we do not serialize its outgoing
+  // edges and we abandon further traversal from this node.
+  //
+  // If the node does not belong to any compartment, we also serialize its outgoing
+  // edges. This case is relevant for Shapes: they don't belong to a specific
+  // compartment and contain edges to parent/kids Shapes we want to include. Note
+  // that these Shapes may contain pointers into our target compartment (the
+  // Shape's getter/setter JSObjects). However, we do not serialize nodes in other
+  // compartments that are reachable from these non-compartment nodes.
+
+  JSCompartment* compartment = edge.referent.compartment();
+
+  if (!compartment || compartments->has(compartment)) {
+    return true;
+  }
+
+  if (policy) {
+    *policy = CoreDumpWriter::EXCLUDE_EDGES;
+  }
+
+  return !!origin.compartment();
+}
+
 // A `CoreDumpWriter` that serializes nodes to protobufs and writes them to the
 // given `ZeroCopyOutputStream`.
 class MOZ_STACK_CLASS StreamWriter : public CoreDumpWriter
 {
   using FrameSet         = js::HashSet<uint64_t>;
   using TwoByteStringMap = js::HashMap<TwoByteString, uint64_t, TwoByteString::HashPolicy>;
   using OneByteStringMap = js::HashMap<const char*, uint64_t>;
 
@@ -1052,16 +1098,18 @@ class MOZ_STACK_CLASS StreamWriter : pub
   // to the core dump.
   TwoByteStringMap twoByteStringsAlreadySerialized;
   // The set of one-byte strings that have already been serialized and written
   // to the core dump.
   OneByteStringMap oneByteStringsAlreadySerialized;
 
   ::google::protobuf::io::ZeroCopyOutputStream& stream;
 
+  JS::CompartmentSet* compartments;
+
   bool writeMessage(const ::google::protobuf::MessageLite& message) {
     // We have to create a new CodedOutputStream when writing each message so
     // that the 64MB size limit used by Coded{Output,Input}Stream to prevent
     // integer overflow is enforced per message rather than on the whole stream.
     ::google::protobuf::io::CodedOutputStream codedStream(&stream);
     codedStream.WriteVarint32(message.ByteSize());
     message.SerializeWithCachedSizes(&codedStream);
     return !codedStream.HadError();
@@ -1182,23 +1230,25 @@ class MOZ_STACK_CLASS StreamWriter : pub
       return nullptr;
 
     return protobufStackFrame.release();
   }
 
 public:
   StreamWriter(JSContext* cx,
                ::google::protobuf::io::ZeroCopyOutputStream& stream,
-               bool wantNames)
+               bool wantNames,
+               JS::CompartmentSet* compartments)
     : cx(cx)
     , wantNames(wantNames)
     , framesAlreadySerialized(cx)
     , twoByteStringsAlreadySerialized(cx)
     , oneByteStringsAlreadySerialized(cx)
     , stream(stream)
+    , compartments(compartments)
   { }
 
   bool init() {
     return framesAlreadySerialized.init() &&
            twoByteStringsAlreadySerialized.init() &&
            oneByteStringsAlreadySerialized.init();
   }
 
@@ -1235,16 +1285,19 @@ public:
 
     if (includeEdges) {
       auto edges = ubiNode.edges(cx, wantNames);
       if (NS_WARN_IF(!edges))
         return false;
 
       for ( ; !edges->empty(); edges->popFront()) {
         ubi::Edge& ubiEdge = edges->front();
+        if (!ShouldIncludeEdge(compartments, ubiNode, ubiEdge)) {
+          continue;
+        }
 
         protobuf::Edge* protobufEdge = protobufNode.add_edges();
         if (NS_WARN_IF(!protobufEdge)) {
           return false;
         }
 
         protobufEdge->set_referent(ubiEdge.referent.identifier());
 
@@ -1324,39 +1377,26 @@ public:
     // every edge arriving at that node. "But, don't we want to serialize every
     // edge in the heap graph?" you ask. Don't worry! This edge is still
     // serialized into the core dump. Serializing a node also serializes each of
     // its edges, and if we are traversing a given edge, we must have already
     // visited and serialized the origin node and its edges.
     if (!first)
       return true;
 
+    CoreDumpWriter::EdgePolicy policy;
+    if (!ShouldIncludeEdge(compartments, origin, edge, &policy))
+      return true;
+
     nodeCount++;
 
-    const JS::ubi::Node& referent = edge.referent;
-
-    if (!compartments)
-      // We aren't targeting a particular set of compartments, so serialize all the
-      // things!
-      return writer.writeNode(referent, CoreDumpWriter::INCLUDE_EDGES);
+    if (policy == CoreDumpWriter::EXCLUDE_EDGES)
+      traversal.abandonReferent();
 
-    // We are targeting a particular set of compartments. If this node is in our target
-    // set, serialize it and all of its edges. If this node is _not_ in our
-    // target set, we also serialize under the assumption that it is a shared
-    // resource being used by something in our target compartments since we reached it
-    // by traversing the heap graph. However, we do not serialize its outgoing
-    // edges and we abandon further traversal from this node.
-
-    JSCompartment* compartment = referent.compartment();
-
-    if (compartments->has(compartment))
-      return writer.writeNode(referent, CoreDumpWriter::INCLUDE_EDGES);
-
-    traversal.abandonReferent();
-    return writer.writeNode(referent, CoreDumpWriter::EXCLUDE_EDGES);
+    return writer.writeNode(edge.referent, policy);
   }
 };
 
 
 bool
 WriteHeapGraph(JSContext* cx,
                const JS::ubi::Node& node,
                CoreDumpWriter& writer,
@@ -1528,28 +1568,30 @@ ThreadSafeChromeUtils::SaveHeapSnapshot(
   nsCOMPtr<nsIOutputStream> outputStream = getCoreDumpOutputStream(rv, start, outFilePath);
   if (NS_WARN_IF(rv.Failed()))
     return;
 
   ZeroCopyNSIOutputStream zeroCopyStream(outputStream);
   ::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream);
 
   JSContext* cx = global.Context();
-  StreamWriter writer(cx, gzipStream, wantNames);
-  if (NS_WARN_IF(!writer.init())) {
-    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
 
   {
     Maybe<AutoCheckCannotGC> maybeNoGC;
     ubi::RootList rootList(cx, maybeNoGC, wantNames);
     if (!EstablishBoundaries(cx, rv, boundaries, rootList, compartments))
       return;
 
+    StreamWriter writer(cx, gzipStream, wantNames,
+                        compartments.initialized() ? &compartments : nullptr);
+    if (NS_WARN_IF(!writer.init())) {
+      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+
     MOZ_ASSERT(maybeNoGC.isSome());
     ubi::Node roots(&rootList);
 
     // Serialize the initial heap snapshot metadata to the core dump.
     if (!writer.writeMetadata(PR_Now()) ||
         // Serialize the heap graph to the core dump, starting from our list of
         // roots.
         !WriteHeapGraph(cx,
--- a/devtools/shared/heapsnapshot/tests/gtest/DoesCrossCompartmentBoundaries.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/DoesCrossCompartmentBoundaries.cpp
@@ -53,18 +53,19 @@ DEF_TEST(DoesCrossCompartmentBoundaries,
     // Should serialize nodeB, because it doesn't belong to a compartment and is
     // therefore assumed to be shared.
     ExpectWriteNode(writer, nodeB);
 
     // Should also serialize nodeC, which is in our target compartments, but a
     // different compartment than A.
     ExpectWriteNode(writer, nodeC);
 
-    // However, should not serialize nodeD because nodeB doesn't belong to one
-    // of our target compartments and so its edges are excluded from serialization.
+    // Should serialize nodeD because it's reachable via B and both nodes B and D
+    // don't belong to a specific compartment.
+    ExpectWriteNode(writer, nodeD);
 
     JS::AutoCheckCannotGC noGC(cx);
 
     ASSERT_TRUE(WriteHeapGraph(cx,
                                JS::ubi::Node(&nodeA),
                                writer,
                                /* wantNames = */ false,
                                &targetCompartments,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -23,17 +23,17 @@
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "Navigator.h"
 #include "URIUtils.h"
 
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/QuotaObject.h"
 #include "mozilla/dom/quota/UsageInfo.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsAutoPtr.h"
 #include "nsIAtom.h"
 #include "nsIFile.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIRunnable.h"
 #include "nsISimpleEnumerator.h"
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioChannelService.h"
 
 #include "base/basictypes.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPrimitives.h"
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -6,17 +6,17 @@
 
 #include "ImageEncoder.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SyncRunnable.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "gfxUtils.h"
 #include "nsIThreadPool.h"
 #include "nsNetUtil.h"
 #include "nsXPCOMCIDInternal.h"
 #include "WorkerPrivate.h"
 #include "YCbCrUtils.h"
 
 using namespace mozilla::gfx;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2006,16 +2006,39 @@ Navigator::GetVRDisplays(ErrorResult& aR
     return p.forget();
   }
 
   mVRGetDisplaysPromises.AppendElement(p);
   return p.forget();
 }
 
 void
+Navigator::GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const
+{
+  /**
+   * Get only the active VR displays.
+   * Callers do not wish to VRDisplay::RefreshVRDisplays, as the enumeration may
+   * activate hardware that is not yet intended to be used.
+   */
+  if (!mWindow || !mWindow->GetDocShell()) {
+    return;
+  }
+  nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
+  win->NotifyVREventListenerAdded();
+  nsTArray<RefPtr<VRDisplay>> displays;
+  if (win->UpdateVRDisplays(displays)) {
+    for (auto display : displays) {
+      if (display->IsPresenting()) {
+        aDisplays.AppendElement(display);
+      }
+    }
+  }
+}
+
+void
 Navigator::NotifyVRDisplaysUpdated()
 {
   // Synchronize the VR devices and resolve the promises in
   // mVRGetDisplaysPromises
   nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
 
   nsTArray<RefPtr<VRDisplay>> vrDisplays;
   if (win->UpdateVRDisplays(vrDisplays)) {
@@ -2025,16 +2048,22 @@ Navigator::NotifyVRDisplaysUpdated()
   } else {
     for (auto p : mVRGetDisplaysPromises) {
       p->MaybeReject(NS_ERROR_FAILURE);
     }
   }
   mVRGetDisplaysPromises.Clear();
 }
 
+void
+Navigator::NotifyActiveVRDisplaysChanged()
+{
+  NavigatorBinding::ClearCachedActiveVRDisplaysValue(this);
+}
+
 //*****************************************************************************
 //    Navigator::nsIMozNavigatorNetwork
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetProperties(nsINetworkProperties** aProperties)
 {
   ErrorResult rv;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -252,17 +252,17 @@ public:
 #ifdef MOZ_B2G_RIL
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
   GamepadServiceTest* RequestGamepadServiceTest();
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
-  void NotifyVRDisplaysUpdated();
+  void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;
 #ifdef MOZ_B2G_FM
   FMRadio* GetMozFMRadio(ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_BT
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
@@ -325,16 +325,20 @@ public:
   already_AddRefed<Promise>
   RequestMediaKeySystemAccess(const nsAString& aKeySystem,
                               const Sequence<MediaKeySystemConfiguration>& aConfig,
                               ErrorResult& aRv);
 private:
   RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
 #endif
 
+public:
+  void NotifyVRDisplaysUpdated();
+  void NotifyActiveVRDisplaysChanged();
+
 private:
   virtual ~Navigator();
 
   bool CheckPermission(const char* type);
   static bool CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType);
 
   already_AddRefed<nsDOMDeviceStorage> FindDeviceStorage(const nsAString& aName,
                                                          const nsAString& aType);
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ProcessGlobal.h
@@ -31,16 +31,18 @@ class ProcessGlobal :
   public nsIScriptObjectPrincipal,
   public nsSupportsWeakReference,
   public mozilla::dom::ipc::MessageManagerCallback,
   public nsWrapperCache
 {
 public:
   explicit ProcessGlobal(nsFrameMessageManager* aMessageManager);
 
+  using mozilla::dom::ipc::MessageManagerCallback::GetProcessMessageManager;
+
   bool Init();
 
   static ProcessGlobal* Get();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIContentProcessMessageManager)
 
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/PContentPermission.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsArrayUtils.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "nsJSUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIDocument.h"
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4044,16 +4044,27 @@ nsDOMWindowUtils::SetNextPaintSyncId(int
 NS_IMETHODIMP
 nsDOMWindowUtils::RespectDisplayPortSuppression(bool aEnabled)
 {
   nsCOMPtr<nsIPresShell> shell(GetPresShell());
   APZCCallbackHelper::RespectDisplayPortSuppression(aEnabled, shell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::ForceReflowInterrupt()
+{
+  nsPresContext* pc = GetPresContext();
+  if (!pc) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  pc->SetPendingInterruptFromTest();
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsTranslationNodeList)
 NS_IMPL_RELEASE(nsTranslationNodeList)
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -25,16 +25,17 @@
 #include "plstr.h"
 #include "mozilla/Sprintf.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsITextControlFrame.h"
+#include "nsNumberControlFrame.h"
 #include "nsUnicharUtils.h"
 #include "nsContentList.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIObserver.h"
 #include "nsIBaseWindow.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/ImageLoader.h"
 #include "nsDocShell.h"
@@ -262,16 +263,43 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
 static LazyLogModule gCspPRLog("CSP");
 
+static nsresult
+GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel)
+{
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  if (httpChannel) {
+    httpChannel.forget(aHttpChannel);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
+  if (!multipart) {
+    *aHttpChannel = nullptr;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIChannel> baseChannel;
+  nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  httpChannel = do_QueryInterface(baseChannel);
+  httpChannel.forget(aHttpChannel);
+
+  return NS_OK;
+}
+
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
 {
 }
 
 void
 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
@@ -2687,25 +2715,20 @@ nsDocument::InitCSP(nsIChannel* aChannel
   if (!CSPService::sCSPEnabled) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
            ("CSP is disabled, skipping CSP init for document %p", this));
     return NS_OK;
   }
 
   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
 
-  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
-  if (!httpChannel) {
-    // check baseChannel for CSP when loading a multipart channel
-    nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
-    if (multipart) {
-      nsCOMPtr<nsIChannel> baseChannel;
-      multipart->GetBaseChannel(getter_AddRefs(baseChannel));
-      httpChannel = do_QueryInterface(baseChannel);
-    }
+  nsCOMPtr<nsIHttpChannel> httpChannel;
+  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   if (httpChannel) {
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("content-security-policy"),
         tCspHeaderValue);
 
     httpChannel->GetResponseHeader(
@@ -2769,18 +2792,16 @@ nsDocument::InitCSP(nsIChannel* aChannel
               applyAppDefaultCSP ? "is app" : "not an app"));
     }
 
     return NS_OK;
   }
 
   MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an app or CSP header specified %p", this));
 
-  nsresult rv;
-
   // If Document is an app check to see if we already set CSP and return early
   // if that is indeed the case.
   //
   // In general (see bug 947831), we should not be setting CSP on a principal
   // that aliases another document. For non-app code this is not a problem
   // since we only share the underlying principal with nested browsing
   // contexts for which a header cannot be set (e.g., about:blank and
   // about:srcodoc iframes) and thus won't try to set the CSP again. This
@@ -8598,20 +8619,25 @@ nsDocument::SetValueMissingState(const n
 {
   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
   radioGroup->mGroupSuffersFromValueMissing = aValue;
 }
 
 void
 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
 {
-  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   PRTime modDate = 0;
   nsresult rv;
 
+  nsCOMPtr<nsIHttpChannel> httpChannel;
+  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
   if (httpChannel) {
     nsAutoCString tmp;
     rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
                                         tmp);
 
     if (NS_SUCCEEDED(rv)) {
       PRTime time;
       PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
@@ -10850,17 +10876,18 @@ nsIDocument::CaretPositionFromPoint(floa
   nsCOMPtr<nsIContent> anonNode = node;
   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
   if (nodeIsAnonymous) {
     node = ptFrame->GetContent();
     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
     nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
     nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
     nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
-    if (!!textFrame) {
+    nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
+    if (textFrame || numberFrame) {
       // If the anonymous content node has a child, then we need to make sure
       // that we get the appropriate child, as otherwise the offset may not be
       // correct when we construct a range for it.
       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
       if (firstChild) {
         anonNode = firstChild;
       }
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -50,17 +50,17 @@
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include <algorithm>
 
 #ifdef MOZ_XUL
 #include "nsIDOMXULTextboxElement.h"
 #include "nsIDOMXULMenuListElement.h"
 #endif
 
 #ifdef ACCESSIBILITY
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -78,17 +78,17 @@
 #include "ContentParent.h"
 #include "TabParent.h"
 #include "mozilla/plugins/PPluginWidgetParent.h"
 #include "../plugins/ipc/PluginWidgetParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
 #include "GeckoProfiler.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
@@ -3521,8 +3521,15 @@ nsFrameLoader::PopulateUserContextIdFrom
       nsresult rv;
       aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
+
+nsIMessageSender*
+nsFrameLoader::GetProcessMessageManager() const
+{
+  return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager()
+                        : nullptr;
+};
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -219,16 +219,18 @@ public:
   void GetURL(nsString& aURL);
 
   void ActivateUpdateHitRegion();
   void DeactivateUpdateHitRegion();
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
+  virtual nsIMessageSender* GetProcessMessageManager() const override;
+
   // public because a callback needs these.
   RefPtr<nsFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
 
 private:
   nsFrameLoader(mozilla::dom::Element* aOwner, bool aNetworkCreated);
   ~nsFrameLoader();
 
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1480,16 +1480,27 @@ nsFrameMessageManager::GetInitialProcess
 
   if (!JS_WrapValue(aCx, &init)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   aResult.set(init);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameMessageManager::GetProcessMessageManager(nsIMessageSender** aPMM)
+{
+  *aPMM = nullptr;
+  if (mCallback) {
+    nsCOMPtr<nsIMessageSender> pmm = mCallback->GetProcessMessageManager();
+    pmm.swap(*aPMM);
+  }
+  return NS_OK;
+}
+
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
   size_t mStrong;
   size_t mWeakAlive;
   size_t mWeakDead;
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -104,16 +104,21 @@ public:
   }
 
   virtual bool KillChild()
   {
     // By default, does nothing.
     return false;
   }
 
+  virtual nsIMessageSender* GetProcessMessageManager() const
+  {
+    return nullptr;
+  }
+
 protected:
   bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                        StructuredCloneData& aData,
                                        ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
                                       StructuredCloneData& aData,
                                       ClonedMessageData& aClonedData);
 };
@@ -202,16 +207,17 @@ public:
   {
     mChildManagers.RemoveObject(aManager);
   }
   void Disconnect(bool aRemoveFromParent = true);
   void Close();
 
   void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
   void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
+
   mozilla::dom::ipc::MessageManagerCallback* GetCallback()
   {
     return mCallback;
   }
 
   nsresult DispatchAsyncMessage(const nsAString& aMessageName,
                                 const JS::Value& aJSON,
                                 const JS::Value& aObjects,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -55,17 +55,17 @@
 #include "jswrapper.h"
 #include "nsReadableUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsJSEnvironment.h"
 #include "ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 // Other Classes
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/dom/ToJSValue.h"
@@ -13402,16 +13402,27 @@ nsGlobalWindow::UpdateVRDisplays(nsTArra
 {
   FORWARD_TO_INNER(UpdateVRDisplays, (aDevices), false);
 
   VRDisplay::UpdateVRDisplays(mVRDisplays, AsInner());
   aDevices = mVRDisplays;
   return true;
 }
 
+void
+nsGlobalWindow::NotifyActiveVRDisplaysChanged()
+{
+  MOZ_ASSERT(IsInnerWindow());
+
+  if (mNavigator) {
+    mNavigator->NotifyActiveVRDisplaysChanged();
+  }
+}
+
+
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -806,16 +806,20 @@ public:
 
   // Inner windows only.
   // Enable/disable updates for VR
   void EnableVRUpdates();
   void DisableVRUpdates();
 
   // Update the VR displays for this window
   bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
+  
+  // Inner windows only.
+  // Called to inform that the set of active VR displays has changed.
+  void NotifyActiveVRDisplaysChanged();
 
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())    \
                : nullptr;                                                     \
   }                                                                           \
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -279,16 +279,25 @@ interface nsIMessageSender : nsIMessageL
    *         to a cross-process frame whose process has crashed.
    */
   [implicit_jscontext, optional_argc]
   void sendAsyncMessage([optional] in AString messageName,
                         [optional] in jsval obj,
                         [optional] in jsval objects,
                         [optional] in nsIPrincipal principal,
                         [optional] in jsval transfers);
+
+ /**
+  * For remote browsers there is always a corresponding process message
+  * manager. The intention of this attribute is to link leaf level frame
+  * message managers on the parent side with the corresponding process
+  * message managers (if there is one). For any other cases this property
+  * is null.
+  */
+  readonly attribute nsIMessageSender processMessageManager;
 };
 
 /**
  * Message "broadcasters" don't have a single "other side" that they
  * send messages to, but rather a set of subordinate message managers.
  * For example, broadcasting a message through a window message
  * manager will broadcast the message to all frame message managers
  * within its window.
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -103,16 +103,20 @@
 #include "DocumentType.h"
 #include <algorithm>
 #include "nsGlobalWindow.h"
 #include "nsDOMMutationObserver.h"
 #include "GeometryUtils.h"
 #include "nsIAnimationObserver.h"
 #include "nsChildContentList.h"
 
+#ifdef ACCESSIBILITY
+#include "mozilla/dom/AccessibleNode.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsINode::nsSlots::nsSlots()
   : mWeakReference(nullptr),
     mEditableDescendantCount(0)
 {
 }
@@ -2574,16 +2578,27 @@ nsINode::GetBoundMutationObservers(nsTAr
       if (mo) {
         MOZ_ASSERT(!aResult.Contains(mo));
         aResult.AppendElement(mo.forget());
       }
     }
   }
 }
 
+already_AddRefed<AccessibleNode>
+nsINode::GetAccessibleNode()
+{
+#ifdef ACCESSIBILITY
+  RefPtr<AccessibleNode> anode = new AccessibleNode(this);
+  return anode.forget();
+#endif
+
+  return nullptr;
+}
+
 size_t
 nsINode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
     n += elm->SizeOfIncludingThis(aMallocSizeOf);
   }
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -79,16 +79,17 @@ namespace dom {
 inline bool IsSpaceCharacter(char16_t aChar) {
   return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' ||
          aChar == '\f';
 }
 inline bool IsSpaceCharacter(char aChar) {
   return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' ||
          aChar == '\f';
 }
+class AccessibleNode;
 struct BoxQuadOptions;
 struct ConvertCoordinateOptions;
 class DOMPoint;
 class DOMQuad;
 class DOMRectReadOnly;
 class Element;
 class EventHandlerNonNull;
 template<typename T> class Optional;
@@ -1801,16 +1802,18 @@ public:
   // Makes nsINode object to keep aObject alive.
   void BindObject(nsISupports* aObject);
   // After calling UnbindObject nsINode object doesn't keep
   // aObject alive anymore.
   void UnbindObject(nsISupports* aObject);
 
   void GetBoundMutationObservers(nsTArray<RefPtr<nsDOMMutationObserver> >& aResult);
 
+  already_AddRefed<mozilla::dom::AccessibleNode> GetAccessibleNode();
+
   /**
    * Returns the length of this node, as specified at
    * <http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node-length>
    */
   uint32_t Length() const;
 
   void GetNodeName(mozilla::dom::DOMString& aNodeName)
   {
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -32,16 +32,18 @@ class nsInProcessTabChildGlobal : public
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIGlobalObject,
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
+  using mozilla::dom::ipc::MessageManagerCallback::GetProcessMessageManager;
+
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
                                                          mozilla::DOMEventTargetHelper)
 
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1585,21 +1585,16 @@ nsJSContext::EndCycleCollectionCallback(
   TimeStamp endCCTimeStamp = TimeStamp::Now();
   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
 
   if (NeedsGCAfterCC()) {
     PokeGC(JS::gcreason::CC_WAITING,
            NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
   }
 
-  PRTime endCCTime;
-  if (sPostGCEventsToObserver) {
-    endCCTime = PR_Now();
-  }
-
   // Log information about the CC via telemetry, JSON and the console.
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime);
 
   if (!sLastCCEndTime.IsNull()) {
     // TimeBetween returns milliseconds, but we want to report seconds.
@@ -1682,17 +1677,18 @@ nsJSContext::EndCycleCollectionCallback(
              u"\"times_before_cc\": %lu, "
              u"\"min\": %lu, "
              u"\"max\": %lu, "
              u"\"avg\": %lu, "
              u"\"total\": %lu, "
              u"\"removed\": %lu } "
        u"}");
     nsString json;
-    json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime, ccNowDuration,
+
+    json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), PR_Now(), ccNowDuration,
                                          gCCStats.mMaxSliceTime,
                                          gCCStats.mTotalSliceTime,
                                          gCCStats.mMaxGCDuration,
                                          gCCStats.mMaxSkippableDuration,
                                          gCCStats.mSuspected,
                                          aResults.mVisitedRefCounted, aResults.mVisitedGCed,
                                          aResults.mFreedRefCounted, aResults.mFreedGCed,
                                          sCCollectedWaitingForGC,
@@ -2533,20 +2529,16 @@ nsJSContext::EnsureStatics()
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_high_frequency_high_limit_mb",
                                        (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_allocation_threshold_mb",
                                        (void *)JSGC_ALLOCATION_THRESHOLD);
 
-  Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
-                                       "javascript.options.mem.gc_decommit_threshold_mb",
-                                       (void *)JSGC_DECOMMIT_THRESHOLD);
-
   Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
                                        "dom.cycle_collector.incremental");
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_min_empty_chunk_count",
                                        (void *)JSGC_MIN_EMPTY_CHUNK_COUNT);
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
@@ -2584,16 +2576,35 @@ nsJSContext::EnsureStatics()
 
   sIsInitialized = true;
 }
 
 void
 nsJSContext::NotifyDidPaint()
 {
   sDidPaintAfterPreviousICCSlice = true;
+  if (sICCTimer) {
+    sICCTimer->Cancel();
+    ICCTimerFired(nullptr, nullptr);
+    if (sICCTimer) {
+      sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
+                                           kICCIntersliceDelay,
+                                           nsITimer::TYPE_REPEATING_SLACK,
+                                           "ICCTimerFired");
+    }
+  } else if (sCCTimer) {
+    sCCTimer->Cancel();
+    CCTimerFired(nullptr, nullptr);
+    if (sCCTimer) {
+      sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
+                                          NS_CC_SKIPPABLE_DELAY,
+                                          nsITimer::TYPE_REPEATING_SLACK,
+                                          "CCTimerFired");
+    }
+  }
 }
 
 nsScriptNameSpaceManager*
 mozilla::dom::GetNameSpaceManager()
 {
   if (sDidShutdown)
     return nullptr;
 
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -48,17 +48,17 @@
 #include "nsProxyRelease.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
 
 #include "mozilla/Attributes.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIScriptError.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 using JS::SourceBufferHolder;
 
 static LazyLogModule gCspPRLog("CSP");
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -672,17 +672,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_encodeToStringWithMaxLength.html]
 [test_error.html]
 [test_EventSource_redirects.html]
 [test_explicit_user_agent.html]
 [test_file_from_blob.html]
 [test_file_negative_date.html]
 [test_fileapi.html]
 [test_fileapi_slice.html]
-skip-if = (toolkit == 'android') || e10s # Android: Bug 775227, e10s: Bug 1226477
+skip-if = (toolkit == 'android') # Android: Bug 775227
 [test_frameLoader_switchProcess.html]
 skip-if = e10s || os != 'linux' || buildapp != 'browser' # Already tests multiprocess
 [test_getAttribute_after_createAttribute.html]
 [test_getElementById.html]
 [test_getTranslationNodes.html]
 [test_getTranslationNodes_limit.html]
 [test_gsp-qualified.html]
 [test_gsp-quirks.html]
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -9,17 +9,17 @@
 #include <algorithm>
 #include <stdarg.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SizePrintfMacros.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/UseCounter.h"
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -409,17 +409,17 @@ class CGDOMJSClass(CGThing):
 
     def declare(self):
         return ""
 
     def define(self):
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
         objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
-        classFlags = "JSCLASS_IS_DOMJSCLASS | "
+        classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
         if self.descriptor.isGlobal():
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
             traceHook = "JS_GlobalObjectTraceHook"
             reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
             traceHook = 'nullptr'
             reservedSlots = slotCount
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -76,17 +76,20 @@ static const js::ClassOps SimpleGlobalCl
 
 static const js::ClassExtension SimpleGlobalClassExtension = {
   nullptr,
   SimpleGlobal_moved
 };
 
 const js::Class SimpleGlobalClass = {
     "",
-    JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
+    JSCLASS_GLOBAL_FLAGS |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_PRIVATE_IS_NSISUPPORTS |
+    JSCLASS_FOREGROUND_FINALIZE,
     &SimpleGlobalClassOps,
     JS_NULL_CLASS_SPEC,
     &SimpleGlobalClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
 // static
 JSObject*
--- a/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonA2dpInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // A2DP module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonAvrcpInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // AVRCP module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonCoreInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // Core module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonGattInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // GATT module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonHandsfreeInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // Handsfree module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonHidInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // Hid module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonSetupInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonSetupInterface.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "BluetoothDaemonSetupInterface.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // Setup module
 //
--- a/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.cpp
@@ -2,17 +2,17 @@
 /* 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 "BluetoothDaemonSocketInterface.h"
 #include "BluetoothSocketMessageWatcher.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 //
 // Socket module
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -29,17 +29,17 @@
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuidHelper.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/SocketBase.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #define ENSURE_BLUETOOTH_IS_ENABLED(runnable, result)                  \
   do {                                                                 \
     if (!IsEnabled()) {                                                \
       DispatchReplyError(runnable,                                     \
         NS_LITERAL_STRING("Bluetooth is not enabled"));                \
       return result;                                                   \
     }                                                                  \
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -43,17 +43,17 @@
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/SocketBase.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/StaticMutex.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #if defined(MOZ_WIDGET_GONK)
 #include "cutils/properties.h"
 #include <dlfcn.h>
 #endif
 
 /**
  * Some rules for dealing with memory in DBus:
--- a/dom/bluetooth/bluez/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluez/BluetoothSocket.cpp
@@ -7,17 +7,17 @@
 #include "BluetoothSocket.h"
 #include <fcntl.h>
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUnixSocketConnector.h"
 #include "BluetoothUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
 #include "nsXULAppAPI.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 using namespace mozilla::ipc;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 static const size_t MAX_READ_SIZE = 1 << 16;
 
 //
--- a/dom/bluetooth/common/BluetoothService.cpp
+++ b/dom/bluetooth/common/BluetoothService.cpp
@@ -13,17 +13,17 @@
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothServiceChildProcess.h"
 #include "BluetoothUtils.h"
 
 #include "jsapi.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
--- a/dom/bluetooth/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth/ipc/BluetoothParent.cpp
@@ -4,17 +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/. */
 
 #include "base/basictypes.h"
 
 #include "BluetoothParent.h"
 
 #include "mozilla/Assertions.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsThreadUtils.h"
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 
 using mozilla::Unused;
--- a/dom/broadcastchannel/BroadcastChannelParent.cpp
+++ b/dom/broadcastchannel/BroadcastChannelParent.cpp
@@ -4,17 +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/. */
 
 #include "BroadcastChannelParent.h"
 #include "BroadcastChannelService.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIScriptSecurityManager.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/AutoUtils.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/InternalRequest.h"
 #include "mozilla/dom/cache/CacheParent.h"
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/dom/cache/TypeUtils.h"
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheWorkerHolder.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::WorkerPrivate;
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/CacheChild.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheOpChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
--- a/dom/cache/CacheOpParent.cpp
+++ b/dom/cache/CacheOpParent.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/CacheOpParent.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/SendStream.h"
 
 namespace mozilla {
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/CacheStorage.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/CacheStorageBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheStorageChild.h"
 #include "mozilla/dom/cache/CacheWorkerHolder.h"
--- a/dom/cache/CacheStorageChild.cpp
+++ b/dom/cache/CacheStorageChild.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/CacheStorageChild.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheOpChild.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
--- a/dom/cache/CacheStorageParent.cpp
+++ b/dom/cache/CacheStorageParent.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/CacheStorageParent.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/CacheOpParent.h"
 #include "mozilla/dom/cache/ManagerId.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 
 namespace mozilla {
 namespace dom {
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -2,17 +2,17 @@
 /* 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 "mozilla/dom/cache/CacheStreamControlChild.h"
 
 #include "mozilla/DebugOnly.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "nsISupportsImpl.h"
 
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -2,17 +2,17 @@
 /* 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 "mozilla/dom/cache/CacheStreamControlParent.h"
 
 #include "mozilla/DebugOnly.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "nsISupportsImpl.h"
 
--- a/dom/cache/FileUtils.cpp
+++ b/dom/cache/FileUtils.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/FileUtils.h"
 
 #include "mozilla/dom/quota/FileStreams.h"
 #include "mozilla/SnappyCompressOutputStream.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIFile.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
 #include "nsISimpleEnumerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/Manager.h"
 
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/Context.h"
 #include "mozilla/dom/cache/DBAction.h"
 #include "mozilla/dom/cache/DBSchema.h"
 #include "mozilla/dom/cache/FileUtils.h"
 #include "mozilla/dom/cache/ManagerId.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/StreamList.h"
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/ReadStream.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/cache/CacheStreamControlChild.h"
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/SnappyUncompressInputStream.h"
 #include "nsIAsyncInputStream.h"
 #include "nsTArray.h"
 
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/cache/TypeUtils.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/InternalRequest.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CameraControlImpl.h"
 #include "base/basictypes.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsPrintfCString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "CameraControlListener.h"
 
 using namespace mozilla;
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -10,17 +10,17 @@
 #include "nsThread.h"
 #include "DeviceStorage.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsIAppsService.h"
 #include "nsIObserverService.h"
 #include "nsIDOMEventListener.h"
 #include "nsIScriptSecurityManager.h"
 #include "Navigator.h"
 #include "nsXULAppAPI.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraCapabilities.h"
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -33,17 +33,17 @@
 #endif
 #include "nsNetCID.h" // for NS_STREAMTRANSPORTSERVICE_CONTRACTID
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 #include "nsThread.h"
 #include "nsITimer.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "nsAlgorithm.h"
 #include "nsPrintfCString.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkRecorderProfiles.h"
 #include "CameraCommon.h"
 #include "GonkCameraParameters.h"
 #include "DeviceStorageFileDescriptor.h"
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -93,17 +93,17 @@
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPath.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/TextMetrics.h"
@@ -5291,22 +5291,16 @@ already_AddRefed<ImageData>
 CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
                                        double aSy, double aSw,
                                        double aSh, ErrorResult& aError)
 {
   if (mDrawObserver) {
     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
   }
 
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
   if (!mCanvasElement && !mDocShell) {
     NS_ERROR("No canvas element and no docshell in GetImageData!!!");
     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Check only if we have a canvas element; if we were created with a docshell,
   // then it's special internal use.
@@ -5405,20 +5399,34 @@ CanvasRenderingContext2D::GetImageDataAr
   }
 
   IntRect srcRect(0, 0, mWidth, mHeight);
   IntRect destRect(aX, aY, aWidth, aHeight);
   IntRect srcReadRect = srcRect.Intersect(destRect);
   RefPtr<DataSourceSurface> readback;
   DataSourceSurface::MappedSurface rawData;
   if (!srcReadRect.IsEmpty()) {
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+    RefPtr<SourceSurface> snapshot;
+    if (mTarget) {
+      snapshot = mTarget->Snapshot();
+    } else if (mBufferProvider) {
+      snapshot = mBufferProvider->BorrowSnapshot();
+    } else {
+      EnsureTarget();
+      snapshot = mTarget->Snapshot();
+    }
+
     if (snapshot) {
       readback = snapshot->GetDataSurface();
     }
+
+    if (!mTarget && mBufferProvider) {
+      mBufferProvider->ReturnSnapshot(snapshot.forget());
+    }
+
     if (!readback || !readback->Map(DataSourceSurface::READ, &rawData)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   IntRect dstWriteRect = srcReadRect;
   dstWriteRect.MoveBy(-aX, -aY);
 
deleted file mode 100644
--- a/dom/canvas/WebGLContextReporter.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "WebGLBuffer.h"
-#include "WebGLContext.h"
-#include "WebGLMemoryTracker.h"
-
-namespace mozilla {
-
-NS_IMETHODIMP
-WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
-                                   nsISupports* data, bool)
-{
-#define REPORT(_path, _kind, _units, _amount, _desc)                         \
-    do {                                                                     \
-      nsresult rv;                                                           \
-      rv = handleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
-                                   _kind, _units, _amount,                   \
-                                   NS_LITERAL_CSTRING(_desc), data);         \
-      NS_ENSURE_SUCCESS(rv, rv);                                             \
-    } while (0)
-
-    REPORT("webgl-texture-memory",
-           KIND_OTHER, UNITS_BYTES, GetTextureMemoryUsed(),
-           "Memory used by WebGL textures.The OpenGL"
-           " implementation is free to store these textures in either video"
-           " memory or main memory. This measurement is only a lower bound,"
-           " actual memory usage may be higher for example if the storage"
-           " is strided.");
-
-    REPORT("webgl-texture-count",
-           KIND_OTHER, UNITS_COUNT, GetTextureCount(),
-           "Number of WebGL textures.");
-
-    REPORT("webgl-buffer-memory",
-           KIND_OTHER, UNITS_BYTES, GetBufferMemoryUsed(),
-           "Memory used by WebGL buffers. The OpenGL"
-           " implementation is free to store these buffers in either video"
-           " memory or main memory. This measurement is only a lower bound,"
-           " actual memory usage may be higher for example if the storage"
-           " is strided.");
-
-    REPORT("explicit/webgl/buffer-cache-memory",
-           KIND_HEAP, UNITS_BYTES, GetBufferCacheMemoryUsed(),
-           "Memory used by WebGL buffer caches. The WebGL"
-           " implementation caches the contents of element array buffers"
-           " only.This adds up with the webgl-buffer-memory value, but"
-           " contrary to it, this one represents bytes on the heap,"
-           " not managed by OpenGL.");
-
-    REPORT("webgl-buffer-count",
-           KIND_OTHER, UNITS_COUNT, GetBufferCount(),
-           "Number of WebGL buffers.");
-
-    REPORT("webgl-renderbuffer-memory",
-           KIND_OTHER, UNITS_BYTES, GetRenderbufferMemoryUsed(),
-           "Memory used by WebGL renderbuffers. The OpenGL"
-           " implementation is free to store these renderbuffers in either"
-           " video memory or main memory. This measurement is only a lower"
-           " bound, actual memory usage may be higher for example if the"
-           " storage is strided.");
-
-    REPORT("webgl-renderbuffer-count",
-           KIND_OTHER, UNITS_COUNT, GetRenderbufferCount(),
-           "Number of WebGL renderbuffers.");
-
-    REPORT("explicit/webgl/shader",
-           KIND_HEAP, UNITS_BYTES, GetShaderSize(),
-           "Combined size of WebGL shader ASCII sources and translation"
-           " logs cached on the heap.");
-
-    REPORT("webgl-shader-count",
-           KIND_OTHER, UNITS_COUNT, GetShaderCount(),
-           "Number of WebGL shaders.");
-
-    REPORT("webgl-context-count",
-           KIND_OTHER, UNITS_COUNT, GetContextCount(),
-           "Number of WebGL contexts.");
-
-#undef REPORT
-
-    return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(WebGLMemoryTracker, nsIMemoryReporter)
-
-StaticRefPtr<WebGLMemoryTracker> WebGLMemoryTracker::sUniqueInstance;
-
-WebGLMemoryTracker*
-WebGLMemoryTracker::UniqueInstance()
-{
-    if (!sUniqueInstance) {
-        sUniqueInstance = new WebGLMemoryTracker;
-        sUniqueInstance->InitMemoryReporter();
-    }
-    return sUniqueInstance;
-}
-
-WebGLMemoryTracker::WebGLMemoryTracker()
-{
-}
-
-void
-WebGLMemoryTracker::InitMemoryReporter()
-{
-    RegisterWeakMemoryReporter(this);
-}
-
-WebGLMemoryTracker::~WebGLMemoryTracker()
-{
-    UnregisterWeakMemoryReporter(this);
-}
-
-MOZ_DEFINE_MALLOC_SIZE_OF(WebGLBufferMallocSizeOf)
-
-int64_t
-WebGLMemoryTracker::GetBufferCacheMemoryUsed()
-{
-    const ContextsArrayType& contexts = Contexts();
-    int64_t result = 0;
-    for(size_t i = 0; i < contexts.Length(); ++i) {
-        for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
-             buffer;
-             buffer = buffer->getNext())
-        {
-            if (buffer->Content() == WebGLBuffer::Kind::ElementArray) {
-                result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
-            }
-        }
-    }
-    return result;
-}
-
-MOZ_DEFINE_MALLOC_SIZE_OF(WebGLShaderMallocSizeOf)
-
-int64_t
-WebGLMemoryTracker::GetShaderSize()
-{
-    const ContextsArrayType& contexts = Contexts();
-    int64_t result = 0;
-    for(size_t i = 0; i < contexts.Length(); ++i) {
-        for (const WebGLShader* shader = contexts[i]->mShaders.getFirst();
-             shader;
-             shader = shader->getNext())
-        {
-            result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
-        }
-    }
-    return result;
-}
-
-} // namespace mozilla
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -6,17 +6,17 @@
 #include "WebGLTexture.h"
 
 #include <algorithm>
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLSampler.h"
 #include "WebGLTexelConversions.h"
 
 namespace mozilla {
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -11,17 +11,17 @@
 #include "gfxPrefs.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "ScopedGLHelpers.h"
 #include "TexUnpackBlob.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLTexelConversions.h"
 
 namespace mozilla {
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -2,17 +2,17 @@
 /* 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 "DeviceStorageRequestParent.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "ContentParent.h"
 #include "nsProxyRelease.h"
 #include "AppProcessChecker.h"
 #include "mozilla/Preferences.h"
 #include "nsNetCID.h"
 
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -52,17 +52,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #include "mozilla/dom/Element.h"
 using namespace mozilla::tasktracer;
 #endif
 
 namespace mozilla {
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -11,17 +11,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/TabParent.h"
 
 #include "HTMLInputElement.h"
 #include "IMEContentObserver.h"
 
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -14,17 +14,17 @@
 #include "nsPresContext.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/TabParent.h"
 
 #ifdef XP_MACOSX
 // Some defiens will be conflict with OSX SDK
 #define TextRange _TextRange
 #define TextRangeArray _TextRangeArray
 #define Comment _Comment
 #endif
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -14,21 +14,20 @@ support-files =
 [test_pointerevent_capture_suppressing_mouse-manual.html]
   support-files = pointerevent_capture_suppressing_mouse-manual.html
 [test_pointerevent_change-touch-action-onpointerdown_touch-manual.html]
   support-files = pointerevent_change-touch-action-onpointerdown_touch-manual.html
   disabled = disabled
 [test_pointerevent_constructor.html]
   support-files = pointerevent_constructor.html
   disabled = should be investigated
-[test_pointerevent_element_haspointercapture.html]
-  support-files = pointerevent_element_haspointercapture.html
+[test_pointerevent_element_haspointercapture-manual.html]
+  support-files = pointerevent_element_haspointercapture-manual.html
 [test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html]
   support-files = pointerevent_gotpointercapture_before_first_pointerevent-manual.html
-  disabled = should be investigated
 [test_pointerevent_lostpointercapture_for_disconnected_node-manual.html]
   support-files = pointerevent_lostpointercapture_for_disconnected_node-manual.html
 [test_pointerevent_lostpointercapture_is_first-manual.html]
   support-files = pointerevent_lostpointercapture_is_first-manual.html
 [test_pointerevent_multiple_primary_pointers_boundary_events-manual.html]
   support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html
   disabled = should be investigated
 [test_pointerevent_pointercancel_touch-manual.html]
rename from dom/events/test/pointerevents/pointerevent_element_haspointercapture.html
rename to dom/events/test/pointerevents/pointerevent_element_haspointercapture-manual.html
rename from dom/events/test/pointerevents/test_pointerevent_element_haspointercapture.html
rename to dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html
--- a/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture.html
+++ b/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <script type="text/javascript" src="mochitest_support_external.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_element_haspointercapture.html";
+        iframe.src = "pointerevent_element_haspointercapture-manual.html";
       }
       function executeTest(int_win) {
         sendMouseEvent(int_win, "target0", "mousemove");
         sendMouseEvent(int_win, "target0", "mousedown", {button:0});
         sendMouseEvent(int_win, "target0", "mousemove", {button:0});
         sendMouseEvent(int_win, "target1", "mousemove", {button:0});
         sendMouseEvent(int_win, "target1", "mouseup", {button:0});
       }
--- a/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html
@@ -13,20 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_gotpointercapture_before_first_pointerevent-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "complete-notice", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:0});
-        sendMouseEvent(int_win, "target0", "mousedown", {button:1});
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:0});
-        sendPointerEvent(int_win, "complete-notice", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:0});
-        sendPointerEvent(int_win, "target0", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
+        sendMouseEvent(int_win, "target0", "mousedown", {button:0});
+        sendMouseEvent(int_win, "target0", "mouseup",   {button:0});
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -26,17 +26,17 @@
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "nsHttpChannel.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "Fetch.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -11,17 +11,17 @@
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
--- a/dom/filehandle/ActorsParent.cpp
+++ b/dom/filehandle/ActorsParent.cpp
@@ -2,17 +2,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/. */
 
 #include "ActorsParent.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileHandleCommon.h"
 #include "mozilla/dom/PBackgroundFileHandleParent.h"
 #include "mozilla/dom/PBackgroundFileRequestParent.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsAutoPtr.h"
 #include "nsComponentManagerUtils.h"
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -9,17 +9,17 @@
 #include "nsNetCID.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 nsresult
--- a/dom/flyweb/FlyWebPublishedServer.cpp
+++ b/dom/flyweb/FlyWebPublishedServer.cpp
@@ -12,17 +12,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsGlobalWindow.h"
 #include "WebSocketChannel.h"
 
 namespace mozilla {
 namespace dom {
 
 static LazyLogModule gFlyWebPublishedServerLog("FlyWebPublishedServer");
--- a/dom/fmradio/ipc/FMRadioParent.cpp
+++ b/dom/fmradio/ipc/FMRadioParent.cpp
@@ -1,16 +1,16 @@
 /* -*- 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 "FMRadioParent.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/DebugOnly.h"
 #include "FMRadioRequestParent.h"
 #include "FMRadioService.h"
 
 BEGIN_FMRADIO_NAMESPACE
 
 FMRadioParent::FMRadioParent()
--- a/dom/fmradio/ipc/FMRadioRequestParent.cpp
+++ b/dom/fmradio/ipc/FMRadioRequestParent.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "FMRadioRequestParent.h"
 #include "FMRadioService.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/PFMRadio.h"
 
 BEGIN_FMRADIO_NAMESPACE
 
 FMRadioRequestParent::FMRadioRequestParent()
   : mActorDestroyed(false)
 {
   MOZ_COUNT_CTOR(FMRadioRequestParent);
--- a/dom/gamepad/GamepadManager.cpp
+++ b/dom/gamepad/GamepadManager.cpp
@@ -24,17 +24,17 @@
 #include "nsIDOMEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include <cstddef>
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/gamepad/GamepadPlatformService.cpp
+++ b/dom/gamepad/GamepadPlatformService.cpp
@@ -4,17 +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/. */
 
 #include "mozilla/dom/GamepadPlatformService.h"
 
 #include "mozilla/dom/GamepadEventChannelParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsIThread.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
--- a/dom/gamepad/GamepadServiceTest.cpp
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -2,27 +2,27 @@
 /* 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 "GamepadServiceTest.h"
 
 #include "mozilla/ErrorResult.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "mozilla/dom/GamepadManager.h"
 #include "mozilla/dom/GamepadPlatformService.h"
 #include "mozilla/dom/GamepadServiceTestBinding.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
--- a/dom/gamepad/ipc/GamepadTestChannelParent.cpp
+++ b/dom/gamepad/ipc/GamepadTestChannelParent.cpp
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GamepadTestChannelParent.h"
 
 #include "mozilla/dom/GamepadPlatformService.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
 GamepadTestChannelParent::RecvGamepadTestEvent(const uint32_t& aID,
                                                const GamepadChangeEvent& aEvent)
 {
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -21,17 +21,17 @@
 #include "nsIDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIObserverService.h"
 #include "nsPIDOMWindow.h"
 #include "nsThreadUtils.h"
 #include "mozilla/HalWakeLock.h"
 #include "mozilla/Hal.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
+#include "mozilla/Unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 #include "mozilla/dom/WakeLock.h"
 
--- a/dom/grid/Grid.cpp
+++ b/dom/grid/Grid.cpp
@@ -1,24 +1,25 @@
 /* -*- 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 "Grid.h"
 
+#include "GridArea.h"
 #include "GridDimension.h"
 #include "mozilla/dom/GridBinding.h"
 #include "nsGridContainerFrame.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols, mAreas)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Grid)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Grid)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Grid)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Grid::Grid(nsISupports* aParent,
@@ -38,16 +39,50 @@ Grid::Grid(nsISupports* aParent,
   mRows->SetLineInfo(rowTrackInfo, rowLineInfo);
 
   const ComputedGridTrackInfo* columnTrackInfo =
     aFrame->GetComputedTemplateColumns();
   const ComputedGridLineInfo* columnLineInfo =
     aFrame->GetComputedTemplateColumnLines();
   mCols->SetTrackInfo(columnTrackInfo);
   mCols->SetLineInfo(columnTrackInfo, columnLineInfo);
+
+  // Add implicit areas first.
+  nsGridContainerFrame::ImplicitNamedAreas* implicitAreas =
+    aFrame->GetImplicitNamedAreas();
+  if (implicitAreas) {
+    for (auto iter = implicitAreas->Iter(); !iter.Done(); iter.Next()) {
+      nsStringHashKey* entry = iter.Get();
+
+      GridArea* area = new GridArea(this,
+                                    nsString(entry->GetKey()),
+                                    GridDeclaration::Implicit,
+                                    0,
+                                    0,
+                                    0,
+                                    0);
+      mAreas.AppendElement(area);
+    }
+  }
+
+  // Add explicit areas next.
+  nsGridContainerFrame::ExplicitNamedAreas* explicitAreas =
+    aFrame->GetExplicitNamedAreas();
+  if (explicitAreas) {
+    for (auto areaInfo : *explicitAreas) {
+      GridArea* area = new GridArea(this,
+                                    areaInfo.mName,
+                                    GridDeclaration::Explicit,
+                                    areaInfo.mRowStart,
+                                    areaInfo.mRowEnd,
+                                    areaInfo.mColumnStart,
+                                    areaInfo.mColumnEnd);
+      mAreas.AppendElement(area);
+    }
+  }
 }
 
 Grid::~Grid()
 {
 }
 
 JSObject*
 Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@@ -62,10 +97,16 @@ Grid::Rows() const
 }
 
 GridDimension*
 Grid::Cols() const
 {
   return mCols;
 }
 
+void
+Grid::GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const
+{
+  aAreas = mAreas;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/Grid.h
+++ b/dom/grid/Grid.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Grid_h
 #define mozilla_dom_Grid_h
 
+#include "GridArea.h"
 #include "mozilla/dom/Element.h"
 #include "nsGridContainerFrame.h"
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -33,19 +34,21 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
   Element* GetParentObject()
   {
     return mParent;
   }
 
   GridDimension* Rows() const;
   GridDimension* Cols() const;
+  void GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const;
 
 protected:
   nsCOMPtr<Element> mParent;
   RefPtr<GridDimension> mRows;
   RefPtr<GridDimension> mCols;
+  nsTArray<RefPtr<GridArea>> mAreas;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_Grid_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridArea.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "GridArea.h"
+#include "mozilla/dom/GridBinding.h"
+#include "Grid.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridArea, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridArea)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridArea)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridArea)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridArea::GridArea(Grid* aParent,
+                   const nsString& aName,
+                   GridDeclaration aType,
+                   uint32_t aRowStart,
+                   uint32_t aRowEnd,
+                   uint32_t aColumnStart,
+                   uint32_t aColumnEnd)
+  : mParent(aParent)
+  , mName(aName)
+  , mType(aType)
+  , mRowStart(aRowStart)
+  , mRowEnd(aRowEnd)
+  , mColumnStart(aColumnStart)
+  , mColumnEnd(aColumnEnd)
+{
+}
+
+GridArea::~GridArea()
+{
+}
+
+JSObject*
+GridArea::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridAreaBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+GridArea::GetName(nsString& aName) const
+{
+  aName = mName;
+}
+
+GridDeclaration
+GridArea::Type() const
+{
+  return mType;
+}
+
+uint32_t
+GridArea::RowStart() const
+{
+  return mRowStart;
+}
+
+uint32_t
+GridArea::RowEnd() const
+{
+  return mRowEnd;
+}
+
+uint32_t
+GridArea::ColumnStart() const
+{
+  return mColumnStart;
+}
+
+uint32_t
+GridArea::ColumnEnd() const
+{
+  return mColumnEnd;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridArea.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_GridArea_h
+#define mozilla_dom_GridArea_h
+
+#include "mozilla/dom/GridBinding.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class Grid;
+
+class GridArea : public nsISupports
+               , public nsWrapperCache
+{
+public:
+  explicit GridArea(Grid *aParent,
+                    const nsString& aName,
+                    GridDeclaration aType,
+                    uint32_t aRowStart,
+                    uint32_t aRowEnd,
+                    uint32_t aColumnStart,
+                    uint32_t aColumnEnd);
+
+protected:
+  virtual ~GridArea();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridArea)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  Grid* GetParentObject()
+  {
+    return mParent;
+  }
+
+  void GetName(nsString& aName) const;
+  GridDeclaration Type() const;
+  uint32_t RowStart() const;
+  uint32_t RowEnd() const;
+  uint32_t ColumnStart() const;
+  uint32_t ColumnEnd() const;
+
+protected:
+  RefPtr<Grid> mParent;
+  const nsString mName;
+  const GridDeclaration mType;
+  const uint32_t mRowStart;
+  const uint32_t mRowEnd;
+  const uint32_t mColumnStart;
+  const uint32_t mColumnEnd;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridTrack_h */
--- a/dom/grid/GridLine.cpp
+++ b/dom/grid/GridLine.cpp
@@ -19,16 +19,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 GridLine::GridLine(GridLines *aParent)
   : mParent(aParent)
   , mStart(0.0)
   , mBreadth(0.0)
+  , mType(GridDeclaration::Implicit)
   , mNumber(0)
 {
   MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines");
 }
 
 GridLine::~GridLine()
 {
 }
@@ -52,28 +53,36 @@ GridLine::Start() const
 }
 
 double
 GridLine::Breadth() const
 {
   return mBreadth;
 }
 
+GridDeclaration
+GridLine::Type() const
+{
+  return mType;
+}
+
 uint32_t
 GridLine::Number() const
 {
   return mNumber;
 }
 
 void
-GridLine::SetLineValues(double aStart,
+GridLine::SetLineValues(const nsTArray<nsString>& aNames,
+                        double aStart,
                         double aBreadth,
                         uint32_t aNumber,
-                        const nsTArray<nsString>& aNames)
+                        GridDeclaration aType)
 {
+  mNames = aNames;
   mStart = aStart;
   mBreadth = aBreadth;
   mNumber = aNumber;
-  mNames = aNames;
+  mType = aType;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/GridLine.h
+++ b/dom/grid/GridLine.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_GridLine_h
 #define mozilla_dom_GridLine_h
 
+#include "mozilla/dom/GridBinding.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class GridLines;
@@ -34,27 +35,30 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
   GridLines* GetParentObject()
   {
     return mParent;
   }
 
   double Start() const;
   double Breadth() const;
+  GridDeclaration Type() const;
   uint32_t Number() const;
 
-  void SetLineValues(double aStart,
+  void SetLineValues(const nsTArray<nsString>& aNames,
+                     double aStart,
                      double aBreadth,
                      uint32_t aNumber,
-                     const nsTArray<nsString>& aNames);
+                     GridDeclaration aType);
 
 protected:
   RefPtr<GridLines> mParent;
+  nsTArray<nsString> mNames;
   double mStart;
   double mBreadth;
+  GridDeclaration mType;
   uint32_t mNumber;
-  nsTArray<nsString> mNames;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_GridLine_h */
--- a/dom/grid/GridLines.cpp
+++ b/dom/grid/GridLines.cpp
@@ -92,21 +92,32 @@ GridLines::SetLineInfo(const ComputedGri
       mLines.AppendElement(line);
 
       nsTArray<nsString> lineNames;
       if (aLineInfo) {
         lineNames = aLineInfo->mNames.SafeElementAt(i, nsTArray<nsString>());
       }
 
       line->SetLineValues(
+        lineNames,
         nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
         nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
                                                  endOfLastTrack),
         i + 1,
-        lineNames
+        (
+          // Implicit if there are no explicit tracks, or if the index
+          // is before the first explicit track, or after
+          // a track beyond the last explicit track.
+          (aTrackInfo->mNumExplicitTracks == 0) ||
+          (i < aTrackInfo->mNumLeadingImplicitTracks) ||
+          (i > aTrackInfo->mNumLeadingImplicitTracks +
+               aTrackInfo->mNumExplicitTracks) ?
+            GridDeclaration::Implicit :
+            GridDeclaration::Explicit
+        )
       );
 
       if (i < aTrackInfo->mEndFragmentTrack) {
         endOfLastTrack = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
       }
     }
   }
 }
--- a/dom/grid/GridTrack.cpp
+++ b/dom/grid/GridTrack.cpp
@@ -19,17 +19,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 GridTrack::GridTrack(GridTracks* aParent)
   : mParent(aParent)
   , mStart(0.0)
   , mBreadth(0.0)
-  , mType(GridTrackType::Implicit)
+  , mType(GridDeclaration::Implicit)
   , mState(GridTrackState::Static)
 {
   MOZ_ASSERT(aParent, "Should never be instantiated with a null GridTracks");
 }
 
 GridTrack::~GridTrack()
 {
 }
@@ -47,32 +47,32 @@ GridTrack::Start() const
 }
 
 double
 GridTrack::Breadth() const
 {
   return mBreadth;
 }
 
-GridTrackType
+GridDeclaration
 GridTrack::Type() const
 {
   return mType;
 }
 
 GridTrackState
 GridTrack::State() const
 {
   return mState;
 }
 
 void
 GridTrack::SetTrackValues(double aStart,
                           double aBreadth,
-                          GridTrackType aType,
+                          GridDeclaration aType,
                           GridTrackState aState)
 {
   mStart = aStart;
   mBreadth = aBreadth;
   mType = aType;
   mState = aState;
 }
 
--- a/dom/grid/GridTrack.h
+++ b/dom/grid/GridTrack.h
@@ -31,25 +31,28 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
   GridTracks* GetParentObject()
   {
     return mParent;
   }
 
   double Start() const;
   double Breadth() const;
-  GridTrackType Type() const;
+  GridDeclaration Type() const;
   GridTrackState State() const;
 
-  void SetTrackValues(double aStart, double aBreadth, GridTrackType aType, GridTrackState aState);
+  void SetTrackValues(double aStart,
+                      double aBreadth,
+                      GridDeclaration aType,
+                      GridTrackState aState);
 
 protected:
   RefPtr<GridTracks> mParent;
   double mStart;
   double mBreadth;
-  GridTrackType mType;
+  GridDeclaration mType;
   GridTrackState mState;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_GridTrack_h */
--- a/dom/grid/GridTracks.cpp
+++ b/dom/grid/GridTracks.cpp
@@ -81,18 +81,18 @@ GridTracks::SetTrackInfo(const ComputedG
       nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mPositions[i]),
       nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mSizes[i]),
       (
         // Implicit if index is before the first explicit track, or after
         // the last explicit track.
         (i < aTrackInfo->mNumLeadingImplicitTracks) ||
         (i >= aTrackInfo->mNumLeadingImplicitTracks +
               aTrackInfo->mNumExplicitTracks) ?
-          GridTrackType::Implicit :
-          GridTrackType::Explicit
+          GridDeclaration::Implicit :
+          GridDeclaration::Explicit
       ),
       GridTrackState(aTrackInfo->mStates[i])
     );
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/moz.build
+++ b/dom/grid/moz.build
@@ -3,25 +3,27 @@
 # 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/.
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 EXPORTS.mozilla.dom += [
     'Grid.h',
+    'GridArea.h',
     'GridDimension.h',
     'GridLine.h',
     'GridLines.h',
     'GridTrack.h',
     'GridTracks.h',
 ]
 
 UNIFIED_SOURCES += [
     'Grid.cpp',
+    'GridArea.cpp',
     'GridDimension.cpp',
     'GridLine.cpp',
     'GridLines.cpp',
     'GridTrack.cpp',
     'GridTracks.cpp',
 ]
 
 LOCAL_INCLUDES += [
--- a/dom/grid/test/chrome.ini
+++ b/dom/grid/test/chrome.ini
@@ -1,5 +1,7 @@
+[chrome/test_grid_areas.html]
 [chrome/test_grid_fragmentation.html]
+[chrome/test_grid_implicit.html]
 [chrome/test_grid_lines.html]
 [chrome/test_grid_object.html]
 [chrome/test_grid_track_state.html]
 [chrome/test_grid_tracks.html]
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_areas.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 600px;
+	height: 600px;
+	grid-gap: 20px;
+	grid-template-columns: 50px 1fr 50px;
+	grid-template-rows: 1fr 1fr 1fr;
+	grid-template-areas: "areaA areaA ....."
+						 "areaB areaC areaC"
+						 "areaB areaC areaC";
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+.a {
+	grid-area: areaA;
+}
+.b {
+	grid-area: areaB;
+}
+.c {
+	grid-area: areaC;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+
+	// test existence of property
+	isnot(typeof(grid.areas), "undefined", "Grid.areas property exists.");
+
+	if (typeof(grid.areas) != "undefined") {
+		// test that the right number of areas are reported
+		is(grid.areas.length, 3, "3 areas are reported.");
+
+		if (grid.areas.length == 3) {
+			// test area names
+			is(grid.areas[0].name, "areaA", "Area 0 has proper name.");
+			is(grid.areas[1].name, "areaB", "Area 1 has proper name.");
+			is(grid.areas[2].name, "areaC", "Area 2 has proper name.");
+
+			// test area types
+			is(grid.areas[0].type, "explicit", "Area 0 is explicit.");
+			is(grid.areas[1].type, "explicit", "Area 1 is explicit.");
+			is(grid.areas[2].type, "explicit", "Area 2 is explicit.");
+
+			// test start and end lines
+			is(grid.areas[0].rowStart, 1, "Area 0 has start row line of 1.");
+			is(grid.areas[0].rowEnd, 2, "Area 0 has end row line of 2.");
+			is(grid.areas[0].columnStart, 1, "Area 0 has start column line of 1.");
+			is(grid.areas[0].columnEnd, 3, "Area 0 has end column line of 3.");
+
+			is(grid.areas[1].rowStart, 2, "Area 1 has start row line of 2.");
+			is(grid.areas[1].rowEnd, 4, "Area 1 has end row line of 4.");
+			is(grid.areas[1].columnStart, 1, "Area 1 has start column line of 1.");
+			is(grid.areas[1].columnEnd, 2, "Area 1 has end column line of 2.");
+
+			is(grid.areas[2].rowStart, 2, "Area 2 has start row line of 2.");
+			is(grid.areas[2].rowEnd, 4, "Area 2 has end row line of 4.");
+			is(grid.areas[2].columnStart, 2, "Area 2 has start column line of 2.");
+			is(grid.areas[2].columnEnd, 4, "Area 2 has end column line of 4.");
+		}
+	}
+
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div id="boxB" class="box b">B</div>
+		<div id="boxC" class="box c">C</div>
+	</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_implicit.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	grid-gap: 10px;
+	grid-template-columns: 100px 50px 100px;
+	grid-template-rows: 50px [areaD-start] 50px [areaD-end];
+	grid-template-areas: "areaA areaA ....."
+						 "..... areaC areaC";
+	grid-auto-columns: 20px;
+	grid-auto-rows: 20px;
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+.a {
+	grid-area: areaA;
+}
+.b {
+	grid-row: span 2 / 2;
+	grid-column: areaA-end / span 2;
+}
+.c {
+	grid-area: areaC;
+}
+.d {
+	grid-area: areaD;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	var boxA = document.getElementById("boxA");
+	var boxB = document.getElementById("boxB");
+	var boxC = document.getElementById("boxC");
+
+	// test column and row line counts
+	is(grid.cols.lines.length, 6,
+		"Grid.cols.lines property has length that respects implicit expansion."
+	);
+	is(grid.rows.lines.length, 4,
+		"Grid.rows.lines property has length that respects implicit expansion."
+	);
+
+	if((grid.cols.lines.length == 6) &&
+	   (grid.rows.lines.length == 4)) {
+
+		// test explicit / implicit lines
+		is(grid.cols.lines[0].type, "explicit", "Grid column line 1 is explicit.");
+		is(grid.cols.lines[4].type, "implicit", "Grid column line 5 is implicit.");
+		is(grid.cols.lines[5].type, "implicit", "Grid column line 6 is implicit.");
+
+		is(grid.rows.lines[0].type, "implicit", "Grid row line 1 is implicit.");
+		is(grid.rows.lines[1].type, "explicit", "Grid row line 2 is explicit.");
+		is(grid.rows.lines[3].type, "explicit", "Grid row line 4 is explicit.");
+	}
+
+	// test column and row track counts
+	is(grid.cols.tracks.length, 5,
+		"Grid.cols.tracks property has length that respects implicit expansion."
+	);
+	is(grid.rows.tracks.length, 3,
+		"Grid.rows.tracks property has length that respects implicit expansion."
+	);
+
+	if((grid.cols.tracks.length == 5) &&
+	   (grid.rows.tracks.length == 3)) {
+
+		// test explicit / implicit tracks
+		is(grid.cols.tracks[0].type, "explicit", "Grid column track 1 is explicit.");
+		is(grid.cols.tracks[3].type, "implicit", "Grid column track 4 is implicit.");
+		is(grid.cols.tracks[4].type, "implicit", "Grid column track 5 is implicit.");
+
+		is(grid.rows.tracks[0].type, "implicit", "Grid row track 1 is implicit.");
+		is(grid.rows.tracks[1].type, "explicit", "Grid row track 2 is explicit.");
+		is(grid.rows.tracks[2].type, "explicit", "Grid row track 3 is explicit.");
+	}
+
+	// test area count
+	is(grid.areas.length, 3,
+		"Grid.areas property has length that respects implicit expansion."
+	);
+
+	for(var i = 0; i < grid.areas.length; i++) {
+		var area = grid.areas[i];
+		if(area.name == "areaD") {
+			is(area.type, "implicit", area.name + " is implicit.");
+
+			// test lines of implicit areas
+			todo_is(area.rowStart, 2, area.name + " has start row line of 2.");
+			todo_is(area.rowEnd, 3, area.name + " has end row line of 3.");
+			todo_is(area.columnStart, 4, area.name + " has start column line of 4.");
+			todo_is(area.columnEnd, 5, area.name + " has end column line of 5.");
+		} else {
+			is(area.type, "explicit", area.name + " is explicit.");
+		}
+	}
+
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div id="boxB" class="box b">B</div>
+		<div id="boxC" class="box c">C</div>
+		<div id="boxD" class="box d">D</div>
+	</div>
+
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -933,18 +933,28 @@ HTMLInputElement::InitFilePicker(FilePic
 
   if (IsPopupBlocked()) {
     win->FirePopupBlockedEvent(doc, nullptr, EmptyString(), EmptyString());
     return NS_OK;
   }
 
   // Get Loc title
   nsXPIDLString title;
-  nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
-                                     "FileUpload", title);
+  nsXPIDLString okButtonLabel;
+  if (aType == FILE_PICKER_DIRECTORY) {
+    nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                       "DirectoryUpload", title);
+
+    nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                       "DirectoryPickerOkButtonLabel",
+                                       okButtonLabel);
+  } else {
+    nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                       "FileUpload", title);
+  }
 
   nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
   if (!filePicker)
     return NS_ERROR_FAILURE;
 
   int16_t mode;
 
   if (aType == FILE_PICKER_DIRECTORY) {
@@ -953,16 +963,20 @@ HTMLInputElement::InitFilePicker(FilePic
     mode = static_cast<int16_t>(nsIFilePicker::modeOpenMultiple);
   } else {
     mode = static_cast<int16_t>(nsIFilePicker::modeOpen);
   }
 
   nsresult rv = filePicker->Init(win, title, mode);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (!okButtonLabel.IsEmpty()) {
+    filePicker->SetOkButtonLabel(okButtonLabel);
+  }
+
   // Native directory pickers ignore file type filters, so we don't spend
   // cycles adding them for FILE_PICKER_DIRECTORY.
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept) &&
       aType != FILE_PICKER_DIRECTORY) {
     SetFilePickerFiltersFromAccept(filePicker);
   } else {
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1508,17 +1508,17 @@ private:
   }
 
   /**
    * Returns true if selection methods can be called on element
    */
   bool SupportsTextSelection() const {
     return mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_SEARCH ||
            mType == NS_FORM_INPUT_URL || mType == NS_FORM_INPUT_TEL ||
-           mType == NS_FORM_INPUT_PASSWORD || mType == NS_FORM_INPUT_NUMBER;
+           mType == NS_FORM_INPUT_PASSWORD;
   }
 
   static bool MayFireChangeOnBlur(uint8_t aType) {
     return IsSingleLineTextControl(false, aType) ||
            aType == NS_FORM_INPUT_RANGE ||
            aType == NS_FORM_INPUT_NUMBER;
   }
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2933,17 +2933,18 @@ static const struct MidasCommand gMidasC
   { "justifyleft",   "cmd_align",       "left", true,  false },
   { "justifyright",  "cmd_align",      "right", true,  false },
   { "justifycenter", "cmd_align",     "center", true,  false },
   { "justifyfull",   "cmd_align",    "justify", true,  false },
   { "removeformat",  "cmd_removeStyles",    "", true,  false },
   { "unlink",        "cmd_removeLinks",     "", true,  false },
   { "insertorderedlist",   "cmd_ol",        "", true,  false },
   { "insertunorderedlist", "cmd_ul",        "", true,  false },
-  { "insertparagraph", "cmd_paragraphState", "p", true,  false },
+  { "insertparagraph", "cmd_insertParagraph", "", true,  false },
+  { "insertlinebreak", "cmd_insertLineBreak", "", true,  false },
   { "formatblock",   "cmd_paragraphState",  "", false, false },
   { "heading",       "cmd_paragraphState",  "", false, false },
   { "styleWithCSS",  "cmd_setDocumentUseCSS", "", false, true },
   { "contentReadOnly", "cmd_setDocumentReadOnly", "", false, true },
   { "insertBrOnReturn", "cmd_insertBrOnReturn", "", false, true },
   { "enableObjectResizing", "cmd_enableObjectResizing", "", false, true },
   { "enableInlineTableEditing", "cmd_enableInlineTableEditing", "", false, true },
 #if 0
--- a/dom/html/test/forms/test_bug1283915.html
+++ b/dom/html/test/forms/test_bug1283915.html
@@ -37,26 +37,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(tField.value, "abc");
     isCursorAtEnd(tField);
 
     var nField = document.getElementById("numField");
     nField.focus();
 
     synthesizeKey("1", {});
     is(nField.value, "1");
-    isCursorAtEnd(nField);
     document.body.offsetWidth;
 
     synthesizeKey("2", {});
     is(nField.value, "12");
-    isCursorAtEnd(nField);
 
     synthesizeKey("3", {});
     is(nField.value, "123");
-    isCursorAtEnd(nField);
 
     SimpleTest.finish();
   }
 
   SimpleTest.waitForFocus(test);
   </script>
 </head>
 <body>
--- a/dom/html/test/forms/test_set_range_text.html
+++ b/dom/html/test/forms/test_set_range_text.html
@@ -15,17 +15,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content">
 
 <!-- "SetRangeText() supported types"-->
 <input type="text" id="input_text"></input>
 <inpu