Merge central to date
authorJustin Wood <Callek@gmail.com>
Thu, 06 Jul 2017 13:24:10 -0400
changeset 1177961 40a20e93d546ecb6b3a4b9567c18781b100e91cb
parent 1177635 175d449452d83921fb48cc943d457b6a6656b2f7 (current diff)
parent 1177852 e23f55311ecb5e0cd14c5010d1b40663b3a2e7d0 (diff)
child 1177962 b30f9166181b9defdee601f18a887f7d7d4d8649
child 1182993 4a4d5749dae29769a0368a017097a435a923e0c6
push id203054
push userCallek@gmail.com
push dateThu, 06 Jul 2017 20:07:38 +0000
treeherdertry@f77cdd5f21b4 [default view] [failures only]
milestone56.0a1
Merge central to date MozReview-Commit-ID: AS1iuq8DOqb
browser/app/profile/firefox.js
browser/components/preferences/in-content/tests/browser_advanced_siteData.js
browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
browser/themes/shared/icons/download-arrow-with-bar.svg
config/external/icu/data/icudt58l.dat
dom/plugins/ipc/PluginAsyncSurrogate.cpp
dom/plugins/ipc/PluginAsyncSurrogate.h
dom/plugins/ipc/PluginDataResolver.h
gfx/thebes/gfxMatrix.cpp
gfx/thebes/gfxRect.cpp
intl/icu-patches/bug-1228227-bug-1263325-libc++-gcc_hidden.diff
intl/icu-patches/bug-1325858-close-key.diff
intl/icu-patches/ucol_getKeywordValuesForLocale-ulist_resetList.diff
intl/icu-patches/unum_formatDoubleForFields.diff
intl/icu/source/common/cmemory.c
intl/icu/source/common/cstring.c
intl/icu/source/common/cwchar.c
intl/icu/source/common/icudataver.c
intl/icu/source/common/locmap.c
intl/icu/source/common/propsvec.c
intl/icu/source/common/uarrsort.c
intl/icu/source/common/ubidi.c
intl/icu/source/common/ubidi_props.c
intl/icu/source/common/ubidiln.c
intl/icu/source/common/ubiditransform.c
intl/icu/source/common/ubidiwrt.c
intl/icu/source/common/ucat.c
intl/icu/source/common/uchar.c
intl/icu/source/common/ucmndata.c
intl/icu/source/common/ucnv.c
intl/icu/source/common/ucnv_cb.c
intl/icu/source/common/ucnv_cnv.c
intl/icu/source/common/ucnv_ct.c
intl/icu/source/common/ucnv_err.c
intl/icu/source/common/ucnv_lmb.c
intl/icu/source/common/ucnv_set.c
intl/icu/source/common/ucnv_u16.c
intl/icu/source/common/ucnv_u32.c
intl/icu/source/common/ucnv_u7.c
intl/icu/source/common/ucnv_u8.c
intl/icu/source/common/ucnvdisp.c
intl/icu/source/common/ucnvhz.c
intl/icu/source/common/ucnvisci.c
intl/icu/source/common/ucnvlat1.c
intl/icu/source/common/ucnvscsu.c
intl/icu/source/common/udatamem.c
intl/icu/source/common/udataswp.c
intl/icu/source/common/uenum.c
intl/icu/source/common/uhash.c
intl/icu/source/common/uinvchar.c
intl/icu/source/common/ulist.c
intl/icu/source/common/uloc_tag.c
intl/icu/source/common/umapfile.c
intl/icu/source/common/umath.c
intl/icu/source/common/ures_cnv.c
intl/icu/source/common/usc_impl.c
intl/icu/source/common/uscript.c
intl/icu/source/common/ustrfmt.c
intl/icu/source/common/utf_impl.c
intl/icu/source/common/utrace.c
intl/icu/source/common/utypes.c
intl/icu/source/common/wintz.c
intl/icu/source/i18n/decContext.c
intl/icu/source/i18n/decNumber.c
intl/icu/source/i18n/ulocdata.c
intl/icu/source/i18n/utmscale.c
intl/icu/source/io/sprintf.c
intl/icu/source/io/sscanf.c
intl/icu/source/io/ufile.c
intl/icu/source/io/ufmt_cmn.c
intl/icu/source/io/uprntf_p.c
intl/icu/source/io/uscanf.c
intl/icu/source/io/uscanf_p.c
intl/icu/source/io/ustdio.c
intl/icu/source/stubdata/stubdata.c
intl/icu/source/tools/toolutil/filestrm.c
intl/icu/source/tools/toolutil/flagparser.c
intl/icu/source/tools/toolutil/pkg_genc.c
intl/icu/source/tools/toolutil/pkg_gencmn.c
intl/icu/source/tools/toolutil/ucm.c
intl/icu/source/tools/toolutil/ucmstate.c
intl/icu/source/tools/toolutil/unewdata.c
intl/icu/source/tools/toolutil/uoptions.c
intl/icu/source/tools/toolutil/uparse.c
intl/icu/source/tools/toolutil/writesrc.c
mobile/android/app/mobile.js
mobile/android/app/src/main/res/color/url_bar_title.xml
mobile/android/app/src/main/res/color/url_bar_title_hint.xml
mobile/android/app/src/main/res/drawable/search_suggestion_button.xml
mobile/android/app/src/main/res/drawable/search_suggestion_prompt_no.xml
mobile/android/app/src/main/res/drawable/search_suggestion_prompt_yes.xml
mobile/android/app/src/main/res/drawable/url_bar_bg.xml
mobile/android/app/src/main/res/layout-large-v11/browser_toolbar.xml
mobile/android/app/src/main/res/layout/browser_search.xml
mobile/android/app/src/main/res/layout/browser_toolbar.xml
mobile/android/app/src/main/res/layout/home_item_row.xml
mobile/android/app/src/main/res/layout/home_search_item_row.xml
mobile/android/app/src/main/res/layout/home_suggestion_prompt.xml
mobile/android/app/src/main/res/layout/search_engine_row.xml
mobile/android/app/src/main/res/layout/suggestion_item.xml
mobile/android/app/src/main/res/layout/toolbar_display_layout.xml
mobile/android/app/src/main/res/layout/toolbar_edit_layout.xml
mobile/android/app/src/main/res/layout/two_line_page_row.xml
mobile/android/base/java/org/mozilla/gecko/home/SearchEngineRow.java
mobile/android/chrome/geckoview/geckoview.js
mobile/android/modules/geckoview/GeckoViewContent.jsm
mobile/android/modules/geckoview/GeckoViewModule.jsm
mobile/android/modules/geckoview/GeckoViewNavigation.jsm
mobile/android/modules/geckoview/GeckoViewProgress.jsm
mozglue/linker/SeekableZStream.cpp
mozglue/linker/SeekableZStream.h
mozglue/linker/szip.cpp
old-configure.in
parser/htmlparser/nsToken.h
python/mozbuild/mozbuild/mach_commands.py
security/manager/ssl/StaticHPKPins.h
taskcluster/ci/android-stuff/kind.yml
taskcluster/ci/build/android.yml
taskcluster/ci/build/linux.yml
taskcluster/ci/build/macosx.yml
taskcluster/ci/hazard/kind.yml
taskcluster/ci/l10n/kind.yml
taskcluster/ci/nightly-l10n/kind.yml
taskcluster/ci/source-test/kind.yml
taskcluster/ci/static-analysis/kind.yml
taskcluster/ci/test/test-platforms.yml
taskcluster/ci/test/tests.yml
taskcluster/mach_commands.py
taskcluster/taskgraph/transforms/job/common.py
taskcluster/taskgraph/transforms/job/hazard.py
taskcluster/taskgraph/transforms/job/mozharness.py
taskcluster/taskgraph/transforms/job/mozharness_test.py
taskcluster/taskgraph/transforms/job/toolchain.py
taskcluster/taskgraph/transforms/repackage.py
taskcluster/taskgraph/transforms/repackage_signing.py
taskcluster/taskgraph/transforms/tests.py
testing/firefox-ui/resources/addons/extensions/restartless_addon_signed.xpi
testing/firefox-ui/resources/addons/extensions/restartless_addon_unsigned.xpi
testing/marionette/marionette.eslintrc.js
testing/marionette/prefs/marionette.js
testing/mozharness/configs/single_locale/tc_android-api-15.py
testing/mozharness/configs/single_locale/tc_linux32.py
testing/mozharness/configs/single_locale/tc_linux64.py
testing/mozharness/configs/single_locale/tc_macosx64.py
testing/mozharness/scripts/desktop_l10n.py
testing/web-platform/meta/XMLHttpRequest/open-during-abort-processing.htm.ini
testing/web-platform/meta/cssom/cssimportrule.html.ini
testing/web-platform/meta/web-animations/animation-model/animation-types/discrete-animation.html.ini
testing/web-platform/meta/webdriver/contexts.py.ini
testing/web-platform/meta/webdriver/navigation.py.ini
testing/web-platform/tests/webdriver/actions/__init__.py
testing/web-platform/tests/webdriver/actions/conftest.py
testing/web-platform/tests/webdriver/actions/key.py
testing/web-platform/tests/webdriver/actions/mouse.py
testing/web-platform/tests/webdriver/actions/sequence.py
testing/web-platform/tests/webdriver/actions/special_keys.py
testing/web-platform/tests/webdriver/actions/support/__init__.py
testing/web-platform/tests/webdriver/actions/support/keys.py
testing/web-platform/tests/webdriver/actions/support/refine.py
testing/web-platform/tests/webdriver/actions/support/test_actions_wdspec.html
testing/web-platform/tests/webdriver/conftest.py
testing/web-platform/tests/webdriver/contexts.py
testing/web-platform/tests/webdriver/navigation.py
testing/web-platform/tests/webdriver/support/__init__.py
testing/web-platform/tests/webdriver/support/asserts.py
testing/web-platform/tests/webdriver/support/fixtures.py
testing/web-platform/tests/webdriver/support/http_request.py
testing/web-platform/tests/webdriver/support/inline.py
testing/web-platform/tests/webdriver/support/merge_dictionaries.py
testing/web-platform/tests/webdriver/tests/support/http_request.py
testing/web-platform/tests/webdriver/window_maximizing.py
third_party/rust/libc/ci/docker/arm-linux-androideabi/accept-licenses.sh
third_party/rust/libc/ci/docker/arm-linux-androideabi/install-ndk.sh
third_party/rust/libc/ci/docker/arm-linux-androideabi/install-sdk.sh
third_party/rust/libc/src/unix/notbsd/android/b32.rs
third_party/rust/libc/src/unix/notbsd/android/b64.rs
toolkit/modules/sessionstore/XPathGenerator.jsm
--- a/.eslintignore
+++ b/.eslintignore
@@ -133,19 +133,16 @@ devtools/server/tests/browser/storage-*.
 devtools/server/tests/browser/stylesheets-nested-iframes.html
 devtools/server/tests/unit/xpcshell_debugging_script.js
 devtools/shared/platform/content/test/test_clipboard.html
 devtools/shared/qrcode/tests/mochitest/test_decode.html
 devtools/shared/tests/mochitest/*.html
 devtools/shared/webconsole/test/test_*.html
 
 # Ignore devtools pre-processed files
-devtools/client/framework/toolbox-process-window.js
-devtools/client/performance/system.js
-devtools/client/webide/webide-prefs.js
 devtools/client/preferences/**
 
 # Ignore devtools third-party libs
 devtools/shared/jsbeautify/*
 devtools/shared/acorn/*
 devtools/shared/gcli/source/*
 devtools/shared/node-properties/*
 devtools/shared/pretty-fast/*
@@ -210,16 +207,17 @@ dom/imptests/**
 dom/interfaces/**
 dom/ipc/**
 dom/json/**
 dom/jsurl/**
 dom/locales/**
 dom/manifest/**
 dom/mathml/**
 dom/media/**
+!dom/media/*.js*
 dom/messagechannel/**
 dom/network/**
 dom/notification/**
 dom/offline/**
 dom/payments/**
 dom/performance/**
 dom/permission/**
 dom/plugins/**
@@ -246,16 +244,19 @@ dom/webidl/**
 dom/workers/**
 dom/worklet/**
 dom/xbl/**
 dom/xhr/**
 dom/xml/**
 dom/xslt/**
 dom/xul/**
 
+# Third-party
+dom/media/webvtt/**
+
 # Exclude everything but self-hosted JS
 js/ductwork/**
 js/examples/**
 js/ipc/**
 js/public/**
 js/xpconnect/**
 js/src/devtools/**
 js/src/octane/**
@@ -292,25 +293,38 @@ security/manager/ssl/security-prefs.js
 security/nss/**
 
 # services/ exclusions
 
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
-# testing/ exclusions
-testing/marionette/**
+# Remote protocol exclusions
+testing/marionette/test_*.js
+testing/marionette/atom.js
+testing/marionette/legacyaction.js
+testing/marionette/client/**
+testing/marionette/harness/**
+
+# other testing/ exclusions
 testing/mochitest/**
 # third party modules
 testing/modules/ajv-4.1.1.js
 testing/modules/sinon-2.3.2.js
 # octothorpe used for pref file comment causes parsing error
 testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
-testing/talos/**
+testing/talos/talos/scripts/jszip.min.js
+testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
+testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js
+testing/talos/talos/tests/canvasmark/**
+testing/talos/talos/tests/dromaeo/**
+testing/talos/talos/tests/v8_7/**
+testing/talos/talos/tests/kraken/**
+
 testing/web-platform/**
 testing/xpcshell/moz-http2/**
 testing/xpcshell/node-http2/**
 
 # Third party services
 services/common/kinto-http-client.js
 services/common/kinto-offline-client.js
 services/sync/tps/extensions/mozmill
--- 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 1341285: From experience, due to all the build changes in a webrtc.org landing make a clobber usually necessary
+Bug 1353650 - Update to ICU 59 requires clobber
--- a/accessible/.eslintrc.js
+++ b/accessible/.eslintrc.js
@@ -5,24 +5,20 @@ module.exports = {
     // Warn about cyclomatic complexity in functions.
     "complexity": ["error", 42],
 
     // XXX These are rules that are enabled in the recommended configuration, but
     // disabled here due to failures when initially implemented. They should be
     // removed (and hence enabled) at some stage.
     "brace-style": "off",
     "consistent-return": "off",
-    "func-call-spacing": "off",
     "quotes": "off",
     "object-shorthand": "off",
     "space-before-function-paren": "off",
     "space-infix-ops": "off",
-    "key-spacing": "off",
-    "keyword-spacing": "off",
     "no-else-return": "off",
     "no-multi-spaces": "off",
-    "no-trailing-spaces": "off",
     "no-unexpected-multiline": "off",
     "no-unsafe-finally": "off",
     "no-useless-call": "off",
     "spaced-comment": "off",
   }
 };
--- a/accessible/atk/nsStateMap.h
+++ b/accessible/atk/nsStateMap.h
@@ -105,10 +105,11 @@ static const AtkStateMap gAtkStateMap[] 
   { ATK_STATE_SINGLE_LINE,                    kMapDirectly },   // states::SINGLE_LINE             = 1 << 40
   { ATK_STATE_TRANSIENT,                      kMapDirectly },   // states::TRANSIENT               = 1 << 41
   { ATK_STATE_VERTICAL,                       kMapDirectly },   // states::VERTICAL                = 1 << 42
   { ATK_STATE_STALE,                          kMapDirectly },   // states::STALE                   = 1 << 43
   { ATK_STATE_ENABLED,                        kMapDirectly },   // states::ENABLED                 = 1 << 44
   { ATK_STATE_SENSITIVE,                      kMapDirectly },   // states::SENSITIVE               = 1 << 45
   { ATK_STATE_EXPANDABLE,                     kMapDirectly },   // states::EXPANDABLE              = 1 << 46
   { kNone,                                    kMapDirectly },   // states::PINNED                  = 1 << 47
-  { kNone,                                    kNoSuchState },   //                                 = 1 << 48
+  { ATK_STATE_ACTIVE,                         kMapDirectly },   // states::CURRENT                 = 1 << 48
+  { kNone,                                    kNoSuchState },   //                                 = 1 << 49
 };
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -592,16 +592,26 @@ static const nsRoleMapEntry sWAIRoleMaps
     roles::GROUPING,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates
   },
+  { // figure
+    &nsGkAtoms::figure,
+    roles::FIGURE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
   { // form
     &nsGkAtoms::form,
     roles::FORM,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eLandmark,
@@ -897,22 +907,22 @@ static const nsRoleMapEntry sWAIRoleMaps
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIAOrientation,
     eARIAReadonly
   },
   { // region
     &nsGkAtoms::region,
-    roles::PANE,
+    roles::REGION,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    kGenericAccType,
+    eLandmark,
     kNoReqStates
   },
   { // row
     &nsGkAtoms::row,
     roles::ROW,
     kUseMapRole,
     eNoValue,
     eNoAction,
@@ -977,17 +987,17 @@ static const nsRoleMapEntry sWAIRoleMaps
     eARIAAutoComplete,
     eARIAMultiline,
     eARIAReadonlyOrEditable
   },
   { // separator
     &nsGkAtoms::separator_,
     roles::SEPARATOR,
     kUseMapRole,
-    eNoValue,
+    eHasValueMinMaxIfFocusable,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     states::HORIZONTAL,
     eARIAOrientation
   },
   { // slider
     &nsGkAtoms::slider,
@@ -1197,16 +1207,17 @@ nsRoleMapEntry aria::gEmptyRoleMap = {
 
 /**
  * Universal (Global) states:
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
 static const EStateRule sWAIUnivStateMap[] = {
   eARIABusy,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,  // Currently under spec review but precedent exists
   eARIAHasPopup,  // Note this is technically a "property"
   eARIAInvalid,
   eARIAModal,
   eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -29,17 +29,25 @@ enum EValueRule
    * Value interface isn't exposed.
    */
   eNoValue,
 
   /**
    * Value interface is implemented, supports value, min and max from
    * aria-valuenow, aria-valuemin and aria-valuemax.
    */
-  eHasValueMinMax
+  eHasValueMinMax,
+
+  /**
+   * Value interface is implemented, but only if the element is focusable.
+   * For instance, in ARIA 1.1 the ability for authors to create adjustable
+   * splitters was provided by supporting the value interface on separators
+   * that are focusable. Non-focusable separators expose no value information.
+   */
+  eHasValueMinMaxIfFocusable
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Action constants
 
 /**
  * Used to define if the role requires to expose action.
--- a/accessible/base/ARIAStateMap.cpp
+++ b/accessible/base/ARIAStateMap.cpp
@@ -140,16 +140,26 @@ aria::MapToState(EStateRule aRule, dom::
       static const TokenTypeData data(
         nsGkAtoms::aria_checked, eMixedType,
         states::CHECKABLE, states::CHECKED);
 
       MapTokenType(aElement, aState, data);
       return true;
     }
 
+    case eARIACurrent:
+    {
+      static const TokenTypeData data(
+        nsGkAtoms::aria_current, eBoolType,
+        0, states::CURRENT);
+
+      MapTokenType(aElement, aState, data);
+      return true;
+    }
+
     case eARIADisabled:
     {
       static const TokenTypeData data(
         nsGkAtoms::aria_disabled, eBoolType,
         0, states::UNAVAILABLE);
 
       MapTokenType(aElement, aState, data);
       return true;
@@ -349,20 +359,22 @@ MapEnumType(dom::Element* aElement, uint
   }
 }
 
 static void
 MapTokenType(dom::Element* aElement, uint64_t* aState,
              const TokenTypeData& aData)
 {
   if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
-    if ((aData.mType & eMixedType) &&
-        aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
+    if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::mixed, eCaseMatters)) {
-      *aState |= aData.mPermanentState | states::MIXED;
+      if (aData.mType & eMixedType)
+        *aState |= aData.mPermanentState | states::MIXED;
+      else // unsupported use of 'mixed' is an authoring error
+        *aState |= aData.mPermanentState | aData.mFalseState;
       return;
     }
 
     if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::_false, eCaseMatters)) {
       *aState |= aData.mPermanentState | aData.mFalseState;
       return;
     }
--- a/accessible/base/ARIAStateMap.h
+++ b/accessible/base/ARIAStateMap.h
@@ -24,16 +24,17 @@ namespace aria {
 enum EStateRule
 {
   eARIANone,
   eARIAAutoComplete,
   eARIABusy,
   eARIACheckableBool,
   eARIACheckableMixed,
   eARIACheckedMixed,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,
   eARIAHasPopup,
   eARIAInvalid,
   eARIAModal,
   eARIAMultiline,
   eARIAMultiSelectable,
   eARIAOrientation,
--- a/accessible/base/Role.h
+++ b/accessible/base/Role.h
@@ -1002,17 +1002,25 @@ enum Role {
 
   /**
    * A complete or self-contained composition in a document, page, application,
    * or site and that is, in principle, independently distributable or reusable,
    * e.g. in syndication.
    */
   ARTICLE = 172,
 
-  LAST_ROLE = ARTICLE
+  /**
+   * A perceivable section containing content that is relevant to a specific,
+   * author-specified purpose and sufficiently important that users will likely
+   * want to be able to navigate to the section easily and to have it listed in
+   * a summary of the page.
+   */
+  REGION = 173,
+
+  LAST_ROLE = REGION
 };
 
 } // namespace role
 
 typedef enum mozilla::a11y::roles::Role role;
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -1394,8 +1394,16 @@ ROLE(FOOTNOTE,
 
 ROLE(ARTICLE,
      "article",
      ATK_ROLE_ARTICLE,
      NSAccessibilityGroupRole,
      ROLE_SYSTEM_DOCUMENT,
      ROLE_SYSTEM_DOCUMENT,
      eNoNameRule)
+
+ROLE(REGION,
+     "region",
+     ATK_ROLE_LANDMARK,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_LANDMARK,
+     eNoNameRule)
--- a/accessible/base/States.h
+++ b/accessible/base/States.h
@@ -272,14 +272,20 @@ namespace states {
    * @see EXPANDED and COLLAPSED states.
    */
   const uint64_t EXPANDABLE = ((uint64_t) 0x1) << 46;
 
   /**
    * The object is pinned, usually indicating it is fixed in place and has permanence.
    */
   const uint64_t PINNED = ((uint64_t) 0x1) << 47;
+
+  /**
+   * The object is the current item within a container or set of related elements.
+   */
+  const uint64_t CURRENT = ((uint64_t) 0x1) << 48;
+
 } // namespace states
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 	
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -90,17 +90,23 @@ Accessible::HasGenericType(AccGenericTyp
 
 inline bool
 Accessible::HasNumericValue() const
 {
   if (mStateFlags & eHasNumericValue)
     return true;
 
   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
-  return roleMapEntry && roleMapEntry->valueRule != eNoValue;
+  if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
+    return false;
+
+  if (roleMapEntry->valueRule == eHasValueMinMaxIfFocusable)
+    return InteractiveState() & states::FOCUSABLE;
+
+  return true;
 }
 
 inline void
 Accessible::ScrollTo(uint32_t aHow) const
 {
   if (mContent)
     nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
 }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1409,16 +1409,32 @@ Accessible::SetCurValue(double aValue)
 
   return NS_SUCCEEDED(
     mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
 }
 
 role
 Accessible::ARIATransformRole(role aRole)
 {
+  // Beginning with ARIA 1.1, user agents are expected to use the native host
+  // language role of the element when the region role is used without a name.
+  // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-region
+  //
+  // XXX: While the name computation algorithm can be non-trivial in the general
+  // case, it should not be especially bad here: If the author hasn't used the
+  // region role, this calculation won't occur. And the region role's name
+  // calculation rule excludes name from content. That said, this use case is
+  // another example of why we should consider caching the accessible name. See:
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1378235.
+  if (aRole == roles::REGION) {
+    nsAutoString name;
+    Name(name);
+    return name.IsEmpty() ? NativeRole() : aRole;
+  }
+
   // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
   // where the accessible role depends on both the role and ARIA state.
   if (aRole == roles::PUSHBUTTON) {
     if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
       // For simplicity, any existing pressed attribute except "" or "undefined"
       // indicates a toggle.
       return roles::TOGGLE_BUTTON;
     }
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1049,16 +1049,23 @@ DocAccessible::ARIAAttributeChanged(Acce
   if (aAttribute == nsGkAtoms::aria_valuenow &&
       (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
        elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
                         nsGkAtoms::_empty, eCaseMatters))) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
     return;
   }
 
+  if (aAttribute == nsGkAtoms::aria_current) {
+    RefPtr<AccEvent> event =
+      new AccStateChangeEvent(aAccessible, states::CURRENT);
+    FireDelayedEvent(event);
+    return;
+  }
+
   if (aAttribute == nsGkAtoms::aria_owns) {
     mNotificationController->ScheduleRelocation(aAccessible);
   }
 }
 
 void
 DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
 {
@@ -2051,72 +2058,70 @@ DocAccessible::RelocateARIAOwnedIfNeeded
   }
 
   return false;
 }
 
 void
 DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
 {
-  nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
-
   MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
   MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns relocation", logging::eVerbose, aOwner);
 #endif
 
+  nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.LookupOrAdd(aOwner);
+
   IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
-  uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
+  uint32_t idx = 0;
   while (nsIContent* childEl = iter.NextElem()) {
     Accessible* child = GetAccessible(childEl);
+    auto insertIdx = aOwner->ChildCount() - owned->Length() + idx;
 
     // Make an attempt to create an accessible if it wasn't created yet.
     if (!child) {
       if (aOwner->IsAcceptableChild(childEl)) {
         child = GetAccService()->CreateAccessible(childEl, aOwner);
         if (child) {
           TreeMutation imut(aOwner);
           aOwner->InsertChildAt(insertIdx, child);
           imut.AfterInsertion(child);
           imut.Done();
 
           child->SetRelocated(true);
-          children->InsertElementAt(arrayIdx, child);
+          owned->InsertElementAt(idx, child);
+          idx++;
 
           // Create subtree before adjusting the insertion index, since subtree
           // creation may alter children in the container.
           CreateSubtree(child);
           FireEventsOnInsertion(aOwner);
-
-          insertIdx = child->IndexInParent() + 1;
-          arrayIdx++;
         }
       }
       continue;
     }
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns traversal", logging::eVerbose,
                     "candidate", child, nullptr);
 #endif
 
     // Same child on same position, no change.
     if (child->Parent() == aOwner &&
         child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
-      MOZ_ASSERT(child == children->ElementAt(arrayIdx), "Not in sync!");
-      insertIdx++; arrayIdx++;
+      MOZ_ASSERT(owned->ElementAt(idx) == child, "Not in sync!");
+      idx++;
       continue;
     }
 
-    MOZ_ASSERT(children->SafeElementAt(arrayIdx) != child, "Already in place!");
+    MOZ_ASSERT(owned->SafeElementAt(idx) != child, "Already in place!");
 
-    nsTArray<RefPtr<Accessible> >::index_type idx = children->IndexOf(child);
-    if (idx < arrayIdx) {
+    if (owned->IndexOf(child) < idx) {
       continue; // ignore second entry of same ID
     }
 
     // A new child is found, check for loops.
     if (child->Parent() != aOwner) {
       Accessible* parent = aOwner;
       while (parent && parent != child && !parent->IsDoc()) {
         parent = parent->Parent();
@@ -2124,33 +2129,34 @@ DocAccessible::DoARIAOwnsRelocation(Acce
       // A referred child cannot be a parent of the owner.
       if (parent == child) {
         continue;
       }
     }
 
     if (MoveChild(child, aOwner, insertIdx)) {
       child->SetRelocated(true);
-      children->InsertElementAt(arrayIdx, child);
-      arrayIdx++;
-      insertIdx = child->IndexInParent() + 1;
+      owned->InsertElementAt(idx, child);
+      idx++;
     }
   }
 
   // Put back children that are not seized anymore.
-  PutChildrenBack(children, arrayIdx);
-  if (children->Length() == 0) {
+  PutChildrenBack(owned, idx);
+  if (owned->Length() == 0) {
     mARIAOwnsHash.Remove(aOwner);
   }
 }
 
 void
 DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
                                uint32_t aStartIdx)
 {
+  MOZ_ASSERT(aStartIdx <= aChildren->Length(), "Wrong removal index");
+
   nsTArray<RefPtr<Accessible> > containers;
   for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
     Accessible* child = aChildren->ElementAt(idx);
     if (!child->IsInDocument()) {
       continue;
     }
 
     // Remove the child from the owner
@@ -2191,16 +2197,18 @@ DocAccessible::PutChildrenBack(nsTArray<
 }
 
 bool
 DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
                          int32_t aIdxInParent)
 {
   MOZ_ASSERT(aChild, "No child");
   MOZ_ASSERT(aChild->Parent(), "No parent");
+  MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
+             "Wrong insertion point for a moving child");
 
   Accessible* curParent = aChild->Parent();
 
 #ifdef A11Y_LOG
   logging::TreeInfo("move child", 0,
                     "old parent", curParent, "new parent", aNewParent,
                     "child", aChild, nullptr);
 #endif
--- a/accessible/interfaces/nsIAccessibleRole.idl
+++ b/accessible/interfaces/nsIAccessibleRole.idl
@@ -996,9 +996,17 @@ interface nsIAccessibleRole : nsISupport
   const unsigned long ROLE_FOOTNOTE = 171;
 
   /**
    * A complete or self-contained composition in a document, page, application,
    * or site and that is, in principle, independently distributable or reusable,
    * e.g. in syndication.
    */
   const unsigned long ROLE_ARTICLE = 172;
+
+  /**
+   * A perceivable section containing content that is relevant to a specific,
+   * author-specified purpose and sufficiently important that users will likely
+   * want to be able to navigate to the section easily and to have it listed in
+   * a summary of the page.
+   */
+  const unsigned long ROLE_REGION = 173;
 };
--- a/accessible/interfaces/nsIAccessibleStates.idl
+++ b/accessible/interfaces/nsIAccessibleStates.idl
@@ -67,10 +67,11 @@ interface nsIAccessibleStates : nsISuppo
   const unsigned long  EXT_STATE_SINGLE_LINE             = 0x00000200;  // This text object can only contain 1 line of text    
   const unsigned long  EXT_STATE_TRANSIENT               = 0x00000400;  // 
   const unsigned long  EXT_STATE_VERTICAL                = 0x00000800;  // Especially used for sliders and scrollbars  
   const unsigned long  EXT_STATE_STALE                   = 0x00001000;  // Object not dead, but not up-to-date either
   const unsigned long  EXT_STATE_ENABLED                 = 0x00002000;  // A widget that is not unavailable
   const unsigned long  EXT_STATE_SENSITIVE               = 0x00004000;  // Same as ENABLED for now
   const unsigned long  EXT_STATE_EXPANDABLE              = 0x00008000;  // If COLLAPSED or EXPANDED
   const unsigned long  EXT_STATE_PINNED                  = 0x00010000;  // Indicates object is pinned.
+  const unsigned long  EXT_STATE_CURRENT                 = 0x00020000;  // Indicates object is the current item in its container
 };
 
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/IAccessible32.manifest
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="Mozilla.Firefox.xul" version="1.0.0.0" />
+  <file name="xul.dll" />
+  <comInterfaceExternalProxyStub
+      iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+      proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
+      name="IAccessible"
+      tlbid="{1EA4DBF0-3C3B-11CF-810C-00AA00389B71}"
+  />
+</assembly>
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/IAccessible64.manifest
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="Mozilla.Firefox.xul" version="1.0.0.0" />
+  <file name="xul.dll" />
+  <comInterfaceExternalProxyStub
+      iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+      proxyStubClsid32="{03022430-ABC4-11D0-BDE2-00AA001A1953}"
+      name="IAccessible"
+  />
+</assembly>
--- a/accessible/ipc/win/PlatformChild.cpp
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/a11y/Compatibility.h"
 #include "mozilla/a11y/PlatformChild.h"
 #include "mozilla/mscom/EnsureMTA.h"
 #include "mozilla/mscom/InterceptorLog.h"
 
 #include "Accessible2.h"
 #include "Accessible2_2.h"
 #include "AccessibleHypertext2.h"
 #include "AccessibleTableCell.h"
@@ -43,16 +44,24 @@ static const mozilla::mscom::ArrayData s
 // we intend to instantiate them. Therefore RegisterProxy() must be called
 // via EnsureMTA.
 PlatformChild::PlatformChild()
   : mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
         mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
   , mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
   , mSdnTypelib(mozilla::mscom::RegisterTypelib(L"AccessibleMarshal.dll"))
 {
+  WORD actCtxResourceId = Compatibility::GetActCtxResourceId();
+
+  mozilla::mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> tmpActCtxMTA;
+  mozilla::mscom::EnsureMTA([actCtxResourceId, &tmpActCtxMTA]() -> void {
+    tmpActCtxMTA.reset(new mozilla::mscom::ActivationContextRegion(actCtxResourceId));
+  });
+  mActCtxMTA = Move(tmpActCtxMTA);
+
   mozilla::mscom::InterceptorLog::Init();
   mozilla::mscom::RegisterArrayData(sPlatformChildArrayData);
 
 
   UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
   mozilla::mscom::EnsureMTA([&customProxy]() -> void {
     customProxy = Move(mozilla::mscom::RegisterProxy());
   });
--- a/accessible/ipc/win/PlatformChild.h
+++ b/accessible/ipc/win/PlatformChild.h
@@ -2,32 +2,35 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_PlatformChild_h
 #define mozilla_a11y_PlatformChild_h
 
+#include "mozilla/mscom/ActivationContext.h"
+#include "mozilla/mscom/Ptr.h"
 #include "mozilla/mscom/Registration.h"
 
 namespace mozilla {
 namespace a11y {
 
 class PlatformChild
 {
 public:
   PlatformChild();
 
   PlatformChild(PlatformChild&) = delete;
   PlatformChild(PlatformChild&&) = delete;
   PlatformChild& operator=(PlatformChild&) = delete;
   PlatformChild& operator=(PlatformChild&&) = delete;
 
 private:
+  mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> mActCtxMTA;
   UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
   UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
   UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
 };
 
 } // namespace mozilla
--- a/accessible/ipc/win/handler/AccessibleHandler.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -217,27 +217,22 @@ AccessibleHandler::ReadHandlerPayload(IS
   if (!deserializer.Read(&mCachedData, &IA2Payload_Decode)) {
     return E_FAIL;
   }
 
   if (!mCachedData.mGeckoBackChannel) {
     return S_OK;
   }
 
-  long pid = static_cast<long>(::GetCurrentProcessId());
-
-  RefPtr<IHandlerControl> ctl;
-  HRESULT hr = gControlFactory.CreateInstance(nullptr, IID_IHandlerControl,
-                                              getter_AddRefs(ctl));
-  if (SUCCEEDED(hr)) {
-    hr = mCachedData.mGeckoBackChannel->put_HandlerControl(pid, ctl);
-    MOZ_ASSERT(SUCCEEDED(hr));
+  RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+  if (!ctl) {
+    return E_OUTOFMEMORY;
   }
 
-  return hr;
+  return ctl->Register(WrapNotNull(mCachedData.mGeckoBackChannel));
 }
 
 REFIID
 AccessibleHandler::MarshalAs(REFIID aIid)
 {
   static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
                 "You have modified NEWEST_IA2_IID. This code needs updating.");
   if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
--- a/accessible/ipc/win/handler/AccessibleHandlerControl.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp
@@ -127,17 +127,18 @@ AccessibleHandlerControl::Create(Accessi
   }
 
   RefPtr<AccessibleHandlerControl> ctl(new AccessibleHandlerControl());
   ctl.forget(aOutObject);
   return S_OK;
 }
 
 AccessibleHandlerControl::AccessibleHandlerControl()
-  : mCacheGen(0)
+  : mIsRegistered(false)
+  , mCacheGen(0)
   , mIA2Proxy(mscom::RegisterProxy(L"ia2marshal.dll"))
   , mHandlerProxy(mscom::RegisterProxy())
 {
   MOZ_ASSERT(mIA2Proxy);
 }
 
 IMPL_IUNKNOWN1(AccessibleHandlerControl, IHandlerControl)
 
@@ -184,10 +185,24 @@ AccessibleHandlerControl::GetHandlerType
   if (!mHandlerProxy) {
     return E_UNEXPECTED;
   }
 
   return mHandlerProxy->GetTypeInfoForGuid(CLSID_AccessibleHandler,
                                            aOutTypeInfo);
 }
 
+HRESULT
+AccessibleHandlerControl::Register(NotNull<IGeckoBackChannel*> aGecko)
+{
+  if (mIsRegistered) {
+    return S_OK;
+  }
+
+  long pid = static_cast<long>(::GetCurrentProcessId());
+  HRESULT hr = aGecko->put_HandlerControl(pid, this);
+  mIsRegistered = SUCCEEDED(hr);
+  MOZ_ASSERT(mIsRegistered);
+  return hr;
+}
+
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/ipc/win/handler/AccessibleHandlerControl.h
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.h
@@ -66,20 +66,23 @@ public:
     return mCacheGen;
   }
 
   HRESULT GetNewText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewText);
   HRESULT GetOldText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldText);
 
   HRESULT GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo);
 
+  HRESULT Register(NotNull<IGeckoBackChannel*> aGecko);
+
 private:
   AccessibleHandlerControl();
   ~AccessibleHandlerControl() = default;
 
+  bool mIsRegistered;
   uint32_t mCacheGen;
   detail::TextChange mTextChange;
   UniquePtr<mscom::RegisteredProxy> mIA2Proxy;
   UniquePtr<mscom::RegisteredProxy> mHandlerProxy;
 };
 
 extern mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory;
 
--- a/accessible/ipc/win/moz.build
+++ b/accessible/ipc/win/moz.build
@@ -8,19 +8,31 @@ if CONFIG['COMPILE_ENVIRONMENT'] and CON
     DIRS += [
         'handler',
         'typelib',
     ]
 
 # With --disable-accessibility, we need to compile PDocAccessible.ipdl (which
 # also depends on COMPtrTypes.h), but not the C++.
 IPDL_SOURCES += ['PDocAccessible.ipdl']
-EXPORTS.mozilla.a11y += ['COMPtrTypes.h']
+
+EXPORTS.mozilla.a11y += [
+    'COMPtrTypes.h',
+]
 
 if CONFIG['ACCESSIBILITY']:
+    if not CONFIG['HAVE_64BIT_BUILD']:
+        EXPORTS += [
+            'IAccessible32.manifest',
+        ]
+
+    EXPORTS += [
+        'IAccessible64.manifest',
+    ]
+
     EXPORTS.mozilla.a11y += [
         'DocAccessibleChild.h',
         'HandlerProvider.h',
         'PlatformChild.h',
         'ProxyAccessible.h'
     ]
 
     SOURCES += [
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -589,17 +589,17 @@ var Output = {
 
     for (let androidEvent of aDetails) {
       androidEvent.type = 'Accessibility:Event';
       if (androidEvent.bounds) {
         androidEvent.bounds = AccessFu.adjustContentBounds(
           androidEvent.bounds, aBrowser);
       }
 
-      switch(androidEvent.eventType) {
+      switch (androidEvent.eventType) {
         case ANDROID_VIEW_TEXT_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustText(
             androidEvent.text);
           break;
         case ANDROID_VIEW_TEXT_SELECTION_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustSelection(
             androidEvent.brailleOutput);
           break;
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -319,17 +319,17 @@ this.ContentControl.prototype = {
     return true;
   },
 
   handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
     // XXX: Add sendToChild. Right now this is only used in Android, so no need.
     let direction = aMessage.json.direction;
     let granularity;
 
-    switch(aMessage.json.granularity) {
+    switch (aMessage.json.granularity) {
       case MOVEMENT_GRANULARITY_CHARACTER:
         granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
         break;
       case MOVEMENT_GRANULARITY_WORD:
         granularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
         break;
       default:
         return;
--- a/accessible/jsat/OutputGenerator.jsm
+++ b/accessible/jsat/OutputGenerator.jsm
@@ -209,17 +209,17 @@ var OutputGenerator = {
    * Adds math roles to the output, for a MathML accessible.
    * @param {Array} aOutput Output array.
    * @param {nsIAccessible} aAccessible current accessible object.
    * @param {String} aRoleStr aAccessible's role string.
    */
   _addMathRoles: function _addMathRoles(aOutput, aAccessible, aRoleStr) {
     // First, determine the actual role to use (e.g. mathmlfraction).
     let roleStr = aRoleStr;
-    switch(aAccessible.role) {
+    switch (aAccessible.role) {
       case Roles.MATHML_CELL:
       case Roles.MATHML_ENCLOSED:
       case Roles.MATHML_LABELED_ROW:
       case Roles.MATHML_ROOT:
       case Roles.MATHML_SQUARE_ROOT:
       case Roles.MATHML_TABLE:
       case Roles.MATHML_TABLE_ROW:
         // Use the default role string.
@@ -253,22 +253,22 @@ var OutputGenerator = {
         roleStr = null;
         break;
     }
 
     // Get the math role based on the position in the parent accessible
     // (e.g. numerator for the first child of a mathmlfraction).
     let mathRole = Utils.getMathRole(aAccessible);
     if (mathRole) {
-      aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift']
-        ({string: this._getOutputName(mathRole)});
+      aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift']({
+        string: this._getOutputName(mathRole)});
     }
     if (roleStr) {
-      aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift']
-        ({string: this._getOutputName(roleStr)});
+      aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift']({
+        string: this._getOutputName(roleStr)});
     }
   },
 
   /**
    * Adds MathML menclose notations to the output.
    * @param {Array} aOutput Output array.
    * @param {nsIAccessible} aAccessible current accessible object.
    */
@@ -679,24 +679,24 @@ this.UtteranceGenerator = {  // jshint i
 
       this._addName(utterance, aAccessible, aFlags);
       this._addLandmark(utterance, aAccessible);
 
       return utterance;
     },
 
     list: function list(aAccessible, aRoleStr, aState, aFlags) {
-      return this._getListUtterance
-        (aAccessible, aRoleStr, aFlags, aAccessible.childCount);
+      return this._getListUtterance(aAccessible, aRoleStr, aFlags,
+        aAccessible.childCount);
     },
 
     definitionlist:
       function definitionlist(aAccessible, aRoleStr, aState, aFlags) {
-        return this._getListUtterance
-          (aAccessible, aRoleStr, aFlags, aAccessible.childCount / 2);
+        return this._getListUtterance(aAccessible, aRoleStr, aFlags,
+          aAccessible.childCount / 2);
       },
 
     application: function application(aAccessible, aRoleStr, aState, aFlags) {
       // Don't utter location of applications, it gets tiring.
       if (aAccessible.name != aAccessible.DOMNode.location) {
         return this.objectOutputFunctions.defaultFunc.apply(this,
           [aAccessible, aRoleStr, aState, aFlags]);
       }
--- a/accessible/jsat/PointerAdapter.jsm
+++ b/accessible/jsat/PointerAdapter.jsm
@@ -34,57 +34,57 @@ var PointerRelay = { // jshint ignore:li
    * value of |false| are cancelled and do not propogate to content.
    */
   get _eventsOfInterest() {
     delete this._eventsOfInterest;
 
     switch (Utils.widgetToolkit) {
       case 'android':
         this._eventsOfInterest = {
-          'touchstart' : true,
-          'touchmove' : true,
-          'touchend' : true };
+          'touchstart': true,
+          'touchmove': true,
+          'touchend': true };
         break;
 
       case 'gonk':
         this._eventsOfInterest = {
-          'touchstart' : true,
-          'touchmove' : true,
-          'touchend' : true,
-          'mousedown' : false,
-          'mousemove' : false,
+          'touchstart': true,
+          'touchmove': true,
+          'touchend': true,
+          'mousedown': false,
+          'mousemove': false,
           'mouseup': false,
           'click': false };
         break;
 
       default:
         // Desktop.
         this._eventsOfInterest = {
-          'mousemove' : true,
-          'mousedown' : true,
+          'mousemove': true,
+          'mousedown': true,
           'mouseup': true,
           'click': false
         };
         if ('ontouchstart' in Utils.win) {
           for (let eventType of ['touchstart', 'touchmove', 'touchend']) {
             this._eventsOfInterest[eventType] = true;
           }
         }
         break;
     }
 
     return this._eventsOfInterest;
   },
 
   _eventMap: {
-    'touchstart' : 'pointerdown',
-    'mousedown' : 'pointerdown',
-    'touchmove' : 'pointermove',
-    'mousemove' : 'pointermove',
-    'touchend' : 'pointerup',
+    'touchstart': 'pointerdown',
+    'mousedown': 'pointerdown',
+    'touchmove': 'pointermove',
+    'mousemove': 'pointermove',
+    'touchend': 'pointerup',
     'mouseup': 'pointerup'
   },
 
   start: function PointerRelay_start(aOnPointerEvent) {
     Logger.debug('PointerRelay.start');
     this.onPointerEvent = aOnPointerEvent;
     for (let eventType in this._eventsOfInterest) {
       Utils.win.addEventListener(eventType, this, true, true);
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -488,17 +488,17 @@ this.Utils = { // jshint ignore:line
     return parent.role === Roles.LISTITEM && parent.childCount > 1 &&
       aStaticText.indexInParent === 0;
   },
 
   dispatchChromeEvent: function dispatchChromeEvent(aType, aDetails) {
     let details = {
       type: aType,
       details: JSON.stringify(
-        typeof aDetails === 'string' ? { eventType : aDetails } : aDetails)
+        typeof aDetails === 'string' ? { eventType: aDetails } : aDetails)
     };
     let window = this.win;
     let shell = window.shell || window.content.shell;
     if (shell) {
       // On B2G device.
       shell.sendChromeEvent(details);
     } else {
       // Dispatch custom event to have support for desktop and screen reader
@@ -632,17 +632,17 @@ this.Logger = { // jshint ignore:line
   },
 
   accessibleToString: function accessibleToString(aAccessible) {
     if (!aAccessible) {
       return '[ null ]';
     }
 
     try {
-      return'[ ' + Utils.AccService.getStringRole(aAccessible.role) +
+      return '[ ' + Utils.AccService.getStringRole(aAccessible.role) +
         ' | ' + aAccessible.name + ' ]';
     } catch (x) {
       return '[ defunct ]';
     }
   },
 
   eventToString: function eventToString(aEvent) {
     let str = Utils.AccService.getStringEventType(aEvent.eventType);
@@ -1063,11 +1063,11 @@ PrefCache.prototype = {
       try {
         this.callback(this.name, this.value, false);
       } catch (x) {
         Logger.logException(x);
       }
     }
   },
 
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsISupportsWeakReference])
 };
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -793,17 +793,17 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
       return @"AXApplicationLog";
     if (roleAtom == nsGkAtoms::marquee)
       return @"AXApplicationMarquee";
     if (roleAtom == nsGkAtoms::math)
       return @"AXDocumentMath";
     if (roleAtom == nsGkAtoms::note_)
       return @"AXDocumentNote";
     if (roleAtom == nsGkAtoms::region)
-      return @"AXDocumentRegion";
+      return mRole == roles::REGION ? @"AXLandmarkRegion" : nil;
     if (roleAtom == nsGkAtoms::status)
       return @"AXApplicationStatus";
     if (roleAtom == nsGkAtoms::tabpanel)
       return @"AXTabPanel";
     if (roleAtom == nsGkAtoms::timer)
       return @"AXApplicationTimer";
     if (roleAtom == nsGkAtoms::tooltip)
       return @"AXUserInterfaceTooltip";
@@ -947,17 +947,16 @@ static const RoleDescrMap sRoleDescrMap[
   { @"AXApplicationStatus", NS_LITERAL_STRING("status") },
   { @"AXApplicationTimer", NS_LITERAL_STRING("timer") },
   { @"AXContentSeparator", NS_LITERAL_STRING("separator") },
   { @"AXDefinition", NS_LITERAL_STRING("definition") },
   { @"AXDocument", NS_LITERAL_STRING("document") },
   { @"AXDocumentArticle", NS_LITERAL_STRING("article") },
   { @"AXDocumentMath", NS_LITERAL_STRING("math") },
   { @"AXDocumentNote", NS_LITERAL_STRING("note") },
-  { @"AXDocumentRegion", NS_LITERAL_STRING("region") },
   { @"AXLandmarkApplication", NS_LITERAL_STRING("application") },
   { @"AXLandmarkBanner", NS_LITERAL_STRING("banner") },
   { @"AXLandmarkComplementary", NS_LITERAL_STRING("complementary") },
   { @"AXLandmarkContentInfo", NS_LITERAL_STRING("content") },
   { @"AXLandmarkMain", NS_LITERAL_STRING("main") },
   { @"AXLandmarkNavigation", NS_LITERAL_STRING("navigation") },
   { @"AXLandmarkRegion", NS_LITERAL_STRING("region") },
   { @"AXLandmarkSearch", NS_LITERAL_STRING("search") },
@@ -976,16 +975,19 @@ struct RoleDescrComparator
   }
 };
 
 - (NSString*)roleDescription
 {
   if (mRole == roles::DOCUMENT)
     return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
 
+  if (mRole == roles::FIGURE)
+    return utils::LocalizedString(NS_LITERAL_STRING("figure"));
+
   if (mRole == roles::HEADING)
     return utils::LocalizedString(NS_LITERAL_STRING("heading"));
 
   NSString* subrole = [self subrole];
 
   if (subrole) {
     size_t idx = 0;
     if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
--- a/accessible/tests/mochitest/aom/test_general.html
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -92,16 +92,16 @@
         'margin-left', 'text-align', 'text-indent', 'margin-right',
         'tag', 'margin-top', 'margin-bottom', 'display',
         'explicit-name'
       ];
     }
 
     is(anode.attributes.length, attrs.length, 'correct number of attributes');
     for (var i = 0; i < attrs.length; i++) {
-      is(anode.attributes[i], attrs[i],
-         `${attrs[i]} attribute is expected at ${i}th index`);
+      ok(attrs.indexOf(anode.attributes[i]) >= 0,
+         `${anode.attributes[i]} attribute is expected and found`);
     }
 
     finish();
   }
   </script>
 </head>
--- a/accessible/tests/mochitest/attributes.js
+++ b/accessible/tests/mochitest/attributes.js
@@ -91,17 +91,17 @@ function testGroupAttrs(aAccOrElmOrID, a
     };
     testAttrs(aAccOrElmOrID, attrs, true);
   }
 
   if (aLevel) {
     is(levelObj.value, aLevel,
        "Wrong group level for " + prettyName(aAccOrElmOrID));
 
-    var attrs = { "level" : String(aLevel) };
+    var attrs = { "level": String(aLevel) };
     testAttrs(aAccOrElmOrID, attrs, true);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Text attributes.
 
 /**
@@ -154,17 +154,17 @@ function testTextAttrs(aID, aOffset, aAt
 
   for (var name in aDefAttrs) {
     if (!(name in expectedAttrs))
       expectedAttrs[name] = aDefAttrs[name];
   }
 
   attrs = getTextAttributes(aID, accessible, true, aOffset,
                             startOffset, endOffset);
-  
+
   if (!attrs)
     return;
 
   compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs);
 }
 
 /**
  * Test default text attributes.
@@ -176,28 +176,28 @@ function testTextAttrs(aID, aOffset, aAt
  * @param aSkipUnexpectedAttrs  [in] points the function doesn't fail if
  *                              unexpected attribute is encountered
  */
 function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs)
 {
   var accessible = getAccessible(aID, [nsIAccessibleText]);
   if (!accessible)
     return;
-  
+
   var defAttrs = null;
-  try{
+  try {
     defAttrs = accessible.defaultTextAttributes;
   } catch (e) {
   }
-  
+
   if (!defAttrs) {
     ok(false, "Can't get default text attributes for " + aID);
     return;
   }
-  
+
   var errorMsg = ". Getting default text attributes for " + aID;
   compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
 }
 
 /**
  * Test text attributes for wrong offset.
  */
 function testTextAttrsWrongOffset(aID, aOffset)
@@ -313,22 +313,22 @@ function testAttrsInternal(aAccOrElmOrID
   var accessible = getAccessible(aAccOrElmOrID);
   if (!accessible)
     return;
 
   var attrs = null;
   try {
     attrs = accessible.attributes;
   } catch (e) { }
-  
+
   if (!attrs) {
     ok(false, "Can't get object attributes for " + prettyName(aAccOrElmOrID));
     return;
   }
-  
+
   var errorMsg = " for " + prettyName(aAccOrElmOrID);
   compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs, aAbsentAttrs);
 }
 
 function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs,
                       aAbsentAttrs)
 {
   // Check if all obtained attributes are expected and have expected value.
@@ -351,17 +351,17 @@ function compareAttrs(aErrorMsg, aAttrs,
     }
   }
 
   // Check if all expected attributes are presented.
   for (var name in aExpectedAttrs) {
     var value = "";
     try {
       value = aAttrs.getStringProperty(name);
-    } catch(e) { }
+    } catch (e) { }
 
     if (!value)
       ok(false,
          "There is no expected attribute '" + name + "' " + aErrorMsg);
   }
 
   // Check if all unexpected attributes are absent.
   if (aAbsentAttrs) {
--- a/accessible/tests/mochitest/attributes/test_dpub_aria_xml-roles.html
+++ b/accessible/tests/mochitest/attributes/test_dpub_aria_xml-roles.html
@@ -57,17 +57,17 @@
         'doc-prologue',
         'doc-pullquote',
         'doc-qna',
         'doc-subtitle',
         'doc-tip',
         'doc-toc'
       ];
       for (let attr of dpub_attrs) {
-        testAttrs(attr, {"xml-roles" : attr}, true);
+        testAttrs(attr, {"xml-roles": attr}, true);
       }
       SimpleTest.finish();
     }
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -17,115 +17,115 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="../common.js"></script>
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // aria
-      testAttrs("atomic", {"atomic" : "true", "container-atomic" : "true"}, true);
-      testAttrs(getNode("atomic").firstChild, {"container-atomic" : "true"}, true);
-      testAbsentAttrs("atomic_false", {"atomic" : "false", "container-atomic" : "false"});
-      testAbsentAttrs(getNode("atomic_false").firstChild, {"container-atomic" : "false"});
+      testAttrs("atomic", {"atomic": "true", "container-atomic": "true"}, true);
+      testAttrs(getNode("atomic").firstChild, {"container-atomic": "true"}, true);
+      testAbsentAttrs("atomic_false", {"atomic": "false", "container-atomic": "false"});
+      testAbsentAttrs(getNode("atomic_false").firstChild, {"container-atomic": "false"});
 
-      testAttrs("autocomplete", {"autocomplete" : "true"}, true);
-      testAttrs("checkbox", {"checkable" : "true"}, true);
-      testAttrs("checkedCheckbox", {"checkable" : "true"}, true);
-      testAbsentAttrs("checkedMenuitem", {"checkable" : "true"}, true);
-      testAttrs("checkedMenuitemCheckbox", {"checkable" : "true"}, true);
-      testAttrs("checkedMenuitemRadio", {"checkable" : "true"}, true);
-      testAttrs("checkedOption", {"checkable" : "true"}, true);
-      testAttrs("checkedRadio", {"checkable" : "true"}, true);
-      testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
-      testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
-      testAttrs("grabbed", {"grabbed" : "true"}, true);
+      testAttrs("autocomplete", {"autocomplete": "true"}, true);
+      testAttrs("checkbox", {"checkable": "true"}, true);
+      testAttrs("checkedCheckbox", {"checkable": "true"}, true);
+      testAbsentAttrs("checkedMenuitem", {"checkable": "true"}, true);
+      testAttrs("checkedMenuitemCheckbox", {"checkable": "true"}, true);
+      testAttrs("checkedMenuitemRadio", {"checkable": "true"}, true);
+      testAttrs("checkedOption", {"checkable": "true"}, true);
+      testAttrs("checkedRadio", {"checkable": "true"}, true);
+      testAttrs("checkedTreeitem", {"checkable": "true"}, true);
+      testAttrs("dropeffect", {"dropeffect": "copy"}, true);
+      testAttrs("grabbed", {"grabbed": "true"}, true);
       testAttrs("haspopupTrue", { "haspopup": "true" }, true);
       testAbsentAttrs("haspopupFalse", { "haspopup": "false" });
       testAbsentAttrs("haspopupEmpty", { "haspopup": "" });
-      testAttrs("hidden", {"hidden" : "true"}, true);
+      testAttrs("hidden", {"hidden": "true"}, true);
       testAbsentAttrs("hidden_false", { "hidden": "false" });
-      testAbsentAttrs("modal", {"modal" : "true"});
-      testAttrs("sortAscending", {"sort" : "ascending"}, true);
-      testAttrs("sortDescending", {"sort" : "descending"}, true);
-      testAttrs("sortNone", {"sort" : "none"}, true);
-      testAttrs("sortOther", {"sort" : "other"}, true);
-      testAttrs("roledescr", {"roledescription" : "spreadshit"}, true);
-      testAttrs("currentPage", {"current" : "page"}, true);
+      testAbsentAttrs("modal", {"modal": "true"});
+      testAttrs("sortAscending", {"sort": "ascending"}, true);
+      testAttrs("sortDescending", {"sort": "descending"}, true);
+      testAttrs("sortNone", {"sort": "none"}, true);
+      testAttrs("sortOther", {"sort": "other"}, true);
+      testAttrs("roledescr", {"roledescription": "spreadshit"}, true);
+      testAttrs("currentPage", {"current": "page"}, true);
 
       // inherited attributes by subdocuments
       var subdoc = getAccessible("iframe").firstChild;
-      testAttrs(subdoc, {"busy" : "true"}, true);
+      testAttrs(subdoc, {"busy": "true"}, true);
 
       // live object attribute
 
       // HTML
-      testAttrs("output", {"live" : "polite"}, true);
+      testAttrs("output", {"live": "polite"}, true);
 
       // ARIA
-      testAttrs("live", {"live" : "polite"}, true);
-      testAttrs("live2", {"live" : "polite"}, true);
-      testAbsentAttrs("live3", {"live" : ""});
-      testAttrs("log", {"live" : "polite"}, true);
-      testAttrs("logAssertive", {"live" : "assertive"}, true);
-      testAttrs("marquee", {"live" : "off"}, true);
-      testAttrs("status", {"live" : "polite"}, true);
-      testAttrs("timer", {"live" : "off"}, true);
-      testAbsentAttrs("tablist", {"live" : "polite"});
+      testAttrs("live", {"live": "polite"}, true);
+      testAttrs("live2", {"live": "polite"}, true);
+      testAbsentAttrs("live3", {"live": ""});
+      testAttrs("log", {"live": "polite"}, true);
+      testAttrs("logAssertive", {"live": "assertive"}, true);
+      testAttrs("marquee", {"live": "off"}, true);
+      testAttrs("status", {"live": "polite"}, true);
+      testAttrs("timer", {"live": "off"}, true);
+      testAbsentAttrs("tablist", {"live": "polite"});
 
       // container-live object attribute
-      testAttrs("liveChild", {"container-live" : "polite"}, true);
-      testAttrs("live2Child", {"container-live" : "polite"}, true);
-      testAttrs("logChild", {"container-live" : "polite"}, true);
-      testAttrs("logAssertiveChild", {"container-live" : "assertive"}, true);
-      testAttrs("marqueeChild", {"container-live" : "off"}, true);
-      testAttrs("statusChild", {"container-live" : "polite"}, true);
-      testAttrs("timerChild", {"container-live" : "off"}, true);
-      testAbsentAttrs("tablistChild", {"container-live" : "polite"});
+      testAttrs("liveChild", {"container-live": "polite"}, true);
+      testAttrs("live2Child", {"container-live": "polite"}, true);
+      testAttrs("logChild", {"container-live": "polite"}, true);
+      testAttrs("logAssertiveChild", {"container-live": "assertive"}, true);
+      testAttrs("marqueeChild", {"container-live": "off"}, true);
+      testAttrs("statusChild", {"container-live": "polite"}, true);
+      testAttrs("timerChild", {"container-live": "off"}, true);
+      testAbsentAttrs("tablistChild", {"container-live": "polite"});
 
       // container-live-role object attribute
-      testAttrs("log", {"container-live-role" : "log"}, true);
-      testAttrs("logAssertive", {"container-live-role" : "log"}, true);
-      testAttrs("marquee", {"container-live-role" : "marquee"}, true);
-      testAttrs("status", {"container-live-role" : "status"}, true);
-      testAttrs("timer", {"container-live-role" : "timer"}, true);
-      testAttrs("logChild", {"container-live-role" : "log"}, true);
-      testAttrs("logAssertive", {"container-live-role" : "log"}, true);
-      testAttrs("logAssertiveChild", {"container-live-role" : "log"}, true);
-      testAttrs("marqueeChild", {"container-live-role" : "marquee"}, true);
-      testAttrs("statusChild", {"container-live-role" : "status"}, true);
-      testAttrs("timerChild", {"container-live-role" : "timer"}, true);
-      testAbsentAttrs("tablistChild", {"container-live-role" : "tablist"});
+      testAttrs("log", {"container-live-role": "log"}, true);
+      testAttrs("logAssertive", {"container-live-role": "log"}, true);
+      testAttrs("marquee", {"container-live-role": "marquee"}, true);
+      testAttrs("status", {"container-live-role": "status"}, true);
+      testAttrs("timer", {"container-live-role": "timer"}, true);
+      testAttrs("logChild", {"container-live-role": "log"}, true);
+      testAttrs("logAssertive", {"container-live-role": "log"}, true);
+      testAttrs("logAssertiveChild", {"container-live-role": "log"}, true);
+      testAttrs("marqueeChild", {"container-live-role": "marquee"}, true);
+      testAttrs("statusChild", {"container-live-role": "status"}, true);
+      testAttrs("timerChild", {"container-live-role": "timer"}, true);
+      testAbsentAttrs("tablistChild", {"container-live-role": "tablist"});
 
       // absent aria-label and aria-labelledby object attribute
-      testAbsentAttrs("label", {"label" : "foo"});
-      testAbsentAttrs("labelledby", {"labelledby" : "label"});
+      testAbsentAttrs("label", {"label": "foo"});
+      testAbsentAttrs("labelledby", {"labelledby": "label"});
 
       // container that has no default live attribute
-      testAttrs("liveGroup", {"live" : "polite"}, true);
-      testAttrs("liveGroupChild", {"container-live" : "polite"}, true);
-      testAttrs("liveGroup", {"container-live-role" : "group"}, true);
-      testAttrs("liveGroupChild", {"container-live-role" : "group"}, true);
+      testAttrs("liveGroup", {"live": "polite"}, true);
+      testAttrs("liveGroupChild", {"container-live": "polite"}, true);
+      testAttrs("liveGroup", {"container-live-role": "group"}, true);
+      testAttrs("liveGroupChild", {"container-live-role": "group"}, true);
 
       // text input type
       testAbsentAttrs("button", { "text-input-type": "button"});
       testAbsentAttrs("checkbox", { "text-input-type": "checkbox"});
       testAbsentAttrs("radio", { "text-input-type": "radio"});
-      testAttrs("email", {"text-input-type" : "email"}, true);
-      testAttrs("search", {"text-input-type" : "search"}, true);
-      testAttrs("tel", {"text-input-type" : "tel"}, true);
-      testAttrs("url", {"text-input-type" : "url"}, true);
+      testAttrs("email", {"text-input-type": "email"}, true);
+      testAttrs("search", {"text-input-type": "search"}, true);
+      testAttrs("tel", {"text-input-type": "tel"}, true);
+      testAttrs("url", {"text-input-type": "url"}, true);
 
       // ARIA
-      testAttrs("searchbox", {"text-input-type" : "search"}, true);
+      testAttrs("searchbox", {"text-input-type": "search"}, true);
 
       // html
-      testAttrs("radio", {"checkable" : "true"}, true); 
-      testAttrs("checkbox", {"checkable" : "true"}, true); 
-      testAttrs("draggable", {"draggable" : "true"}, true);
+      testAttrs("radio", {"checkable": "true"}, true);
+      testAttrs("checkbox", {"checkable": "true"}, true);
+      testAttrs("draggable", {"draggable": "true"}, true);
       testAttrs("th1", { "abbr": "SS#" }, true);
       testAttrs("th2", { "abbr": "SS#" }, true);
       testAttrs("th2", { "axis": "social" }, true);
 
       // don't barf on an empty abbr element.
       testAbsentAttrs("th3", { "abbr": "" }, true);
 
       // application accessible
@@ -137,17 +137,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         }
         testAttrs(getApplicationAccessible(), attrs, false);
       }
 
       // no object attributes
       testAbsentAttrs(getAccessible("listitem").firstChild, { "tag": "" });
 
       // experimental aria
-      testAttrs("experimental", {"blah" : "true"}, true);
+      testAttrs("experimental", {"blah": "true"}, true);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/attributes/test_obj_group.html
+++ b/accessible/tests/mochitest/attributes/test_obj_group.html
@@ -144,36 +144,36 @@
       testGroupAttrs("tree3_ti1b", 2, 2, 2);
       testGroupAttrs("tree3_ti2", 2, 2, 1);
       testGroupAttrs("tree3_ti2a", 1, 2, 2);
       testGroupAttrs("tree3_ti2b", 2, 2, 2);
 
       //////////////////////////////////////////////////////////////////////////
       // ARIA grid
       testGroupAttrs("grid_row1", 1, 2);
-      testAbsentAttrs("grid_cell1", {"posinset":"", "setsize":""});
-      testAbsentAttrs("grid_cell2", {"posinset":"", "setsize":""});
+      testAbsentAttrs("grid_cell1", {"posinset": "", "setsize": ""});
+      testAbsentAttrs("grid_cell2", {"posinset": "", "setsize": ""});
 
       testGroupAttrs("grid_row2", 2, 2);
-      testAbsentAttrs("grid_cell3", {"posinset":"", "setsize":""});
-      testAbsentAttrs("grid_cell4", {"posinset":"", "setsize":""});
+      testAbsentAttrs("grid_cell3", {"posinset": "", "setsize": ""});
+      testAbsentAttrs("grid_cell4", {"posinset": "", "setsize": ""});
 
       //////////////////////////////////////////////////////////////////////////
       // ARIA treegrid
       testGroupAttrs("treegrid_row1", 1, 2, 1);
-      testAbsentAttrs("treegrid_cell1", {"posinset":"", "setsize":""});
-      testAbsentAttrs("treegrid_cell2", {"posinset":"", "setsize":""});
+      testAbsentAttrs("treegrid_cell1", {"posinset": "", "setsize": ""});
+      testAbsentAttrs("treegrid_cell2", {"posinset": "", "setsize": ""});
 
       testGroupAttrs("treegrid_row2", 1, 1, 2);
-      testAbsentAttrs("treegrid_cell3", {"posinset":"", "setsize":""});
-      testAbsentAttrs("treegrid_cell4", {"posinset":"", "setsize":""});
+      testAbsentAttrs("treegrid_cell3", {"posinset": "", "setsize": ""});
+      testAbsentAttrs("treegrid_cell4", {"posinset": "", "setsize": ""});
 
       testGroupAttrs("treegrid_row3", 2, 2, 1);
-      testAbsentAttrs("treegrid_cell5", {"posinset":"", "setsize":""});
-      testAbsentAttrs("treegrid_cell6", {"posinset":"", "setsize":""});
+      testAbsentAttrs("treegrid_cell5", {"posinset": "", "setsize": ""});
+      testAbsentAttrs("treegrid_cell6", {"posinset": "", "setsize": ""});
 
       //////////////////////////////////////////////////////////////////////////
       // HTML headings
       testGroupAttrs("h1", 0, 0, 1);
       testGroupAttrs("h2", 0, 0, 2);
       testGroupAttrs("h3", 0, 0, 3);
       testGroupAttrs("h4", 0, 0, 4);
       testGroupAttrs("h5", 0, 0, 5);
--- a/accessible/tests/mochitest/attributes/test_tag.html
+++ b/accessible/tests/mochitest/attributes/test_tag.html
@@ -15,24 +15,24 @@
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
 
     function doTest()
     {
       // And some AT may look for this
-      testAttrs("nav", {"tag" : "nav"}, true);
-      testAttrs("header", {"tag" : "header"}, true);
-      testAttrs("footer", {"tag" : "footer"}, true);
-      testAttrs("article", {"tag" : "article"}, true);
-      testAttrs("aside", {"tag" : "aside"}, true);
-      testAttrs("section", {"tag" : "section"}, true);
-      testAttrs("main", {"tag" : "article"}, true);
-      testAttrs("form", {"tag" : "article"}, true);
+      testAttrs("nav", {"tag": "nav"}, true);
+      testAttrs("header", {"tag": "header"}, true);
+      testAttrs("footer", {"tag": "footer"}, true);
+      testAttrs("article", {"tag": "article"}, true);
+      testAttrs("aside", {"tag": "aside"}, true);
+      testAttrs("section", {"tag": "section"}, true);
+      testAttrs("main", {"tag": "article"}, true);
+      testAttrs("form", {"tag": "article"}, true);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/attributes/test_xml-roles.html
+++ b/accessible/tests/mochitest/attributes/test_xml-roles.html
@@ -15,86 +15,87 @@
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
 
     function doTest()
     {
       // Some AT may look for this
-      testAttrs("nav", {"xml-roles" : "navigation"}, true);
-      testAttrs("header", {"xml-roles" : "banner"}, true);
-      testAbsentAttrs("article_header", {"xml-roles" : "banner"});
-      testAbsentAttrs("section_header", {"xml-roles" : "banner"});
-      testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
-      testAbsentAttrs("article_footer", {"xml-roles" : "contentinfo"});
-      testAbsentAttrs("section_footer", {"xml-roles" : "contentinfo"});
-      testAttrs("aside", {"xml-roles" : "complementary"}, true);
-      testAttrs("section", {"xml-roles" : "region"}, true);
-      testAttrs("main", {"xml-roles" : "main"}, true); // // ARIA override
-      testAttrs("form", {"xml-roles" : "form"}, true);
-      testAttrs("feed", {"xml-roles" : "feed"}, true);
-      testAttrs("article", {"xml-roles" : "article"}, true);
-      testAttrs("main_element", {"xml-roles" : "main"}, true);
+      testAttrs("nav", {"xml-roles": "navigation"}, true);
+      testAttrs("header", {"xml-roles": "banner"}, true);
+      testAbsentAttrs("article_header", {"xml-roles": "banner"});
+      testAbsentAttrs("section_header", {"xml-roles": "banner"});
+      testAttrs("footer", {"xml-roles": "contentinfo"}, true);
+      testAbsentAttrs("article_footer", {"xml-roles": "contentinfo"});
+      testAbsentAttrs("section_footer", {"xml-roles": "contentinfo"});
+      testAttrs("aside", {"xml-roles": "complementary"}, true);
+      testAttrs("section", {"xml-roles": "region"}, true);
+      testAttrs("main", {"xml-roles": "main"}, true); // // ARIA override
+      testAttrs("form", {"xml-roles": "form"}, true);
+      testAttrs("feed", {"xml-roles": "feed"}, true);
+      testAttrs("article", {"xml-roles": "article"}, true);
+      testAttrs("main_element", {"xml-roles": "main"}, true);
+      testAttrs("figure", {"xml-roles": "figure"}, true);
 
-      testAttrs("search", {"xml-roles" : "searchbox"}, true);
+      testAttrs("search", {"xml-roles": "searchbox"}, true);
 
-      testAttrs("open-1", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-2", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-3", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-4", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-5", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-6", {"xml-roles" : "open-fence"}, true);
-      testAttrs("open-7", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-1", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-2", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-3", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-4", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-5", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-6", {"xml-roles": "open-fence"}, true);
+      testAttrs("open-7", {"xml-roles": "open-fence"}, true);
 
-      testAttrs("sep-1", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-2", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-3", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-4", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-5", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-6", {"xml-roles" : "separator"}, true);
-      testAttrs("sep-7", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-1", {"xml-roles": "separator"}, true);
+      testAttrs("sep-2", {"xml-roles": "separator"}, true);
+      testAttrs("sep-3", {"xml-roles": "separator"}, true);
+      testAttrs("sep-4", {"xml-roles": "separator"}, true);
+      testAttrs("sep-5", {"xml-roles": "separator"}, true);
+      testAttrs("sep-6", {"xml-roles": "separator"}, true);
+      testAttrs("sep-7", {"xml-roles": "separator"}, true);
 
-      testAttrs("close-1", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-2", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-3", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-4", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-5", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-6", {"xml-roles" : "close-fence"}, true);
-      testAttrs("close-7", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-1", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-2", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-3", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-4", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-5", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-6", {"xml-roles": "close-fence"}, true);
+      testAttrs("close-7", {"xml-roles": "close-fence"}, true);
 
-      testAttrs("num", {"xml-roles" : "numerator"}, true);
-      testAttrs("den", {"xml-roles" : "denominator"}, true);
+      testAttrs("num", {"xml-roles": "numerator"}, true);
+      testAttrs("den", {"xml-roles": "denominator"}, true);
 
-      testAttrs("sub-1", {"xml-roles" : "subscript"}, true);
-      testAttrs("sub-2", {"xml-roles" : "subscript"}, true);
-      testAttrs("sub-3", {"xml-roles" : "subscript"}, true);
-      testAttrs("sup-1", {"xml-roles" : "superscript"}, true);
-      testAttrs("sup-2", {"xml-roles" : "superscript"}, true);
-      testAttrs("sup-3", {"xml-roles" : "superscript"}, true);
-      testAttrs("sup-4", {"xml-roles" : "superscript"}, true);
-      testAttrs("presub-1", {"xml-roles" : "presubscript"}, true);
-      testAttrs("presub-2", {"xml-roles" : "presubscript"}, true);
-      testAttrs("presup-1", {"xml-roles" : "presuperscript"}, true);
+      testAttrs("sub-1", {"xml-roles": "subscript"}, true);
+      testAttrs("sub-2", {"xml-roles": "subscript"}, true);
+      testAttrs("sub-3", {"xml-roles": "subscript"}, true);
+      testAttrs("sup-1", {"xml-roles": "superscript"}, true);
+      testAttrs("sup-2", {"xml-roles": "superscript"}, true);
+      testAttrs("sup-3", {"xml-roles": "superscript"}, true);
+      testAttrs("sup-4", {"xml-roles": "superscript"}, true);
+      testAttrs("presub-1", {"xml-roles": "presubscript"}, true);
+      testAttrs("presub-2", {"xml-roles": "presubscript"}, true);
+      testAttrs("presup-1", {"xml-roles": "presuperscript"}, true);
 
-      testAttrs("under-1", {"xml-roles" : "underscript"}, true);
-      testAttrs("under-2", {"xml-roles" : "underscript"}, true);
-      testAttrs("over-1", {"xml-roles" : "overscript"}, true);
-      testAttrs("over-2", {"xml-roles" : "overscript"}, true);
+      testAttrs("under-1", {"xml-roles": "underscript"}, true);
+      testAttrs("under-2", {"xml-roles": "underscript"}, true);
+      testAttrs("over-1", {"xml-roles": "overscript"}, true);
+      testAttrs("over-2", {"xml-roles": "overscript"}, true);
 
-      testAttrs("root-index-1", {"xml-roles" : "root-index"}, true);
+      testAttrs("root-index-1", {"xml-roles": "root-index"}, true);
 
-      testAttrs("base-1", {"xml-roles" : "base"}, true);
-      testAttrs("base-2", {"xml-roles" : "base"}, true);
-      testAttrs("base-3", {"xml-roles" : "base"}, true);
-      testAttrs("base-4", {"xml-roles" : "base"}, true);
-      testAttrs("base-5", {"xml-roles" : "base"}, true);
-      testAttrs("base-6", {"xml-roles" : "base"}, true);
-      testAttrs("base-7", {"xml-roles" : "base"}, true);
-      testAttrs("base-8", {"xml-roles" : "base"}, true);
+      testAttrs("base-1", {"xml-roles": "base"}, true);
+      testAttrs("base-2", {"xml-roles": "base"}, true);
+      testAttrs("base-3", {"xml-roles": "base"}, true);
+      testAttrs("base-4", {"xml-roles": "base"}, true);
+      testAttrs("base-5", {"xml-roles": "base"}, true);
+      testAttrs("base-6", {"xml-roles": "base"}, true);
+      testAttrs("base-7", {"xml-roles": "base"}, true);
+      testAttrs("base-8", {"xml-roles": "base"}, true);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
@@ -135,16 +136,22 @@
      title="modify HTML5 header and footer accessibility API mapping">
     Bug 849624
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
      title="ARIA 1.1: Support role 'searchbox'">
     Bug 1121518
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1356049"
+     title="Map ARIA figure role">
+    Bug 1356049
+  </a>
+
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <nav id="nav">a nav</nav>
   <header id="header">a header</header>
   <footer id="footer">a footer</footer>
@@ -158,16 +165,17 @@
   </section>
   <aside id="aside">by the way I am an aside</aside>
   <section id="section">a section</section>
   <article id="main" role="main">a main area</article>
   <article id="form" role="form">a form area</article>
   <div id="feed" role="feed">a feed</div>
   <article id="article">article</article>
   <main id="main_element">another main area</main>
+  <div id="figure" role="figure">a figure</div>
 
   <input id="search" type="search"/>
 
   <!-- open-fence, separator, close-fence -->
   <math><mo id="open-1">(</mo><mi>x</mi><mo id="sep-1">,</mo><mi>y</mi><mo id="close-1">)</mo></math>
   <math><mrow><mo id="open-2">(</mo><mi>x</mi><mo id="sep-2">,</mo><mi>y</mi><mo id="close-2">)</mo></mrow></math>
   <math><mstyle><mo id="open-3">(</mo><mi>x</mi><mo id="sep-3">,</mo><mi>y</mi><mo id="close-3">)</mo></mstyle></math>
   <math><msqrt><mo id="open-4">(</mo><mi>x</mi><mo id="sep-4">,</mo><mi>y</mi><mo id="close-4">)</mo></msqrt></math>
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -248,17 +248,17 @@ const DONOTFAIL_IF_NO_INTERFACE = 2;
 function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIf)
 {
   if (!aAccOrElmOrID)
     return null;
 
   var elm = null;
 
   if (aAccOrElmOrID instanceof nsIAccessible) {
-    try { elm = aAccOrElmOrID.DOMNode; } catch(e) { }
+    try { elm = aAccOrElmOrID.DOMNode; } catch (e) { }
 
   } else if (aAccOrElmOrID instanceof nsIDOMNode) {
     elm = aAccOrElmOrID;
 
   } else {
     elm = document.getElementById(aAccOrElmOrID);
     if (!elm) {
       ok(false, "Can't get DOM element for " + aAccOrElmOrID);
@@ -545,17 +545,17 @@ function testAccessibleTree(aAccOrElmOrI
 
         // nsIAccessible::parent
         var parent = null;
         try { parent = child.parent; } catch (e) {}
         is(parent, acc, "Wrong parent of " + prettyName(child));
 
         // nsIAccessible::indexInParent
         var indexInParent = -1;
-        try { indexInParent = child.indexInParent; } catch(e) {}
+        try { indexInParent = child.indexInParent; } catch (e) {}
         is(indexInParent, i,
            "Wrong index in parent of " + prettyName(child));
 
         // nsIAccessible::nextSibling
         var expectedNextSibling = (i < childCount - 1) ?
           children.queryElementAt(i + 1, nsIAccessible) : null;
         var nextSibling = null;
         try { nextSibling = child.nextSibling; } catch (e) {}
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -301,18 +301,18 @@
         ]
       };
       testElm("code_container", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:col and HTML:colgroup under table
 
       obj =
-        { TABLE : [
-          { ROW :[
+        { TABLE: [
+          { ROW: [
             { role: ROLE_CELL },
             { role: ROLE_CELL },
             { role: ROLE_CELL }
           ] }
         ] };
       testElm("colNcolgroup_table", obj);
 
       //////////////////////////////////////////////////////////////////////////
@@ -854,17 +854,17 @@
       //////////////////////////////////////////////////////////////////////////
       // HTML:keygen
 
       obj = {
         role: ROLE_COMBOBOX,
         states: STATE_COLLAPSED | STATE_HASPOPUP,
         extraStates: EXT_STATE_EXPANDABLE,
         actions: "open",
-        children: [ 
+        children: [
           { COMBOBOX_LIST: [
             { role: ROLE_COMBOBOX_OPTION }, // high grade
             { role: ROLE_COMBOBOX_OPTION } // medium grade
           ] }
         ]
       };
       testElm("keygen", obj);
 
@@ -1267,17 +1267,17 @@
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:u contained by paragraph
 
       obj = {
         role: ROLE_PARAGRAPH,
         textAttrs: {
           0: { },
-          6: { "text-underline-style" : "solid" }
+          6: { "text-underline-style": "solid" }
         },
         children: [
           { role: ROLE_TEXT_LEAF }, // plain text
           { role: ROLE_TEXT_LEAF } // HTML:u text
         ]
       };
       testElm("u_container", obj);
 
--- a/accessible/tests/mochitest/elm/test_figure.html
+++ b/accessible/tests/mochitest/elm/test_figure.html
@@ -28,17 +28,17 @@
 
       todo(false, "figure name gets extra whitespace in the end!");
       testName("figure", "figure caption ");
       testName("figcaption", null);
 
       testRelation("figure", RELATION_LABELLED_BY, "figcaption");
       testRelation("figcaption", RELATION_LABEL_FOR, "figure");
 
-      testAttrs("figure", {"xml-roles" : "figure"}, true);
+      testAttrs("figure", {"xml-roles": "figure"}, true);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -32,23 +32,23 @@
 
       // nsIAccessible::name
       var applicationName = "";
       if (LINUX || SOLARIS) {
         applicationName = appInfo.name;
       } else {
         try {
           applicationName = brandBundle.GetStringFromName("brandShortName");
-        } catch(e) {
+        } catch (e) {
         }
 
         if (applicationName == "")
           applicationName = "Gecko based application";
       }
-      is (accessible.name, applicationName, "wrong application accessible name");
+      is(accessible.name, applicationName, "wrong application accessible name");
 
       // nsIAccessibleApplication
       is(accessible.appName, appInfo.name, "Wrong application name");
       is(accessible.appVersion, appInfo.version, "Wrong application version");
       is(accessible.platformName, "Gecko", "Wrong platform name");
       is(accessible.platformVersion, appInfo.platformVersion,
          "Wrong platform version");
 
--- a/accessible/tests/mochitest/events/test_aria_objattr.html
+++ b/accessible/tests/mochitest/events/test_aria_objattr.html
@@ -45,18 +45,18 @@
     function updateARIAHidden(aID, aIsDefined, aChildId)
     {
       this.__proto__ = new updateAttribute(aID, "aria-hidden",
                                            aIsDefined ? "true" : "false");
 
       this.finalCheck = function updateARIAHidden()
       {
         if (aIsDefined) {
-          testAttrs(aID, {"hidden" : "true"}, true);
-          testAttrs(aChildId, {"hidden" : "true"}, true);
+          testAttrs(aID, {"hidden": "true"}, true);
+          testAttrs(aChildId, {"hidden": "true"}, true);
         } else {
           testAbsentAttrs(aID, { "hidden": "true"});
           testAbsentAttrs(aChildId, { "hidden": "true"});
         }
       }
     }
 
     // Debug stuff.
--- a/accessible/tests/mochitest/events/test_aria_statechange.html
+++ b/accessible/tests/mochitest/events/test_aria_statechange.html
@@ -63,16 +63,35 @@
       };
 
       this.getID = function busyify_getID()
       {
         return prettyName(aID) + " aria-busy changed to '" + aIsBusy + "'";
       };
     }
 
+    function makeCurrent(aID, aIsCurrent, aValue)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.eventSeq = [
+        new stateChangeChecker(EXT_STATE_CURRENT, true, aIsCurrent, this.DOMNode)
+      ];
+
+      this.invoke = function makeCurrent_invoke()
+      {
+        this.DOMNode.setAttribute("aria-current", aValue);
+      };
+
+      this.getID = function makeCurrent_getID()
+      {
+        return prettyName(aID) + " aria-current changed to " + aValue;
+      };
+    }
+
     function setAttrOfMixedType(aID, aAttr, aState, aValue)
     {
       this.DOMNode = getNode(aID);
 
       this.eventSeq = [
         new stateChangeChecker(aState, kOrdinalState,
                                aValue == "true", this.DOMNode)
       ];
@@ -143,16 +162,22 @@
       gQueue.push(new busyify("aria_doc", true));
       gQueue.push(new busyify("aria_doc", false));
 
       buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
       buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
       buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
       buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
 
+      gQueue.push(new makeCurrent("current_page_1", false, "false"));
+      gQueue.push(new makeCurrent("current_page_2", true, "page"));
+      gQueue.push(new makeCurrent("current_page_2", false, "false"));
+      gQueue.push(new makeCurrent("current_page_3", true, "true"));
+      gQueue.push(new makeCurrent("current_page_3", false, ""));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -178,16 +203,21 @@
      title="Pressed state is not exposed on a button element with aria-pressed attribute">
     Mozilla Bug 989958
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
      title="Support ARIA 1.1 switch role">
     Mozilla Bug 1136563
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1355921"
+     title="Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE">
+    Mozilla Bug 1355921
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <div id="eventdump"></div>
 
   <!-- aria-expanded -->
@@ -199,10 +229,15 @@
 
   <!-- aria-pressed -->
   <div id="pressable" role="button"></div>
   <button id="pressable_native"></button>
 
   <!-- aria-checked -->
   <div id="checkable" role="checkbox"></div>
   <div id="checkableBool" role="switch"></div>
+
+  <!-- aria-current -->
+  <div id="current_page_1" role="link" aria-current="page">1</div>
+  <div id="current_page_2" role="link" aria-current="false">2</div>
+  <div id="current_page_3" role="link">3</div>
 </body>
 </html>
--- a/accessible/tests/mochitest/events/test_dragndrop.html
+++ b/accessible/tests/mochitest/events/test_dragndrop.html
@@ -31,17 +31,17 @@
 
       this.invoke = function changeGrabbed_invoke() {
         if (aGrabValue != undefined) {
           this.DOMNode.setAttribute("aria-grabbed", aGrabValue);
         }
       }
 
       this.check = function changeGrabbed_check() {
-        testAttrs(aNodeOrID, {"grabbed" : aGrabValue}, true);
+        testAttrs(aNodeOrID, {"grabbed": aGrabValue}, true);
       }
 
       this.getID = function changeGrabbed_getID() {
         return prettyName(aNodeOrID) + " aria-grabbed changed";
       }
     }
 
     // aria dropeffect invoker
@@ -51,17 +51,17 @@
 
       this.invoke = function changeDropeffect_invoke() {
         if (aDropeffectValue != undefined) {
           this.DOMNode.setAttribute("aria-dropeffect", aDropeffectValue);
         }
       }
 
       this.check = function changeDropeffect_check() {
-        testAttrs(aNodeOrID, {"dropeffect" : aDropeffectValue}, true);
+        testAttrs(aNodeOrID, {"dropeffect": aDropeffectValue}, true);
       }
 
       this.getID = function changeDropeffect_getID() {
         return prettyName(aNodeOrID) + " aria-dropeffect changed";
       }
     }
 
     function doTests()
--- a/accessible/tests/mochitest/events/test_textselchange.html
+++ b/accessible/tests/mochitest/events/test_textselchange.html
@@ -22,17 +22,17 @@
     var gQueue = null;
 
     // gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     function getOnclickSeq(aID)
     {
       return [
-        new caretMoveChecker(0, aID), 
+        new caretMoveChecker(0, aID),
         new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
       ];
     }
 
     function doTests()
     {
       // test caret move events and caret offsets
       gQueue = new eventQueue();
--- a/accessible/tests/mochitest/events/test_valuechange.html
+++ b/accessible/tests/mochitest/events/test_valuechange.html
@@ -35,28 +35,28 @@
         ];
 
       this.invoke = function changeARIAValue_invoke() {
 
         // Note: this should not fire an EVENT_VALUE_CHANGE when aria-valuetext
         // is not empty
         if (aValuenow != undefined)
           this.DOMNode.setAttribute("aria-valuenow", aValuenow);
- 
+
         // Note: this should always fire an EVENT_VALUE_CHANGE
         if (aValuetext != undefined)
           this.DOMNode.setAttribute("aria-valuetext", aValuetext);
       }
 
       this.check = function changeARIAValue_check() {
         var acc = getAccessible(aNodeOrID, [nsIAccessibleValue]);
         if (!acc)
           return;
 
-        // Note: always test against valuetext first because the existence of 
+        // Note: always test against valuetext first because the existence of
         // aria-valuetext takes precedence over aria-valuenow in gecko.
         is(acc.value, (aValuetext != undefined)? aValuetext : aValuenow,
             "Wrong value of " + prettyName(aNodeOrID));
       }
 
       this.getID = function changeARIAValue_getID() {
         return prettyName(aNodeOrID) + " value changed";
       }
@@ -156,26 +156,32 @@
     //gA11yEventDumpToConsole = true;
     function doTests()
     {
       // Test initial values
       testValue("slider_vn", "5", 5, 0, 1000, 0);
       testValue("slider_vnvt", "plain", 0, 0, 5, 0);
       testValue("slider_vt", "hi", 0, 0, 3, 0);
       testValue("scrollbar", "5", 5, 0, 1000, 0);
+      testValue("splitter", "5", 5, 0, 1000, 0);
       testValue("progress", "22%", 22, 0, 100, 0);
       testValue("range", "6", 6, 0, 10, 1);
 
+      // Test that elements which should not expose values do not
+      var accSeparator = getAccessible("separator", [nsIAccessibleValue], null, DONOTFAIL_IF_NO_INTERFACE);
+      ok(!accSeparator, "value interface is not exposed for separator");
+
       // Test value change events
       gQueue = new eventQueue();
 
       gQueue.push(new changeARIAValue("slider_vn", "6", undefined));
       gQueue.push(new changeARIAValue("slider_vt", undefined, "hey!"));
       gQueue.push(new changeARIAValue("slider_vnvt", "3", "sweet"));
       gQueue.push(new changeARIAValue("scrollbar", "6", undefined));
+      gQueue.push(new changeARIAValue("splitter", "6", undefined));
 
       gQueue.push(new changeValue("combobox", "hello"));
 
       gQueue.push(new changeProgressValue("progress", "50"));
       gQueue.push(new changeRangeValue("range"));
 
       gQueue.push(new changeSelectValue("select", "VK_DOWN", "2nd"));
       gQueue.push(new changeSelectValue("select", "3", "3rd"));
@@ -232,16 +238,24 @@
 
   <div id="slider_vnvt" role="slider" aria-valuenow="0" aria-valuetext="plain"
        aria-valuemin="0" aria-valuemax="5">sweetness slider</div>
 
   <!-- ARIA scrollbar -->
   <div id="scrollbar" role="scrollbar" aria-valuenow="5"
        aria-valuemin="0" aria-valuemax="1000">slider</div>
 
+  <!-- ARIA separator which is focusable (i.e. a splitter) -->
+  <div id="splitter" role="separator" tabindex="0" aria-valuenow="5"
+       aria-valuemin="0" aria-valuemax="1000">splitter</div>
+
+  <!-- ARIA separator which is not focusable and should not expose values -->
+  <div id="separator" role="separator" aria-valuenow="5"
+       aria-valuemin="0" aria-valuemax="1000">splitter</div>
+
   <!-- ARIA combobox -->
   <input id="combobox" role="combobox" aria-autocomplete="inline">
 
   <!-- progress bar -->
   <progress id="progress" value="22" max="100"></progress>
 
   <!-- input@type="range" -->
   <input type="range" id="range" min="0" max="10" value="6">
--- a/accessible/tests/mochitest/grid.js
+++ b/accessible/tests/mochitest/grid.js
@@ -76,17 +76,17 @@ function grid(aTableIdentifier)
   }
 
   this.handleKeyEvent = function handleKeyEvent(aEvent)
   {
     if (aEvent.target.localName != "td")
       return;
 
     var cell = aEvent.target;
-    switch(aEvent.keyCode) {
+    switch (aEvent.keyCode) {
       case nsIDOMKeyEvent.DOM_VK_UP:
         var colsCount = this.getColsCount();
         var idx = this.getIndexByCell(cell);
         var upidx = idx - colsCount;
         if (upidx >= 0) {
           cell.removeAttribute("tabindex");
           var upcell = this.getCellAtIndex(upidx);
           upcell.setAttribute("tabindex", "0");
--- a/accessible/tests/mochitest/hittest/test_browser.html
+++ b/accessible/tests/mochitest/hittest/test_browser.html
@@ -25,27 +25,27 @@
       var tgtX = hitX + hitWidth / 2;
       var tgtY = hitY + hitHeight / 2;
 
       var rootAcc = getRootAccessible();
       var docAcc = getAccessible(document);
       var outerDocAcc = docAcc.parent;
 
       var hitAcc = rootAcc.getDeepestChildAtPoint(tgtX, tgtY);
-      is(hitAcc, hititem, "Hit match at " + tgtX + "," + tgtY + 
+      is(hitAcc, hititem, "Hit match at " + tgtX + "," + tgtY +
                           ". Found: " + prettyName(hitAcc));
       var hitAcc2 = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
-      is(hitAcc, hitAcc2, "Hit match at " + tgtX + "," + tgtY + 
+      is(hitAcc, hitAcc2, "Hit match at " + tgtX + "," + tgtY +
                           ". Found: " + prettyName(hitAcc2));
 
       hitAcc = outerDocAcc.getChildAtPoint(tgtX, tgtY);
-      is(hitAcc, docAcc, "Hit match at " + tgtX + "," + tgtY + 
+      is(hitAcc, docAcc, "Hit match at " + tgtX + "," + tgtY +
                          ". Found: " + prettyName(hitAcc));
       hitAcc = docAcc.getChildAtPoint(tgtX, tgtY);
-      is(hitAcc, hittest, "Hit match at " + tgtX + "," + tgtY + 
+      is(hitAcc, hittest, "Hit match at " + tgtX + "," + tgtY +
                           ". Found: " + prettyName(hitAcc));
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
--- a/accessible/tests/mochitest/hittest/test_shadowroot.html
+++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
@@ -58,15 +58,16 @@
     // This routine adds the comment children of each 'component' to its
     // shadow root.
     var components = document.querySelectorAll('.components');
     for (var i = 0; i < components.length; i++) {
       var component = components[i];
       var shadow = component.createShadowRoot();
       for (var child = component.firstChild; child; child = child.nextSibling) {
         if (child.nodeType === 8)
+          // eslint-disable-next-line no-unsanitized/property
           shadow.innerHTML = child.data;
       }
     }
   </script>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_content_text.html
+++ b/accessible/tests/mochitest/jsat/test_content_text.html
@@ -120,17 +120,17 @@
            new ExpectedAnnouncement('navigating'),
            new ExpectedEditState({
             editing: false,
             multiline: false,
             atStart: true,
             atEnd: false })],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(
-            [{ string : 'entry' }],
+            [{ string: 'entry' }],
             { focused: 'html'})],
           [ContentMessages.activateCurrent(0),
            new ExpectedClickAction(),
            new ExpectedAnnouncement('editing'),
            new ExpectedEditState({
             editing: true,
             multiline: false,
             atStart: true,
@@ -146,17 +146,17 @@
             editing: false,
             multiline: false,
             atStart: true,
             atEnd: false
            },{ focused: 'html' })
          ],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(
-            [{ string : 'entry' }],
+            [{ string: 'entry' }],
             { focused: 'html'})],
           [ContentMessages.activateCurrent(0),
            new ExpectedClickAction(),
            new ExpectedAnnouncement('editing'),
            new ExpectedEditState({
             editing: true,
             multiline: false,
             atStart: true,
--- a/accessible/tests/mochitest/jsat/test_landmarks.html
+++ b/accessible/tests/mochitest/jsat/test_landmarks.html
@@ -57,21 +57,21 @@
         expectedUtterance: [
           [{"string": "footer"}, "a footer within an article"],
           ["a footer within an article", {"string": "footer"}]],
         expectedBraille: [
           [{"string": "footerAbbr"}, "a footer within an article"],
           ["a footer within an article", {"string": "footerAbbr"}]]
       }, {
         accOrElmOrID: "section_header",
-        expectedUtterance: [[{"string":"header"}, "a header within a section"],
-                            ["a header within a section", {"string":"header"}]],
+        expectedUtterance: [[{"string": "header"}, "a header within a section"],
+                            ["a header within a section", {"string": "header"}]],
         expectedBraille: [
-          [{"string":"headerAbbr"}, "a header within a section"],
-          ["a header within a section", {"string":"headerAbbr"}]]
+          [{"string": "headerAbbr"}, "a header within a section"],
+          ["a header within a section", {"string": "headerAbbr"}]]
       }, {
         accOrElmOrID: "section_footer",
         expectedUtterance: [
           [{"string": "footer"}, "a footer within a section"],
           ["a footer within a section", {"string": "footer"}]],
         expectedBraille: [
           [{"string": "footerAbbr"}, "a footer within a section"],
           ["a footer within a section", {"string": "footerAbbr"}]]
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -60,19 +60,19 @@ https://bugzilla.mozilla.org/show_bug.cg
           expectedUtterance: [[{"string": "headingLevel", "args": [1]},
             "Test heading"], ["Test heading",
             {"string": "headingLevel", "args": [1]}]],
           expectedBraille: [[{"string": "headingAbbr"}, "Test heading"],
                             ["Test heading", {"string": "headingAbbr"}]]
         }, {
           accOrElmOrID: "list",
           expectedUtterance: [[{"string": "list"},
-            {"string": "listItemsCount", "count":1}, {"string": "listStart"},
+            {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
             "1.", "list one"], ["1.", "list one", {"string": "listStart"},
-            {"string": "list"}, {"string": "listItemsCount", "count":1}]
+            {"string": "list"}, {"string": "listItemsCount", "count": 1}]
           ],
           expectedBraille: [[{"string": "listAbbr"}, "list one"],
             ["list one", {"string": "listAbbr"}]]
         }, {
           accOrElmOrID: "dlist",
           expectedUtterance: [[{"string": "definitionlist"},
             {"string": "listItemsCount", "count": 0.5}, "dd one"], ["dd one",
             {"string": "definitionlist"},
@@ -94,32 +94,32 @@ https://bugzilla.mozilla.org/show_bug.cg
           expectedUtterance: [[{"string": "list"},
             {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
             "list two"], ["list two", {"string": "listStart"},
             {"string": "list"}, {"string": "listItemsCount", "count": 1}]
           ],
           expectedBraille: [["*", "list two"], ["*", "list two"]]
         }, {
           accOrElmOrID: "cell",
-          expectedUtterance: [[{"string":"table"},
+          expectedUtterance: [[{"string": "table"},
             {"string": "tblColumnInfo", "count": 1},
             {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
             {"string": "columnInfo", "args": [1]},
             {"string": "rowInfo", "args": [1]}, {"string": "list"},
             {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
             {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
             {"string": "link"}, "Peaches", {"string": "listEnd"},
             {"string": "link"}, "Plums"], ["Apples", {"string": "link"},
             {"string": "listStart"}, "Bananas", {"string": "link"}, "Peaches",
             {"string": "link"}, "Plums", {"string": "link"},
             {"string": "listEnd"}, {"string": "list"},
             {"string": "listItemsCount", "count": 4},
             {"string": "columnInfo", "args": [1]},
             {"string": "rowInfo", "args": [1]}, "Fruits and vegetables",
-            {"string":"table"}, {"string": "tblColumnInfo", "count": 1},
+            {"string": "table"}, {"string": "tblColumnInfo", "count": 1},
             {"string": "tblRowInfo", "count": 1}]],
           expectedBraille: [[{"string": "cellInfoAbbr", "args": [ 1, 1]},
             {"string": "listAbbr"}, {"string": "linkAbbr"}, "Apples",
             {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
             "Peaches", {"string": "linkAbbr"}, "Plums"], ["Apples",
             {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
             "Peaches", {"string": "linkAbbr"}, "Plums", {"string": "linkAbbr"},
             {"string": "listAbbr"},
@@ -404,19 +404,19 @@ https://bugzilla.mozilla.org/show_bug.cg
           expectedUtterance: [[{"string": "listboxoption"}, "555-12345"],
                               ["555-12345", {"string": "listboxoption"}]],
           expectedBraille: [[{"string": "listboxoptionAbbr"}, "555-12345"],
                             ["555-12345", {"string": "listboxoptionAbbr"}]]
         }, {
           accOrElmOrID: "columnheader",
           oldAccOrElmOrID: "grid",
           expectedUtterance: [[{"string": "columnInfo", "args": [1]},
-            {"string": "rowInfo", "args" :[1]}, "Sunday"], ["Sunday",
+            {"string": "rowInfo", "args": [1]}, "Sunday"], ["Sunday",
             {"string": "columnInfo", "args": [1]},
-            {"string": "rowInfo", "args" :[1]}]],
+            {"string": "rowInfo", "args": [1]}]],
           expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 1]},
             "Sunday"], ["Sunday", {"string": "cellInfoAbbr", "args": [1, 1]}]]
         }, {
           accOrElmOrID: "rowheader",
           oldAccOrElmOrID: "grid",
           expectedUtterance: [[{"string": "columnInfo", "args": [1]},
             {"string": "rowInfo", "args": [2]}, "Sunday", "Week 1"], ["Week 1",
             {"string": "columnInfo", "args": [1]},
--- a/accessible/tests/mochitest/jsat/test_output_mathml.html
+++ b/accessible/tests/mochitest/jsat/test_output_mathml.html
@@ -14,162 +14,162 @@
           src="jsatcommon.js"></script>
   <script type="application/javascript">
 
     function doTest() {
       // Test the following accOrElmOrID.
       var tests = [{
           accOrElmOrID: "math-1",
           expectedUtterance: [
-            [{"string":"open-fence"},"(","x",",","y",{"string":"close-fence"},")"],
-            ["(",{"string":"open-fence"},"x",",","y",")",{"string":"close-fence"}]
+            [{"string": "open-fence"},"(","x",",","y",{"string": "close-fence"},")"],
+            ["(",{"string": "open-fence"},"x",",","y",")",{"string": "close-fence"}]
           ],
           expectedBraille: [
-            [{"string":"open-fenceAbbr"},"(","x",",","y",{"string":"close-fenceAbbr"},")"],
-            ["(",{"string":"open-fenceAbbr"},"x",",","y",")",{"string":"close-fenceAbbr"}]
+            [{"string": "open-fenceAbbr"},"(","x",",","y",{"string": "close-fenceAbbr"},")"],
+            ["(",{"string": "open-fenceAbbr"},"x",",","y",")",{"string": "close-fenceAbbr"}]
           ]
         }, {
           accOrElmOrID: "mfrac-1",
           expectedUtterance: [
-            [{"string":"mathmlfraction"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
-            ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfraction"}]
+            [{"string": "mathmlfraction"},{"string": "numerator"},"a",{"string": "denominator"},"b"],
+            ["a",{"string": "numerator"},"b",{"string": "denominator"},{"string": "mathmlfraction"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlfractionAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
-            ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionAbbr"}]
+            [{"string": "mathmlfractionAbbr"},{"string": "numeratorAbbr"},"a",{"string": "denominatorAbbr"},"b"],
+            ["a",{"string": "numeratorAbbr"},"b",{"string": "denominatorAbbr"},{"string": "mathmlfractionAbbr"}]
           ]
         }, {
           accOrElmOrID: "mfrac-2",
           expectedUtterance: [
-            [{"string":"mathmlfractionwithoutbar"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
-            ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfractionwithoutbar"}]
+            [{"string": "mathmlfractionwithoutbar"},{"string": "numerator"},"a",{"string": "denominator"},"b"],
+            ["a",{"string": "numerator"},"b",{"string": "denominator"},{"string": "mathmlfractionwithoutbar"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlfractionwithoutbarAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
-            ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionwithoutbarAbbr"}]
+            [{"string": "mathmlfractionwithoutbarAbbr"},{"string": "numeratorAbbr"},"a",{"string": "denominatorAbbr"},"b"],
+            ["a",{"string": "numeratorAbbr"},"b",{"string": "denominatorAbbr"},{"string": "mathmlfractionwithoutbarAbbr"}]
           ]
         }, {
           accOrElmOrID: "msub-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b"],
-            ["a",{"string":"base"},"b",{"string":"subscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "subscript"},"b"],
+            ["a",{"string": "base"},"b",{"string": "subscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "subscriptAbbr"},"b"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "subscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "msup-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"superscript"},"b"],
-            ["a",{"string":"base"},"b",{"string":"superscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "superscript"},"b"],
+            ["a",{"string": "base"},"b",{"string": "superscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"superscriptAbbr"},"b"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "superscriptAbbr"},"b"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "superscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "msubsup-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c"],
-            ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "subscript"},"b",{"string": "superscript"},"c"],
+            ["a",{"string": "base"},"b",{"string": "subscript"},"c",{"string": "superscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "subscriptAbbr"},"b",{"string": "superscriptAbbr"},"c"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "subscriptAbbr"},"c",{"string": "superscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "mmultiscripts-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c",{"string":"superscript"},"d",{"string":"presubscript"},"e",{"string":"presubscript"},"f",{"string":"presuperscript"},"g"],
-            ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},"d",{"string":"superscript"},"e",{"string":"presubscript"},"f",{"string":"presubscript"},"g",{"string":"presuperscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "subscript"},"b",{"string": "superscript"},"c",{"string": "superscript"},"d",{"string": "presubscript"},"e",{"string": "presubscript"},"f",{"string": "presuperscript"},"g"],
+            ["a",{"string": "base"},"b",{"string": "subscript"},"c",{"string": "superscript"},"d",{"string": "superscript"},"e",{"string": "presubscript"},"f",{"string": "presubscript"},"g",{"string": "presuperscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"presubscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presuperscriptAbbr"},"g"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"superscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presubscriptAbbr"},"g",{"string":"presuperscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "subscriptAbbr"},"b",{"string": "superscriptAbbr"},"c",{"string": "superscriptAbbr"},"d",{"string": "presubscriptAbbr"},"e",{"string": "presubscriptAbbr"},"f",{"string": "presuperscriptAbbr"},"g"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "subscriptAbbr"},"c",{"string": "superscriptAbbr"},"d",{"string": "superscriptAbbr"},"e",{"string": "presubscriptAbbr"},"f",{"string": "presubscriptAbbr"},"g",{"string": "presuperscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "munder-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b"],
-            ["a",{"string":"base"},"b",{"string":"underscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "underscript"},"b"],
+            ["a",{"string": "base"},"b",{"string": "underscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "underscriptAbbr"},"b"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "underscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "mover-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"overscript"},"b"],
-            ["a",{"string":"base"},"b",{"string":"overscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "overscript"},"b"],
+            ["a",{"string": "base"},"b",{"string": "overscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"overscriptAbbr"},"b"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "overscriptAbbr"},"b"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "overscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "munderover-1",
           expectedUtterance: [
-            [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b",{"string":"overscript"},"c"],
-            ["a",{"string":"base"},"b",{"string":"underscript"},"c",{"string":"overscript"},{"string":"mathmlscripted"}]
+            [{"string": "mathmlscripted"},{"string": "base"},"a",{"string": "underscript"},"b",{"string": "overscript"},"c"],
+            ["a",{"string": "base"},"b",{"string": "underscript"},"c",{"string": "overscript"},{"string": "mathmlscripted"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b",{"string":"overscriptAbbr"},"c"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},"c",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+            [{"string": "mathmlscriptedAbbr"},{"string": "baseAbbr"},"a",{"string": "underscriptAbbr"},"b",{"string": "overscriptAbbr"},"c"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "underscriptAbbr"},"c",{"string": "overscriptAbbr"},{"string": "mathmlscriptedAbbr"}]
           ]
         }, {
           accOrElmOrID: "mroot-1",
           expectedUtterance: [
-            [{"string":"mathmlroot"},{"string":"base"},"a",{"string":"root-index"},"b"],
-            ["a",{"string":"base"},"b",{"string":"root-index"},{"string":"mathmlroot"}]
+            [{"string": "mathmlroot"},{"string": "base"},"a",{"string": "root-index"},"b"],
+            ["a",{"string": "base"},"b",{"string": "root-index"},{"string": "mathmlroot"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlrootAbbr"},{"string":"baseAbbr"},"a",{"string":"root-indexAbbr"},"b"],
-            ["a",{"string":"baseAbbr"},"b",{"string":"root-indexAbbr"},{"string":"mathmlrootAbbr"}]
+            [{"string": "mathmlrootAbbr"},{"string": "baseAbbr"},"a",{"string": "root-indexAbbr"},"b"],
+            ["a",{"string": "baseAbbr"},"b",{"string": "root-indexAbbr"},{"string": "mathmlrootAbbr"}]
           ]
         }, {
           accOrElmOrID: "mtable-1",
           expectedUtterance: [
-            [{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2},{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"a",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"d",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},"f"],
-            ["a",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"d",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"f",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2}]
+            [{"string": "mathmltable"},{"string": "tblColumnInfo","count": 3},{"string": "tblRowInfo","count": 2},{"string": "columnInfo","args": [1]},{"string": "rowInfo","args": [1]},"a",{"string": "columnInfo","args": [2]},{"string": "rowInfo","args": [1]},"b",{"string": "columnInfo","args": [3]},{"string": "rowInfo","args": [1]},"c",{"string": "columnInfo","args": [1]},{"string": "rowInfo","args": [2]},"d",{"string": "columnInfo","args": [2]},{"string": "rowInfo","args": [2]},"e",{"string": "columnInfo","args": [3]},{"string": "rowInfo","args": [2]},"f"],
+            ["a",{"string": "columnInfo","args": [1]},{"string": "rowInfo","args": [1]},"b",{"string": "columnInfo","args": [2]},{"string": "rowInfo","args": [1]},"c",{"string": "columnInfo","args": [3]},{"string": "rowInfo","args": [1]},"d",{"string": "columnInfo","args": [1]},{"string": "rowInfo","args": [2]},"e",{"string": "columnInfo","args": [2]},{"string": "rowInfo","args": [2]},"f",{"string": "columnInfo","args": [3]},{"string": "rowInfo","args": [2]},{"string": "mathmltable"},{"string": "tblColumnInfo","count": 3},{"string": "tblRowInfo","count": 2}]
           ],
           expectedBraille: [
-            [{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2},{"string":"cellInfoAbbr","args":[1,1]},"a",{"string":"cellInfoAbbr","args":[2,1]},"b",{"string":"cellInfoAbbr","args":[3,1]},"c",{"string":"cellInfoAbbr","args":[1,2]},"d",{"string":"cellInfoAbbr","args":[2,2]},"e",{"string":"cellInfoAbbr","args":[3,2]},"f"],
-            ["a",{"string":"cellInfoAbbr","args":[1,1]},"b",{"string":"cellInfoAbbr","args":[2,1]},"c",{"string":"cellInfoAbbr","args":[3,1]},"d",{"string":"cellInfoAbbr","args":[1,2]},"e",{"string":"cellInfoAbbr","args":[2,2]},"f",{"string":"cellInfoAbbr","args":[3,2]},{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2}]
+            [{"string": "mathmltableAbbr"},{"string": "tblColumnInfoAbbr","count": 3},{"string": "tblRowInfoAbbr","count": 2},{"string": "cellInfoAbbr","args": [1,1]},"a",{"string": "cellInfoAbbr","args": [2,1]},"b",{"string": "cellInfoAbbr","args": [3,1]},"c",{"string": "cellInfoAbbr","args": [1,2]},"d",{"string": "cellInfoAbbr","args": [2,2]},"e",{"string": "cellInfoAbbr","args": [3,2]},"f"],
+            ["a",{"string": "cellInfoAbbr","args": [1,1]},"b",{"string": "cellInfoAbbr","args": [2,1]},"c",{"string": "cellInfoAbbr","args": [3,1]},"d",{"string": "cellInfoAbbr","args": [1,2]},"e",{"string": "cellInfoAbbr","args": [2,2]},"f",{"string": "cellInfoAbbr","args": [3,2]},{"string": "mathmltableAbbr"},{"string": "tblColumnInfoAbbr","count": 3},{"string": "tblRowInfoAbbr","count": 2}]
           ]
       }, {
           accOrElmOrID: "menclose-1",
           expectedUtterance: [
-            [{"string":"mathmlenclosed"},{"string":"notation-longdiv"},"a"],
-            ["a",{"string":"notation-longdiv"},{"string":"mathmlenclosed"}]
+            [{"string": "mathmlenclosed"},{"string": "notation-longdiv"},"a"],
+            ["a",{"string": "notation-longdiv"},{"string": "mathmlenclosed"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlenclosedAbbr"},{"string":"notation-longdivAbbr"},"a"],
-            ["a",{"string":"notation-longdivAbbr"},{"string":"mathmlenclosedAbbr"}]
+            [{"string": "mathmlenclosedAbbr"},{"string": "notation-longdivAbbr"},"a"],
+            ["a",{"string": "notation-longdivAbbr"},{"string": "mathmlenclosedAbbr"}]
           ]
         }, {
           accOrElmOrID: "menclose-2",
           expectedUtterance: [
-            [{"string":"mathmlenclosed"},{"string":"notation-circle"},"a"],
-            ["a",{"string":"notation-circle"},{"string":"mathmlenclosed"}]
+            [{"string": "mathmlenclosed"},{"string": "notation-circle"},"a"],
+            ["a",{"string": "notation-circle"},{"string": "mathmlenclosed"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlenclosedAbbr"},{"string":"notation-circleAbbr"},"a"],
-            ["a",{"string":"notation-circleAbbr"},{"string":"mathmlenclosedAbbr"}]
+            [{"string": "mathmlenclosedAbbr"},{"string": "notation-circleAbbr"},"a"],
+            ["a",{"string": "notation-circleAbbr"},{"string": "mathmlenclosedAbbr"}]
           ]
         }, {
           accOrElmOrID: "menclose-3",
           expectedUtterance: [
-            [{"string":"mathmlenclosed"},{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},"a"],
-            ["a",{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},{"string":"mathmlenclosed"}]
+            [{"string": "mathmlenclosed"},{"string": "notation-left"},{"string": "notation-top"},{"string": "notation-bottom"},"a"],
+            ["a",{"string": "notation-left"},{"string": "notation-top"},{"string": "notation-bottom"},{"string": "mathmlenclosed"}]
           ],
           expectedBraille: [
-            [{"string":"mathmlenclosedAbbr"},{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},"a"],
-            ["a",{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},{"string":"mathmlenclosedAbbr"}]
+            [{"string": "mathmlenclosedAbbr"},{"string": "notation-leftAbbr"},{"string": "notation-topAbbr"},{"string": "notation-bottomAbbr"},"a"],
+            ["a",{"string": "notation-leftAbbr"},{"string": "notation-topAbbr"},{"string": "notation-bottomAbbr"},{"string": "mathmlenclosedAbbr"}]
           ]
         }];
 
       // Test all possible utterance order preference values.
       function testOutputOrder(aOutputOrder) {
         return function() {
           SpecialPowers.pushPrefEnv({
             "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -64,17 +64,17 @@ var gTestIterator =
 
     this.ruleIdx++;
     if (this.ruleIdx == this.ruleElms.length) {
       // When test is finished then name is empty and no explict-name.
       var defaultName = this.ruleSetElm.hasAttribute("defaultName") ?
         this.ruleSetElm.getAttribute("defaultName") : null;
       testName(this.elm, defaultName,
                "Default name test (" + gTestIterator.testID + "). ");
-      testAbsentAttrs(this.elm, {"explicit-name" : "true"});
+      testAbsentAttrs(this.elm, {"explicit-name": "true"});
 
       this.markupIdx++;
       if (this.markupIdx == this.markupElms.length) {
         //disableLogging("tree"); // debugging
         SimpleTest.finish();
         return;
       }
 
@@ -212,19 +212,19 @@ function testNameForAttrRule(aElm, aRule
       name += labelElm.getAttribute("textequiv");
     }
   }
 
   var msg = "Attribute '" + attr + "' test (" + gTestIterator.testID + "). ";
   testName(aElm, name, msg);
 
   if (aRule.getAttribute("explict-name") != "false")
-    testAttrs(aElm, {"explicit-name" : "true"}, true);
+    testAttrs(aElm, {"explicit-name": "true"}, true);
   else
-    testAbsentAttrs(aElm, {"explicit-name" : "true"});
+    testAbsentAttrs(aElm, {"explicit-name": "true"});
 
   // If @recreated attribute is used then this attribute change recreates an
   // accessible. Wait for reorder event in this case or otherwise proceed next
   // test immediately.
   if (aRule.hasAttribute("recreated")) {
     waitForEvent(EVENT_REORDER, aElm.parentNode,
                  gTestIterator.iterateNext, gTestIterator);
     aElm.removeAttribute(attr);
@@ -282,17 +282,17 @@ function testNameForElmRule(aElm, aRule)
   if (!labelElm) {
     ok(false, msg + " Failed to find '" + tagname + "' element.");
     gTestIterator.iterateNext();
     return;
   }
 
   var msg = "Element '" + tagname + "' test (" + gTestIterator.testID + ").";
   testName(aElm, labelElm.getAttribute("textequiv"), msg);
-  testAttrs(aElm, {"explicit-name" : "true"}, true);
+  testAttrs(aElm, {"explicit-name": "true"}, true);
 
   var parentNode = labelElm.parentNode;
 
   if (gDumpToConsole) {
     dump("\nProcessed elm rule. Wait for reorder event on " +
          prettyName(parentNode) + "\n");
   }
   waitForEvent(EVENT_REORDER, parentNode,
@@ -300,17 +300,17 @@ function testNameForElmRule(aElm, aRule)
 
   parentNode.removeChild(labelElm);
 }
 
 function testNameForSubtreeRule(aElm, aRule)
 {
   var msg = "From subtree test (" + gTestIterator.testID + ").";
   testName(aElm, aElm.getAttribute("textequiv"), msg);
-  testAbsentAttrs(aElm, {"explicit-name" : "true"});
+  testAbsentAttrs(aElm, {"explicit-name": "true"});
 
   if (gDumpToConsole) {
     dump("\nProcessed from subtree rule. Wait for reorder event on " +
          prettyName(aElm) + "\n");
   }
   waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
 
   while (aElm.firstChild)
@@ -369,12 +369,12 @@ function evaluateXPath(aNode, aExpr, aRe
   while (res = result.iterateNext())
     found.push(res);
 
   return found;
 }
 
 function htmlDocResolver(aPrefix) {
   var ns = {
-    'html' : 'http://www.w3.org/1999/xhtml'
+    'html': 'http://www.w3.org/1999/xhtml'
   };
   return ns[aPrefix] || null;
 }
--- a/accessible/tests/mochitest/name/test_general.html
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -22,41 +22,41 @@
       // Simple label provided via ARIA
       testName("btn_simple_aria_label", "I am a button");
 
       // aria-label and aria-labelledby, expect aria-labelledby
       testName("btn_both_aria_labels", "text I am a button, two");
 
       //////////////////////////////////////////////////////////////////////////
       // aria-labelledby
-      
+    
       // Single relation. The value of 'aria-labelledby' contains the ID of
       // an element. Gets the name from text node of that element.
       testName("btn_labelledby_text", "text");
 
       // Multiple relations. The value of 'aria-labelledby' contains the IDs
       // of elements. Gets the name from text nodes of those elements.
       testName("btn_labelledby_texts", "text1 text2");
 
       //////////////////////////////////////////////////////////////////////////
       // Name from named accessible
 
       testName("input_labelledby_namedacc", "Data");
 
       //////////////////////////////////////////////////////////////////////////
       // Name from subtree (single relation labelled_by).
-      
+    
       // Gets the name from text nodes contained by nested elements
       testName("btn_labelledby_mixed", "nomore text");
 
       // Gets the name from text nodes contained by nested elements, ignores
       // hidden elements (bug 443081).
       testName("btn_labelledby_mixed_hidden_child", "nomore text2");
 
-      // Gets the name from hidden text nodes contained by nested elements, 
+      // Gets the name from hidden text nodes contained by nested elements,
       // (label element is hidden entirely), (bug 443081).
       testName("btn_labelledby_mixed_hidden", "lala more hidden text");
 
       // Gets the name from text nodes contained by nested elements having block
       // representation (every text node value in the name should be devided by
       // spaces)
       testName("btn_labelledby_mixed_block", "text more text");
 
@@ -157,17 +157,17 @@
 
       //////////////////////////////////////////////////////////////////////////
       // textarea name
 
       // textarea's name should have the value, which initially is specified by
       // a text child.
       testName("textareawithchild", "Story Foo is ended.");
 
-      // new textarea name should reflect the value change. 
+      // new textarea name should reflect the value change.
       var elem = document.getElementById("textareawithchild");
       elem.value = "Bar";
 
       testName("textareawithchild", "Story Bar is ended.");
 
       //////////////////////////////////////////////////////////////////////////
       // controls having a value used as a part of computed name
 
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -95,16 +95,17 @@ const ROLE_PARAGRAPH = nsIAccessibleRole
 const ROLE_PARENT_MENUITEM = nsIAccessibleRole.ROLE_PARENT_MENUITEM;
 const ROLE_PASSWORD_TEXT = nsIAccessibleRole.ROLE_PASSWORD_TEXT;
 const ROLE_PROGRESSBAR = nsIAccessibleRole.ROLE_PROGRESSBAR;
 const ROLE_PROPERTYPAGE = nsIAccessibleRole.ROLE_PROPERTYPAGE;
 const ROLE_PUSHBUTTON = nsIAccessibleRole.ROLE_PUSHBUTTON;
 const ROLE_RADIOBUTTON = nsIAccessibleRole.ROLE_RADIOBUTTON;
 const ROLE_RADIO_GROUP = nsIAccessibleRole.ROLE_RADIO_GROUP;
 const ROLE_RADIO_MENU_ITEM = nsIAccessibleRole.ROLE_RADIO_MENU_ITEM;
+const ROLE_REGION = nsIAccessibleRole.ROLE_REGION;
 const ROLE_RICH_OPTION = nsIAccessibleRole.ROLE_RICH_OPTION;
 const ROLE_ROW = nsIAccessibleRole.ROLE_ROW;
 const ROLE_ROWHEADER = nsIAccessibleRole.ROLE_ROWHEADER;
 const ROLE_SCROLLBAR = nsIAccessibleRole.ROLE_SCROLLBAR;
 const ROLE_SECTION = nsIAccessibleRole.ROLE_SECTION;
 const ROLE_SEPARATOR = nsIAccessibleRole.ROLE_SEPARATOR;
 const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
 const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
@@ -149,17 +150,17 @@ function getRole(aAccOrElmOrID)
 {
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return -1;
 
   var role = -1;
   try {
     role = acc.role;
-  } catch(e) {
+  } catch (e) {
     ok(false, "Role for " + aAccOrElmOrID + " could not be retrieved!");
   }
 
   return role;
 }
 
 /**
  * Analogy of SimpleTest.is function used to check the role.
--- a/accessible/tests/mochitest/role/test_aria.html
+++ b/accessible/tests/mochitest/role/test_aria.html
@@ -26,16 +26,17 @@
       testRole("aria_checkbox", ROLE_CHECKBUTTON);
       testRole("aria_columnheader", ROLE_COLUMNHEADER);
       testRole("aria_combobox", ROLE_COMBOBOX);
       testRole("aria_dialog", ROLE_DIALOG);
       testRole("aria_directory", ROLE_LIST);
       testRole("aria_document", ROLE_DOCUMENT);
       testRole("aria_form", ROLE_FORM);
       testRole("aria_feed", ROLE_GROUPING);
+      testRole("aria_figure", ROLE_FIGURE);
       testRole("aria_grid", ROLE_TABLE);
       testRole("aria_gridcell", ROLE_GRID_CELL);
       testRole("aria_group", ROLE_GROUPING);
       testRole("aria_heading", ROLE_HEADING);
       testRole("aria_img", ROLE_GRAPHIC);
       testRole("aria_link", ROLE_LINK);
       testRole("aria_list", ROLE_LIST);
       testRole("aria_listbox", ROLE_LISTBOX);
@@ -48,17 +49,21 @@
       testRole("aria_menuitem", ROLE_MENUITEM);
       testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM);
       testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM);
       testRole("aria_note", ROLE_NOTE);
       testRole("aria_presentation", ROLE_TEXT); // weak role
       testRole("aria_progressbar", ROLE_PROGRESSBAR);
       testRole("aria_radio", ROLE_RADIOBUTTON);
       testRole("aria_radiogroup", ROLE_RADIO_GROUP);
-      testRole("aria_region", ROLE_PANE);
+      testRole("aria_region_no_name", ROLE_TEXT);
+      testRole("aria_region_has_label", ROLE_REGION);
+      testRole("aria_region_has_labelledby", ROLE_REGION);
+      testRole("aria_region_has_title", ROLE_REGION);
+      testRole("aria_region_empty_name", ROLE_TEXT);
       testRole("aria_row", ROLE_ROW);
       testRole("aria_rowheader", ROLE_ROWHEADER);
       testRole("aria_scrollbar", ROLE_SCROLLBAR);
       testRole("aria_searchbox", ROLE_ENTRY);
       testRole("aria_separator", ROLE_SEPARATOR);
       testRole("aria_slider", ROLE_SLIDER);
       testRole("aria_spinbutton", ROLE_SPINBUTTON);
       testRole("aria_status", ROLE_STATUSBAR);
@@ -102,21 +107,21 @@
       var weak_landmarks = ["banner", "complementary", "contentinfo",
           "main", "navigation", "search"];
       for (l in weak_landmarks)
         testRole(weak_landmarks[l], ROLE_SECTION);
 
       for (l in weak_landmarks) {
         var id = weak_landmarks[l] + "_table";
         testRole(id, ROLE_TABLE);
-        
+    
         var accessibleTable = getAccessible(id, [nsIAccessibleTable], null,
                                             DONOTFAIL_IF_NO_INTERFACE);
         ok(!!accessibleTable, "landmarked table should have nsIAccessibleTable");
-        
+    
         if (accessibleTable)
           is(accessibleTable.getCellAt(0,0).firstChild.name, "hi", "no cell");
       }
 
       //////////////////////////////////////////////////////////////////////////
       // test gEmptyRoleMap
       testRole("buttontable_row", ROLE_NOTHING);
       testRole("buttontable_cell", ROLE_NOTHING);
@@ -186,16 +191,21 @@
      title="Support ARIA 1.1 switch role">
     Bug 1136563
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
      title="Support ARIA 1.1 searchbox role">
     Bug 1121518
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1356049"
+     title="Map ARIA figure role">
+    Bug 1356049
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <span id="aria_alert" role="alert"/>
   <span id="aria_alertdialog" role="alertdialog"/>
   <span id="aria_application" role="application"/>
@@ -204,16 +214,17 @@
   <span id="aria_checkbox" role="checkbox"/>
   <span id="aria_columnheader" role="columnheader"/>
   <span id="aria_combobox" role="combobox"/>
   <span id="aria_dialog" role="dialog"/>
   <span id="aria_directory" role="directory"/>
   <span id="aria_document" role="document"/>
   <span id="aria_form" role="form"/>
   <span id="aria_feed" role="feed"/>
+  <span id="aria_figure" role="figure"/>
   <span id="aria_grid" role="grid"/>
   <span id="aria_gridcell" role="gridcell"/>
   <span id="aria_group" role="group"/>
   <span id="aria_heading" role="heading"/>
   <span id="aria_img" role="img"/>
   <span id="aria_link" role="link"/>
   <span id="aria_list" role="list"/>
   <span id="aria_listbox" role="listbox"/>
@@ -226,17 +237,21 @@
   <span id="aria_menuitem" role="menuitem"/>
   <span id="aria_menuitemcheckbox" role="menuitemcheckbox"/>
   <span id="aria_menuitemradio" role="menuitemradio"/>
   <span id="aria_note" role="note"/>
   <span id="aria_presentation" role="presentation" tabindex="0"/>
   <span id="aria_progressbar" role="progressbar"/>
   <span id="aria_radio" role="radio"/>
   <span id="aria_radiogroup" role="radiogroup"/>
-  <span id="aria_region" role="region"/>
+  <span id="aria_region_no_name" role="region"/>
+  <span id="aria_region_has_label" role="region" aria-label="label"/>
+  <span id="aria_region_has_labelledby" role="region" aria-labelledby="label"/><span id="label" aria-label="label">
+  <span id="aria_region_has_title" role="region" title="title"/>
+  <span id="aria_region_empty_name" role="region" aria-label="" title="" aria-labelledby="empty"/><span id="empty"/>
   <span id="aria_row" role="row"/>
   <span id="aria_rowheader" role="rowheader"/>
   <span id="aria_scrollbar" role="scrollbar"/>
   <span id="aria_searchbox" role="textbox"/>
   <span id="aria_separator" role="separator"/>
   <span id="aria_slider" role="slider"/>
   <span id="aria_spinbutton" role="spinbutton"/>
   <span id="aria_status" role="status"/>
--- a/accessible/tests/mochitest/selectable/test_aria.html
+++ b/accessible/tests/mochitest/selectable/test_aria.html
@@ -67,17 +67,17 @@
       select.addItemToSelection(0);
       testSelectableSelection(id, [ "listbox2_item1" ]);
       select.removeItemFromSelection(0);
       testSelectableSelection(id, [ ]);
       select.selectAll();
       testSelectableSelection(id, [ "listbox2_item1", "listbox2_item2" ]);
       select.unselectAll();
       testSelectableSelection(id, [ ]);
-      
+    
       //////////////////////////////////////////////////////////////////////////
       // role="grid"
 
       id = "grid1";
       ok(isAccessible(id, [nsIAccessibleSelectable]),
          "No selectable accessible for " + id);
 
       testSelectableSelection(id, [ ]);
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -32,16 +32,17 @@ const STATE_PROTECTED = nsIAccessibleSta
 const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
 const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
 const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
 const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
 const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
 const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;
 
 const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
+const EXT_STATE_CURRENT = nsIAccessibleStates.EXT_STATE_CURRENT;
 const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
 const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
 const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED;
 const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
 const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
 const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL;
 const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
 const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED;
@@ -182,17 +183,17 @@ function testStatesInSubtree(aAccOrElmOr
     // Right now, text leafs don't get tested because the states are not being
     // propagated.
     testStates(acc, aState, aExtraState, aAbsentState);
 
   // Iterate over its children to see if the state got propagated.
   var children = null;
   try {
     children = acc.children;
-  } catch(e) {}
+  } catch (e) {}
   ok(children, "Could not get children for " + aAccOrElmOrID +"!");
 
   if (children) {
     for (var i = 0; i < children.length; i++) {
       var childAcc = children.queryElementAt(i, nsIAccessible);
       testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState);
     }
   }
--- a/accessible/tests/mochitest/states/test_aria.html
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -41,17 +41,17 @@
           testStates(acc, STATE_FOCUSABLE);
         }
       }
 
       // Iterate over its children to see if the state got propagated.
       var children = null;
       try {
         children = acc.children;
-      } catch(e) {}
+      } catch (e) {}
       ok(children, "Could not get children for " + aAccOrElmOrID +"!");
 
       if (children) {
         for (var i = 0; i < children.length; i++) {
           var childAcc = children.queryElementAt(i, nsIAccessible);
           testAriaDisabledTree(childAcc);
         }
       }
@@ -89,16 +89,17 @@
         testStates(checkboxElem, STATE_MIXED, 0);
       }
 
       // aria-checked
       testStates("aria_checked_checkbox", STATE_CHECKED);
       testStates("aria_mixed_checkbox", STATE_MIXED);
       testStates("aria_checked_switch", STATE_CHECKED);
       testStates("aria_mixed_switch", 0, 0, STATE_MIXED); // unsupported
+      testStates("aria_mixed_switch", 0, 0, STATE_CHECKED); // not checked due to being unsupported
 
       // test disabled group and all its descendants to see if they are
       // disabled, too. See bug 429285.
       testAriaDisabledTree("group");
 
       // aria-modal
       testStates("aria_modal", 0, EXT_STATE_MODAL);
       testStates("aria_modal_false", 0, 0, 0, EXT_STATE_MODAL);
@@ -261,16 +262,23 @@
       testStates("aria_vtreegrid", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
 
       // indeterminate ARIA progressbars (no aria-valuenow or aria-valuetext attribute)
       // should expose mixed state
       testStates("aria_progressbar", STATE_MIXED);
       testStates("aria_progressbar_valuenow", 0, 0, STATE_MIXED);
       testStates("aria_progressbar_valuetext", 0, 0, STATE_MIXED);
 
+      // aria-current
+      testStates("current_page_1", 0, EXT_STATE_CURRENT);
+      testStates("page_2", 0, 0, EXT_STATE_CURRENT);
+      testStates("page_3", 0, 0, EXT_STATE_CURRENT);
+      testStates("page_4", 0, 0, EXT_STATE_CURRENT);
+      testStates("current_foo", 0, EXT_STATE_CURRENT);
+
       testStates("aria_listbox", STATE_FOCUSABLE);
       testStates("aria_grid", STATE_FOCUSABLE);
       testStates("aria_tree", STATE_FOCUSABLE);
       testStates("aria_treegrid", STATE_FOCUSABLE);
       testStates("aria_listbox_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_grid_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_tree_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_treegrid_disabled", 0, 0, STATE_FOCUSABLE);
@@ -355,16 +363,21 @@
      title="Pressed state is not exposed on a button element with aria-pressed attribute">
     Mozilla Bug 989958
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
      title="Support ARIA 1.1 switch role">
     Mozilla Bug 1136563
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1355921"
+     title="Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE">
+    Mozilla Bug 1355921
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="textbox_autocomplete_inline" role="textbox" aria-autocomplete="inline"></div>
   <div id="textbox_autocomplete_list" role="textbox" aria-autocomplete="list"></div>
@@ -626,10 +639,16 @@
   <div id="aria_treegrid_disabled" role="treegrid" aria-disabled="true">
     <div role="row"><div role="gridcell">H</div></div>
     <div role="row"><div role="gridcell">h</div></div>
   </div>
 
   <!-- Test that directory is readonly -->
   <div id="aria_directory" role="directory"></div>
 
+  <!-- aria-current -->
+  <div id="current_page_1" role="link" aria-current="page">1</div>
+  <div id="page_2" role="link" aria-current="false">2</div>
+  <div id="page_3" role="link">3</div>
+  <div id="page_4" role="link" aria-current="">4</div>
+  <div id="current_foo" aria-current="foo">foo</div>
 </body>
 </html>
--- a/accessible/tests/mochitest/states/test_docarticle.html
+++ b/accessible/tests/mochitest/states/test_docarticle.html
@@ -29,17 +29,17 @@
 
         document.designMode = "on";
 
         testStates(docAcc, 0, EXT_STATE_EDITABLE, STATE_READONLY);
         testStates("aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
         testStates("editable_aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
         testStates("article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
         testStates("editable_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
-  
+
         document.designMode = "off";
 
         testStates(docAcc, STATE_READONLY);
         testStates("aria_article", STATE_READONLY);
         testStates("editable_aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
         testStates("article", STATE_READONLY);
         testStates("editable_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
       }
--- a/accessible/tests/mochitest/table.js
+++ b/accessible/tests/mochitest/table.js
@@ -298,17 +298,17 @@ function testTableIndexes(aIdentifier, a
           try {
             strIdx = attrs.getStringProperty("table-cell-index");
           } catch (e) {
             ok(false,
                cellId + ": no cell index from object attributes on the cell accessible at index " + idx + ".");
           }
 
           if (strIdx) {
-            is (parseInt(strIdx), idx,
+            is(parseInt(strIdx), idx,
                 cellId + ": cell index from object attributes of cell accessible isn't corrent.");
           }
 
           // cell: table
           try {
             is(cellAcc.table, tableAcc,
                cellId + ": wrong table accessible for the cell.");
 
@@ -399,22 +399,22 @@ function testTableSelection(aIdentifier,
   is(acc.selectedColumnCount, selCols.length,
      msg + "Wrong count of selected columns for " + prettyName(aIdentifier));
 
   // getSelectedColumns test
   var actualSelColsCountObj = { value: null };
   var actualSelCols = acc.getSelectedColumnIndices(actualSelColsCountObj);
 
   var actualSelColsCount = actualSelColsCountObj.value;
-  is (actualSelColsCount, selCols.length,
+  is(actualSelColsCount, selCols.length,
       msg + "Wrong count of selected columns for " + prettyName(aIdentifier) +
       "from getSelectedColumns.");
 
   for (var i = 0; i < actualSelColsCount; i++) {
-    is (actualSelCols[i], selCols[i],
+    is(actualSelCols[i], selCols[i],
         msg + "Column at index " + selCols[i] + " should be selected.");
   }
 
   // Rows selection tests.
   var selRows = [];
 
   // isRowSelected test
   for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
@@ -439,22 +439,22 @@ function testTableSelection(aIdentifier,
   is(acc.selectedRowCount, selRows.length,
      msg + "Wrong count of selected rows for " + prettyName(aIdentifier));
 
   // getSelectedRows test
   var actualSelrowCountObj = { value: null };
   var actualSelRows = acc.getSelectedRowIndices(actualSelrowCountObj);
 
   var actualSelrowCount = actualSelrowCountObj.value;
-  is (actualSelrowCount, selRows.length,
+  is(actualSelrowCount, selRows.length,
       msg + "Wrong count of selected rows for " + prettyName(aIdentifier) +
       "from getSelectedRows.");
 
   for (var i = 0; i < actualSelrowCount; i++) {
-    is (actualSelRows[i], selRows[i],
+    is(actualSelRows[i], selRows[i],
         msg + "Row at index " + selRows[i] + " should be selected.");
   }
 
   // Cells selection tests.
   var selCells = [];
 
   // isCellSelected test
   for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
--- a/accessible/tests/mochitest/table/test_layoutguess.html
+++ b/accessible/tests/mochitest/table/test_layoutguess.html
@@ -61,17 +61,17 @@
       testAbsentAttrs("table6.2.3", attr);
 
       // table with abbr element
       testAbsentAttrs("table6.3", attr);
 
       // table with abbr element having empty text node
       testAbsentAttrs("table6.4", attr);
 
-      // table with abbr element and non-empty text node 
+      // table with abbr element and non-empty text node
       testAttrs("table6.5", attr, true);
 
       // layout table with nested table
       testAttrs("table9", attr, true);
 
       // layout table with 1 column
       testAttrs("table10", attr, true);
 
--- a/accessible/tests/mochitest/table/test_table_2.html
+++ b/accessible/tests/mochitest/table/test_table_2.html
@@ -10,21 +10,21 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
 
   <script type="text/javascript">
 
 function doTest()
 {
-  // Test table with role=alert. 
+  // Test table with role=alert.
   var tableInterfaceExposed = true;
   var accTable3 = getAccessible("table3", [nsIAccessibleTable], null, DONOTFAIL_IF_NO_INTERFACE);
   if (!accTable3)
-    tableInterfaceExposed = false;  
+    tableInterfaceExposed = false;
   ok(tableInterfaceExposed, "table interface is not exposed");
 
   if (tableInterfaceExposed) {
     testRole(accTable3, ROLE_ALERT);
 
     is(accTable3.getCellAt(0,0).firstChild.name, "cell0", "wrong cell");
     is(accTable3.getCellAt(0,1).firstChild.name, "cell1", "wrong cell");
   }
--- a/accessible/tests/mochitest/test_OuterDocAccessible.html
+++ b/accessible/tests/mochitest/test_OuterDocAccessible.html
@@ -39,33 +39,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 
           // see bug 440770, no actions wanted on outer doc
           is(outerDocAcc.actionCount, 0,
              "Wrong number of actions for internal frame!");
 
           try {
             outerDocAcc.getActionName(0);
             do_throw("No exception thrown for actionName!");
-          } catch(e) {
+          } catch (e) {
             ok(e.result, ns_error_invalid_arg,
                "Wrong return value for actionName call!");
           }
 
           try {
             actionTempStr = outerDocAcc.getActionDescription(0);
             do_throw("No exception thrown for actionDescription!");
-          } catch(e) {
+          } catch (e) {
             ok(e.result, ns_error_invalid_arg,
                "Wrong return value for actionDescription call!");
           }
 
           try {
             outerDocAcc.doAction(0);
             do_throw("No exception thrown for doAction!");
-          } catch(e) {
+          } catch (e) {
             ok(e.result, ns_error_invalid_arg,
                "Wrong return value for doAction call!");
           }
         }
       }
 
       SimpleTest.finish();
     }
--- a/accessible/tests/mochitest/test_aria_token_attrs.html
+++ b/accessible/tests/mochitest/test_aria_token_attrs.html
@@ -31,16 +31,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       testStates("button_pressed_true", STATE_PRESSED, 0, STATE_CHECKABLE);
       testStates("button_pressed_false", 0, 0, STATE_CHECKABLE | STATE_PRESSED);
       testStates("button_pressed_empty", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
       testStates("button_pressed_undefined", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
       testStates("button_pressed_absent", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
 
       // test (checkbox) checkable and checked states
       testStates("checkbox_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("checkbox_checked_mixed", (STATE_CHECKABLE | STATE_MIXED), 0, STATE_CHECKED);
       testStates("checkbox_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("checkbox_checked_empty", STATE_CHECKABLE , 0, STATE_CHECKED);
       testStates("checkbox_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("checkbox_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test native checkbox checked state and aria-checked state (if conflict, native wins)
       testStates("native_checkbox_nativechecked_ariatrue", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("native_checkbox_nativechecked_ariafalse", (STATE_CHECKABLE | STATE_CHECKED));
@@ -93,51 +94,55 @@ https://bugzilla.mozilla.org/show_bug.cg
       testStates("option_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("option_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("option_checked_empty", 0 , 0, STATE_CHECKABLE | STATE_CHECKED);
       testStates("option_checked_undefined", 0, 0, STATE_CHECKABLE | STATE_CHECKED);
       testStates("option_checked_absent", 0, 0, STATE_CHECKABLE | STATE_CHECKED);
 
       // test (menuitem) checkable and checked states, which are unsupported on this role
       testStates("menuitem_checked_true", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("menuitem_checked_mixed", 0, 0, (STATE_CHECKABLE | STATE_CHECKED | STATE_MIXED));
       testStates("menuitem_checked_false", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitem_checked_empty", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitem_checked_undefined", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitem_checked_absent", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
 
       // test (menuitemcheckbox) checkable and checked states
       testStates("menuitemcheckbox_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("menuitemcheckbox_checked_mixed", (STATE_CHECKABLE | STATE_MIXED), 0, STATE_CHECKED);
       testStates("menuitemcheckbox_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemcheckbox_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemcheckbox_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemcheckbox_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test (menuitemcheckbox) readonly states
       testStates("menuitemcheckbox_readonly_true", STATE_READONLY);
       testStates("menuitemcheckbox_readonly_false", 0, 0, STATE_READONLY);
       testStates("menuitemcheckbox_readonly_empty", 0, 0, STATE_READONLY);
       testStates("menuitemcheckbox_readonly_undefined", 0, 0, STATE_READONLY);
       testStates("menuitemcheckbox_readonly_absent", 0, 0, STATE_READONLY);
 
       // test (menuitemradio) checkable and checked states
       testStates("menuitemradio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("menuitemradio_checked_mixed", STATE_CHECKABLE, 0, (STATE_MIXED | STATE_CHECKED));
       testStates("menuitemradio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemradio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemradio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemradio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test (menuitemradio) readonly states
       testStates("menuitemradio_readonly_true", STATE_READONLY);
       testStates("menuitemradio_readonly_false", 0, 0, STATE_READONLY);
       testStates("menuitemradio_readonly_empty", 0, 0, STATE_READONLY);
       testStates("menuitemradio_readonly_undefined", 0, 0, STATE_READONLY);
       testStates("menuitemradio_readonly_absent", 0, 0, STATE_READONLY);
 
       // test (radio) checkable and checked states
       testStates("radio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("radio_checked_mixed", STATE_CHECKABLE, 0, (STATE_MIXED | STATE_CHECKED));
       testStates("radio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("radio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("radio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("radio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test (radiogroup) readonly states
       testStates("radiogroup_readonly_true", STATE_READONLY);
       testStates("radiogroup_readonly_false", 0, 0, STATE_READONLY);
@@ -230,16 +235,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <div id="button_pressed_true" role="button" aria-pressed="true">This button has aria-pressed="true" and should get ROLE_TOGGLE_BUTTON. It should also get STATE_PRESSED.</div>
   <div id="button_pressed_false" role="button" aria-pressed="false">This button has aria-pressed="false" and should get ROLE_TOGGLE_BUTTON.</div>
   <div id="button_pressed_empty" role="button" aria-pressed="">This button has aria-pressed="" and should <emph>not</emph> get ROLE_BUTTON.</div>
   <div id="button_pressed_undefined" role="button" aria-pressed="undefined">This button has aria-pressed="undefined" and should <emph>not</emph> get ROLE_TOGGLE_BUTTON.</div>
   <div id="button_pressed_absent" role="button">This button has <emph>no</emph> aria-pressed attribute and should <emph>not</emph> get ROLE_TOGGLE_BUTTON.</div>
 
   <div id="checkbox_checked_true" role="checkbox" aria-checked="true">This checkbox has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+  <div id="checkbox_checked_mixed" role="checkbox" aria-checked="mixed">This checkbox has aria-checked="mixed" and should get STATE_CHECKABLE. It should also get STATE_MIXED.</div>
   <div id="checkbox_checked_false" role="checkbox" aria-checked="false">This checkbox has aria-checked="false" and should get STATE_CHECKABLE.</div>
   <div id="checkbox_checked_empty" role="checkbox" aria-checked="">This checkbox has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
   <div id="checkbox_checked_undefined" role="checkbox" aria-checked="undefined">This checkbox has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
   <div id="checkbox_checked_absent" role="checkbox">This checkbox has <emph>no</emph> aria-checked attribute and should get STATE_CHECKABLE.</div>
 
   <form action="">
     <input id="native_checkbox_nativechecked_ariatrue" type="checkbox" checked="checked" aria-checked="true"/>
     <input id="native_checkbox_nativechecked_ariafalse" type="checkbox" checked="checked" aria-checked="false"/>
@@ -291,47 +297,51 @@ https://bugzilla.mozilla.org/show_bug.cg
     <div id="option_checked_undefined" role="option" aria-checked="undefined">item</div>
   </div>
   <div id="listbox_multiselectable_absent" role="listbox">
     <div id="option_checked_absent" role="option">item</div>
   </div>
 
   <div role="menu">
     <div id="menuitem_checked_true" role="menuitem" aria-checked="true">Generic menuitems don't support aria-checked.</div>
+    <div id="menuitem_checked_mixed" role="menuitem" aria-checked="mixed">Generic menuitems don't support aria-checked.</div>
     <div id="menuitem_checked_false" role="menuitem" aria-checked="false">Generic menuitems don't support aria-checked.</div>
     <div id="menuitem_checked_empty" role="menuitem" aria-checked="">Generic menuitems don't support aria-checked.</div>
     <div id="menuitem_checked_undefined" role="menuitem" aria-checked="undefined">Generic menuitems don't support aria-checked.</div>
     <div id="menuitem_checked_absent" role="menuitem">Generic menuitems don't support aria-checked.</div>
 
     <div id="menuitemcheckbox_checked_true" role="menuitemcheckbox" aria-checked="true">This menuitemcheckbox has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+    <div id="menuitemcheckbox_checked_mixed" role="menuitemcheckbox" aria-checked="mixed">This menuitemcheckbox has aria-checked="mixed" and should get STATE_CHECKABLE. It should also get STATE_MIXED.</div>
     <div id="menuitemcheckbox_checked_false" role="menuitemcheckbox" aria-checked="false">This menuitemcheckbox has aria-checked="false" and should get STATE_CHECKABLE.</div>
     <div id="menuitemcheckbox_checked_empty" role="menuitemcheckbox" aria-checked="">This menuitemcheckbox has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
     <div id="menuitemcheckbox_checked_undefined" role="menuitemcheckbox" aria-checked="undefined">This menuitemcheckbox has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
     <div id="menuitemcheckbox_checked_absent" role="menuitemcheckbox">This menuitemcheckbox has <emph>no</emph> aria-checked attribute and should <emph>not</emph> get STATE_CHECKABLE.</div>
 
     <div id="menuitemcheckbox_readonly_true" role="menuitemcheckbox" aria-readonly="true">This menuitemcheckbox has aria-readonly="true" and should get STATE_READONLY.</div>
     <div id="menuitemcheckbox_readonly_false" role="menuitemcheckbox" aria-readonly="false">This menuitemcheckbox has aria-readonly="false" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemcheckbox_readonly_empty" role="menuitemcheckbox" aria-readonly="">This menuitemcheckbox has aria-readonly="" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemcheckbox_readonly_undefined" role="menuitemcheckbox" aria-readonly="undefined">This menuitemcheckbox has aria-readonly="undefined" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemcheckbox_readonly_absent" role="menuitemcheckbox">This menuitemcheckbox has <emph>no</emph> aria-readonly attribute and should <emph>not</emph> get STATE_READONLY.</div>
 
     <div id="menuitemradio_checked_true" role="menuitemradio" aria-checked="true">This menuitem has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+    <div id="menuitemradio_checked_mixed" role="menuitemradio" aria-checked="mixed">This menuitem has aria-checked="mixed" and should get STATE_CHECKABLE. It should not get STATE_MIXED.</div>
     <div id="menuitemradio_checked_false" role="menuitemradio" aria-checked="false">This menuitem has aria-checked="false" and should get STATE_CHECKABLE.</div>
     <div id="menuitemradio_checked_empty" role="menuitemradio" aria-checked="">This menuitem has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
     <div id="menuitemradio_checked_undefined" role="menuitemradio" aria-checked="undefined">This menuitem has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
     <div id="menuitemradio_checked_absent" role="menuitemradio">This menuitem has <emph>no</emph> aria-checked attribute but should get STATE_CHECKABLE.</div>
   </div>
 
     <div id="menuitemradio_readonly_true" role="menuitemradio" aria-readonly="true">This menuitemradio has aria-readonly="true" and should get STATE_READONLY.</div>
     <div id="menuitemradio_readonly_false" role="menuitemradio" aria-readonly="false">This menuitemradio has aria-readonly="false" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemradio_readonly_empty" role="menuitemradio" aria-readonly="">This menuitemradio has aria-readonly="" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemradio_readonly_undefined" role="menuitemradio" aria-readonly="undefined">This menuitemradio has aria-readonly="undefined" and should <emph>not</emph> get STATE_READONLY.</div>
     <div id="menuitemradio_readonly_absent" role="menuitemradio">This menuitemradio has <emph>no</emph> aria-readonly attribute and should <emph>not</emph> get STATE_READONLY.</div>
 
   <div id="radio_checked_true" role="radio" aria-checked="true">This menuitem has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_CHECKED.</div>
+  <div id="radio_checked_mixed" role="radio" aria-checked="mixed">This radio button has aria-checked="mixed" and should get STATE_CHECKABLE. It should not get STATE_MIXED.</div>
   <div id="radio_checked_false" role="radio" aria-checked="false">This menuitem has aria-checked="false" and should get STATE_CHECKABLE.</div>
   <div id="radio_checked_empty" role="radio" aria-checked="">This menuitem has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
   <div id="radio_checked_undefined" role="radio" aria-checked="undefined">This menuitem has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
   <div id="radio_checked_absent" role="radio">This menuitem has <emph>no</emph> aria-checked attribute but should get STATE_CHECKABLE.</div>
 
   <div id="radiogroup_readonly_true" role="radiogroup" aria-readonly="true">
     <div role="radio">yes</div>
     <div role="radio">no</div>
--- a/accessible/tests/mochitest/test_nsIAccessibleDocument.html
+++ b/accessible/tests/mochitest/test_nsIAccessibleDocument.html
@@ -56,25 +56,25 @@ https://bugzilla.mozilla.org/show_bug.cg
         // uncomment the below two lines to enable the test.
 //        is(docAcc.docType, "HTML",
 //           "Wrong type of document!");
 
         // Test for correct nsIDOMDocument retrieval.
         var domDoc = null;
         try {
           domDoc = docAcc.DOMDocument.QueryInterface(nsIDOMDocument);
-        } catch(e) {}
+        } catch (e) {}
         ok(domDoc, "no nsIDOMDocument for this doc accessible!");
         is(domDoc, document, "Document nodes do not match!");
 
         // Test for correct nsIDOMWindow retrieval.
         var domWindow = null;
         try {
           domWindow = docAcc.window.QueryInterface(nsIDOMWindow);
-        } catch(e) {}
+        } catch (e) {}
         ok(domWindow, "no nsIDOMWindow for this doc accessible!");
         is(domWindow, window, "Window nodes do not match!");
       }
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/test_nsIAccessibleImage.html
+++ b/accessible/tests/mochitest/test_nsIAccessibleImage.html
@@ -43,25 +43,25 @@ https://bugzilla.mozilla.org/show_bug.cg
       if (parentY.value < 0)
         parentY.value = -1;
 
       // See if asking image for child at image's screen coordinates gives
       // correct accessible. getChildAtPoint operates on screen coordinates.
       var tempAcc = null;
       try {
         tempAcc = aAcc.getChildAtPoint(screenX.value, screenY.value);
-      } catch(e) {}
+      } catch (e) {}
       is(tempAcc, aAcc,
          "Wrong accessible returned for position of " + aID + "!");
 
       // get image's parent.
       var imageParentAcc = null;
       try {
         imageParentAcc = aAcc.parent;
-      } catch(e) {}
+      } catch (e) {}
       ok(imageParentAcc, "no parent accessible for " + aID + "!");
 
       if (imageParentAcc) {
         // See if parent's screen coordinates plus image's parent relative
         // coordinates equal to image's screen coordinates.
         var parentAccX = {}, parentAccY = {}, parentAccWidth = {},
             parentAccHeight = {};
         imageParentAcc.getBounds(parentAccX, parentAccY, parentAccWidth,
--- a/accessible/tests/mochitest/textattrs/test_general.html
+++ b/accessible/tests/mochitest/textattrs/test_general.html
@@ -548,17 +548,17 @@
        //////////////////////////////////////////////////////////////////////////
       // area19, "HTML5 mark tag" test
       // text enclosed in mark tag will have a different background color
       ID = "area19";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
 
       attrs = {};
       testTextAttrs(ID, 0, attrs, defAttrs, 0, 10);
-      
+    
       tempElem = getNode(ID).firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem);
       attrs = { "background-color": gComputedStyle.backgroundColor };
       testTextAttrs(ID, 11, attrs, defAttrs, 10, 17);
 
       attrs = {};
       testTextAttrs(ID, 18, attrs, defAttrs, 17, 28);
 
--- a/accessible/tests/mochitest/tree/test_brokencontext.html
+++ b/accessible/tests/mochitest/tree/test_brokencontext.html
@@ -65,17 +65,17 @@
 
     // HTML table display:inline inside table. We shouldn't be fooled
     // by the outside table and shouldn't create table accessible and table cell
     // accessible in this case.
     checkIfNotAccessible("inline_table2");
     checkIfNotAccessible("tr_in_inline_table2");
     checkIfTDGeneric("td_in_inline_table2");
 
-    // HTML table display:block inside table. 
+    // HTML table display:block inside table.
     checkIfNotAccessible("block_table");
     checkIfNotAccessible("tr_in_block_table");
     checkIfTDGeneric("td_in_block_table");
 
     ////////////////////////////////////////////////////////////////////////////
     // HTML list elements outside list context.
 
     ok(!isAccessible("presentation_ul"),
--- a/accessible/tests/mochitest/tree/test_txtctrl.html
+++ b/accessible/tests/mochitest/tree/test_txtctrl.html
@@ -68,17 +68,17 @@
           {
             role: ROLE_TEXT_LEAF,
             children: []
           }
         ]
       };
 
       testAccessibleTree("txc5", accTree);
-      
+    
       // input@type="tel", value
       accTree = {
         role: ROLE_ENTRY,
         children: [
           { // text child
             role: ROLE_TEXT_LEAF,
             children: []
           }
--- a/accessible/tests/mochitest/treeupdate/test_table.html
+++ b/accessible/tests/mochitest/treeupdate/test_table.html
@@ -31,20 +31,20 @@
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, aTableID)
       ];
 
       this.finalCheck = function appendCaption_finalCheck()
       {
         var tree =
           { TABLE: [
-            { CAPTION: [ 
+            { CAPTION: [
               { TEXT_LEAF: [] }
             ] },
-            { ROW: [ 
+            { ROW: [
               { CELL: [ {TEXT_LEAF: [] }]},
               { CELL: [ {TEXT_LEAF: [] }]}
             ] }
           ] };
         testAccessibleTree(aTableID, tree);
       }
 
       this.getID = function appendCaption_getID()
--- a/accessible/tests/mochitest/treeview.js
+++ b/accessible/tests/mochitest/treeview.js
@@ -143,17 +143,17 @@ nsTreeView.prototype =
   isSorted: function isSorted() {},
   toggleOpenState: function toggleOpenState(aIndex)
   {
     var data = this.getDataForIndex(aIndex);
 
     data.open = !data.open;
     var rowCount = this.getRowCountIntl(data.children);
 
-    if (data.open) 
+    if (data.open)
       this.mTree.rowCountChanged(aIndex + 1, rowCount);
     else
       this.mTree.rowCountChanged(aIndex + 1, -rowCount);
   },
   selectionChanged: function selectionChanged() {},
   cycleHeader: function cycleHeader(aCol) {},
   cycleCell: function cycleCell(aRow, aCol)
   {
@@ -213,17 +213,17 @@ nsTreeView.prototype =
   },
 
   getRowCountIntl: function getRowCountIntl(aChildren)
   {
     var rowCount = 0;
     for (var childIdx = 0; childIdx < aChildren.length; childIdx++) {
       rowCount++;
 
-      var data = aChildren[childIdx];      
+      var data = aChildren[childIdx];
       if (data.open)
         rowCount += this.getRowCountIntl(data.children);
     }
 
     return rowCount;
   },
 
   getInfoByIndexIntl: function getInfoByIndexIntl(aRowIdx, aInfo,
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -1,22 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Compatibility.h"
 
+#include "mozilla/WindowsVersion.h"
+#include "nsExceptionHandler.h"
+#include "nsUnicharUtils.h"
 #include "nsWindowsDllInterceptor.h"
 #include "nsWinUtils.h"
 #include "Statistics.h"
 
 #include "mozilla/Preferences.h"
 
+#include <shlobj.h>
+
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 /**
  * Return true if module version is lesser than the given version.
  */
 bool
 IsModuleVersionLessThan(HMODULE aModuleHandle, DWORD aMajor, DWORD aMinor)
@@ -198,8 +203,183 @@ Compatibility::Init()
     // SEH handlers.
     if (!sVectoredExceptionHandler) {
       sVectoredExceptionHandler =
         AddVectoredExceptionHandler(TRUE, &DetectInSendMessageExCompat);
     }
   }
 }
 
+#if !defined(HAVE_64BIT_BUILD)
+
+static bool
+ReadCOMRegDefaultString(const nsString& aRegPath, nsAString& aOutBuf)
+{
+  aOutBuf.Truncate();
+
+  // Get the required size and type of the registry value.
+  // We expect either REG_SZ or REG_EXPAND_SZ.
+  DWORD type;
+  DWORD bufLen = 0;
+  LONG result = ::RegGetValue(HKEY_CLASSES_ROOT, aRegPath.get(),
+                              nullptr, RRF_RT_ANY, &type, nullptr, &bufLen);
+  if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
+    return false;
+  }
+
+  // Now obtain the value
+  DWORD flags = type == REG_SZ ? RRF_RT_REG_SZ : RRF_RT_REG_EXPAND_SZ;
+
+  aOutBuf.SetLength((bufLen + 1) / sizeof(char16_t));
+
+  result = ::RegGetValue(HKEY_CLASSES_ROOT, aRegPath.get(), nullptr,
+                         flags, nullptr, aOutBuf.BeginWriting(), &bufLen);
+  if (result != ERROR_SUCCESS) {
+    aOutBuf.Truncate();
+    return false;
+  }
+
+  // Truncate terminator
+  aOutBuf.Truncate((bufLen + 1) / sizeof(char16_t) - 1);
+  return true;
+}
+
+static bool
+IsSystemOleAcc(nsCOMPtr<nsIFile>& aFile)
+{
+  // Use FOLDERID_SystemX86 so that Windows doesn't give us a redirected
+  // system32 if we're a 32-bit process running on a 64-bit OS. This is
+  // necessary because the values that we are reading from the registry
+  // are not redirected; they reference SysWOW64 directly.
+  PWSTR systemPath = nullptr;
+  HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_SystemX86, 0, nullptr,
+                                      &systemPath);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  nsCOMPtr<nsIFile> oleAcc;
+  nsresult rv = NS_NewLocalFile(nsDependentString(systemPath), false,
+                                getter_AddRefs(oleAcc));
+
+  ::CoTaskMemFree(systemPath);
+  systemPath = nullptr;
+
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  rv = oleAcc->Append(NS_LITERAL_STRING("oleacc.dll"));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  bool isEqual;
+  rv = oleAcc->Equals(aFile, &isEqual);
+  return NS_SUCCEEDED(rv) && isEqual;
+}
+
+static bool
+IsTypelibPreferred()
+{
+  // If IAccessible's Proxy/Stub CLSID is kUniversalMarshalerClsid, then any
+  // external a11y clients are expecting to use a typelib.
+  NS_NAMED_LITERAL_STRING(kUniversalMarshalerClsid,
+      "{00020424-0000-0000-C000-000000000046}");
+
+  NS_NAMED_LITERAL_STRING(kIAccessiblePSClsidPath,
+      "Interface\\{618736E0-3C3D-11CF-810C-00AA00389B71}\\ProxyStubClsid32");
+
+  nsAutoString psClsid;
+  if (!ReadCOMRegDefaultString(kIAccessiblePSClsidPath, psClsid)) {
+    return false;
+  }
+
+  return psClsid.Equals(kUniversalMarshalerClsid,
+                        nsCaseInsensitiveStringComparator());
+}
+
+static bool
+IsIAccessibleTypelibRegistered()
+{
+  // The system default IAccessible typelib is always registered with version
+  // 1.1, under the neutral locale (LCID 0).
+  NS_NAMED_LITERAL_STRING(kIAccessibleTypelibRegPath,
+    "TypeLib\\{1EA4DBF0-3C3B-11CF-810C-00AA00389B71}\\1.1\\0\\win32");
+
+  nsAutoString typelibPath;
+  if (!ReadCOMRegDefaultString(kIAccessibleTypelibRegPath, typelibPath)) {
+    return false;
+  }
+
+  nsCOMPtr<nsIFile> libTestFile;
+  nsresult rv = NS_NewLocalFile(typelibPath, false, getter_AddRefs(libTestFile));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  return IsSystemOleAcc(libTestFile);
+}
+
+static bool
+IsIAccessiblePSRegistered()
+{
+  NS_NAMED_LITERAL_STRING(kIAccessiblePSRegPath,
+    "CLSID\\{03022430-ABC4-11D0-BDE2-00AA001A1953}\\InProcServer32");
+
+  nsAutoString proxyStubPath;
+  if (!ReadCOMRegDefaultString(kIAccessiblePSRegPath, proxyStubPath)) {
+    return false;
+  }
+
+  nsCOMPtr<nsIFile> libTestFile;
+  nsresult rv = NS_NewLocalFile(proxyStubPath, false, getter_AddRefs(libTestFile));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  return IsSystemOleAcc(libTestFile);
+}
+
+static bool
+UseIAccessibleProxyStub()
+{
+  // If a typelib is preferred then external clients are expecting to use
+  // typelib marshaling, so we should use that whenever available.
+  if (IsTypelibPreferred() && IsIAccessibleTypelibRegistered()) {
+    return false;
+  }
+
+  // Otherwise we try the proxy/stub
+  if (IsIAccessiblePSRegistered()) {
+    return true;
+  }
+
+  // If we reach this point then something is seriously wrong with the
+  // IAccessible configuration in the computer's registry. Let's annotate this
+  // so that we can easily determine this condition during crash analysis.
+  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IAccessibleConfig"),
+                                     NS_LITERAL_CSTRING("NoSystemTypeLibOrPS"));
+  return false;
+}
+
+#endif // !defined(HAVE_64BIT_BUILD)
+
+uint16_t
+Compatibility::GetActCtxResourceId()
+{
+#if defined(HAVE_64BIT_BUILD)
+  // The manifest for 64-bit Windows is embedded with resource ID 64.
+  return 64;
+#else
+  // The manifest for 32-bit Windows is embedded with resource ID 32.
+  // Beginning with Windows 10 Creators Update, 32-bit builds always use the
+  // 64-bit manifest. Older builds of Windows may or may not require the 64-bit
+  // manifest: UseIAccessibleProxyStub() determines the course of action.
+  if (mozilla::IsWin10CreatorsUpdateOrLater() ||
+      UseIAccessibleProxyStub()) {
+    return 64;
+  }
+
+  return 32;
+#endif // defined(HAVE_64BIT_BUILD)
+}
+
--- a/accessible/windows/msaa/Compatibility.h
+++ b/accessible/windows/msaa/Compatibility.h
@@ -34,16 +34,22 @@ public:
    */
   static bool IsWE() { return !!(sConsumers & WE); }
 
   /**
    * Return true if Dolphin mode is enabled.
    */
   static bool IsDolphin() { return !!(sConsumers & DOLPHIN); }
 
+  /**
+   * @return ID of a11y manifest resource to be passed to
+   * mscom::ActivationContext
+   */
+  static uint16_t GetActCtxResourceId();
+
 private:
   Compatibility();
   Compatibility(const Compatibility&);
   Compatibility& operator = (const Compatibility&);
 
   /**
    * Initialize compatibility mode. Called by platform (see Platform.h) during
    * accessibility initialization.
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -9,31 +9,32 @@
 #include "AccEvent.h"
 #include "Compatibility.h"
 #include "HyperTextAccessibleWrap.h"
 #include "ia2AccessibleText.h"
 #include "nsIWindowsRegKey.h"
 #include "nsIXULRuntime.h"
 #include "nsWinUtils.h"
 #include "mozilla/a11y/ProxyAccessible.h"
+#include "mozilla/mscom/ActivationContext.h"
 #include "mozilla/mscom/InterceptorLog.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/WindowsVersion.h"
 #include "ProxyWrappers.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::mscom;
 
 static StaticAutoPtr<RegisteredProxy> gRegCustomProxy;
 static StaticAutoPtr<RegisteredProxy> gRegProxy;
 static StaticAutoPtr<RegisteredProxy> gRegAccTlb;
 static StaticAutoPtr<RegisteredProxy> gRegMiscTlb;
-
 void
 a11y::PlatformInit()
 {
   Compatibility::Init();
 
   nsWinUtils::MaybeStartWindowEmulation();
   ia2AccessibleText::InitTextChangeData();
   if (BrowserTabsRemoteAutostart()) {
--- a/addon-sdk/source/lib/sdk/places/host/host-tags.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-tags.js
@@ -32,28 +32,30 @@ const EVENT_MAP = {
 
 function tag (message) {
   let data = message.data;
   let resData = {
     id: message.id,
     event: message.event
   };
 
-  resData.data = taggingService.tagURI(newURI(data.url), data.tags);
+  if (data.tags && data.tags.length > 0)
+    resData.data = taggingService.tagURI(newURI(data.url), data.tags);
   respond(resData);
 }
 
 function untag (message) {
   let data = message.data;
   let resData = {
     id: message.id,
     event: message.event
   };
 
-  resData.data = taggingService.untagURI(newURI(data.url), data.tags);
+  if (!data.tags || data.tags.length > 0)
+    resData.data = taggingService.untagURI(newURI(data.url), data.tags);
   respond(resData);
 }
 
 function getURLsByTag (message) {
   let data = message.data;
   let resData = {
     id: message.id,
     event: message.event
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -3986,16 +3986,19 @@
       <serialNumber>MWzraR3LLhU9m/qKEhvVLQ==</serialNumber>
     </certItem>
     <certItem issuerName="MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=">
       <serialNumber>CdYL9vSQCEKzBwjO10ud2w==</serialNumber>
     </certItem>
     <certItem issuerName="MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=">
       <serialNumber>CMNfzETd7XxesS9FOUj9Mg==</serialNumber>
     </certItem>
+    <certItem issuerName="MGsxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRWSVNBMS8wLQYDVQQLEyZWaXNhIEludGVybmF0aW9uYWwgU2VydmljZSBBc3NvY2lhdGlvbjEcMBoGA1UEAxMTVmlzYSBlQ29tbWVyY2UgUm9vdA==">
+      <serialNumber>B2VhZAPxCDH3s9Mkbu3HfQ==</serialNumber>
+    </certItem>
     <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
       <serialNumber>CLc=</serialNumber>
     </certItem>
     <certItem issuerName="MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ==">
       <serialNumber>ATFEdg==</serialNumber>
     </certItem>
     <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
       <serialNumber>BAAAAAABIg08D3U=</serialNumber>
@@ -4598,16 +4601,19 @@
       <serialNumber>VN2yeFexyXjPf34fHGmbhg==</serialNumber>
     </certItem>
     <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
       <serialNumber>BAAAAAABLF5/Gog=</serialNumber>
     </certItem>
     <certItem issuerName="MDMxCzAJBgNVBAYTAlBUMQ0wCwYDVQQKDARTQ0VFMRUwEwYDVQQDDAxFQ1JhaXpFc3RhZG8=">
       <serialNumber>cx0HrIEQg8JHWTP7DzOxSQ==</serialNumber>
     </certItem>
+    <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+      <serialNumber>ANUANvVYN7xqAISA9rvJPzQ=</serialNumber>
+    </certItem>
     <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy">
       <serialNumber>GpO48aJ8GngtwECqZhm/xA==</serialNumber>
     </certItem>
     <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=">
       <serialNumber>EM8bDLBnnoYe4LnWpLIhS4esr3I=</serialNumber>
     </certItem>
     <certItem issuerName="MFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
       <serialNumber>IyIVazG4RE9AERkb+ekH8w==</serialNumber>
@@ -4922,16 +4928,19 @@
       <serialNumber>dhjnNtYx6cojdAE55TgIBA==</serialNumber>
     </certItem>
     <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
       <serialNumber>HNo1DR4XCe4mS1iUMsY6Wg==</serialNumber>
     </certItem>
     <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
       <serialNumber>BAAAAAABEAuMoRs=</serialNumber>
     </certItem>
+    <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+      <serialNumber>CgFBQQAAATjtdPY5AAAAAg==</serialNumber>
+    </certItem>
     <certItem issuerName="MGExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEbMBkGA1UEAxMSR2VvVHJ1c3QgRFYgU1NMIENB">
       <serialNumber>CWhp</serialNumber>
     </certItem>
     <certItem issuerName="MF8xCzAJBgNVBAYTAlRXMRIwEAYDVQQKDAlUQUlXQU4tQ0ExEDAOBgNVBAsMB1Jvb3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
       <serialNumber>DL8=</serialNumber>
     </certItem>
     <certItem issuerName="MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==">
       <serialNumber>Ermw0Q==</serialNumber>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -294,43 +294,32 @@ pref("browser.urlbar.clickSelectsAll", t
 pref("browser.urlbar.doubleClickSelectsAll", true);
 #else
 pref("browser.urlbar.doubleClickSelectsAll", false);
 #endif
 
 // Control autoFill behavior
 pref("browser.urlbar.autoFill", true);
 pref("browser.urlbar.autoFill.typed", true);
+pref("browser.urlbar.speculativeConnect.enabled", true);
 
 // 0: Match anywhere (e.g., middle of words)
 // 1: Match on word boundaries and then try matching anywhere
 // 2: Match only on word boundaries (e.g., after / or .)
 // 3: Match at the beginning of the url or title
 pref("browser.urlbar.matchBehavior", 1);
 pref("browser.urlbar.filter.javascript", true);
 
 // the maximum number of results to show in autocomplete when doing richResults
 pref("browser.urlbar.maxRichResults", 10);
 // The amount of time (ms) to wait after the user has stopped typing
 // before starting to perform autocomplete.  50 is the default set in
 // autocomplete.xml.
 pref("browser.urlbar.delay", 50);
 
-// The special characters below can be typed into the urlbar to either restrict
-// the search to visited history, bookmarked, tagged pages; or force a match on
-// just the title text or url.
-pref("browser.urlbar.restrict.history", "^");
-pref("browser.urlbar.restrict.bookmark", "*");
-pref("browser.urlbar.restrict.tag", "+");
-pref("browser.urlbar.restrict.openpage", "%");
-pref("browser.urlbar.restrict.typed", "~");
-pref("browser.urlbar.restrict.searches", "$");
-pref("browser.urlbar.match.title", "#");
-pref("browser.urlbar.match.url", "@");
-
 // The default behavior for the urlbar can be configured to use any combination
 // of the match filters with each additional filter adding more results (union).
 pref("browser.urlbar.suggest.history",              true);
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
 pref("browser.urlbar.suggest.searches",             true);
 pref("browser.urlbar.userMadeSearchSuggestionsChoice", false);
 // The suggestion opt-in notification will be shown on 4 different days.
@@ -686,17 +675,17 @@ pref("accessibility.typeaheadfind.flashB
 
 // Tracks when accessibility is loaded into the previous session.
 pref("accessibility.loadedInLastSession", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 // Should plugins that are hidden show the infobar UI?
-pref("plugins.show_infobar", true);
+pref("plugins.show_infobar", false);
 
 // Should dismissing the hidden plugin infobar suppress it permanently?
 pref("plugins.remember_infobar_dismissal", true);
 
 pref("plugin.default.state", 1);
 
 // Plugins bundled in XPIs are enabled by default.
 pref("plugin.defaultXpi.state", 2);
@@ -1073,17 +1062,19 @@ pref("security.sandbox.gpu.level", 0);
 // This pref is discussed in bug 1083344, the naming is inspired from its
 // Windows counterpart, but on Mac it's an integer which means:
 // 0 -> "no sandbox" (nightly only)
 // 1 -> "preliminary content sandboxing enabled: write access to
 //       home directory is prevented"
 // 2 -> "preliminary content sandboxing enabled with profile protection:
 //       write access to home directory is prevented, read and write access
 //       to ~/Library and profile directories are prevented (excluding
-//       $PROFILE/{extensions,weave})"
+//       $PROFILE/{extensions,chrome})"
+// 3 -> "no global read/write access, read access permitted to
+//       $PROFILE/{extensions,chrome}"
 // This setting is read when the content process is started. On Mac the content
 // process is killed when all windows are closed, so a change will take effect
 // when the 1st window is opened.
 #if defined(NIGHTLY_BUILD)
 pref("security.sandbox.content.level", 2);
 #else
 pref("security.sandbox.content.level", 1);
 #endif
@@ -1148,18 +1139,16 @@ pref("browser.taskbar.previews.cachetime
 pref("browser.taskbar.lists.enabled", true);
 pref("browser.taskbar.lists.frequent.enabled", true);
 pref("browser.taskbar.lists.recent.enabled", false);
 pref("browser.taskbar.lists.maxListItemCount", 7);
 pref("browser.taskbar.lists.tasks.enabled", true);
 pref("browser.taskbar.lists.refreshInSeconds", 120);
 #endif
 
-// The sync engines to use.
-pref("services.sync.registerEngines", "Bookmarks,Form,History,Password,Prefs,Tab,Addons,ExtensionStorage");
 // Preferences to be synced by default
 pref("services.sync.prefs.sync.accessibility.blockautorefresh", true);
 pref("services.sync.prefs.sync.accessibility.browsewithcaret", true);
 pref("services.sync.prefs.sync.accessibility.typeaheadfind", true);
 pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true);
 pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true);
 // The addons prefs related to repository verification are intentionally
 // not synced for security reasons. If a system is compromised, a user
@@ -1697,16 +1686,18 @@ pref("browser.sessionstore.restore_tabs_
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 pref("urlclassifier.malwareTable", "goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-proto,test-phish-simple");
 
 pref("browser.suppress_first_window_animation", true);
 
 // Preferences for Photon onboarding system extension
 pref("browser.onboarding.enabled", true);
+// Mark this as an upgraded profile so we don't offer the initial new user onboarding tour.
+pref("browser.onboarding.tourset-version", 1);
 pref("browser.onboarding.hidden", false);
 // On the Activity-Stream page, the snippet's position overlaps with our notification.
 // So use `browser.onboarding.notification.finished` to let the AS page know
 // if our notification is finished and safe to show their snippet.
 pref("browser.onboarding.notification.finished", false);
 
 // Preferences for the Screenshots feature:
 // Temporarily disable Screenshots in Beta & Release, so that we can gradually
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -172,21 +172,23 @@
         // defined, get the generic message
         var errTitle = document.getElementById("et_" + err);
         var errDesc  = document.getElementById("ed_" + err);
         if (!errTitle || !errDesc) {
           errTitle = document.getElementById("et_generic");
           errDesc  = document.getElementById("ed_generic");
         }
 
+        // eslint-disable-next-line no-unsanitized/property
         document.querySelector(".title-text").innerHTML = errTitle.innerHTML;
 
         var sd = document.getElementById("errorShortDescText");
         if (sd) {
           if (gIsCertError) {
+          // eslint-disable-next-line no-unsanitized/property
             sd.innerHTML = errDesc.innerHTML;
           } else {
             sd.textContent = getDescription();
           }
         }
         if (showCaptivePortalUI) {
           initPageCaptivePortal();
           return;
@@ -196,16 +198,17 @@
           return;
         }
         addAutofocus("errorTryAgain");
 
         document.body.className = "neterror";
 
         var ld = document.getElementById("errorLongDesc");
         if (ld) {
+        // eslint-disable-next-line no-unsanitized/property
           ld.innerHTML = errDesc.innerHTML;
         }
 
         if (err == "sslv3Used") {
           document.getElementById("learnMoreContainer").style.display = "block";
           let learnMoreLink = document.getElementById("learnMoreLink");
           learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-sslv3-error-messages-firefox";
           document.body.className = "certerror";
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -329,16 +329,17 @@ function showSnippets() {
   }
   _snippetsShown = true;
 
   let snippets = gSnippetsMap.get("snippets");
   // If there are remotely fetched snippets, try to to show them.
   if (snippets) {
     // Injecting snippets can throw if they're invalid XML.
     try {
+      // eslint-disable-next-line no-unsanitized/property
       snippetsElt.innerHTML = snippets;
       // Scripts injected by innerHTML are inactive, so we have to relocate them
       // through DOM manipulation to activate their contents.
       Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
         let relocatedScript = document.createElement("script");
         relocatedScript.type = "text/javascript";
         relocatedScript.text = elt.text;
         elt.parentNode.replaceChild(relocatedScript, elt);
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -5,31 +5,37 @@
 /**
  * Controls the "full zoom" setting and its site-specific preferences.
  */
 var FullZoom = {
   // Identifies the setting in the content prefs database.
   name: "browser.content.full-zoom",
 
   // browser.zoom.siteSpecific preference cache
+  // Enabling privacy.resistFingerprinting implies disabling browser.zoom.siteSpecific.
   _siteSpecificPref: undefined,
 
   // browser.zoom.updateBackgroundTabs preference cache
   updateBackgroundTabs: undefined,
 
   // This maps the browser to monotonically increasing integer
   // tokens. _browserTokenMap[browser] is increased each time the zoom is
   // changed in the browser. See _getBrowserToken and _ignorePendingZoomAccesses.
   _browserTokenMap: new WeakMap(),
 
   // Stores initial locations if we receive onLocationChange
   // events before we're initialized.
   _initialLocations: new WeakMap(),
 
   get siteSpecific() {
+    if (this._siteSpecificPref === undefined) {
+      this._siteSpecificPref =
+        !gPrefService.getBoolPref("privacy.resistFingerprinting") &&
+        gPrefService.getBoolPref("browser.zoom.siteSpecific");
+    }
     return this._siteSpecificPref;
   },
 
   // nsISupports
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                          Ci.nsIObserver,
                                          Ci.nsIContentPrefObserver,
@@ -41,24 +47,27 @@ var FullZoom = {
   init: function FullZoom_init() {
     gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);
 
     // Register ourselves with the service so we know when our pref changes.
     this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
                  getService(Ci.nsIContentPrefService2);
     this._cps2.addObserverForName(this.name, this);
 
-    this._siteSpecificPref =
-      gPrefService.getBoolPref("browser.zoom.siteSpecific");
     this.updateBackgroundTabs =
       gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
+
     // Listen for changes to the browser.zoom branch so we can enable/disable
     // updating background tabs and per-site saving and restoring of zoom levels.
     gPrefService.addObserver("browser.zoom.", this, true);
 
+    // Also need to listen to privacy.resistFingerprinting in order to update
+    // this._siteSpecificPref.
+    gPrefService.addObserver("privacy.resistFingerprinting", this, true);
+
     // If we received onLocationChange events for any of the current browsers
     // before we were initialized we want to replay those upon initialization.
     for (let browser of gBrowser.browsers) {
       if (this._initialLocations.has(browser)) {
         this.onLocationChange(...this._initialLocations.get(browser), browser);
       }
     }
 
@@ -88,19 +97,21 @@ var FullZoom = {
   },
 
   // nsIObserver
 
   observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "nsPref:changed":
         switch (aData) {
+          case "privacy.resistFingerprinting":
+            // fall through
           case "browser.zoom.siteSpecific":
-            this._siteSpecificPref =
-              gPrefService.getBoolPref("browser.zoom.siteSpecific");
+            // Invalidate pref cache.
+            this._siteSpecificPref = undefined;
             break;
           case "browser.zoom.updateBackgroundTabs":
             this.updateBackgroundTabs =
               gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
             break;
         }
         break;
     }
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -119,16 +119,17 @@ var gEMEHandler = {
       });
     }
 
     let iconURL = "chrome://browser/skin/drm-icon.svg#chains-black";
 
     // Do a little dance to get rich content into the notification:
     let fragment = document.createDocumentFragment();
     let descriptionContainer = document.createElement("description");
+    // eslint-disable-next-line no-unsanitized/property
     descriptionContainer.innerHTML = message;
     while (descriptionContainer.childNodes.length) {
       fragment.appendChild(descriptionContainer.childNodes[0]);
     }
 
     box.appendNotification(fragment, notificationId, iconURL, box.PRIORITY_WARNING_MEDIUM,
                            buttons);
   },
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -380,17 +380,17 @@
         ondragover="PlacesMenuDNDHandler.onDragOver(event);"
         ondrop="PlacesMenuDNDHandler.onDrop(event);">
     <menupopup id="bookmarksMenuPopup"
 #ifndef XP_MACOSX
                placespopup="true"
 #endif
                context="placesContext"
                openInTabs="children"
-               oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+               oncommand="BookmarksEventHandler.onCommand(event);"
                onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
                onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
                                if (!this.parentNode._placesView)
                                  new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                tooltip="bhTooltip" popupsinherittooltip="true">
       <menuitem id="bookmarksShowAll"
                 label="&showAllBookmarks2.label;"
                 command="Browser:ShowAllBookmarks"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -693,16 +693,30 @@ var PlacesCommandHook = {
     if (!organizer || organizer.closed) {
       // No currently open places window, so open one with the specified mode.
       openDialog("chrome://browser/content/places/places.xul",
                  "", "chrome,toolbar=yes,dialog=no,resizable", aLeftPaneRoot);
     } else {
       organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
       organizer.focus();
     }
+  },
+
+  searchBookmarks() {
+    if (!focusAndSelectUrlBar()) {
+      return;
+    }
+    for (let char of ["*", " "]) {
+      let code = char.charCodeAt(0);
+      gURLBar.inputField.dispatchEvent(new KeyboardEvent("keypress", {
+        keyCode: code,
+        charCode: code,
+        bubbles: true
+      }));
+    }
   }
 };
 
 XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
   "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");
 
 // View for the history menu.
 function HistoryMenu(aPopupShowingEvent) {
@@ -878,33 +892,31 @@ var BookmarksEventHandler = {
     if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) {
       // Don't open the root folder in tabs when the empty area on the toolbar
       // is middle-clicked or when a non-bookmark item except for Open in Tabs)
       // in a bookmarks menupopup is middle-clicked.
       if (target.localName == "menu" || target.localName == "toolbarbutton")
         PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView);
     } else if (aEvent.button == 1) {
       // left-clicks with modifier are already served by onCommand
-      this.onCommand(aEvent, aView);
+      this.onCommand(aEvent);
     }
   },
 
   /**
    * Handler for command event for an item in the bookmarks toolbar.
    * Menus and submenus from the folder buttons bubble up to this handler.
    * Opens the item.
    * @param aEvent
    *        DOMEvent for the command
-   * @param aView
-   *        The places view which aEvent should be associated with.
    */
-  onCommand: function BEH_onCommand(aEvent, aView) {
+  onCommand: function BEH_onCommand(aEvent) {
     var target = aEvent.originalTarget;
     if (target._placesNode)
-      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
+      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
   },
 
   fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
     var node;
     var cropped = false;
     var targetURI;
 
     if (aDocument.tooltipNode.localName == "treechildren") {
@@ -1351,17 +1363,17 @@ var BookmarkingUI = {
     // On non-photon, this code should never be reached. However, if you click
     // the outer button's border, some cpp code for the menu button's XBL
     // binding decides to open the popup even though the dropmarker is invisible.
     //
     // Separately, in Photon, if the button is in the dynamic portion of the
     // overflow panel, we want to show a subview instead.
     if (this.button.getAttribute("cui-areatype") == CustomizableUI.TYPE_MENU_PANEL ||
         (AppConstants.MOZ_PHOTON_THEME && this.button.hasAttribute("overflowedItem"))) {
-      this._showSubview();
+      this._showSubView();
       event.preventDefault();
       event.stopPropagation();
       return;
     }
 
     let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed) {
@@ -1835,34 +1847,37 @@ var BookmarkingUI = {
       this.dropmarkerNotifier.removeAttribute("notification");
       this.button.removeAttribute("notification");
 
       this.dropmarkerNotifier.style.transform = "";
       this.notifier.style.transform = "";
     }, 1000);
   },
 
-  _showSubview() {
+  showSubView(anchor) {
+    this._showSubView(anchor);
+  },
+
+  _showSubView(anchor = document.getElementById(this.BOOKMARK_BUTTON_ID)) {
     let view = document.getElementById("PanelUI-bookmarks");
     view.addEventListener("ViewShowing", this);
     view.addEventListener("ViewHiding", this);
-    let anchor = document.getElementById(this.BOOKMARK_BUTTON_ID);
     anchor.setAttribute("closemenu", "none");
     PanelUI.showSubView("PanelUI-bookmarks", anchor,
                         CustomizableUI.AREA_PANEL);
   },
 
   onCommand: function BUI_onCommand(aEvent) {
     if (aEvent.target != aEvent.currentTarget) {
       return;
     }
 
     // Handle special case when the button is in the panel.
     if (this.button.getAttribute("cui-areatype") == CustomizableUI.TYPE_MENU_PANEL) {
-      this._showSubview();
+      this._showSubView();
       return;
     }
     let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed) {
       // Close the overflow panel because the Edit Bookmark panel will appear.
       widget.node.removeAttribute("closemenu");
     }
@@ -1891,54 +1906,65 @@ var BookmarkingUI = {
         break;
       case "ViewHiding":
         this.onPanelMenuViewHiding(aEvent);
         break;
     }
   },
 
   onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
+    let panelview = aEvent.target;
     this.updateBookmarkPageMenuItem();
     // Update checked status of the toolbar toggle.
     let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
-    let personalToolbar = document.getElementById("PersonalToolbar");
-    if (personalToolbar.collapsed)
-      viewToolbar.removeAttribute("checked");
-    else
-      viewToolbar.setAttribute("checked", "true");
+    if (viewToolbar) {
+      let personalToolbar = document.getElementById("PersonalToolbar");
+      if (personalToolbar.collapsed)
+        viewToolbar.removeAttribute("checked");
+      else
+        viewToolbar.setAttribute("checked", "true");
+    }
     // Get all statically placed buttons to supply them with keyboard shortcuts.
-    let staticButtons = viewToolbar.parentNode.getElementsByTagName("toolbarbutton");
+    let staticButtons = panelview.getElementsByTagName("toolbarbutton");
     for (let i = 0, l = staticButtons.length; i < l; ++i)
       CustomizableUI.addShortcut(staticButtons[i]);
     // Setup the Places view.
-    this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU",
-                                                  "panelMenu_bookmarksMenu",
-                                                  "panelMenu_bookmarksMenu", {
-                                                    extraClasses: {
-                                                      entry: "subviewbutton",
-                                                      footer: "panel-subview-footer"
-                                                    }
-                                                  });
-    aEvent.target.removeEventListener("ViewShowing", this);
+    if (gPhotonStructure) {
+      // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
+      let query = "place:queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
+        "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
+        "&maxResults=42&excludeQueries=1";
+      this._panelMenuView = new PlacesPanelview(document.getElementById("panelMenu_bookmarksMenu"),
+        panelview, query);
+    } else {
+      this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU",
+        "panelMenu_bookmarksMenu", "panelMenu_bookmarksMenu", {
+          extraClasses: {
+            entry: "subviewbutton",
+            footer: "panel-subview-footer"
+          }
+        });
+    }
+    panelview.removeEventListener("ViewShowing", this);
   },
 
   onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
     this._panelMenuView.uninit();
     delete this._panelMenuView;
     aEvent.target.removeEventListener("ViewHiding", this);
   },
 
-  onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) {
+  onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent) {
     let target = aEvent.originalTarget;
     if (!target._placesNode)
       return;
     if (PlacesUtils.nodeIsContainer(target._placesNode))
       PlacesCommandHook.showPlacesOrganizer([ "BookmarksMenu", target._placesNode.itemId ]);
     else
-      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
+      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
     PanelUI.hide();
   },
 
   // nsINavBookmarkObserver
   onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGuid) {
     if (aURI && aURI.equals(this._uri)) {
       // If a new bookmark has been added to the tracked uri, register it.
       if (!this._itemGuids.has(aGuid)) {
--- a/browser/base/content/browser-sidebar.js
+++ b/browser/base/content/browser-sidebar.js
@@ -142,20 +142,20 @@ var SidebarUI = {
     if (!this._positionStart) {
       // DOM ordering is:     |  sidebar-box  | splitter |   appcontent  |
       // Want to display as:  |   appcontent  | splitter |  sidebar-box  |
       // So we just swap box and appcontent ordering
       let appcontent = document.getElementById("appcontent");
       let boxOrdinal = this._box.ordinal;
       this._box.ordinal = appcontent.ordinal;
       appcontent.ordinal = boxOrdinal;
-      // Indicate we've switched ordering to the splitter
-      this._splitter.setAttribute("overlapend", true);
+      // Indicate we've switched ordering to the box
+      this._box.setAttribute("positionend", true);
     } else {
-      this._splitter.removeAttribute("overlapend");
+      this._box.removeAttribute("positionend");
     }
 
     this.hideSwitcherPanel();
   },
 
   /**
    * Try and adopt the status of the sidebar from another window.
    * @param {Window} sourceWindow - Window to use as a source for sidebar status.
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -13,17 +13,21 @@
   --lwt-background-alignment: right top;
   --lwt-background-tiling: no-repeat;
 }
 
 :root:-moz-lwtheme {
   color: var(--lwt-text-color) !important;
 }
 
+%ifndef MOZ_PHOTON_THEME
 :root:-moz-lwtheme:not([customization-lwtheme]) {
+%else
+:root:-moz-lwtheme {
+%endif
   background-color: var(--lwt-accent-color) !important;
   background-image: var(--lwt-header-image), var(--lwt-additional-images) !important;
   background-position: var(--lwt-background-alignment) !important;
   background-repeat: var(--lwt-background-tiling) !important;
 }
 
 #main-window:not([chromehidden~="toolbar"]) {
 %ifdef XP_MACOSX
@@ -345,40 +349,76 @@ toolbarpaletteitem > toolbaritem[sdkstyl
   display: -moz-box;
 }
 
 @media not all and (min-resolution: 1.1dppx) {
   .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image, inherit);
   }
 
+  .webextension-browser-action:-moz-lwtheme-brighttext {
+    list-style-image: var(--webextension-toolbar-image-light, inherit);
+  }
+
+  .webextension-browser-action:-moz-lwtheme-darktext {
+    list-style-image: var(--webextension-toolbar-image-dark, inherit);
+  }
+
   .webextension-browser-action[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > .webextension-browser-action {
     list-style-image: var(--webextension-menupanel-image, inherit);
   }
 
+  .webextension-browser-action[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
+    list-style-image: var(--webextension-menupanel-image-light, inherit);
+  }
+
+  .webextension-browser-action[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
+    list-style-image: var(--webextension-menupanel-image-dark, inherit);
+  }
+
   .webextension-page-action {
     list-style-image: var(--webextension-urlbar-image, inherit);
   }
 
   .webextension-menuitem {
     list-style-image: var(--webextension-menuitem-image, inherit);
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image-2x, inherit);
   }
 
+  .webextension-browser-action:-moz-lwtheme-brighttext {
+    list-style-image: var(--webextension-toolbar-image-2x-light, inherit);
+  }
+
+  .webextension-browser-action:-moz-lwtheme-darktext {
+    list-style-image: var(--webextension-toolbar-image-2x-dark, inherit);
+  }
+
   .webextension-browser-action[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > .webextension-browser-action {
     list-style-image: var(--webextension-menupanel-image-2x, inherit);
   }
 
+  .webextension-browser-action[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
+    list-style-image: var(--webextension-menupanel-image-2x-light, inherit);
+  }
+
+  .webextension-browser-action[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
+    list-style-image: var(--webextension-menupanel-image-2x-dark, inherit);
+  }
+
   .webextension-page-action {
     list-style-image: var(--webextension-urlbar-image-2x, inherit);
   }
 
   .webextension-menuitem {
     list-style-image: var(--webextension-menuitem-image-2x, inherit);
   }
 }
@@ -429,16 +469,24 @@ toolbar:not(#TabsToolbar) > #personal-bo
   display: -moz-box;
 }
 
 #reload-button:not([displaystop]) + #stop-button,
 #reload-button[displaystop] {
   visibility: collapse;
 }
 
+/* The reload-button is only disabled temporarily when it becomes visible
+   to prevent users from accidentally clicking it. We don't however need
+   to show this disabled state, as the flicker that it generates is short
+   enough to be visible but not long enough to explain anything to users. */
+#reload-button[disabled]:not(:-moz-window-inactive) > .toolbarbutton-icon {
+  opacity: 1 !important;
+}
+
 #PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
 #panelMenu_bookmarksMenu > .bookmark-item {
   max-width: none;
 }
 
@@ -1143,29 +1191,31 @@ toolbarpaletteitem[place="palette"] > #d
 
 #BMB_bookmarksPopup[arrowposition="before_end"]:-moz-locale-dir(ltr),
 #BMB_bookmarksPopup[arrowposition="before_start"]:-moz-locale-dir(rtl) {
   transform-origin: calc(100% - 20px) bottom;
 }
 %endif
 
 /* Customize mode */
+%ifndef MOZ_PHOTON_THEME
 #navigator-toolbox,
 #browser-bottombox,
 #content-deck {
   transition-property: margin-left, margin-right;
   transition-duration: 200ms;
   transition-timing-function: linear;
 }
 
 #tab-view-deck[fastcustomizeanimation] #navigator-toolbox,
 #tab-view-deck[fastcustomizeanimation] #content-deck {
   transition-duration: 1ms;
   transition-timing-function: linear;
 }
+%endif
 
 #PanelUI-contents > .panel-customization-placeholder > .panel-customization-placeholder-child {
   list-style-image: none;
 }
 
 /* Apply crisp rendering for favicons at exactly 2dppx resolution */
 @media (resolution: 2dppx) {
   #PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon,
@@ -1190,31 +1240,58 @@ toolbarpaletteitem[place="palette"] > #d
   flex-grow: 1; /* Grow so there isn't empty space below the footer */
   min-height: 0; /* Allow this to shrink so the footer doesn't get pushed out. */
 }
 
 #customization-container[photon] > #customization-content-container > #customization-palette-container {
   flex-grow: 1;
 }
 
+#customization-container[photon] #customization-panelHolder {
+  padding-top: 10px;
+  padding-bottom: 10px;
+}
+
 #customization-panelHolder > #widget-overflow-fixed-list {
-  padding-bottom: 50px; /* Make sure there's always space to drop stuff. */
+  flex: 0 1 auto; /* Size to content, but allow ourselves to shrink */
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+}
+
+#customization-container:not([photon]) #customization-panelHolder {
+  overflow-y: hidden;
 }
 
-#customization-panelHolder {
-  overflow-y: hidden;
+#customization-container[photon] #customization-panel-container {
+  display: flex;
+  flex-direction: column;
+  flex: none;
+}
+
+#customization-container[photon] #customization-panelWrapper,
+#customization-container[photon] #customization-panelWrapper > .panel-arrowcontent,
+#customization-container[photon] #customization-panelHolder {
+  flex-direction: column;
+  display: flex;
+  min-height: 0;
+}
+
+#customization-container[photon] #customization-panelWrapper {
+  flex: 1 1 auto;
+  height: 0; /* Don't let my contents affect ancestors' content-based sizing */
+  align-items: end; /* align to the end on the cross-axis (affects arrow) */
 }
 
 #customization-panelWrapper,
 #customization-panelWrapper > .panel-arrowcontent {
   -moz-box-flex: 1;
 }
 
-#customization-panelWrapper > .panel-arrowcontent {
-  padding: 0 !important;
+#customization-container:not([photon]) #customization-panelWrapper > .panel-arrowcontent {
   overflow: hidden;
 }
 
 #customization-panelHolder > #PanelUI-mainView {
   display: flex;
   flex-direction: column;
   /* Hack alert - by manually setting the preferred height to 0, we convince
      #PanelUI-mainView to shrink when the window gets smaller in customization
@@ -1270,22 +1347,36 @@ toolbarpaletteitem[place="palette"] {
   /* icon (32) + margin (2 * 4) + button padding/border (2 * 4) + label margin (~2) + label
    * line-height (1.5em): */
   height: calc(50px + 1.5em);
   margin-bottom: 5px;
   overflow: hidden;
   display: inline-block;
 }
 
+#main-window[photon-structure] toolbarpaletteitem[place="palette"] {
+  -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbarpaletteitem-palette-wrapping-label");
+  width: 7em;
+  /* icon (16) + margin (9 + 12) + 3 lines of text: */
+  height: calc(39px + 3em);
+  margin-inline-end: 24px;
+  overflow: visible;
+}
+
 toolbarpaletteitem[place="palette"][hidden] {
   display: none;
 }
 
 #customization-palette .toolbarpaletteitem-box {
   -moz-box-pack: center;
+  width: 7em;
+  max-width: 7em;
+}
+
+#main-window:not([photon-structure]) #customization-palette .toolbarpaletteitem-box {
   -moz-box-flex: 1;
   width: 10em;
   max-width: 10em;
 }
 
 #main-window[customizing=true] .addon-banner-item,
 #main-window[customizing=true] .panel-banner-item {
   display: none;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1307,18 +1307,17 @@ var gBrowserInit = {
     SidebarUI.init();
 
     // Certain kinds of automigration rely on this notification to complete
     // their tasks BEFORE the browser window is shown. SessionStore uses it to
     // restore tabs into windows AFTER important parts like gMultiProcessBrowser
     // have been initialized.
     Services.obs.notifyObservers(window, "browser-window-before-show");
 
-    gUIDensity.update();
-    gPrefService.addObserver(gUIDensity.prefDomain, gUIDensity);
+    gUIDensity.init();
 
     let isResistFingerprintingEnabled = gPrefService.getBoolPref("privacy.resistFingerprinting");
 
     // Set a sane starting width/height for all resolutions on new profiles.
     if (isResistFingerprintingEnabled) {
       // When the fingerprinting resistance is enabled, making sure that we don't
       // have a maximum window to interfere with generating rounded window dimensions.
       document.documentElement.setAttribute("sizemode", "normal");
@@ -1629,16 +1628,20 @@ var gBrowserInit = {
     LightWeightThemeWebInstaller.init();
 
     if (Win7Features)
       Win7Features.onOpenWindow();
 
     FullScreen.init();
     PointerLock.init();
 
+    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+      ContextMenuTouchModeObserver.init();
+    }
+
     // initialize the sync UI
     gSync.init();
 
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
     gBrowserThumbnails.init();
 
@@ -1766,17 +1769,17 @@ var gBrowserInit = {
     FullScreen.uninit();
 
     gSync.uninit();
 
     gExtensionsNotifications.uninit();
 
     Services.obs.removeObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
 
-    gPrefService.removeObserver(gUIDensity.prefDomain, gUIDensity);
+    gUIDensity.uninit();
 
     try {
       gBrowser.removeProgressListener(window.XULBrowserWindow);
       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
     } catch (ex) {
     }
 
     PlacesToolbarHelper.uninit();
@@ -1838,16 +1841,19 @@ var gBrowserInit = {
       } catch (ex) {
         Cu.reportError(ex);
       }
 
       if (this.gmpInstallManager) {
         this.gmpInstallManager.uninit();
       }
 
+      if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+        ContextMenuTouchModeObserver.uninit();
+      }
       BrowserOffline.uninit();
       IndexedDBPromptHelper.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
 
     // Final window teardown, do this last.
     window.XULBrowserWindow = null;
@@ -5111,17 +5117,17 @@ nsBrowserAccess.prototype = {
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
-  openURI(aURI, aOpener, aWhere, aFlags) {
+  openURI(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
     // This function should only ever be called if we're opening a URI
     // from a non-remote browser window (via nsContentTreeOwner).
     if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
       Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
                      "openURI should only ever be called from non-remote browsers.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
@@ -5143,21 +5149,19 @@ nsBrowserAccess.prototype = {
       if (isExternal &&
           gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
         aWhere = gPrefService.getIntPref("browser.link.open_newwindow.override.external");
       else
         aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
     }
 
     let referrer = aOpener ? makeURI(aOpener.location.href) : null;
-    let triggeringPrincipal = null;
     let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET;
     if (aOpener && aOpener.document) {
       referrerPolicy = aOpener.document.referrerPolicy;
-      triggeringPrincipal = aOpener.document.nodePrincipal;
     }
     let isPrivate = aOpener
                   ? PrivateBrowsingUtils.isContentWindowPrivate(aOpener)
                   : PrivateBrowsingUtils.isWindowPrivate(window);
 
     switch (aWhere) {
       case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
         // FIXME: Bug 408379. So how come this doesn't send the
@@ -5181,28 +5185,28 @@ nsBrowserAccess.prototype = {
         let forceNotRemote = !!aOpener;
         let userContextId = aOpener && aOpener.document
                               ? aOpener.document.nodePrincipal.originAttributes.userContextId
                               : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
         let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
         let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy,
                                             isPrivate, isExternal,
                                             forceNotRemote, userContextId,
-                                            openerWindow, triggeringPrincipal);
+                                            openerWindow, aTriggeringPrincipal);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = window.content;
         if (aURI) {
           let loadflags = isExternal ?
                             Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
                             Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
           gBrowser.loadURIWithFlags(aURI.spec, {
-                                    triggeringPrincipal,
+                                    aTriggeringPrincipal,
                                     flags: loadflags,
                                     referrerURI: referrer,
                                     referrerPolicy,
                                     });
         }
         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
@@ -5444,36 +5448,60 @@ var gTabletModePageCounter = {
 };
 
 function displaySecurityInfo() {
   BrowserPageInfo(null, "securityTab");
 }
 
 // Updates the UI density (for touch and compact mode) based on the uidensity pref.
 var gUIDensity = {
+  MODE_NORMAL: 0,
   MODE_COMPACT: 1,
   MODE_TOUCH: 2,
-  prefDomain: "browser.uidensity",
+  uiDensityPref: "browser.uidensity",
+  autoTouchModePref: "browser.touchmode.auto",
+
+  init() {
+    this.update();
+    gPrefService.addObserver(this.uiDensityPref, this);
+    gPrefService.addObserver(this.autoTouchModePref, this);
+  },
+
+  uninit() {
+    gPrefService.removeObserver(this.uiDensityPref, this);
+    gPrefService.removeObserver(this.autoTouchModePref, this);
+  },
 
   observe(aSubject, aTopic, aPrefName) {
-    if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
+    if (aTopic != "nsPref:changed" ||
+        (aPrefName != this.uiDensityPref &&
+         aPrefName != this.autoTouchModePref)) {
       return;
+    }
 
     this.update();
   },
 
-  update() {
-    let mode;
+  getCurrentDensity() {
     // Automatically override the uidensity to touch in Windows tablet mode.
     if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
         WindowsUIUtils.inTabletMode &&
-        gPrefService.getBoolPref("browser.touchmode.auto")) {
-      mode = this.MODE_TOUCH;
-    } else {
-      mode = gPrefService.getIntPref(this.prefDomain);
+        gPrefService.getBoolPref(this.autoTouchModePref)) {
+      return { mode: this.MODE_TOUCH, overridden: true };
+    }
+    return { mode: gPrefService.getIntPref(this.uiDensityPref), overridden: false };
+  },
+
+  setCurrentMode(mode) {
+    gPrefService.setIntPref(this.uiDensityPref, mode);
+  },
+
+  update(mode) {
+    if (mode == null) {
+      mode = this.getCurrentDensity().mode;
     }
 
     let doc = document.documentElement;
     switch (mode) {
     case this.MODE_COMPACT:
       doc.setAttribute("uidensity", "compact");
       break;
     case this.MODE_TOUCH:
@@ -8164,47 +8192,47 @@ function switchToTabHavingURI(aURI, aOpe
   return false;
 }
 
 var RestoreLastSessionObserver = {
   init() {
     if (SessionStore.canRestoreLastSession &&
         !PrivateBrowsingUtils.isWindowPrivate(window)) {
       if (Services.prefs.getBoolPref("browser.tabs.restorebutton")) {
-        let {restoreTabsButton} = gBrowser.tabContainer;
+        let {restoreTabsButton, restoreTabsButtonWrapperWidth} = gBrowser.tabContainer;
         let restoreTabsButtonWrapper = restoreTabsButton.parentNode;
         restoreTabsButtonWrapper.setAttribute("session-exists", "true");
         gBrowser.tabContainer.updateSessionRestoreVisibility();
+        restoreTabsButton.style.maxWidth = `${restoreTabsButtonWrapperWidth}px`;
         gBrowser.tabContainer.addEventListener("TabOpen", this);
       }
       Services.obs.addObserver(this, "sessionstore-last-session-cleared", true);
       goSetCommandEnabled("Browser:RestoreLastSession", true);
     }
   },
 
   handleEvent(event) {
     switch (event.type) {
      case "TabOpen":
         this.removeRestoreButton();
         break;
     }
   },
 
   removeRestoreButton() {
-    let {restoreTabsButton, restoreTabsButtonWrapperWidth} = gBrowser.tabContainer;
+    let {restoreTabsButton} = gBrowser.tabContainer;
     let restoreTabsButtonWrapper = restoreTabsButton.parentNode;
-    restoreTabsButtonWrapper.removeAttribute("session-exists");
     gBrowser.tabContainer.addEventListener("transitionend", function maxWidthTransitionHandler(e) {
-      if (e.propertyName == "max-width") {
+      if (e.target == gBrowser.tabContainer && e.propertyName == "max-width") {
         gBrowser.tabContainer.updateSessionRestoreVisibility();
         gBrowser.tabContainer.removeEventListener("transitionend", maxWidthTransitionHandler);
       }
     });
-    restoreTabsButton.style.maxWidth = `${restoreTabsButtonWrapperWidth}px`;
-    requestAnimationFrame(() => restoreTabsButton.style.maxWidth = 0);
+    restoreTabsButtonWrapper.removeAttribute("session-exists");
+    restoreTabsButton.style.maxWidth = 0;
     gBrowser.tabContainer.removeEventListener("TabOpen", this);
   },
 
   observe() {
     // The last session can only be restored once so there's
     // no way we need to re-enable our menu item.
     Services.obs.removeObserver(this, "sessionstore-last-session-cleared");
     goSetCommandEnabled("Browser:RestoreLastSession", false);
@@ -8214,16 +8242,41 @@ var RestoreLastSessionObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 function restoreLastSession() {
   SessionStore.restoreLastSession();
 }
 
+/* Observes context menus and adjusts their size for better
+ * usability when opened via a touch screen. */
+var ContextMenuTouchModeObserver = {
+  init() {
+    window.addEventListener("popupshowing", this, true);
+  },
+
+  handleEvent(event) {
+    let target = event.originalTarget;
+    if (target.localName != "menupopup") {
+      return;
+    }
+
+    if (event.mozInputSource == MouseEvent.MOZ_SOURCE_TOUCH) {
+      target.setAttribute("touchmode", "true");
+    } else {
+      target.removeAttribute("touchmode");
+    }
+  },
+
+  uninit() {
+    window.removeEventListener("popupshowing", this, true);
+  },
+};
+
 var TabContextMenu = {
   contextTab: null,
   _updateToggleMuteMenuItem(aTab, aConditionFn) {
     ["muted", "soundplaying"].forEach(attr => {
       if (!aConditionFn || aConditionFn(attr)) {
         if (aTab.hasAttribute(attr)) {
           aTab.toggleMuteMenuItem.setAttribute(attr, "true");
         } else {
@@ -8283,17 +8336,17 @@ var TabContextMenu = {
     // Hide "Bookmark All Tabs" for a pinned tab.  Update its state if visible.
     let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
     bookmarkAllTabs.hidden = this.contextTab.pinned;
     if (!bookmarkAllTabs.hidden)
       PlacesCommandHook.updateBookmarkAllTabsCommand();
 
     // Adjust the state of the toggle mute menu item.
     let toggleMute = document.getElementById("context_toggleMuteTab");
-    if (this.contextTab.hasAttribute("blocked")) {
+    if (this.contextTab.hasAttribute("activemedia-blocked")) {
       toggleMute.label = gNavigatorBundle.getString("playTab.label");
       toggleMute.accessKey = gNavigatorBundle.getString("playTab.accesskey");
     } else if (this.contextTab.hasAttribute("muted")) {
       toggleMute.label = gNavigatorBundle.getString("unmuteTab.label");
       toggleMute.accessKey = gNavigatorBundle.getString("unmuteTab.accesskey");
     } else {
       toggleMute.label = gNavigatorBundle.getString("muteTab.label");
       toggleMute.accessKey = gNavigatorBundle.getString("muteTab.accesskey");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -471,17 +471,17 @@
                            label="&copyURLCmd.label;"
                            command="PageAction:copyURL"/>
             <toolbarbutton id="page-action-email-link-button"
                            class="subviewbutton subviewbutton-iconic"
                            label="&emailPageCmd.label;"
                            command="PageAction:emailLink"/>
             <toolbarbutton id="page-action-send-to-device-button"
                            class="subviewbutton subviewbutton-iconic subviewbutton-nav"
-                           label="&sendToDevice.label;"
+                           label="&sendToDevice.label2;"
                            closemenu="none"
                            oncommand="gPageActionButton.showSendToDeviceView(this);"/>
           </vbox>
         </panelview>
         <panelview id="page-action-sendToDeviceView"
                    class="PanelUI-subView"
                    title="&sendToDevice.viewTitle;">
           <vbox id="page-action-sendToDeviceView-body" class="panel-subview-body">
@@ -845,18 +845,18 @@
                          tooltiptext="&urlbar.microphoneBlocked.tooltip;"/>
                   <image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
                          tooltiptext="&urlbar.screenBlocked.tooltip;"/>
                   <image data-permission-id="persistent-storage" class="blocked-permission-icon persistent-storage-icon" role="button"
                          tooltiptext="&urlbar.persistentStorageBlocked.tooltip;"/>
                 </box>
                 <box id="notification-popup-box"
                      hidden="true"
-                     onmouseover="document.getElementById('identity-icon').classList.add('no-hover');"
-                     onmouseout="document.getElementById('identity-icon').classList.remove('no-hover');"
+                     onmouseover="document.getElementById('identity-box').classList.add('no-hover');"
+                     onmouseout="document.getElementById('identity-box').classList.remove('no-hover');"
                      align="center">
                   <image id="default-notification-icon" class="notification-anchor-icon" role="button"
                          tooltiptext="&urlbar.defaultNotificationAnchor.tooltip;"/>
                   <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
                          tooltiptext="&urlbar.geolocationNotificationAnchor.tooltip;"/>
                   <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
                          tooltiptext="&urlbar.addonsNotificationAnchor.tooltip;"/>
                   <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
@@ -974,17 +974,17 @@
                        oncommand="BookmarkingUI.onCommand(event);">
           <observes element="bookmarkThisPageBroadcaster" attribute="starred"/>
           <observes element="bookmarkThisPageBroadcaster" attribute="buttontooltiptext"/>
           <menupopup id="BMB_bookmarksPopup"
                      class="cui-widget-panel cui-widget-panelview cui-widget-panelWithFooter PanelUI-subView"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
-                     oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+                     oncommand="BookmarksEventHandler.onCommand(event);"
                      onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
                      onpopupshowing="BookmarkingUI.onPopupShowing(event);
                                      BookmarkingUI.attachPlacesView(event, this);"
                      tooltip="bhTooltip" popupsinherittooltip="true">
             <menuitem id="BMB_viewBookmarksSidebar"
                       class="subviewbutton"
                       label="&viewBookmarksSidebar2.label;"
                       type="checkbox"
@@ -1083,17 +1083,21 @@
                        onclick="BrowserGoHome(event);"
                        cui-areatype="toolbar"
                        aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
       </hbox>
 
       <toolbarbutton id="nav-bar-overflow-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
                      skipintoolbarset="true"
-                     tooltiptext="&navbarOverflow.label;"/>
+                     tooltiptext="&navbarOverflow.label;">
+        <box class="toolbarbutton-animatable-box">
+          <image class="toolbarbutton-animatable-image"/>
+        </box>
+      </toolbarbutton>
 
       <toolbaritem id="PanelUI-button"
                    class="chromeclass-toolbar-additional"
                    removable="false">
         <toolbarbutton id="PanelUI-menu-button"
                        class="toolbarbutton-1 badged-button"
                        consumeanchor="PanelUI-button"
                        label="&brandShortName;"
@@ -1138,17 +1142,17 @@
                        class="toolbarbutton-1"
                        mousethrough="never"
                        label="&bookmarksToolbarItem.label;"
                        oncommand="PlacesToolbarHelper.onPlaceholderCommand();"/>
         <hbox flex="1"
               id="PlacesToolbar"
               context="placesContext"
               onclick="BookmarksEventHandler.onClick(event, this._placesView);"
-              oncommand="BookmarksEventHandler.onCommand(event, this._placesView);"
+              oncommand="BookmarksEventHandler.onCommand(event);"
               tooltip="bhTooltip"
               popupsinherittooltip="true">
           <hbox flex="1">
             <hbox id="PlacesToolbarDropIndicatorHolder" align="center" collapsed="true">
               <image id="PlacesToolbarDropIndicator"
                      mousethrough="always"
                      collapsed="true"/>
             </hbox>
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -615,16 +615,17 @@ ContentSearchUIController.prototype = {
   },
 
   _updateSearchWithHeader() {
     if (!this._strings) {
       return;
     }
     let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
     if (this.input.value) {
+      // eslint-disable-next-line no-unsanitized/property
       searchWithHeader.innerHTML = this._strings.searchForSomethingWith;
       searchWithHeader.querySelector(".contentSearchSearchWithHeaderSearchText").textContent = this.input.value;
     } else {
       searchWithHeader.textContent = this._strings.searchWithHeader;
     }
   },
 
   _speculativeConnect() {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -57,24 +57,24 @@ function openContextMenu(aMessage) {
                               disableSetDesktopBackground: data.disableSetDesktopBg,
                               loginFillInfo: data.loginFillInfo,
                               parentAllowsMixedContent: data.parentAllowsMixedContent,
                               userContextId: data.userContextId,
                             };
   let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
   let event = gContextMenuContentData.event;
 
-  // Set touch mode to get larger menu items.
-  if (event.mozInputSource == MouseEvent.MOZ_SOURCE_TOUCH) {
-    popup.setAttribute("touchmode", "true");
-  } else {
-    popup.removeAttribute("touchmode");
-  }
+  // The event is a CPOW that can't be passed into the native openPopupAtScreen
+  // function. Therefore we synthesize a new MouseEvent to propagate the
+  // inputSource to the subsequently triggered popupshowing event.
+  var newEvent = document.createEvent("MouseEvent");
+  newEvent.initNSMouseEvent("contextmenu", true, true, null, 0, event.screenX, event.screenY,
+                            0, 0, false, false, false, false, 0, null, 0, event.mozInputSource);
 
-  popup.openPopupAtScreen(event.screenX, event.screenY, true);
+  popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent);
 }
 
 function nsContextMenu(aXulMenu, aIsShift) {
   this.shouldDisplay = true;
   this.initMenu(aXulMenu, aIsShift);
 }
 
 // Prototype for nsContextMenu "class."
@@ -111,17 +111,17 @@ nsContextMenu.prototype = {
         onCanvas: this.onCanvas,
         onEditableArea: this.onEditableArea,
         onPassword: this.onPassword,
         srcUrl: this.mediaURL,
         frameUrl: gContextMenuContentData ? gContextMenuContentData.docLocation : undefined,
         pageUrl: this.browser ? this.browser.currentURI.spec : undefined,
         linkText: this.linkTextStr,
         linkUrl: this.linkURL,
-        selectionText: this.isTextSelected ? this.selectionInfo.text : undefined,
+        selectionText: this.isTextSelected ? this.selectionInfo.fullText : undefined,
         frameId: this.frameOuterWindowID,
       };
       subject.wrappedJSObject = subject;
       Services.obs.notifyObservers(subject, "on-build-contextmenu");
     }
 
     this.isFrameImage = document.getElementById("isFrameImage");
     this.ellipsis = "\u2026";
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -19,27 +19,27 @@
 }
 
 .tab-close-button[pinned],
 .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]):not([blocked]),
+.tab-icon-sound:not([soundplaying]):not([muted]):not([activemedia-blocked]),
 .tab-icon-sound[pinned],
 .tab-sharing-icon-overlay,
 .tab-icon-overlay {
   display: none;
 }
 
 .tab-sharing-icon-overlay[sharing]:not([selected]),
 .tab-icon-overlay[soundplaying][pinned],
 .tab-icon-overlay[muted][pinned],
-.tab-icon-overlay[blocked][pinned],
+.tab-icon-overlay[activemedia-blocked][pinned],
 .tab-icon-overlay[crashed] {
   display: -moz-box;
 }
 
 .tab-label {
   white-space: nowrap;
 }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2155,19 +2155,19 @@
         "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
         "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
         "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
         "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
         "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
         "characterSet", "fullZoom", "textZoom", "webProgress",
         "addProgressListener", "removeProgressListener", "audioPlaybackStarted",
         "audioPlaybackStopped", "pauseMedia", "stopMedia",
-        "blockMedia", "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
+        "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
         "purgeSessionHistory", "stopScroll", "startScroll",
-        "userTypedValue", "userTypedClear"
+        "userTypedValue", "userTypedClear", "mediaBlocked"
       ]</field>
 
       <method name="_createLazyBrowser">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             let browser = aTab.linkedBrowser;
 
@@ -2221,18 +2221,30 @@
                       // initializing the reload.
                       aTab.addEventListener("SSTabRestoring", () => {
                         browser[name](params);
                       }, { once: true });
                       gBrowser._insertBrowser(aTab);
                     };
                   };
                   break;
+                case "resumeMedia":
+                  getter = () => {
+                    return () => {
+                      // No need to insert a browser, so we just call the browser's
+                      // method.
+                      aTab.addEventListener("SSTabRestoring", () => {
+                        browser[name]();
+                      }, { once: true });
+                    };
+                  };
+                  break;
                 case "userTypedValue":
                 case "userTypedClear":
+                case "mediaBlocked":
                   getter = () => {
                     return SessionStore.getLazyTabValue(aTab, name);
                   };
                   break;
                 default:
                   getter = () => {
                     if (AppConstants.NIGHTLY_BUILD) {
                       let message =
@@ -5076,17 +5088,17 @@
           } else if (tab._overPlayingIcon) {
             let stringID;
             if (tab.selected) {
               stringID = tab.linkedBrowser.audioMuted ?
                 "tabs.unmuteAudio.tooltip" :
                 "tabs.muteAudio.tooltip";
               label = stringWithShortcut(stringID, "key_toggleMute");
             } else {
-              if (tab.linkedBrowser.audioBlocked) {
+              if (tab.hasAttribute("activemedia-blocked")) {
                 stringID = "tabs.unblockAudio.tooltip";
               } else {
                 stringID = tab.linkedBrowser.audioMuted ?
                   "tabs.unmuteAudio.background.tooltip" :
                   "tabs.muteAudio.background.tooltip";
               }
 
               label = this.mStringBundle.getString(stringID);
@@ -5735,33 +5747,33 @@
       </handler>
       <handler event="DOMAudioPlaybackBlockStarted">
         <![CDATA[
           var tab = this.getTabFromAudioEvent(event)
           if (!tab) {
             return;
           }
 
-          if (!tab.hasAttribute("blocked")) {
-            tab.setAttribute("blocked", true);
-            this._tabAttrModified(tab, ["blocked"]);
+          if (!tab.hasAttribute("activemedia-blocked")) {
+            tab.setAttribute("activemedia-blocked", true);
+            this._tabAttrModified(tab, ["activemedia-blocked"]);
             tab.startMediaBlockTimer();
           }
         ]]>
       </handler>
       <handler event="DOMAudioPlaybackBlockStopped">
         <![CDATA[
           var tab = this.getTabFromAudioEvent(event)
           if (!tab) {
             return;
           }
 
-          if (tab.hasAttribute("blocked")) {
-            tab.removeAttribute("blocked");
-            this._tabAttrModified(tab, ["blocked"]);
+          if (tab.hasAttribute("activemedia-blocked")) {
+            tab.removeAttribute("activemedia-blocked");
+            this._tabAttrModified(tab, ["activemedia-blocked"]);
             let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
             hist.add(2 /* unblockByVisitingTab */);
             tab.finishMediaBlockTimer();
           }
         ]]>
       </handler>
     </handlers>
   </binding>
@@ -6058,21 +6070,21 @@
               }
 
               break;
           }
         ]]></body>
       </method>
 
       <property name="_isCustomizing" readonly="true">
-        <getter>
+        <getter><![CDATA[
           let root = document.documentElement;
           return root.getAttribute("customizing") == "true" ||
-                 root.getAttribute("customize-exiting") == "true";
-        </getter>
+                 (!AppConstants.MOZ_PHOTON_THEME && root.getAttribute("customize-exiting") == "true");
+        ]]></getter>
       </property>
 
       <method name="_setPositionalAttributes">
         <body><![CDATA[
           let visibleTabs = this.tabbrowser.visibleTabs;
 
           if (!visibleTabs.length)
             return;
@@ -7347,30 +7359,30 @@
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:hbox class="tab-label-container"
                     xbl:inherits="pinned,selected=visuallyselected,labeldirection"
                     onoverflow="this.setAttribute('textoverflow', 'true');"
                     onunderflow="this.removeAttribute('textoverflow');"
                     flex="1">
             <xul:label class="tab-text tab-label"
                        xbl:inherits="xbl:text=label,accesskey,fadein,pinned,selected=visuallyselected,attention"
                        role="presentation"/>
           </xul:hbox>
-          <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected"
+          <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected=visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
@@ -7433,21 +7445,16 @@
           return this.getAttribute("hidden") == "true";
         </getter>
       </property>
       <property name="muted" readonly="true">
         <getter>
           return this.getAttribute("muted") == "true";
         </getter>
       </property>
-      <property name="blocked" readonly="true">
-        <getter>
-          return this.getAttribute("blocked") == "true";
-        </getter>
-      </property>
       <!--
       Describes how the tab ended up in this mute state. May be any of:
 
        - undefined: The tabs mute state has never changed.
        - null: The mute state was last changed through the UI.
        - Any string: The ID was changed through an extension API. The string
                      must be the ID of the extension which changed it.
       -->
@@ -7462,19 +7469,19 @@
       </property>
 
       <property name="soundPlaying" readonly="true">
         <getter>
           return this.getAttribute("soundplaying") == "true";
         </getter>
       </property>
 
-      <property name="soundBlocked" readonly="true">
+      <property name="activeMediaBlocked" readonly="true">
         <getter>
-          return this.getAttribute("blocked") == "true";
+          return this.getAttribute("activemedia-blocked") == "true";
         </getter>
       </property>
 
       <property name="lastAccessed">
         <getter>
           return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
         </getter>
       </property>
@@ -7485,17 +7492,17 @@
         ]]></body>
       </method>
 
       <field name="mOverCloseButton">false</field>
       <property name="_overPlayingIcon" readonly="true">
         <getter><![CDATA[
           let iconVisible = this.hasAttribute("soundplaying") ||
                             this.hasAttribute("muted") ||
-                            this.hasAttribute("blocked");
+                            this.hasAttribute("activemedia-blocked");
           let soundPlayingIcon =
             document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
           let overlayIcon =
             document.getAnonymousElementByAttribute(this, "anonid", "overlay-icon");
 
           return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
                  (overlayIcon && overlayIcon.matches(":hover") && iconVisible);
         ]]></getter>
@@ -7582,19 +7589,19 @@
             return;
           }
 
           let tabContainer = this.parentNode;
           let browser = this.linkedBrowser;
           let modifiedAttrs = [];
           let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
 
-          if (browser.audioBlocked) {
-            this.removeAttribute("blocked");
-            modifiedAttrs.push("blocked");
+          if (this.hasAttribute("activemedia-blocked")) {
+            this.removeAttribute("activemedia-blocked");
+            modifiedAttrs.push("activemedia-blocked");
 
             browser.resumeMedia();
             hist.add(3 /* unblockByClickingIcon */);
             this.finishMediaBlockTimer();
           } else {
             if (browser.audioMuted) {
               browser.unmute();
               this.removeAttribute("muted");
--- a/browser/base/content/test/contextMenu/browser.ini
+++ b/browser/base/content/test/contextMenu/browser.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 support-files =
   !/browser/base/content/test/general/contextmenu_common.js
   subtst_contextmenu_webext.html
 
 [browser_contextmenu_mozextension.js]
+[browser_contextmenu_touch.js]
+skip-if = !(os == 'win' && os_version == '10.0')
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_touch.js
@@ -0,0 +1,73 @@
+/* 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/. */
+
+/* This test checks that context menus are in touchmode
+ * when opened through a touch event (long tap). */
+
+async function openAndCheckContextMenu(contextMenu, target) {
+  is(contextMenu.state, "closed", "Context menu is initally closed.");
+
+  let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  EventUtils.synthesizeNativeTapAtCenter(target, true);
+  await popupshown;
+
+  is(contextMenu.state, "open", "Context menu is open.");
+  is(contextMenu.getAttribute("touchmode"), "true", "Context menu is in touchmode.");
+
+  contextMenu.hidePopup();
+
+  popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  EventUtils.synthesizeMouseAtCenter(target, {type: "contextmenu"});
+  await popupshown;
+
+  is(contextMenu.state, "open", "Context menu is open.");
+  ok(!contextMenu.hasAttribute("touchmode"), "Context menu is not in touchmode.");
+
+  contextMenu.hidePopup();
+}
+
+// Test the content area context menu.
+add_task(async function test_contentarea_contextmenu_touch() {
+  await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
+    let contextMenu = document.getElementById("contentAreaContextMenu");
+    await openAndCheckContextMenu(contextMenu, browser);
+  });
+});
+
+// Test the back and forward buttons.
+add_task(async function test_back_forward_button_contextmenu_touch() {
+  await BrowserTestUtils.withNewTab("http://example.com", async function(browser) {
+    let contextMenu = document.getElementById("backForwardMenu");
+
+    let backbutton = document.getElementById("back-button");
+    let notDisabled = BrowserTestUtils.waitForCondition(() => !backbutton.hasAttribute("disabled"));
+    BrowserTestUtils.loadURI(browser, "http://example.org");
+    await notDisabled;
+    await openAndCheckContextMenu(contextMenu, backbutton);
+
+
+    let forwardbutton = document.getElementById("forward-button");
+    notDisabled = BrowserTestUtils.waitForCondition(() => !forwardbutton.hasAttribute("disabled"));
+    backbutton.click();
+    await notDisabled;
+    await openAndCheckContextMenu(contextMenu, forwardbutton);
+  });
+});
+
+// Test the toolbar context menu.
+add_task(async function test_toolbar_contextmenu_touch() {
+  let toolbarContextMenu = document.getElementById("toolbar-context-menu");
+  let target = document.getElementById("PanelUI-menu-button");
+  await openAndCheckContextMenu(toolbarContextMenu, target);
+});
+
+// Test the urlbar input context menu.
+add_task(async function test_urlbar_contextmenu_touch() {
+  let urlbar = document.getElementById("urlbar");
+  let textBox = document.getAnonymousElementByAttribute(urlbar,
+                                      "anonid", "textbox-input-box");
+  let menu = document.getAnonymousElementByAttribute(textBox,
+                                      "anonid", "input-box-contextmenu");
+  await openAndCheckContextMenu(menu, textBox);
+});
--- a/browser/base/content/test/general/browser_aboutSupport_newtab_security_state.js
+++ b/browser/base/content/test/general/browser_aboutSupport_newtab_security_state.js
@@ -1,18 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: window.location is null");
-
-
 add_task(async function checkIdentityOfAboutSupport() {
   let tab = gBrowser.loadOneTab("about:support", {
     referrerURI: null,
     inBackground: false,
     allowThirdPartyFixup: false,
     relatedToCurrent: false,
     skipAnimation: true,
     allowMixedContent: false,
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 /**
  * Test opening and closing the bookmarks panel.
  */
 
 let bookmarkPanel = document.getElementById("editBookmarkPanel");
-let bookmarkStar = BookmarkingUI.star;
+let bookmarkStar = AppConstants.MOZ_PHOTON_THEME ? BookmarkingUI.star : BookmarkingUI.button;
 let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
 let editBookmarkPanelRemoveButtonRect;
 
 StarUI._closePanelQuickForTesting = true;
 
 async function test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
                                 shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
   await BrowserTestUtils.withNewTab({gBrowser, url: "about:home"}, async function(browser) {
--- a/browser/base/content/test/general/browser_bug520538.js
+++ b/browser/base/content/test/general/browser_bug520538.js
@@ -1,15 +1,16 @@
 function test() {
   var tabCount = gBrowser.tabs.length;
   gBrowser.selectedBrowser.focus();
   window.browserDOMWindow.openURI(makeURI("about:blank"),
                                   null,
                                   Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
-                                  Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+                                  Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
+                                  Services.scriptSecurityManager.getSystemPrincipal());
   is(gBrowser.tabs.length, tabCount + 1,
      "'--new-tab about:blank' opens a new tab");
   is(gBrowser.selectedTab, gBrowser.tabs[tabCount],
      "'--new-tab about:blank' selects the new tab");
   is(document.activeElement, gURLBar.inputField,
      "'--new-tab about:blank' focuses the location bar");
   gBrowser.removeCurrentTab();
 }
--- a/browser/base/content/test/general/browser_bug537474.js
+++ b/browser/base/content/test/general/browser_bug537474.js
@@ -1,7 +1,8 @@
 add_task(async function() {
   let browserLoadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   window.browserDOMWindow.openURI(makeURI("about:"), null,
-                                  Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW, null)
+                                  Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW, null,
+                                  Services.scriptSecurityManager.getSystemPrincipal())
   await browserLoadedPromise;
   is(gBrowser.currentURI.spec, "about:", "page loads in the current content window");
 });
--- a/browser/base/content/test/general/browser_bug575561.js
+++ b/browser/base/content/test/general/browser_bug575561.js
@@ -42,56 +42,50 @@ add_task(async function() {
     let link = doc.createElement("a");
     link.textContent = "Link to Mozilla";
     link.href = "about:logo";
     doc.body.appendChild(link);
     return link;
   }, true, false, false, "about:robots");
 });
 
-var waitForPageLoad = async function(browser, linkLocation) {
-  await waitForDocLoadComplete();
-
-  is(browser.contentDocument.location.href, linkLocation, "Link should not open in a new tab");
-};
-
-var waitForTabOpen = async function() {
-  let event = await promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true);
-  ok(true, "Link should open a new tab");
-
-  await waitForDocLoadComplete(event.target.linkedBrowser);
-  await Promise.resolve();
-
-  gBrowser.removeCurrentTab();
-};
-
-var testLink = async function(aLinkIndexOrFunction, pinTab, expectNewTab, testSubFrame, aURL = TEST_URL) {
+async function testLink(aLinkIndexOrFunction, pinTab, expectNewTab, testSubFrame, aURL = TEST_URL) {
   let appTab = BrowserTestUtils.addTab(gBrowser, aURL, {skipAnimation: true});
   if (pinTab)
     gBrowser.pinTab(appTab);
   gBrowser.selectedTab = appTab;
 
-  await waitForDocLoadComplete();
-
   let browser = appTab.linkedBrowser;
-  if (testSubFrame)
-    browser = browser.contentDocument.querySelector("iframe");
+  await BrowserTestUtils.browserLoaded(browser);
 
-  let link;
-  if (typeof aLinkIndexOrFunction == "function") {
-    link = aLinkIndexOrFunction(browser.contentDocument);
+  let promise;
+  if (expectNewTab) {
+    promise = BrowserTestUtils.waitForNewTab(gBrowser).then(tab => {
+      let loaded = tab.linkedBrowser.documentURI.spec;
+      return BrowserTestUtils.removeTab(tab).then(() => loaded);
+    });
   } else {
-    link = browser.contentDocument.querySelectorAll("a")[aLinkIndexOrFunction];
+    promise = BrowserTestUtils.browserLoaded(browser, testSubFrame);
   }
 
-  let promise;
-  if (expectNewTab)
-    promise = waitForTabOpen();
-  else
-    promise = waitForPageLoad(browser, link.href);
+  let href;
+  if (typeof aLinkIndexOrFunction === "function") {
+    ok(!browser.isRemoteBrowser, "don't pass a function for a remote browser");
+    let link = aLinkIndexOrFunction(browser.contentDocument);
+    info("Clicking " + link.textContent);
+    link.click();
+    href = link.href;
+  } else {
+    href = await ContentTask.spawn(browser, [ testSubFrame, aLinkIndexOrFunction ], function([ subFrame, index ]) {
+      let doc = subFrame ? content.document.querySelector("iframe").contentDocument : content.document;
+      let link = doc.querySelectorAll("a")[index];
 
-  info("Clicking " + link.textContent);
-  link.click();
+      info("Clicking " + link.textContent);
+      link.click();
+      return link.href;
+    });
+  }
 
-  await promise;
-
-  gBrowser.removeTab(appTab);
-};
+  info(`Waiting on load of ${href}`);
+  let loaded = await promise;
+  is(loaded, href, "loaded the right document");
+  await BrowserTestUtils.removeTab(appTab);
+}
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -35,50 +35,41 @@ function promisePopupNotificationShown(n
 
 var TESTS = [
 function test_install_http() {
   is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
 
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "http://example.org/browser/browser/base/content/test/general/bug592338.html");
-  gBrowser.selectedBrowser.addEventListener("pageshow", function() {
-    if (gBrowser.contentDocument.location.href == "about:blank")
-      return;
-
-    gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee);
+  // NB: Not https so no installs allowed.
+  const URL = "http://example.org/browser/browser/base/content/test/general/bug592338.html";
+  BrowserTestUtils.openNewForegroundTab({ gBrowser, url: URL }).then(async function() {
+    let prompted = promisePopupNotificationShown("addon-webext-permissions");
+    BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
+    await prompted;
 
-    executeSoon(function() {
-      BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
+    is(LightweightThemeManager.currentTheme, null, "Should not have installed the test theme");
 
-      is(LightweightThemeManager.currentTheme, null, "Should not have installed the test theme");
+    gBrowser.removeTab(gBrowser.selectedTab);
 
-      gBrowser.removeTab(gBrowser.selectedTab);
+    pm.remove(makeURI("http://example.org/"), "install");
 
-      pm.remove(makeURI("http://example.org/"), "install");
-
-      runNextTest();
-    });
+    runNextTest();
   });
 },
 
 function test_install_lwtheme() {
   is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
 
   var pm = Services.perms;
   pm.add(makeURI("https://example.com/"), "install", pm.ALLOW_ACTION);
 
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "https://example.com/browser/browser/base/content/test/general/bug592338.html");
-  gBrowser.selectedBrowser.addEventListener("pageshow", function() {
-    if (gBrowser.contentDocument.location.href == "about:blank")
-      return;
-
-    gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee);
-
+  const URL = "https://example.com/browser/browser/base/content/test/general/bug592338.html";
+  BrowserTestUtils.openNewForegroundTab({ gBrowser, url: URL }).then(() => {
     let promise = promisePopupNotificationShown("addon-installed");
     BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
     promise.then(() => {
       is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
 
       LightweightThemeManager.currentTheme = null;
       gBrowser.removeTab(gBrowser.selectedTab);
       Services.perms.remove(makeURI("http://example.com/"), "install");
--- a/browser/base/content/test/general/browser_bug734076.js
+++ b/browser/base/content/test/general/browser_bug734076.js
@@ -90,17 +90,16 @@ add_task(async function() {
     info("Run subtest " + test.name);
     let commandToRun = await test.go();
 
     let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
     await BrowserTestUtils.synthesizeMouse(test.element, 3, 3,
           { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
     await popupShownPromise;
     info("onImage: " + gContextMenu.onImage);
-    info("target: " + gContextMenu.target.tagName);
 
     let loadedAfterCommandPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
     document.getElementById(commandToRun).click();
     await loadedAfterCommandPromise;
 
     await test.verify();
 
     let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
@@ -158,27 +158,25 @@ function checkState(expectedStates, cont
 const kPage = "http://example.org/browser/browser/" +
               "base/content/test/general/dummy_page.html";
 
 add_task(async function() {
   await pushPrefs(
     ["full-screen-api.transition-duration.enter", "0 0"],
     ["full-screen-api.transition-duration.leave", "0 0"]);
 
-  let tab = BrowserTestUtils.addTab(gBrowser, kPage);
-  let browser = tab.linkedBrowser;
-  gBrowser.selectedTab = tab;
-  await waitForDocLoadComplete();
+  registerCleanupFunction(async function() {
+    if (window.fullScreen) {
+      executeSoon(() => BrowserFullScreen());
+      await waitForFullscreenChanges(FS_CHANGE_SIZE);
+    }
+  });
 
-  registerCleanupFunction(() => {
-    if (browser.contentWindow.fullScreen) {
-      BrowserFullScreen();
-    }
-    gBrowser.removeTab(tab);
-  });
+  let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: kPage });
+  let browser = tab.linkedBrowser;
 
   gMessageManager = browser.messageManager;
   gMessageManager.loadFrameScript(
     "data:,(" + frameScript.toString() + ")();", false);
   gMessageManager.addMessageListener(
     "Test:FullscreenChanged", captureUnexpectedFullscreenChange);
 
   // Wait for the document being activated, so that
@@ -232,9 +230,11 @@ add_task(async function() {
 
     // Exit fullscreen mode if we are still in
     if (window.fullScreen) {
       info("> Cleanup");
       executeSoon(() => BrowserFullScreen());
       await waitForFullscreenChanges(FS_CHANGE_SIZE);
     }
   }
+
+  await BrowserTestUtils.removeTab(tab);
 });
--- a/browser/base/content/test/general/browser_e10s_chrome_process.js
+++ b/browser/base/content/test/general/browser_e10s_chrome_process.js
@@ -10,22 +10,22 @@ function makeTest(name, startURL, startP
     if (!gMultiProcessBrowser) {
       startProcessIsRemote = false;
       endProcessIsRemote = false;
     }
 
     // Load the initial URL and make sure we are in the right initial process
     info("Loading initial URL");
     browser.loadURI(startURL);
-    await waitForDocLoadComplete();
+    await BrowserTestUtils.browserLoaded(browser);
 
     is(browser.currentURI.spec, startURL, "Shouldn't have been redirected");
     is(browser.isRemoteBrowser, startProcessIsRemote, "Should be displayed in the right process");
 
-    let docLoadedPromise = waitForDocLoadComplete();
+    let docLoadedPromise = BrowserTestUtils.browserLoaded(browser);
     let expectSyncChange = await transitionTask(browser, endURL);
     if (expectSyncChange) {
       is(browser.isRemoteBrowser, endProcessIsRemote, "Should have switched to the right process synchronously");
     }
     await docLoadedPromise;
 
     is(browser.currentURI.spec, endURL, "Should have made it to the final URL");
     is(browser.isRemoteBrowser, endProcessIsRemote, "Should be displayed in the right process");
@@ -125,22 +125,20 @@ async function loadURI(browser, uri) {
   return true;
 },
 
 // Loads the new page by finding a link with the right href in the document and
 // clicking it
 function clickLink(browser, uri) {
   info("Clicking link");
 
-  function frame_script(frameUri) {
+  ContentTask.spawn(browser, uri, function frame_script(frameUri) {
     let link = content.document.querySelector("a[href='" + frameUri + "']");
     link.click();
-  }
-
-  browser.messageManager.loadFrameScript("data:,(" + frame_script.toString() + ")(" + JSON.stringify(uri) + ");", false);
+  });
 
   return false;
 },
 ];
 
 // Creates a set of test tasks, one for each combination of TESTS and TRANSITIONS.
 for (let test of TESTS) {
   for (let transition of TRANSITIONS) {
--- a/browser/base/content/test/general/browser_plainTextLinks.js
+++ b/browser/base/content/test/general/browser_plainTextLinks.js
@@ -5,17 +5,17 @@ function testExpected(expected, msg) {
 function testLinkExpected(expected, msg) {
   is(gContextMenu.linkURL, expected, msg);
 }
 
 add_task(async function() {
   const url = "data:text/html;charset=UTF-8,Test For Non-Hyperlinked url selection";
   await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
-  await SimpleTest.promiseFocus(gBrowser.selectedBrowser.contentWindowAsCPOW);
+  await SimpleTest.promiseFocus(gBrowser.selectedBrowser);
 
   // Initial setup of the content area.
   await ContentTask.spawn(gBrowser.selectedBrowser, { }, async function(arg) {
     let doc = content.document;
     let range = doc.createRange();
     let selection = content.getSelection();
 
     let mainDiv = doc.createElement("div");
--- a/browser/base/content/test/general/browser_scope.js
+++ b/browser/base/content/test/general/browser_scope.js
@@ -1,10 +1,4 @@
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
-
 function test() {
   ok(!!gBrowser, "gBrowser exists");
   is(gBrowser, getBrowser(), "both ways of getting tabbrowser work");
 }
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -66,16 +66,19 @@ function focusInChild() {
   var contentFM = Components.classes["@mozilla.org/focus-manager;1"].
                       getService(Components.interfaces.nsIFocusManager);
 
   function getWindowDocId(target) {
     return (String(target.location).indexOf("1") >= 0) ? "window1" : "window2";
   }
 
   function eventListener(event) {
+    // Stop the shim code from seeing this event process.
+    event.stopImmediatePropagation();
+
     var id;
     if (event.target instanceof Components.interfaces.nsIDOMWindow)
       id = getWindowDocId(event.originalTarget) + "-window";
     else if (event.target instanceof Components.interfaces.nsIDOMDocument)
       id = getWindowDocId(event.originalTarget) + "-document";
     else
       id = event.originalTarget.id;
     sendSyncMessage("Browser:FocusChanged", { details: event.type + ": " + id });
@@ -131,17 +134,17 @@ add_task(async function() {
   browser1 = gBrowser.getBrowserForTab(tab1);
 
   tab2 = BrowserTestUtils.addTab(gBrowser);
   browser2 = gBrowser.getBrowserForTab(tab2);
 
   await promiseTabLoadEvent(tab1, "data:text/html," + escape(testPage1));
   await promiseTabLoadEvent(tab2, "data:text/html," + escape(testPage2));
 
-  var childFocusScript = "data:,(" + focusInChild.toString() + ")();";
+  var childFocusScript = "data:,(" + escape(focusInChild.toString()) + ")();";
   browser1.messageManager.loadFrameScript(childFocusScript, true);
   browser2.messageManager.loadFrameScript(childFocusScript, true);
 
   gURLBar.focus();
   await SimpleTest.promiseFocus();
 
   if (gMultiProcessBrowser) {
     window.messageManager.addMessageListener("Browser:FocusChanged", message => {
@@ -375,20 +378,16 @@ function _browser_tabfocus_test_eventOcc
     if (target == browser2.contentWindow || target == browser2.contentDocument) {
       return "window2";
     }
     return "main-window";
   }
 
   var id;
 
-  // Some focus events from the child bubble up? Ignore them.
-  if (Cu.isCrossProcessWrapper(event.originalTarget))
-    return;
-
   if (event.target instanceof Window)
     id = getWindowDocId(event.originalTarget) + "-window";
   else if (event.target instanceof Document)
     id = getWindowDocId(event.originalTarget) + "-document";
   else if (event.target.id == "urlbar" && event.originalTarget.localName == "input")
     id = "urlbar";
   else if (event.originalTarget.localName == "browser")
     id = (event.originalTarget == browser1) ? "browser1" : "browser2";
--- a/browser/base/content/test/general/browser_tabs_owner.js
+++ b/browser/base/content/test/general/browser_tabs_owner.js
@@ -1,20 +1,8 @@
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: gBrowser._finalizeTabSwitch is not a function");
-
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: gBrowser._finalizeTabSwitch is not a function");
-
 function test() {
   BrowserTestUtils.addTab(gBrowser);
   BrowserTestUtils.addTab(gBrowser);
   BrowserTestUtils.addTab(gBrowser);
 
   var tabs = gBrowser.tabs;
   var owner;
 
--- a/browser/base/content/test/general/browser_visibleFindSelection.js
+++ b/browser/base/content/test/general/browser_visibleFindSelection.js
@@ -1,26 +1,30 @@
 add_task(async function() {
   const childContent = "<div style='position: absolute; left: 2200px; background: green; width: 200px; height: 200px;'>" +
                        "div</div><div  style='position: absolute; left: 0px; background: red; width: 200px; height: 200px;'>" +
                        "<span id='s'>div</span></div>";
 
   let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
-  await promiseTabLoadEvent(tab, "data:text/html," + escape(childContent));
-  await SimpleTest.promiseFocus(gBrowser.selectedBrowser.contentWindowAsCPOW);
+  await promiseTabLoadEvent(tab, "data:text/html;charset=utf-8," + escape(childContent));
+  await SimpleTest.promiseFocus(gBrowser.selectedBrowser);
+
+  let remote = gBrowser.selectedBrowser.isRemoteBrowser;
 
   let findBarOpenPromise = promiseWaitForEvent(gBrowser, "findbaropen");
   EventUtils.synthesizeKey("f", { accelKey: true });
   await findBarOpenPromise;
 
   ok(gFindBarInitialized, "find bar is now initialized");
 
   // Finds the div in the green box.
-  let scrollPromise = promiseWaitForEvent(gBrowser, "scroll");
+  let scrollPromise = remote ?
+    BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "scroll") :
+    BrowserTestUtils.waitForEvent(gBrowser, "scroll");
   EventUtils.synthesizeKey("d", {});
   EventUtils.synthesizeKey("i", {});
   EventUtils.synthesizeKey("v", {});
   await scrollPromise;
 
   // Wait for one paint to ensure we've processed the previous key events and scrolling.
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     return new Promise(
@@ -28,17 +32,19 @@ add_task(async function() {
         content.requestAnimationFrame(() => {
           setTimeout(resolve, 0);
         });
       }
     );
   });
 
   // Finds the div in the red box.
-  scrollPromise = promiseWaitForEvent(gBrowser, "scroll");
+  scrollPromise = remote ?
+    BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "scroll") :
+    BrowserTestUtils.waitForEvent(gBrowser, "scroll");
   EventUtils.synthesizeKey("g", { accelKey: true });
   await scrollPromise;
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     Assert.ok(content.document.getElementById("s").getBoundingClientRect().left >= 0,
       "scroll should include find result");
   });
 
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -1,13 +1,17 @@
 [DEFAULT]
 support-files =
   head.js
 [browser_appmenu_reflows.js]
 [browser_startup.js]
+[browser_startup_content.js]
+skip-if = !e10s
+[browser_startup_images.js]
+skip-if = !debug
 [browser_tabclose_grow_reflows.js]
 [browser_tabclose_reflows.js]
 [browser_tabopen_reflows.js]
 [browser_tabopen_squeeze_reflows.js]
 [browser_tabswitch_reflows.js]
 [browser_toolbariconcolor_restyles.js]
 [browser_windowclose_reflows.js]
 [browser_windowopen_reflows.js]
--- a/browser/base/content/test/performance/browser_appmenu_reflows.js
+++ b/browser/base/content/test/performance/browser_appmenu_reflows.js
@@ -71,17 +71,42 @@ const EXPECTED_APPMENU_OPEN_REFLOWS = [
   [
     "handleEvent@resource:///modules/PanelMultiView.jsm",
     "openPopup@chrome://global/content/bindings/popup.xml",
   ],
 ];
 
 const EXPECTED_APPMENU_SUBVIEW_REFLOWS = [
   /**
-   * Nothing here! Please don't add anything new!
+   * The synced tabs view has labels that are multiline. Because of bugs in
+   * XUL layout relating to multiline text in scrollable containers, we need
+   * to manually read their height in order to ensure container heights are
+   * correct. Unfortunately this requires 2 sync reflows.
+   *
+   * If we add more views where this is necessary, we may need to duplicate
+   * these expected reflows further.
+   *
+   * Because the test dirties the frame tree by manipulating margins,
+   * getBoundingClientRect() in the descriptionHeightWorkaround code
+   * seems to sometimes fire multiple times. Bug 1363361 will change how the
+   * test dirties the frametree, after which this (2 hits in that method)
+   * should become deterministic and we can re-enable the subview testing
+   * for the remotetabs subview (this is bug 1376822). In the meantime,
+   * that subview only is excluded from this test.
+  [
+    "descriptionHeightWorkaround@resource:///modules/PanelMultiView.jsm",
+    "onTransitionEnd@resource:///modules/PanelMultiView.jsm",
+  ],
+  [
+    "descriptionHeightWorkaround@resource:///modules/PanelMultiView.jsm",
+    "onTransitionEnd@resource:///modules/PanelMultiView.jsm",
+  ],
+   */
+  /**
+   * Please don't add anything new!
    */
 ];
 
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   await SpecialPowers.pushPrefEnv({
     set: [["browser.photon.structure.enabled", true]],
@@ -105,18 +130,27 @@ add_task(async function() {
     // exhausted, we go back up a level.
     async function openSubViewsRecursively(currentView) {
       let navButtons = Array.from(currentView.querySelectorAll(".subviewbutton-nav"));
       if (!navButtons) {
         return;
       }
 
       for (let button of navButtons) {
+        // We skip the remote tabs subview, see the comments above
+        // in EXPECTED_APPMENU_SUBVIEW_REFLOWS. bug 1376822 tracks
+        // re-enabling this.
+        if (button.id == "appMenu-library-remotetabs-button") {
+          info("Skipping " + button.id);
+          continue;
+        }
+        info("Click " + button.id);
         button.click();
         await BrowserTestUtils.waitForEvent(PanelUI.panel, "ViewShown");
+        info("Shown " + PanelUI.multiView.instance._currentSubView.id);
         // Unfortunately, I can't find a better accessor to the current
         // subview, so I have to reach the PanelMultiView instance
         // here.
         await openSubViewsRecursively(PanelUI.multiView.instance._currentSubView);
         PanelUI.multiView.goBack();
         await BrowserTestUtils.waitForEvent(PanelUI.panel, "ViewShown");
       }
     }
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -100,17 +100,17 @@ const startupPhases = {
 
 function test() {
   if (!AppConstants.NIGHTLY_BUILD && !AppConstants.DEBUG) {
     ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
        "the startup recorder component shouldn't exist in this non-nightly non-debug build.");
     return;
   }
 
-  let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data;
+  let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data.code;
   // Keep only the file name for components, as the path is an absolute file
   // URL rather than a resource:// URL like for modules.
   for (let phase in data) {
     data[phase].components =
       data[phase].components.map(f => f.replace(/.*\//, ""))
                             .filter(c => c != "startupRecorder.js");
   }
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test records which services, JS components and JS modules are loaded
+ * when creating a new content process.
+ *
+ * If you made changes that cause this test to fail, it's likely because you
+ * are loading more JS code during content process startup.
+ *
+ * If your code isn't strictly required to show a page, consider loading it
+ * lazily. If you can't, consider delaying its load until after we have started
+ * handling user events.
+ */
+
+"use strict";
+
+const blacklist = {
+  components: new Set([
+    "PushComponents.js",
+    "TelemetryStartup.js",
+  ]),
+  modules: new Set([
+    "resource:///modules/ContentWebRTC.jsm",
+    "resource://gre/modules/InlineSpellChecker.jsm",
+    "resource://gre/modules/InlineSpellCheckerContent.jsm",
+    "resource://gre/modules/Promise.jsm",
+    "resource://gre/modules/Task.jsm",
+    "resource://gre/modules/debug.js",
+    "resource://gre/modules/osfile.jsm",
+  ]),
+  services: new Set([
+    "@mozilla.org/base/telemetry-startup;1",
+    "@mozilla.org/push/Service;1",
+  ])
+};
+
+add_task(async function() {
+  SimpleTest.requestCompleteLog();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser,
+                                                         forceNewProcess: true});
+
+  let mm = gBrowser.selectedBrowser.messageManager;
+  let promise = BrowserTestUtils.waitForMessage(mm, "Test:LoadedScripts");
+
+  // Load a custom frame script to avoid using ContentTask which loads Task.jsm
+  mm.loadFrameScript("data:text/javascript,(" + function() {
+    /* eslint-env mozilla/frame-script */
+    const {classes: Cc, interfaces: Ci, manager: Cm} = Components;
+    Cm.QueryInterface(Ci.nsIServiceManager);
+
+    let loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
+    sendAsyncMessage("Test:LoadedScripts", {
+      /* Keep only the file name for components, as the path is an absolute file
+         URL rather than a resource:// URL like for modules. */
+      components: loader.loadedComponents().map(f => f.replace(/.*\//, "")),
+      modules: loader.loadedModules(),
+      services: Object.keys(Cc).filter(c => {
+        try {
+          Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
+          return true;
+        } catch (e) {
+          return false;
+        }
+      })
+    });
+  } + ")()", false);
+
+  let loadedList = await promise;
+  for (let scriptType in blacklist) {
+    info(scriptType);
+    for (let file of blacklist[scriptType]) {
+      ok(!loadedList[scriptType].includes(file), `${file} is not allowed`);
+    }
+    for (let file of loadedList[scriptType]) {
+      info(file);
+    }
+  }
+
+  await BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_images.js
@@ -0,0 +1,229 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* A whitelist of images that are loaded at startup but not shown.
+ * List items support the following attributes:
+ *  - file: The location of the loaded image file.
+ *  - hidpi: An alternative hidpi file location for retina screens, if one exists.
+ *           May be the magic string <not loaded> in strange cases where
+ *           only the low-resolution image is loaded but not shown.
+ *  - platforms: An array of the platforms where the issue is occurring.
+ *               Possible values are linux, win, macosx.
+ *  - intermittentNotLoaded: an array of platforms where this image is
+ *                           intermittently not loaded, e.g. because it is
+ *                           loaded during the time we stop recording.
+ *  - intermittentShown: An array of platforms where this image is
+ *                       intermittently shown, contrary to what our
+ *                       whitelist says.
+ *
+ * Please don't add items to this list. Please remove items from this list.
+ */
+const whitelist = [
+  {
+    file: "chrome://browser/skin/fxa/sync-illustration.svg",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-overflow-indicator.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/stop.svg",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/sidebars.svg",
+    platforms: ["linux", "win", "macosx"],
+    intermittentNotLoaded: ["macosx"],
+  },
+  {
+    file: "chrome://pocket-shared/skin/pocket.svg",
+    platforms: ["linux", "win", "macosx"],
+    intermittentNotLoaded: ["macosx"],
+  },
+  {
+    file: "chrome://browser/skin/places/toolbarDropMarker.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/bookmark-hollow.svg",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tracking-protection-16.svg#enabled",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/toolbarbutton-dropdown-arrow.png",
+    platforms: ["win"],
+  },
+  {
+    file: "chrome://global/skin/icons/autoscroll.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-background-end.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tab-background-end@2x.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-background-middle.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tab-background-middle@2x.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-background-start.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tab-background-start@2x.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tabDragIndicator@2x.png",
+    platforms: ["linux", "win", "macosx"],
+  },
+
+  {
+    file: "resource://gre-resources/loading-image.png",
+    platforms: ["win", "macosx"],
+    intermittentNotLoaded: ["win", "macosx"],
+  },
+  {
+    file: "resource://gre-resources/broken-image.png",
+    platforms: ["win", "macosx"],
+    intermittentNotLoaded: ["win", "macosx"],
+  },
+
+  {
+    file: "chrome://browser/skin/places/unfiledBookmarks.png",
+    hidpi: "<not loaded>",
+    platforms: ["win", "macosx"],
+    intermittentNotLoaded: ["win", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/urlbar-history-dropmarker.png",
+    hidpi: "<not loaded>",
+    platforms: ["win", "macosx"],
+    intermittentShown: ["win", "macosx"],
+  },
+
+  {
+    file: "chrome://browser/skin/yosemite/tab-selected-start-inactive.svg",
+    platforms: ["macosx"],
+  },
+  {
+    file: "chrome://browser/skin/yosemite/tab-active-middle-inactive.png",
+    platforms: ["macosx"],
+  },
+  {
+    file: "chrome://browser/skin/yosemite/tab-selected-end-inactive.svg",
+    platforms: ["macosx"],
+  },
+  {
+    file: "chrome://browser/skin/yosemite/tab-stroke-start-inactive.png",
+    platforms: ["macosx"],
+  },
+  {
+    file: "chrome://browser/skin/yosemite/tab-stroke-end-inactive.png",
+    platforms: ["macosx"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/newtab.png",
+    platforms: ["macosx"],
+  },
+
+  {
+    file: "chrome://global/skin/icons/chevron.png",
+    hidpi: "chrome://global/skin/icons/chevron@2x.png",
+    platforms: ["macosx"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png",
+    hidpi: "chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon@2x.png",
+    platforms: ["macosx"],
+  },
+
+  {
+    file: "chrome://global/skin/toolbar/chevron.gif",
+    platforms: ["win", "linux"],
+  },
+  {
+    file: "chrome://browser/skin/reload-stop-go.png",
+    platforms: ["win", "linux"],
+    intermittentShown: ["win", "linux"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/alltabs.png",
+    platforms: ["linux"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-arrow-left.svg",
+    platforms: ["win"],
+  },
+
+  {
+    file: "chrome://global/skin/icons/resizer.png",
+    platforms: ["win"],
+  },
+
+  {
+    file: "chrome://global/skin/icons/resizer.png",
+    platforms: ["win"],
+  },
+
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-arrow-left.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tab-arrow-left@2x.png",
+    platforms: ["linux", "macosx"],
+  },
+  {
+    file: "chrome://browser/skin/tabbrowser/tab-arrow-right.png",
+    hidpi: "chrome://browser/skin/tabbrowser/tab-arrow-right@2x.png",
+    platforms: ["macosx"],
+  },
+];
+
+function test() {
+  let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data.images;
+  let platformWhitelist = whitelist.filter(el => el.platforms.includes(AppConstants.platform));
+
+  let loadedImages = data["image-loading"];
+  let shownImages = data["image-drawing"];
+
+  for (let loaded of loadedImages.values()) {
+    let whitelistItem = platformWhitelist.find(el => {
+      if (window.devicePixelRatio >= 2 && el.hidpi && el.hidpi == loaded) {
+        return true;
+      }
+      return el.file == loaded;
+    });
+    if (whitelistItem) {
+      if (!whitelistItem.intermittentShown ||
+          !whitelistItem.intermittentShown.includes(AppConstants.platform)) {
+        todo(shownImages.has(loaded), `Whitelisted image ${loaded} should not have been shown.`);
+      }
+      continue;
+    }
+    ok(shownImages.has(loaded), `Loaded image ${loaded} should have been shown.`);
+  }
+
+  // Check for unneeded whitelist entries.
+  for (let item of platformWhitelist) {
+    if (!item.intermittentNotLoaded ||
+        !item.intermittentNotLoaded.includes(AppConstants.platform)) {
+      if (window.devicePixelRatio >= 2 && item.hidpi) {
+        if (item.hidpi != "<not loaded>") {
+          ok(loadedImages.has(item.hidpi), `Whitelisted image ${item.hidpi} should have been loaded.`);
+        }
+      } else {
+        ok(loadedImages.has(item.file), `Whitelisted image ${item.file} should have been loaded.`);
+      }
+    }
+  }
+}
--- a/browser/base/content/test/plugins/browser_CTP_notificationBar.js
+++ b/browser/base/content/test/plugins/browser_CTP_notificationBar.js
@@ -1,33 +1,34 @@
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 
 add_task(async function() {
+  await SpecialPowers.pushPrefEnv({ set: [
+    ["plugins.show_infobar", true],
+    ["plugins.click_to_play", true],
+    ["extensions.blocklist.supressUI", true],
+  ]});
+
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
-    Services.prefs.clearUserPref("plugins.click_to_play");
-    Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
     gBrowser.removeCurrentTab();
     window.focus();
     gTestBrowser = null;
   });
 
-  Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
-
   let newTab = BrowserTestUtils.addTab(gBrowser);
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
 });
 
 add_task(async function() {
-  Services.prefs.setBoolPref("plugins.click_to_play", true);
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
 
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await promisePopupNotification("click-to-play-plugins");
--- a/browser/base/content/test/plugins/browser_CTP_remove_navigate.js
+++ b/browser/base/content/test/plugins/browser_CTP_remove_navigate.js
@@ -1,25 +1,26 @@
 const gTestRoot = getRootDirectory(gTestPath);
 const gHttpTestRoot = gTestRoot.replace("chrome://mochitests/content/",
                                         "http://127.0.0.1:8888/");
 
 add_task(async function() {
+  await SpecialPowers.pushPrefEnv({ set: [
+    ["plugins.click_to_play", true],
+    ["extensions.blocklist.suppressUI", true],
+    ["plugins.show_infobar", true],
+  ]});
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
-    Services.prefs.clearUserPref("plugins.click_to_play");
-    Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
     gBrowser.removeCurrentTab();
     window.focus();
   });
 
-  Services.prefs.setBoolPref("plugins.click_to_play", true);
-  Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
 });
 
 /**
  * Tests that if a plugin is removed just as we transition to
  * a different page, that we don't show the hidden plugin
  * notification bar on the new page.
--- a/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
+++ b/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
@@ -16,18 +16,21 @@ add_task(async function setup() {
   let originalPluginState = getTestPluginEnabledState();
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
   registerCleanupFunction(() => {
     setTestPluginEnabledState(originalPluginState);
   });
 
   // And then make the plugin hidden.
   await SpecialPowers.pushPrefEnv({
-    set: [[HIDDEN_CTP_PLUGIN_PREF, TEST_PLUGIN_NAME]],
-  })
+    set: [
+      [HIDDEN_CTP_PLUGIN_PREF, TEST_PLUGIN_NAME],
+      ["plugins.show_infobar", true],
+    ],
+  });
 });
 
 /**
  * Tests that if a plugin is click-to-play and in the
  * HIDDEN_CTP_PLUGIN_PREF list, then it shouldn't be visible
  * when iterating navigator.plugins.
  */
 add_task(async function test_plugin_is_hidden_on_iteration() {
--- a/browser/base/content/test/sidebar/browser_sidebar_move.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_move.js
@@ -28,27 +28,31 @@ function getBrowserChildrenWithOrdinals(
 }
 
 add_task(async function() {
   await SidebarUI.show("viewBookmarksSidebar");
   SidebarUI.showSwitcherPanel();
 
   let reversePositionButton = document.getElementById("sidebar-reverse-position");
   let originalLabel = reversePositionButton.getAttribute("label");
+  let box = document.getElementById("sidebar-box");
 
   // Default (position: left)
   Assert.deepEqual(getBrowserChildrenWithOrdinals(),
     EXPECTED_START_ORDINALS, "Correct ordinal (start)");
+  ok(!box.hasAttribute("positionend"), "Positioned start");
 
   // Moved to right
   SidebarUI.reversePosition();
   SidebarUI.showSwitcherPanel();
   Assert.deepEqual(getBrowserChildrenWithOrdinals(),
     EXPECTED_END_ORDINALS, "Correct ordinal (end)");
   isnot(reversePositionButton.getAttribute("label"), originalLabel, "Label changed");
+  ok(box.hasAttribute("positionend"), "Positioned end");
 
   // Moved to back to left
   SidebarUI.reversePosition();
   SidebarUI.showSwitcherPanel();
   Assert.deepEqual(getBrowserChildrenWithOrdinals(),
     EXPECTED_START_ORDINALS, "Correct ordinal (start)");
+  ok(!box.hasAttribute("positionend"), "Positioned start");
   is(reversePositionButton.getAttribute("label"), originalLabel, "Label is back to normal");
 });
--- a/browser/base/content/test/siteIdentity/browser_bug906190.js
+++ b/browser/base/content/test/siteIdentity/browser_bug906190.js
@@ -35,16 +35,17 @@ async function doTest(parentTabSpec, chi
 
     // Wait for the script in the page to update the contents of the test div.
     let testDiv = content.document.getElementById("mctestdiv");
     await BrowserTestUtils.waitForCondition(
       () => testDiv.innerHTML == "Mixed Content Blocker disabled");
 
     // Add the link for the child tab to the page.
     let mainDiv = content.document.createElement("div");
+    // eslint-disable-next-line no-unsanitized/property
     mainDiv.innerHTML =
       '<p><a id="linkToOpenInNewTab" href="' + childTabSpec + '">Link</a></p>';
     content.document.body.appendChild(mainDiv);
 
     // Execute the test in the child tabs with the two methods to open it.
     for (let openFn of [simulateCtrlClick, simulateContextMenuOpenInTab]) {
       let promiseTabLoaded = waitForSomeTabToLoad();
       openFn(browser);
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
@@ -30,16 +30,17 @@
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
    is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
 
    var blockedMixedDisplay = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
    is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
 
    var newValue = "Verifying MCB does not trigger warning/error for an http page with https css that includes http font";
+   // eslint-disable-next-line no-unsanitized/property
    document.getElementById("testDiv").innerHTML = newValue;
   }
 </script>
 </head>
 <body onload="checkLoadStates()">
   <div class="testDiv" id="testDiv">
     Testing MCB does not trigger warning/error for an http page with https css that includes http font
   </div>
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
@@ -31,16 +31,17 @@
    is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
 
    var blockedMixedDisplay = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
    is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
 
    var newValue = "Verifying MCB does not trigger warning/error for an http page ";
     newValue += "with https css that imports another http css which includes http font";
+   // eslint-disable-next-line no-unsanitized/property
    document.getElementById("testDiv").innerHTML = newValue;
   }
 </script>
 </head>
 <body onload="checkLoadStates()">
   <div class="testDiv" id="testDiv">
     Testing MCB does not trigger warning/error for an http page with https css that imports another http css which includes http font
   </div>
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
@@ -30,16 +30,17 @@
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
    is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
 
    var blockedMixedDisplay = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
    is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
 
    var newValue = "Verifying MCB does not trigger warning/error for an http page with https css that includes http image";
+   // eslint-disable-next-line no-unsanitized/property
    document.getElementById("testDiv").innerHTML = newValue;
   }
 </script>
 </head>
 <body onload="checkLoadStates()">
   <div class="testDiv" id="testDiv">
     Testing MCB does not trigger warning/error for an http page with https css that includes http image
   </div>
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -1,19 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: Assert is null");
-
-
 var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
 
 var tabsToRemove = [];
 
 function removeProvider(provider) {
   return new Promise(resolve => {
     // a full install sets the manifest into a pref, addProvider alone doesn't,
     // make sure we uninstall if the manifest was added.
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -27,17 +27,17 @@ var gExceptionPaths = [
   "resource://pdf.js/web/images/",
 ];
 
 // These are not part of the omni.ja file, so we find them only when running
 // the test on a non-packaged build.
 if (AppConstants.platform == "macosx")
   gExceptionPaths.push("resource://gre/res/cursors/");
 
-var whitelist = new Set([
+var whitelist = [
   // browser/extensions/pdfjs/content/PdfStreamConverter.jsm
   {file: "chrome://pdf.js/locale/chrome.properties"},
   {file: "chrome://pdf.js/locale/viewer.properties"},
 
   // security/manager/pki/resources/content/device_manager.js
   {file: "chrome://pippki/content/load_device.xul"},
 
   // Used by setting this url as a pref in about:config
@@ -119,25 +119,16 @@ var whitelist = new Set([
   {file: "resource://pdf.js/web/debugger.js"},
 
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339420
   {file: "chrome://branding/content/icon128.png"},
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
-  // Bug 1343824
-  {file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
-   platforms: ["linux", "win"]},
-  {file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
-   platforms: ["linux", "win"]},
-  {file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
-   platforms: ["linux", "win"]},
-  {file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
-   platforms: ["linux", "win"]},
   // Bug 1316187
   {file: "chrome://global/content/customizeToolbar.xul"},
   // Bug 1343837
   {file: "chrome://global/content/findUtils.js"},
   // Bug 1343843
   {file: "chrome://global/content/url-classifier/unittests.xul"},
   // Bug 1343839
   {file: "chrome://global/locale/headsUpDisplay.properties"},
@@ -177,17 +168,32 @@ var whitelist = new Set([
   {file: "resource://gre/modules/Manifest.jsm"},
   // Bug 1351089
   {file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"},
   // Bug 1351097
   {file: "resource://gre/modules/accessibility/AccessFu.jsm"},
   // Bug 1351637
   {file: "resource://gre/modules/sdk/bootstrap.js"},
 
-].filter(item =>
+];
+
+if (!AppConstants.MOZ_PHOTON_THEME) {
+  whitelist.push(
+    // Bug 1343824
+    {file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
+     platforms: ["linux", "win"]},
+    {file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
+     platforms: ["linux", "win"]},
+    {file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
+     platforms: ["linux", "win"]},
+    {file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
+     platforms: ["linux", "win"]});
+}
+
+whitelist = new Set(whitelist.filter(item =>
   ("isFromDevTools" in item) == isDevtools &&
   (!item.skipNightly || !AppConstants.NIGHTLY_BUILD) &&
   (!item.platforms || item.platforms.includes(AppConstants.platform))
 ).map(item => item.file));
 
 const ignorableWhitelist = new Set([
   // chrome://xslt-qa/ isn't referenced, but isn't included in packaged builds,
   // so it's fine to just ignore it and ignore if the exceptions are unused.
--- a/browser/base/content/test/sync/browser_aboutAccounts.js
+++ b/browser/base/content/test/sync/browser_aboutAccounts.js
@@ -1,18 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: window.location is null");
-
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/sync/";
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -105,16 +105,17 @@ subsuite = clipboard
 [browser_urlbar_canonize_on_autofill.js]
 [browser_urlbar_blanking.js]
 support-files =
   file_blank_but_not_blank.html
 [browser_urlbar_locationchange_urlbar_edit_dos.js]
 support-files =
   file_urlbar_edit_dos.html
 [browser_urlbar_searchsettings.js]
+[browser_urlbar_search_speculative_connect.js]
 [browser_urlbar_stop_pending.js]
 support-files =
   slow-page.sjs
 [browser_urlbar_remoteness_switch.js]
 run-if = e10s
 [browser_urlHighlight.js]
 [browser_wyciwyg_urlbarCopying.js]
 subsuite = clipboard
--- a/browser/base/content/test/urlbar/browser_bug562649.js
+++ b/browser/base/content/test/urlbar/browser_bug562649.js
@@ -1,14 +1,15 @@
 function test() {
   const URI = "data:text/plain,bug562649";
   window.browserDOMWindow.openURI(makeURI(URI),
                                   null,
                                   Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
-                                  Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+                                  Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
+                                  Services.scriptSecurityManager.getSystemPrincipal());
 
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI");
   is(gURLBar.value, URI, "location bar value matches test URI");
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
   gBrowser.removeCurrentTab({ skipPermitUnload: true });
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs");
   is(gURLBar.value, URI, "location bar value matches test URI after switching tabs");
--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
+++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
@@ -6,16 +6,18 @@
 
 requestLongerTimeout(2);
 
 const TEST_URL_BASES = [
   "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#tabmatch",
   "http://example.org/browser/browser/base/content/test/urlbar/moz.png#tabmatch"
 ];
 
+const RESTRICT_TOKEN_OPENPAGE = "%";
+
 var gController = Cc["@mozilla.org/autocomplete/controller;1"].
                   getService(Ci.nsIAutoCompleteController);
 
 var gTabCounter = 0;
 
 add_task(async function step_1() {
   info("Running step 1");
   let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
@@ -204,10 +206,10 @@ function checkAutocompleteResults(aExpec
     getSearchAt(aIndex) { return this.searches[aIndex]; },
     QueryInterface: XPCOMUtils.generateQI([
       Ci.nsIAutoCompleteInput,
       Ci.nsIAutoCompletePopup,
     ])
   };
 
   info("Searching open pages.");
-  gController.startSearch(Services.prefs.getCharPref("browser.urlbar.restrict.openpage"));
+  gController.startSearch(RESTRICT_TOKEN_OPENPAGE);
 }
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
@@ -55,16 +55,18 @@ add_task(async function focus() {
   assertFooterVisible(true);
 
   // Check the Change Options link.
   let changeOptionsLink = document.getElementById("search-suggestions-change-settings");
   let prefsPromise = BrowserTestUtils.waitForLocationChange(gBrowser, "about:preferences#general-search");
   changeOptionsLink.click();
   await prefsPromise;
   Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+  // The preferences page does fancy stuff with focus, ensure to unload it.
+  await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:blank");
 });
 
 add_task(async function new_tab() {
   // Opening a new tab when the urlbar is unfocused, should focusing it and thus
   // open the popup in order to show the notification.
   setupVisibleHint();
   gURLBar.blur();
   let popupPromise = promisePopupShown(gURLBar.popup);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_urlbar_search_speculative_connect.js
@@ -0,0 +1,82 @@
+"use strict";
+
+// This test ensures that we setup a speculative network
+// connection for autoFilled values.
+
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+let gHttpServer = null;
+let gScheme = "http";
+let gHost = "localhost"; // 'localhost' by default.
+let gPort = -1;
+
+add_task(async function setup() {
+  if (!gHttpServer) {
+    gHttpServer = new HttpServer();
+    try {
+      gHttpServer.start(gPort);
+      gPort = gHttpServer.identity.primaryPort;
+      gHttpServer.identity.setPrimary(gScheme, gHost, gPort);
+    } catch (ex) {
+      info("We can't launch our http server successfully.")
+    }
+  }
+  is(gHttpServer.identity.has(gScheme, gHost, gPort), true, "make sure we have this domain listed");
+
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.urlbar.autoFill", true],
+          ["browser.urlbar.speculativeConnection.enabled", true],
+          // In mochitest this number is 0 by default but we have to turn it on.
+          ["network.http.speculative-parallel-limit", 6],
+          // The http server is using IPv4, so it's better to disable IPv6 to avoid weird
+          // networking problem.
+          ["network.dns.disableIPv6", true]],
+  });
+
+  await PlacesTestUtils.addVisits([{
+    uri: `${gScheme}://${gHost}:${gPort}`,
+    title: "test visit for speculative connection",
+    transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }]);
+
+  // Bug 764062 - we can't get port number from autocomplete result, so we have to mock
+  // this function to add it manually.
+  let oldSpeculativeConnect = gURLBar.popup.maybeSetupSpeculativeConnect.bind(gURLBar.popup);
+  gURLBar.popup.maybeSetupSpeculativeConnect = (uriString) => {
+    info(`Original uri is ${uriString}`);
+    let newUriString = uriString.substr(0, uriString.length - 1) +
+                       ":" + gPort + "/";
+    info(`New uri is ${newUriString}`);
+    oldSpeculativeConnect(newUriString);
+  };
+
+  registerCleanupFunction(async function() {
+    await PlacesTestUtils.clearHistory();
+    gURLBar.popup.maybeSetupSpeculativeConnect = oldSpeculativeConnect;
+    gHttpServer.identity.remove(gScheme, gHost, gPort);
+    gHttpServer.stop(() => {
+      gHttpServer = null;
+    });
+  });
+});
+
+add_task(async function autofill_tests() {
+  const test = {
+    search: gHost.substr(0, 2),
+    autofilledValue: `${gHost}/`
+  };
+
+  info(`Searching for '${test.search}'`);
+  await promiseAutocompleteResultPopup(test.search, window, true);
+  is(gURLBar.inputField.value, test.autofilledValue,
+     `Autofilled value is as expected for search '${test.search}'`);
+
+  await BrowserTestUtils.waitForCondition(() => {
+    if (gHttpServer) {
+      is(gHttpServer.connectionNumber, 1,
+         `${gHttpServer.connectionNumber} speculative connection has been setup.`)
+      return gHttpServer.connectionNumber == 1;
+    }
+    return false;
+  }, "Waiting for connection setup");
+});
+
--- a/browser/base/content/test/webrtc/get_user_media.html
+++ b/browser/base/content/test/webrtc/get_user_media.html
@@ -13,16 +13,17 @@ try {
   dump("audio: " + audioDevice + "\nvideo: " + videoDevice + "\n");
   useFakeStreams = false;
 } catch (e) {
   dump("TEST DEVICES: No test devices found (in media.{audio,video}_loopback_dev, using fake streams.\n");
   useFakeStreams = true;
 }
 
 function message(m) {
+  // eslint-disable-next-line no-unsanitized/property
   document.getElementById("message").innerHTML = m;
   window.parent.postMessage(m, "*");
 }
 
 var gStreams = [];
 
 function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) {
   var opts = {video: aVideo, audio: aAudio};
--- a/browser/base/content/test/webrtc/get_user_media_in_frame.html
+++ b/browser/base/content/test/webrtc/get_user_media_in_frame.html
@@ -13,16 +13,17 @@ try {
   dump("audio: " + audioDevice + "\nvideo: " + videoDevice + "\n");
   useFakeStreams = false;
 } catch (e) {
   dump("TEST DEVICES: No test devices found (in media.{audio,video}_loopback_dev, using fake streams.\n");
   useFakeStreams = true;
 }
 
 function message(m) {
+  // eslint-disable-next-line no-unsanitized/property
   document.getElementById("message").innerHTML = m;
   window.parent.postMessage(m, "*");
 }
 
 var gStreams = [];
 
 function requestDevice(aAudio, aVideo, aShare) {
   var opts = {video: aVideo, audio: aAudio};
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -2109,28 +2109,60 @@ file, You can obtain one at http://mozil
               parts.push(this._bundle.GetStringFromName(type + "ResultLabel"));
             } catch (e) {}
 
             return parts.filter(str => str).join(" ");
           ]]>
         </body>
       </method>
 
+      <method name="maybeSetupSpeculativeConnect">
+        <parameter name="aUriString"/>
+        <body><![CDATA[
+          // We shouldn't leak autocomplete result in the private context.
+          if (!Services.prefs.getBoolPref("browser.urlbar.speculativeConnect.enabled") ||
+              this.input.inPrivateContext) {
+            return;
+          }
+          try {
+            let uri = makeURI(aUriString);
+            Services.io.speculativeConnect2(uri, gBrowser.contentPrincipal, null);
+          } catch (ex) {
+            // Can't setup speculative connection for this uri string for some
+            // reason, just ignore it.
+          }
+        ]]></body>
+      </method>
+
       <method name="onResultsAdded">
         <body>
           <![CDATA[
             // If nothing is selected yet, select the first result if it is a
             // pre-selected "heuristic" result.  (See UnifiedComplete.js.)
             if (this.selectedIndex == -1 && this._isFirstResultHeuristic) {
               // Don't fire DOMMenuItemActive so that screen readers still see
               // the input as being focused.
               this.richlistbox.suppressMenuItemEvent = true;
               this.input.controller.setInitiallySelectedIndex(0);
               this.richlistbox.suppressMenuItemEvent = false;
             }
+            // If this is the first time we get the result from the current
+            // search, and the result is an "autofill" result, that means it's
+            // the site that user frequently visits. Then we could speculatively
+            // connect to this site as a performance optimization.
+            if (!this.input.gotResultForCurrentQuery &&
+                this.input.mController.matchCount > 0 &&
+                this.input.mController.getStyleAt(0).includes("autofill")) {
+              let uri = this.input.mController.getFinalCompleteValueAt(0);
+              // "http" will be stripped out, but other scheme won't.
+              if (!uri.includes("://")) {
+                uri = "http://" + uri;
+              }
+              this.maybeSetupSpeculativeConnect(uri);
+            }
 
             // When a result is present the footer should always be visible.
             this.footer.collapsed = false;
 
             this.input.gotResultForCurrentQuery = true;
             this.input.maybeReplayDeferredKeyEvents();
           ]]>
         </body>
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -11,20 +11,19 @@
        orient="vertical">
 
   <broadcasterset>
     <broadcaster id="identity-popup-mcb-learn-more" class="text-link plain" value="&identity.learnMore;"/>
     <broadcaster id="identity-popup-insecure-login-forms-learn-more" class="text-link plain" value="&identity.learnMore;"/>
   </broadcasterset>
 
   <panelmultiview id="identity-popup-multiView"
-                  mainViewId="identity-popup-mainView"
-                  descriptionheightworkaround="true">
-    <panelview id="identity-popup-mainView" flex="1">
-
+                  mainViewId="identity-popup-mainView">
+    <panelview id="identity-popup-mainView" flex="1"
+               descriptionheightworkaround="true">
       <!-- Security Section -->
       <hbox id="identity-popup-security" class="identity-popup-section">
         <vbox id="identity-popup-security-content" flex="1">
           <label class="plain">
             <label class="identity-popup-headline identity-popup-host"></label>
             <label class="identity-popup-headline identity-popup-hostless" crop="end"/>
           </label>
           <description class="identity-popup-connection-not-secure"
@@ -92,17 +91,18 @@
           <vbox id="identity-popup-permission-list"/>
           <description id="identity-popup-permission-reload-hint">&identity.permissionsReloadHint;</description>
           <description id="identity-popup-permission-empty-hint">&identity.permissionsEmpty;</description>
         </vbox>
       </hbox>
     </panelview>
 
     <!-- Security SubView -->
-    <panelview id="identity-popup-securityView">
+    <panelview id="identity-popup-securityView"
+               descriptionheightworkaround="true">
       <vbox id="identity-popup-securityView-header">
         <label class="plain">
           <label class="identity-popup-headline identity-popup-host"></label>
           <label class="identity-popup-headline identity-popup-hostless" crop="end"/>
         </label>
         <description class="identity-popup-connection-not-secure"
                      when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
         <description class="identity-popup-connection-secure"
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -40,16 +40,18 @@ XPCOMUtils.defineLazyPreferenceGetter(th
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const kSpecialWidgetPfx = "customizableui-special-";
 
 const kPrefCustomizationState        = "browser.uiCustomization.state";
 const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
 const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
 const kPrefDrawInTitlebar            = "browser.tabs.drawInTitlebar";
+const kPrefUIDensity                 = "browser.uidensity";
+const kPrefAutoTouchMode             = "browser.touchmode.auto";
 
 const kExpectedWindowURL = "chrome://browser/content/browser.xul";
 
 /**
  * The keys are the handlers that are fired when the event type (the value)
  * is fired on the subview. A widget that provides a subview has the option
  * of providing onViewShowing and onViewHiding event handlers.
  */
@@ -154,16 +156,18 @@ var gNewElementCount = 0;
 var gGroupWrapperCache = new Map();
 var gSingleWrapperCache = new WeakMap();
 var gListeners = new Set();
 
 var gUIStateBeforeReset = {
   uiCustomizationState: null,
   drawInTitlebar: null,
   currentTheme: null,
+  uiDensity: null,
+  autoTouchMode: null,
 };
 
 var gDefaultPanelPlacements = null;
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
   let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
@@ -2546,23 +2550,27 @@ var CustomizableUIInternal = {
 
     gResetting = false;
   },
 
   _resetUIState() {
     try {
       gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
       gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
+      gUIStateBeforeReset.uiDensity = Services.prefs.getIntPref(kPrefUIDensity);
+      gUIStateBeforeReset.autoTouchMode = Services.prefs.getBoolPref(kPrefAutoTouchMode);
       gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme;
     } catch (e) { }
 
     this._resetExtraToolbars();
 
     Services.prefs.clearUserPref(kPrefCustomizationState);
     Services.prefs.clearUserPref(kPrefDrawInTitlebar);
+    Services.prefs.clearUserPref(kPrefUIDensity);
+    Services.prefs.clearUserPref(kPrefAutoTouchMode);
     LightweightThemeManager.currentTheme = null;
     log.debug("State reset");
 
     // Reset placements to make restoring default placements possible.
     gPlacements = new Map();
     gDirtyAreaCache = new Set();
     gSeenWidgets = new Set();
     // Clear the saved state to ensure that defaults will be used.
@@ -2623,23 +2631,27 @@ var CustomizableUIInternal = {
         gUIStateBeforeReset.drawInTitlebar == null) {
       return;
     }
     gUndoResetting = true;
 
     let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
     let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
     let currentTheme = gUIStateBeforeReset.currentTheme;
+    let uiDensity = gUIStateBeforeReset.uiDensity;
+    let autoTouchMode = gUIStateBeforeReset.autoTouchMode;
 
     // Need to clear the previous state before setting the prefs
     // because pref observers may check if there is a previous UI state.
     this._clearPreviousUIState();
 
     Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
     Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
+    Services.prefs.setIntPref(kPrefUIDensity, uiDensity);
+    Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode);
     LightweightThemeManager.currentTheme = currentTheme;
     this.loadSavedState();
     // If the user just customizes toolbar/titlebar visibility, gSavedState will be null
     // and we don't need to do anything else here:
     if (gSavedState) {
       for (let areaId of Object.keys(gSavedState.placements)) {
         let placements = gSavedState.placements[areaId];
         gPlacements.set(areaId, placements);
@@ -2804,16 +2816,26 @@ var CustomizableUIInternal = {
         if (currentPlacements[i] != defaultPlacements[i]) {
           log.debug("Found " + currentPlacements[i] + " in " + areaId + " where " +
                     defaultPlacements[i] + " was expected!");
           return false;
         }
       }
     }
 
+    if (Services.prefs.prefHasUserValue(kPrefUIDensity)) {
+      log.debug(kPrefUIDensity + " pref is non-default");
+      return false;
+    }
+
+    if (Services.prefs.prefHasUserValue(kPrefAutoTouchMode)) {
+      log.debug(kPrefAutoTouchMode + " pref is non-default");
+      return false;
+    }
+
     if (Services.prefs.prefHasUserValue(kPrefDrawInTitlebar)) {
       log.debug(kPrefDrawInTitlebar + " pref is non-default");
       return false;
     }
 
     if (LightweightThemeManager.currentTheme) {
       log.debug(LightweightThemeManager.currentTheme + " theme is non-default");
       return false;
@@ -3535,17 +3557,19 @@ this.CustomizableUI = {
    * Can the last Restore Defaults operation be undone.
    *
    * @return A boolean stating whether an undo of the
    *         Restore Defaults can be performed.
    */
   get canUndoReset() {
     return gUIStateBeforeReset.uiCustomizationState != null ||
            gUIStateBeforeReset.drawInTitlebar != null ||
-           gUIStateBeforeReset.currentTheme != null;
+           gUIStateBeforeReset.currentTheme != null ||
+           gUIStateBeforeReset.autoTouchMode != null ||
+           gUIStateBeforeReset.uiDensity != null;
   },
 
   /**
    * Get the placement of a widget. This is by far the best way to obtain
    * information about what the state of your widget is. The internals of
    * this call are cheap (no DOM necessary) and you will know where the user
    * has put your widget.
    *
@@ -4192,16 +4216,17 @@ OverflowableToolbar.prototype = {
   },
 
   _onClickChevron(aEvent) {
     if (this._chevron.open) {
       this._panel.hidePopup();
       this._chevron.open = false;
     } else if (this._panel.state != "hiding") {
       this.show();
+      this._chevron.removeAttribute("animate");
     }
   },
 
   _onPanelHiding(aEvent) {
     this._chevron.open = false;
     this._panel.removeEventListener("dragover", this);
     this._panel.removeEventListener("dragend", this);
     let doc = aEvent.target.ownerDocument;
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -326,16 +326,17 @@ const CustomizableWidgets = [
         link.textContent = bundle.getString(`appMenuRemoteTabs.mobilePromo.${os}`);
         link.setAttribute("mobile-promo-os", os);
         link.className = "text-link remotetabs-promo-link";
         return link.outerHTML;
       });
       let promoParentElt = doc.getElementById("PanelUI-remotetabs-mobile-promo");
       // Put it all together...
       let contents = bundle.getFormattedString("appMenuRemoteTabs.mobilePromo.text2", formatArgs);
+      // eslint-disable-next-line no-unsanitized/property
       promoParentElt.innerHTML = contents;
       // We manually manage the "click" event to open the promo links because
       // allowing the "text-link" widget handle it has 2 problems: (1) it only
       // supports button 0 and (2) it's tricky to intercept when it does the
       // open and auto-close the panel. (1) can probably be fixed, but (2) is
       // trickier without hard-coding here the knowledge of exactly what buttons
       // it does support.
       // So we allow left and middle clicks to open the link in a new tab and
@@ -437,16 +438,18 @@ const CustomizableWidgets = [
           }
           if (paginationInfo && paginationInfo.clientId == client.id) {
             this._appendClient(client, fragment, paginationInfo.maxTabs);
           } else {
             this._appendClient(client, fragment);
           }
         }
         this._tabsList.appendChild(fragment);
+        let panelView = this._tabsList.closest("panelview");
+        panelView.panelMultiView.descriptionHeightWorkaround(panelView);
       }).catch(err => {
         Cu.reportError(err);
       }).then(() => {
         // an observer for tests.
         Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated");
       });
     },
     _clearTabList() {
@@ -599,20 +602,25 @@ const CustomizableWidgets = [
     tooltiptext: "sidebar-button.tooltiptext2",
     onCommand(aEvent) {
       let win = aEvent.target.ownerGlobal;
       win.SidebarUI.toggle();
     },
     onCreated(aNode) {
       // Add an observer so the button is checked while the sidebar is open
       let doc = aNode.ownerDocument;
-      let obnode = doc.createElementNS(kNSXUL, "observes");
-      obnode.setAttribute("element", "sidebar-box");
-      obnode.setAttribute("attribute", "checked");
-      aNode.appendChild(obnode);
+      let obChecked = doc.createElementNS(kNSXUL, "observes");
+      obChecked.setAttribute("element", "sidebar-box");
+      obChecked.setAttribute("attribute", "checked");
+      let obPosition = doc.createElementNS(kNSXUL, "observes");
+      obPosition.setAttribute("element", "sidebar-box");
+      obPosition.setAttribute("attribute", "positionend");
+
+      aNode.appendChild(obChecked);
+      aNode.appendChild(obPosition);
     }
   }, {
     id: "social-share-button",
     // custom build our button so we can attach to the share command
     type: "custom",
     onBuild(aDocument) {
       let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
       node.setAttribute("id", this.id);
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -327,19 +327,21 @@ CustomizeMode.prototype = {
           // natural style/layout flush.
           await new Promise(resolve => window.requestIdleCallback(resolve));
         }
 
         // Hide the palette before starting the transition for increased perf.
         this.visiblePalette.hidden = true;
         this.visiblePalette.removeAttribute("showing");
 
-        // Disable the button-text fade-out mask
-        // during the transition for increased perf.
-        window.PanelUI.contents.setAttribute("customize-transitioning", "true");
+        if (!AppConstants.MOZ_PHOTON_THEME) {
+          // Disable the button-text fade-out mask
+          // during the transition for increased perf.
+          window.PanelUI.contents.setAttribute("customize-transitioning", "true");
+        }
 
         // Move the mainView in the panel to the holder so that we can see it
         // while customizing.
         let mainView = window.PanelUI.mainView;
         panelHolder.appendChild(mainView);
 
         let customizeButton = document.getElementById("PanelUI-customize");
         customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
@@ -407,17 +409,19 @@ CustomizeMode.prototype = {
         // Force layout reflow to ensure the animation runs,
         // and make it async so it doesn't affect the timing.
         this.visiblePalette.clientTop;
         this.visiblePalette.setAttribute("showing", "true");
       }, 0);
       this._updateEmptyPaletteNotice();
 
       this._updateLWThemeButtonIcon();
-      this.maybeShowTip(panelHolder);
+      if (!AppConstants.MOZ_PHOTON_THEME) {
+        this.maybeShowTip(panelHolder);
+      }
 
       this._handler.isEnteringCustomizeMode = false;
       if (!gPhotonStructure) {
         window.PanelUI.contents.removeAttribute("customize-transitioning");
       }
 
       CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
       this._enableOutlinesTimeout = window.setTimeout(() => {
@@ -629,16 +633,27 @@ CustomizeMode.prototype = {
    * order - customize-entered, customize-exiting, remove LWT swapping,
    * pre-customization mode.
    *
    * When in the customize-entering, customize-entered, or customize-exiting
    * phases, there is a "customizing" attribute set on the main-window to simplify
    * excluding certain styles while in any phase of customize mode.
    */
   _doTransition(aEntering) {
+    if (AppConstants.MOZ_PHOTON_THEME) {
+      let docEl = this.document.documentElement;
+      if (aEntering) {
+        docEl.setAttribute("customizing", true);
+        docEl.setAttribute("customize-entered", true);
+      } else {
+        docEl.removeAttribute("customizing");
+        docEl.removeAttribute("customize-entered");
+      }
+      return Promise.resolve();
+    }
     let deck = this.document.getElementById("content-deck");
     let customizeTransitionEndPromise = new Promise(resolve => {
       let customizeTransitionEnd = (aEvent) => {
         if (aEvent != "timedout" &&
             (aEvent.originalTarget != deck || aEvent.propertyName != "margin-left")) {
           return;
         }
         this.window.clearTimeout(catchAllTimeout);
@@ -706,16 +721,17 @@ CustomizeMode.prototype = {
       return;
 
     let anchorNode = aAnchor || this.document.getElementById("customization-panelHolder");
     let messageNode = this.tipPanel.querySelector(".customization-tipPanel-contentMessage");
     if (!messageNode.childElementCount) {
       // Put the tip contents in the popup.
       let bundle = this.document.getElementById("bundle_browser");
       const kLabelClass = "customization-tipPanel-link";
+      // eslint-disable-next-line no-unsanitized/property
       messageNode.innerHTML = bundle.getFormattedString("customizeTips.tip0", [
         "<label class=\"customization-tipPanel-em\" value=\"" +
           bundle.getString("customizeTips.tip0.hint") + "\"/>",
         this.document.getElementById("bundle_brand").getString("brandShortName"),
         "<label class=\"" + kLabelClass + " text-link\" value=\"" +
         bundle.getString("customizeTips.tip0.learnMore") + "\"/>"
       ]);
 
@@ -784,16 +800,22 @@ CustomizeMode.prototype = {
       aNode = aNode.firstChild;
     }
     let panel = gPhotonStructure ? CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
                                  : CustomizableUI.AREA_PANEL;
     CustomizableUI.addWidgetToArea(aNode.id, panel);
     if (!this._customizing) {
       CustomizableUI.dispatchToolboxEvent("customizationchange");
     }
+
+    if (AppConstants.MOZ_PHOTON_ANIMATIONS &&
+        Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
+      let overflowButton = this.document.getElementById("nav-bar-overflow-button");
+      overflowButton.setAttribute("animate", "true");
+    }
   },
 
   removeFromArea(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
     if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
       aNode = aNode.firstChild;
     }
     CustomizableUI.removeWidgetFromArea(aNode.id);
@@ -1107,32 +1129,41 @@ CustomizeMode.prototype = {
 
   async _wrapToolbarItems() {
     for (let area of CustomizableUI.areas) {
       await this._wrapToolbarItem(area);
     }
   },
 
   _addDragHandlers(aTarget) {
+    // Allow dropping on the padding of the arrow panel.
+    if (gPhotonStructure && aTarget.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
+      aTarget = this.document.getElementById("customization-panelHolder");
+    }
     aTarget.addEventListener("dragstart", this, true);
     aTarget.addEventListener("dragover", this, true);
     aTarget.addEventListener("dragexit", this, true);
     aTarget.addEventListener("drop", this, true);
     aTarget.addEventListener("dragend", this, true);
   },
 
   _wrapItemsInArea(target) {
     for (let child of target.children) {
       if (this.isCustomizableItem(child)) {
         this.wrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
       }
     }
   },
 
   _removeDragHandlers(aTarget) {
+    // Remove handler from different target if it was added to
+    // allow dropping on the padding of the arrow panel.
+    if (gPhotonStructure && aTarget.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
+      aTarget = this.document.getElementById("customization-panelHolder");
+    }
     aTarget.removeEventListener("dragstart", this, true);
     aTarget.removeEventListener("dragover", this, true);
     aTarget.removeEventListener("dragexit", this, true);
     aTarget.removeEventListener("drop", this, true);
     aTarget.removeEventListener("dragend", this, true);
   },
 
   _unwrapItemsInArea(target) {
@@ -1356,16 +1387,108 @@ CustomizeMode.prototype = {
   },
 
   getMoreThemes(aEvent) {
     aEvent.target.parentNode.parentNode.hidePopup();
     let getMoreURL = Services.urlFormatter.formatURLPref("lightweightThemes.getMoreURL");
     this.window.openUILinkIn(getMoreURL, "tab");
   },
 
+  updateUIDensity(mode) {
+    this.window.gUIDensity.update(mode);
+  },
+
+  setUIDensity(mode) {
+    let win = this.window;
+    let gUIDensity = win.gUIDensity;
+    let currentDensity = gUIDensity.getCurrentDensity();
+    let panel = win.document.getElementById("customization-uidensity-menu");
+
+    Services.prefs.setIntPref(gUIDensity.uiDensityPref, mode);
+
+    // If the user is choosing a different UI density mode while
+    // the mode is overriden to Touch, remove the override.
+    if (currentDensity.overridden) {
+      Services.prefs.setBoolPref(gUIDensity.autoTouchModePref, false);
+    }
+
+    this._onUIChange();
+    panel.hidePopup();
+  },
+
+  resetUIDensity() {
+    this.window.gUIDensity.update();
+  },
+
+  onUIDensityMenuShowing() {
+    let win = this.window;
+    let doc = win.document;
+    let gUIDensity = win.gUIDensity;
+    let currentDensity = gUIDensity.getCurrentDensity();
+
+    let normalButton = doc.getElementById("customization-uidensity-menu-button-normal");
+    normalButton.mode = gUIDensity.MODE_NORMAL;
+
+    let compactButton = doc.getElementById("customization-uidensity-menu-button-compact");
+    compactButton.mode = gUIDensity.MODE_COMPACT;
+
+    let buttons = [normalButton, compactButton];
+
+    let touchButton = doc.getElementById("customization-uidensity-menu-button-touch");
+    // Touch mode can not be enabled in OSX right now.
+    if (touchButton) {
+      touchButton.mode = gUIDensity.MODE_TOUCH;
+      buttons.push(touchButton);
+    }
+
+    // Mark the active mode button.
+    for (let button of buttons) {
+      if (button.mode == currentDensity.mode) {
+        button.setAttribute("aria-checked", "true");
+        button.setAttribute("active", "true");
+      } else {
+        button.removeAttribute("aria-checked");
+        button.removeAttribute("active");
+      }
+    }
+
+    // Add menu items for automatically switching to Touch mode in Windows Tablet Mode,
+    // which is only available in Windows 10.
+    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+      let spacer = doc.getElementById("customization-uidensity-touch-spacer");
+      let checkbox = doc.getElementById("customization-uidensity-autotouchmode-checkbox");
+      spacer.removeAttribute("hidden");
+      checkbox.removeAttribute("hidden");
+
+      // Show a hint that the UI density was overridden automatically.
+      if (currentDensity.overridden) {
+        let sb = Services.strings.createBundle("chrome://browser/locale/uiDensity.properties");
+        touchButton.setAttribute("acceltext",
+                                 sb.GetStringFromName("uiDensity.menu-button-touch.acceltext"));
+      } else {
+        touchButton.removeAttribute("acceltext");
+      }
+
+      let autoTouchMode = Services.prefs.getBoolPref(win.gUIDensity.autoTouchModePref);
+      if (autoTouchMode) {
+        checkbox.setAttribute("checked", "true");
+      } else {
+        checkbox.removeAttribute("checked");
+      }
+    }
+  },
+
+  updateAutoTouchMode(checked) {
+    Services.prefs.setBoolPref("browser.touchmode.auto", checked);
+    // Re-render the menu items since the active mode might have
+    // change because of this.
+    this.onUIDensityMenuShowing();
+    this._onUIChange();
+  },
+
   onLWThemesMenuShowing(aEvent) {
     const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
     const RECENT_LWT_COUNT = 5;
 
     this._clearLWThemesMenu(aEvent.target);
 
     function previewTheme(aPreviewThemeEvent) {
       LightweightThemeManager.previewTheme(
@@ -2190,24 +2313,33 @@ CustomizeMode.prototype = {
       CustomizableUI.onWidgetDrag(aDraggedItem.id);
     } else {
       aDraggedItem.parentNode.hidden = true;
     }
     return size;
   },
 
   _getCustomizableParent(aElement) {
+    if (gPhotonStructure && aElement) {
+      // Deal with drag/drop on the padding of the panel in photon.
+      let containingPanelHolder = aElement.closest("#customization-panelHolder");
+      if (containingPanelHolder) {
+        return containingPanelHolder.firstChild;
+      }
+    }
+
     let areas = CustomizableUI.areas;
     areas.push(kPaletteId);
     while (aElement) {
       if (areas.indexOf(aElement.id) != -1) {
         return aElement;
       }
       aElement = aElement.parentNode;
     }
+
     return null;
   },
 
   _getDragOverNode(aEvent, aAreaElement, aAreaType, aDraggedItemId) {
     let expectedParent = aAreaElement.customizationTarget || aAreaElement;
     // Our tests are stupid. Cope:
     if (!aEvent.clientX && !aEvent.clientY) {
       return aEvent.target;
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -200,16 +200,25 @@ this.PanelMultiView = class {
                                    .getInterface(Ci.nsIDOMWindowUtils);
   }
   get _screenManager() {
     if (this.__screenManager)
       return this.__screenManager;
     return this.__screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
                                     .getService(Ci.nsIScreenManager);
   }
+  /**
+   * Getter that returns the currently visible subview OR the subview that is
+   * about to be shown whilst a 'ViewShowing' event is being dispatched.
+   *
+   * @return {panelview}
+   */
+  get current() {
+    return this._viewShowing || this._currentSubView
+  }
   get _currentSubView() {
     return this.panelViews ? this.panelViews.currentView : this.__currentSubView;
   }
   set _currentSubView(panel) {
     if (this.panelViews)
       this.panelViews.currentView = panel;
     else
       this.__currentSubView = panel;
@@ -290,16 +299,20 @@ this.PanelMultiView = class {
     });
     ["goBack", "descriptionHeightWorkaround", "setMainView", "showMainView",
      "showSubView"].forEach(method => {
       Object.defineProperty(this.node, method, {
         enumerable: true,
         value: (...args) => this[method](...args)
       });
     });
+    Object.defineProperty(this.node, "current", {
+      enumerable: true,
+      get: () => this.current
+    });
   }
 
   destructor() {
     // Guard against re-entrancy.
     if (!this.node)
       return;
 
     if (this._mainView) {
@@ -315,16 +328,17 @@ this.PanelMultiView = class {
       this._moveOutKids(this._viewStack);
       this.panelViews.clear();
     } else {
       this._clickCapturer.removeEventListener("click", this);
     }
     this._panel.removeEventListener("popupshowing", this);
     this._panel.removeEventListener("popupshown", this);
     this._panel.removeEventListener("popuphidden", this);
+    this.node.dispatchEvent(new this.window.CustomEvent("destructed"));
     this.node = this._clickCapturer = this._viewContainer = this._mainViewContainer =
       this._subViews = this._viewStack = this.__dwu = this._panelViewCache = null;
   }
 
   /**
    * Remove any child subviews into the panelViewCache, to ensure
    * they remain usable even if this panelmultiview instance is removed
    * from the DOM.
@@ -339,16 +353,26 @@ this.PanelMultiView = class {
     let subviews = Array.from(viewNodeContainer.childNodes);
     for (let subview of subviews) {
       // XBL lists the 'children' XBL element explicitly. :-(
       if (subview.nodeName != "children")
         this._panelViewCache.appendChild(subview);
     }
   }
 
+  _placeSubView(viewNode) {
+    if (this.panelViews) {
+      this._viewStack.appendChild(viewNode);
+      if (!this.panelViews.includes(viewNode))
+        this.panelViews.push(viewNode);
+    } else {
+      this._subViews.appendChild(viewNode);
+    }
+  }
+
   goBack(target) {
     let [current, previous] = this.panelViews.back();
     return this.showSubView(current, target, previous);
   }
 
   /**
    * Checks whether it is possible to navigate backwards currently. Returns
    * false if this is the panelmultiview's mainview, true otherwise.
@@ -404,25 +428,22 @@ this.PanelMultiView = class {
   showSubView(aViewId, aAnchor, aPreviousView) {
     const {document, window} = this;
     return (async () => {
       // Support passing in the node directly.
       let viewNode = typeof aViewId == "string" ? this.node.querySelector("#" + aViewId) : aViewId;
       if (!viewNode) {
         viewNode = document.getElementById(aViewId);
         if (viewNode) {
-          if (this.panelViews) {
-            this._viewStack.appendChild(viewNode);
-            this.panelViews.push(viewNode);
-          } else {
-            this._subViews.appendChild(viewNode);
-          }
+          this._placeSubView(viewNode);
         } else {
           throw new Error(`Subview ${aViewId} doesn't exist!`);
         }
+      } else if (viewNode.parentNode == this._panelViewCache) {
+        this._placeSubView(viewNode);
       }
 
       let reverse = !!aPreviousView;
       let previousViewNode = aPreviousView || this._currentSubView;
       let playTransition = (!!previousViewNode && previousViewNode != viewNode);
 
       let dwu, previousRect;
       if (playTransition || this.panelViews) {
@@ -467,16 +488,17 @@ this.PanelMultiView = class {
           if (custWidget.onInit)
             custWidget.onInit(aAnchor);
           custWidget.onViewShowing({ target: viewNode, preventDefault: () => cancel = true, detail });
         }
       }
       if (this.panelViews && this._mainViewWidth)
         viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
 
+      this._viewShowing = viewNode;
       let evt = new window.CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
       viewNode.dispatchEvent(evt);
 
       if (!cancel)
         cancel = evt.defaultPrevented;
       if (detail.blockers.size) {
         try {
           let results = await Promise.all(detail.blockers);
@@ -486,16 +508,17 @@ this.PanelMultiView = class {
           cancel = true;
         }
       }
 
       if (cancel) {
         return;
       }
 
+      this._viewShowing = null;
       this._currentSubView = viewNode;
       viewNode.setAttribute("current", true);
       if (this.panelViews) {
         this.node.setAttribute("viewtype", "subview");
         if (!playTransition)
           this.descriptionHeightWorkaround(viewNode);
       }
 
@@ -840,17 +863,33 @@ this.PanelMultiView = class {
         // height of the opposite margin, but we cannot get their actual values
         // because the panel is not visible yet. However, we know that this is
         // currently 11px on Mac, 13px on Windows, and 13px on Linux. We also
         // want an extra margin, both for visual reasons and to prevent glitches
         // due to small rounding errors. So, we just use a value that makes
         // sense for all platforms. If the arrow visuals change significantly,
         // this value will be easy to adjust.
         const EXTRA_MARGIN_PX = 20;
-        this._viewStack.style.maxHeight = (maxHeight - EXTRA_MARGIN_PX) + "px";
+        maxHeight -= EXTRA_MARGIN_PX;
+        this._viewStack.style.maxHeight = maxHeight + "px";
+
+        // When using block-in-box layout inside a scrollable frame, like in the
+        // main menu contents scroller, if we allow the contents to scroll then
+        // it will not cause its container to expand. Thus, we layout first
+        // without any scrolling (using "display: flex;"), and only if the view
+        // exceeds the available space we set the height explicitly and enable
+        // scrolling.
+        if (this._mainView.hasAttribute("blockinboxworkaround")) {
+          let mainViewHeight =
+              this._dwu.getBoundsWithoutFlushing(this._mainView).height;
+          if (mainViewHeight > maxHeight) {
+            this._mainView.style.height = maxHeight + "px";
+            this._mainView.setAttribute("exceeding", "true");
+          }
+        }
         break;
       case "popupshown":
         // Now that the main view is visible, we can check the height of the
         // description elements it contains.
         this.descriptionHeightWorkaround();
         break;
       case "popuphidden":
         this.node.removeAttribute("panelopen");
@@ -862,16 +901,22 @@ this.PanelMultiView = class {
               panelView.style.removeProperty("max-width");
             }
           }
           this.window.removeEventListener("keydown", this);
           this._panel.removeEventListener("mousemove", this);
           this._resetKeyNavigation();
           this._mainViewHeight = 0;
         }
+        // Always try to layout the panel normally when reopening it. This is
+        // also the layout that will be used in customize mode.
+        if (this._mainView.hasAttribute("blockinboxworkaround")) {
+          this._mainView.style.removeProperty("height");
+          this._mainView.removeAttribute("exceeding");
+        }
         break;
     }
   }
 
   /**
    * Allow for navigating subview buttons using the arrow keys and the Enter key.
    * The Up and Down keys can be used to navigate the list up and down and the
    * Enter, Right or Left - depending on the text direction - key can be used to
@@ -1007,39 +1052,51 @@ this.PanelMultiView = class {
       let bounds = dwu.getBoundsWithoutFlushing(button);
       return bounds.width > 0 && bounds.height > 0;
     });
   }
 
   /**
    * If the main view or a subview contains wrapping elements, the attribute
    * "descriptionheightworkaround" should be set on the view to force all the
-   * "description" or wrapping toolbarbutton elements to a fixed height.
-   * If the attribute is set and the visibility, contents, or width of any of
-   * these elements changes, this function should be called to refresh the
-   * calculated heights.
+   * wrapping "description", "label" or "toolbarbutton" elements to a fixed
+   * height. If the attribute is set and the visibility, contents, or width
+   * of any of these elements changes, this function should be called to
+   * refresh the calculated heights.
    *
    * This may trigger a synchronous layout.
    *
    * @param viewNode
    *        Indicates the node to scan for descendant elements. This is the main
    *        view if omitted.
    */
   descriptionHeightWorkaround(viewNode = this._mainView) {
-    if (!this.node.hasAttribute("descriptionheightworkaround")) {
+    if (!viewNode.hasAttribute("descriptionheightworkaround")) {
       // This view does not require the workaround.
       return;
     }
 
     // We batch DOM changes together in order to reduce synchronous layouts.
     // First we reset any change we may have made previously. The first time
     // this is called, and in the best case scenario, this has no effect.
     let items = [];
-    for (let element of viewNode.querySelectorAll(
-         "description:not([hidden]):not([value]),toolbarbutton[wrap]:not([hidden])")) {
+    // Non-hidden <label> or <description> elements that also aren't empty
+    // and also don't have a value attribute can be multiline (if their
+    // text content is long enough).
+    let isMultiline = ":not(:-moz-any([hidden],[value],:empty))";
+    let selector = [
+      "description" + isMultiline,
+      "label" + isMultiline,
+      "toolbarbutton[wrap]:not([hidden])",
+    ].join(",");
+    for (let element of viewNode.querySelectorAll(selector)) {
+      // Ignore items in hidden containers.
+      if (element.closest("[hidden]")) {
+        continue;
+      }
       // Take the label for toolbarbuttons; it only exists on those elements.
       element = element.labelElement || element;
 
       let bounds = element.getBoundingClientRect();
       let previous = this._multiLineElementsMap.get(element);
       // We don't need to (re-)apply the workaround for invisible elements or
       // on elements we've seen before and haven't changed in the meantime.
       if (!bounds.width || !bounds.height ||
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -46,16 +46,73 @@
             <toolbarbutton class="customization-lwtheme-menu-footeritem"
                            label="&customizeMode.lwthemes.menuGetMore;"
                            accesskey="&customizeMode.lwthemes.menuGetMore.accessKey;"
                            tabindex="0"
                            oncommand="gCustomizeMode.getMoreThemes(event);"/>
           </hbox>
         </panel>
       </button>
+#ifdef MOZ_PHOTON_THEME
+      <button id="customization-uidensity-button"
+              label="&customizeMode.uidensity;"
+              class="customizationmode-button"
+              type="menu">
+        <panel type="arrow" id="customization-uidensity-menu"
+               onpopupshowing="gCustomizeMode.onUIDensityMenuShowing();"
+               position="topcenter bottomleft"
+               flip="none"
+               role="menu">
+          <menuitem id="customization-uidensity-menu-button-normal"
+                    class="menuitem-iconic customization-uidensity-menu-button"
+                    role="menuitemradio"
+                    label="&customizeMode.uidensity.menuNormal.label;"
+                    accesskey="&customizeMode.uidensity.menuNormal.accessKey;"
+                    tooltiptext="&customizeMode.uidensity.menuNormal.tooltip;"
+                    tabindex="0"
+                    onfocus="gCustomizeMode.updateUIDensity(this.mode);"
+                    onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
+                    onblur="gCustomizeMode.resetUIDensity();"
+                    onmouseout="gCustomizeMode.resetUIDensity();"
+                    oncommand="gCustomizeMode.setUIDensity(this.mode);" />
+          <menuitem id="customization-uidensity-menu-button-compact"
+                    class="menuitem-iconic customization-uidensity-menu-button"
+                    role="menuitemradio"
+                    label="&customizeMode.uidensity.menuCompact.label;"
+                    accesskey="&customizeMode.uidensity.menuCompact.accessKey;"
+                    tooltiptext="&customizeMode.uidensity.menuCompact.tooltip;"
+                    tabindex="0"
+                    onfocus="gCustomizeMode.updateUIDensity(this.mode);"
+                    onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
+                    onblur="gCustomizeMode.resetUIDensity();"
+                    onmouseout="gCustomizeMode.resetUIDensity();"
+                    oncommand="gCustomizeMode.setUIDensity(this.mode);" />
+#ifndef XP_MACOSX
+          <menuitem id="customization-uidensity-menu-button-touch"
+                    class="menuitem-iconic customization-uidensity-menu-button"
+                    role="menuitemradio"
+                    label="&customizeMode.uidensity.menuTouch.label;"
+                    accesskey="&customizeMode.uidensity.menuTouch.accessKey;"
+                    tooltiptext="&customizeMode.uidensity.menuTouch.tooltip;"
+                    tabindex="0"
+                    onfocus="gCustomizeMode.updateUIDensity(this.mode);"
+                    onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
+                    onblur="gCustomizeMode.resetUIDensity();"
+                    onmouseout="gCustomizeMode.resetUIDensity();"
+                    oncommand="gCustomizeMode.setUIDensity(this.mode);">
+          </menuitem>
+          <spacer hidden="true" id="customization-uidensity-touch-spacer"/>
+          <checkbox id="customization-uidensity-autotouchmode-checkbox"
+                    hidden="true"
+                    label="&customizeMode.uidensity.autoTouchMode.checkbox.label;"
+                    oncommand="gCustomizeMode.updateAutoTouchMode(this.checked)"/>
+#endif
+        </panel>
+      </button>
+#endif
 
       <spacer id="customization-footer-spacer"/>
       <button id="customization-undo-reset-button"
               class="customizationmode-button"
               hidden="true"
               oncommand="gCustomizeMode.undoReset();"
               label="&undoCmd.label;"/>
       <button id="customization-reset-button"
@@ -67,21 +124,17 @@
               oncommand="gCustomizeMode.exit();"
               label="&customizeMode.done;"
               class="customizationmode-button"/>
     </hbox>
   </box>
   <box id="customization-content-container"/>
   <vbox id="customization-panel-container">
     <vbox id="customization-panelWrapper">
-      <html:style html:type="text/html" scoped="scoped">
-        @import url(chrome://global/skin/popup.css);
-      </html:style>
       <box class="panel-arrowbox">
-        <box flex="1"/>
         <image class="panel-arrow" side="top"/>
       </box>
       <box class="panel-arrowcontent" side="top" flex="1">
         <hbox id="customization-panelHolder"/>
         <box class="panel-inner-arrowcontentfooter" hidden="true"/>
       </box>
     </vbox>
   </vbox>
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -6,17 +6,18 @@
        role="group"
        type="arrow"
        hidden="true"
        flip="slide"
        position="bottomcenter topright"
        noautofocus="true">
   <panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView"
                   viewCacheId="appMenu-viewCache">
-    <panelview id="PanelUI-mainView" context="customizationPanelContextMenu">
+    <panelview id="PanelUI-mainView" context="customizationPanelContextMenu"
+               descriptionheightworkaround="true" blockinboxworkaround="true">
       <vbox id="PanelUI-contents-scroller">
         <vbox id="PanelUI-contents" class="panelUI-grid"/>
       </vbox>
 
       <footer id="PanelUI-footer">
         <vbox id="PanelUI-footer-addons"></vbox>
         <toolbarbutton class="panel-banner-item"
                        label-update-available="&updateAvailable.panelUI.label;"
@@ -104,17 +105,18 @@
         <vbox id="PanelUI-historyItems" tooltip="bhTooltip"/>
       </vbox>
       <toolbarbutton id="PanelUI-historyMore"
                      class="panel-subview-footer subviewbutton"
                      label="&appMenuHistory.showAll.label;"
                      oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
     </panelview>
 
-    <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView">
+    <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView"
+               descriptionheightworkaround="true">
       <label value="&appMenuRemoteTabs.label;" class="panel-subview-header"/>
       <vbox class="panel-subview-body">
         <!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
         <!-- When Sync is ready to sync -->
         <vbox id="PanelUI-remotetabs-main" observes="sync-syncnow-state">
           <vbox id="PanelUI-remotetabs-buttons">
             <toolbarbutton id="PanelUI-remotetabs-view-sidebar"
                            class="subviewbutton subviewbutton-iconic"
@@ -205,49 +207,72 @@
         </hbox>
       </vbox>
     </panelview>
 
     <panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
       <label value="&bookmarksMenu.label;" class="panel-subview-header"/>
       <vbox class="panel-subview-body">
         <toolbarbutton id="panelMenuBookmarkThisPage"
+#ifndef MOZ_PHOTON_THEME
                        class="subviewbutton"
+#else
+                       class="subviewbutton subviewbutton-iconic"
+#endif
                        observes="bookmarkThisPageBroadcaster"
                        command="Browser:AddBookmarkAs"
                        onclick="PanelUI.hide();"/>
+#ifndef MOZ_PHOTON_THEME
         <toolbarseparator/>
+#endif
         <toolbarbutton id="panelMenu_viewBookmarksSidebar"
                        label="&viewBookmarksSidebar2.label;"
+#ifndef MOZ_PHOTON_THEME
                        class="subviewbutton"
+#else
+                       class="subviewbutton subviewbutton-iconic"
+#endif
                        key="viewBookmarksSidebarKb"
                        oncommand="SidebarUI.toggle('viewBookmarksSidebar'); PanelUI.hide();">
           <observes element="viewBookmarksSidebar" attribute="checked"/>
         </toolbarbutton>
+#ifndef MOZ_PHOTON_THEME
         <toolbarbutton id="panelMenu_viewBookmarksToolbar"
                        label="&viewBookmarksToolbar.label;"
                        type="checkbox"
                        toolbarId="PersonalToolbar"
                        class="subviewbutton"
                        oncommand="onViewToolbarCommand(event); PanelUI.hide();"/>
         <toolbarseparator/>
         <toolbarbutton id="panelMenu_bookmarksToolbar"
                        label="&personalbarCmd.label;"
                        class="subviewbutton cui-withicon"
                        oncommand="PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar'); PanelUI.hide();"/>
         <toolbarbutton id="panelMenu_unsortedBookmarks"
                        label="&otherBookmarksCmd.label;"
                        class="subviewbutton cui-withicon"
                        oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks'); PanelUI.hide();"/>
         <toolbarseparator class="small-separator"/>
+#else
+        <toolbarbutton id="panelMenu_searchBookmarks"
+                       label="&searchBookmarks.label;"
+                       class="subviewbutton subviewbutton-iconic"
+                       oncommand="PlacesCommandHook.searchBookmarks(); PanelUI.hide();"/>
+        <toolbarseparator/>
+        <label id="panelMenu_recentBookmarks"
+               value="&recentBookmarks.label;"
+               class="subview-subheader"/>
+#endif
         <toolbaritem id="panelMenu_bookmarksMenu"
                      orient="vertical"
                      smoothscroll="false"
+#ifndef MOZ_PHOTON_THEME
                      onclick="if (event.button == 1) BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
                      oncommand="BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
+#endif
                      flatList="true"
                      tooltip="bhTooltip">
           <!-- bookmarks menu items will go here -->
         </toolbaritem>
       </vbox>
       <toolbarbutton id="panelMenu_showAllBookmarks"
                      label="&showAllBookmarks2.label;"
                      class="subviewbutton panel-subview-footer"
@@ -293,17 +318,18 @@
         <vbox>
           <label id="PanelUI-characterEncodingView-autodetect-label"/>
           <vbox id="PanelUI-characterEncodingView-autodetect"
                 class="PanelUI-characterEncodingView-list"/>
         </vbox>
       </vbox>
     </panelview>
 
-    <panelview id="PanelUI-panicView" flex="1">
+    <panelview id="PanelUI-panicView" flex="1"
+               descriptionheightworkaround="true">
       <vbox class="panel-subview-body">
         <hbox id="PanelUI-panic-timeframe">
           <image id="PanelUI-panic-timeframe-icon" alt=""/>
           <vbox flex="1">
             <hbox id="PanelUI-panic-header">
               <image id="PanelUI-panic-timeframe-icon-small" alt=""/>
               <description id="PanelUI-panic-mainDesc" flex="1">&panicButton.view.mainTimeframeDesc;</description>
             </hbox>
@@ -348,17 +374,18 @@
   <panelmultiview mainViewId="widget-overflow-mainView">
     <panelview id="widget-overflow-mainView"
                context="toolbar-context-menu">
 #endif
       <vbox id="widget-overflow-scroller">
         <vbox id="widget-overflow-list" class="widget-overflow-list"
               overflowfortoolbar="nav-bar"/>
         <toolbarseparator id="widget-overflow-fixed-separator" hidden="true"/>
-        <vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true"/>
+        <vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true"
+              emptylabel="&customizeMode.emptyOverflowList.description;"/>
       </vbox>
 #ifdef MOZ_PHOTON_THEME
     </panelview>
   </panelmultiview>
 #endif
 </panel>
 
 <panel id="customization-tipPanel"
@@ -507,19 +534,19 @@
        class="cui-widget-panel"
        role="group"
        type="arrow"
        hidden="true"
        flip="slide"
        position="bottomcenter topright"
        noautofocus="true">
   <photonpanelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"
-                        descriptionheightworkaround="true"
                         viewCacheId="appMenu-viewCache">
-    <panelview id="appMenu-mainView" class="PanelUI-subView">
+    <panelview id="appMenu-mainView" class="PanelUI-subView"
+               descriptionheightworkaround="true">
       <vbox class="panel-subview-body">
         <vbox id="appMenu-addon-banners"/>
         <toolbarbutton class="panel-banner-item"
                        label-update-available="&updateAvailable.panelUI.label;"
                        label-update-manual="&updateManual.panelUI.label;"
                        label-update-restart="&updateRestart.panelUI.label2;"
                        oncommand="PanelUI._onBannerItemSelected(event)"
                        wrap="true"
@@ -637,21 +664,16 @@
                        command="Browser:OpenFile"
                        />
         <toolbarbutton id="appMenu-save-file-button"
                        class="subviewbutton"
                        label="&savePageCmd.label;"
                        key="key_savePage"
                        command="Browser:SavePage"
                        />
-        <toolbarbutton id="appMenu-page-setup-button"
-                       class="subviewbutton"
-                       label="&printSetupCmd.label;"
-                       command="cmd_pageSetup"
-                       />
         <toolbarbutton id="appMenu-print-button"
                        class="subviewbutton subviewbutton-iconic"
                        label="&printButton.label;"
                        key="printKb"
 #ifdef XP_MACOSX
                        command="cmd_print"
 #else
                        command="cmd_printPreview"
@@ -705,16 +727,21 @@
                        label="&goOfflineCmd.label;"
                        type="checkbox"
                        observes="workOfflineMenuitemState"
                        oncommand="BrowserOffline.toggleOfflineStatus();"/>
       </vbox>
     </panelview>
     <panelview id="appMenu-libraryView" class="PanelUI-subView">
       <vbox class="panel-subview-body">
+        <toolbarbutton id="appMenu-library-bookmarks-button"
+                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
+                       label="&bookmarksMenuButton.label;"
+                       closemenu="none"
+                       oncommand="BookmarkingUI.showSubView(this);"/>
         <toolbarbutton id="appMenu-library-history-button"
                        class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                        label="&historyMenu.label;"
                        closemenu="none"
                        oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
         <toolbarbutton id="appMenu-library-remotetabs-button"
                        class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                        label="&appMenuRemoteTabs.label;"
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -116,19 +116,21 @@ const PanelUI = {
   // we need to know whether anything is in the permanent panel area.
   _initPhotonPanel() {
     if (gPhotonStructure) {
       this.overflowFixedList.hidden = false;
       // Also unhide the separator. We use CSS to hide/show it based on the panel's content.
       this.overflowFixedList.previousSibling.hidden = false;
       CustomizableUI.registerMenuPanel(this.overflowFixedList, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
       this.navbar.setAttribute("photon-structure", "true");
+      document.documentElement.setAttribute("photon-structure", "true");
       this.updateOverflowStatus();
     } else {
       this.navbar.removeAttribute("photon-structure");
+      document.documentElement.removeAttribute("photon-structure");
     }
   },
 
   _initElements() {
     for (let [k, v] of Object.entries(this.kElements)) {
       if (!v) {
         continue;
       }
--- a/browser/components/customizableui/content/panelUI.xml
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -67,17 +67,17 @@
   <binding id="panelview">
     <content>
       <xul:box class="panel-header" anonid="header">
         <xul:toolbarbutton anonid="back"
                            class="subviewbutton subviewbutton-iconic subviewbutton-back"
                            closemenu="none"
                            tabindex="0"
                            tooltip="&backCmd.label;"
-                           onclick="document.getBindingParent(this).panelMultiView.goBack()"/>
+                           onclick="document.getBindingParent(this).panelMultiView.goBack(); this.blur()"/>
         <xul:label xbl:inherits="value=title"/>
       </xul:box>
       <children/>
     </content>
     <implementation>
       <property name="header"
                 readonly="true"
                 onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'header');"/>
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -142,16 +142,17 @@ skip-if = os == "linux" # crashing on Li
 tags = fullscreen
 skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
+[browser_customizemode_uidensity.js]
 [browser_exit_background_customize_mode.js]
 [browser_overflow_use_subviews.js]
 [browser_panel_keyboard_navigation.js]
 [browser_panel_toggle.js]
 [browser_panelUINotifications.js]
 [browser_panelUINotifications_fullscreen.js]
 tags = fullscreen
 skip-if = os == "mac"
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_customizemode_uidensity.js
@@ -0,0 +1,181 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PREF_UI_DENSITY = "browser.uidensity";
+const PREF_AUTO_TOUCH_MODE = "browser.touchmode.auto";
+
+async function testModeButton(mode, modePref) {
+  await startCustomizing();
+
+  let win = document.getElementById("main-window");
+  let popupButton = document.getElementById("customization-uidensity-button");
+  let popup = document.getElementById("customization-uidensity-menu");
+
+  // Show the popup.
+  let popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(popupButton, {});
+  await popupShownPromise;
+
+  let button = document.getElementById("customization-uidensity-menu-button-" + mode);
+  let normalButton = document.getElementById("customization-uidensity-menu-button-normal");
+
+  is(normalButton.getAttribute("active"), "true",
+     "Normal mode button should be active by default.");
+
+  // Hover over the mode button and wait until the UI density is updated.
+  EventUtils.synthesizeMouseAtCenter(button, { type: "mouseover" });
+  await BrowserTestUtils.waitForAttribute("uidensity", win, mode);
+
+  is(win.getAttribute("uidensity"), mode,
+     `UI Density should be set to ${mode} on ${mode} button hover.`);
+
+  is(Services.prefs.getIntPref(PREF_UI_DENSITY), window.gUIDensity.MODE_NORMAL,
+     `UI Density pref should still be set to normal on ${mode} button hover.`);
+
+  // Hover the normal button again and check that the UI density reset to normal.
+  EventUtils.synthesizeMouseAtCenter(normalButton, { type: "mouseover" });
+  await BrowserTestUtils.waitForCondition(() => !win.hasAttribute("uidensity"));
+
+  ok(!win.hasAttribute("uidensity"),
+     `UI Density should be reset when no longer hovering the ${mode} button.`);
+
+  // Select the custom UI density and wait for the popup to be hidden.
+  let popupHiddenPromise = popupHidden(popup);
+  EventUtils.synthesizeMouseAtCenter(button, {});
+  await popupHiddenPromise;
+
+  // Check that the click permanently changed the UI density.
+  is(win.getAttribute("uidensity"), mode,
+     `UI Density should be set to ${mode} on ${mode} button click.`);
+  is(Services.prefs.getIntPref(PREF_UI_DENSITY), modePref,
+     `UI Density pref should be set to ${mode} when clicking the ${mode} button.`);
+
+  // Open the popup again.
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(popupButton, {});
+  await popupShownPromise;
+
+  // Check that the button is still active after opening and closing the popup.
+  is(button.getAttribute("active"), "true", `${mode} mode button should be active.`);
+
+  // Hide the popup again.
+  popupHiddenPromise = popupHidden(popup);
+  EventUtils.synthesizeMouseAtCenter(popupButton, {});
+  await popupHiddenPromise;
+
+  // Check that the button is still active after re-opening customize mode.
+  await endCustomizing();
+  await startCustomizing();
+
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(popupButton, {});
+  await popupShownPromise;
<