Merge autoland to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 28 Nov 2018 11:48:31 +0200
changeset 448473 9234dc84cd93547d60e7f74949b422003691677d
parent 448459 4b9f8acdf5f780ed38490f40a74bb33cac3f264d (current diff)
parent 448472 209ed62867bf0159b4d9a49fdd98bcea43c64287 (diff)
child 448474 5c66354bff282452a6f1a3c911fa8756b6e752af
push id35114
push usernbeleuzu@mozilla.com
push dateWed, 28 Nov 2018 09:51:41 +0000
treeherdermozilla-central@9234dc84cd93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
toolkit/content/tests/widgets/test_ua_widget.html
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -97,17 +97,17 @@ LogDocURI(nsIDocument* aDocumentNode)
 
 static void
 LogDocShellState(nsIDocument* aDocumentNode)
 {
   printf("docshell busy: ");
 
   nsAutoCString docShellBusy;
   nsCOMPtr<nsIDocShell> docShell = aDocumentNode->GetDocShell();
-  uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
+  nsIDocShell::BusyFlags busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
   docShell->GetBusyFlags(&busyFlags);
   if (busyFlags == nsIDocShell::BUSY_FLAGS_NONE) {
     printf("'none'");
   }
   if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY) {
     printf("'busy'");
   }
   if (busyFlags & nsIDocShell::BUSY_FLAGS_BEFORE_PAGE_LOAD) {
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1702,16 +1702,18 @@ pref("extensions.formautofill.creditCard
 // 0: none
 // 1: submitted a manually-filled credit card form (but didn't see the doorhanger
 //    because of a duplicate profile in the storage)
 // 2: saw the doorhanger
 // 3: submitted an autofill'ed credit card form
 pref("extensions.formautofill.creditCards.used", 0);
 pref("extensions.formautofill.firstTimeUse", true);
 pref("extensions.formautofill.heuristics.enabled", true);
+// Whether the user enabled the OS re-auth dialog.
+pref("extensions.formautofill.reauth.enabled", false);
 pref("extensions.formautofill.section.enabled", true);
 pref("extensions.formautofill.loglevel", "Warn");
 
 #ifdef NIGHTLY_BUILD
 // Comma separated list of countries Form Autofill is available in.
 pref("extensions.formautofill.supportedCountries", "US,CA,DE");
 pref("extensions.formautofill.supportRTL", true);
 #else
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -370,30 +370,16 @@ Object.defineProperty(this, "gFindBarPro
 async function gLazyFindCommand(cmd, ...args) {
   let fb = await gFindBarPromise;
   // We could be closed by now, or the tab with XBL binding could have gone away:
   if (fb && fb[cmd]) {
     fb[cmd].apply(fb, args);
   }
 }
 
-Object.defineProperty(this, "AddonManager", {
-  configurable: true,
-  enumerable: true,
-  get() {
-    let tmp = {};
-    ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
-    return this.AddonManager = tmp.AddonManager;
-  },
-  set(val) {
-    delete this.AddonManager;
-    return this.AddonManager = val;
-  },
-});
-
 
 var gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore",
--- a/browser/components/payments/test/browser/browser.ini
+++ b/browser/components/payments/test/browser/browser.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 head = head.js
 prefs =
   browser.pagethumbnails.capturing_disabled=true
   dom.payments.request.enabled=true
   extensions.formautofill.creditCards.available=true
+  extensions.formautofill.reauth.enabled=true
 skip-if = !e10s # Bug 1365964 - Payment Request isn't implemented for non-e10s
 support-files =
   blank_page.html
 
 [browser_address_edit.js]
 skip-if = verify && debug && os == 'mac'
 [browser_address_edit_hidden_fields.js]
 [browser_card_edit.js]
--- a/browser/extensions/formautofill/OSKeyStore.jsm
+++ b/browser/extensions/formautofill/OSKeyStore.jsm
@@ -104,17 +104,17 @@ var OSKeyStore = {
       log.debug("ensureLoggedIn: Has a pending unlock operation");
       return this._pendingUnlockPromise;
     }
     log.debug("ensureLoggedIn: Creating new pending unlock promise. reauth: ", reauth);
 
     let unlockPromise;
 
     // Decides who should handle reauth
-    if (typeof reauth == "boolean" && !reauth) {
+    if (!this._reauthEnabledByUser || (typeof reauth == "boolean" && !reauth)) {
       unlockPromise = Promise.resolve();
     } else if (!AppConstants.MOZILLA_OFFICIAL && this._testReauth) {
       unlockPromise = this._reauthInTests();
     } else if (AppConstants.platform == "win" ||
                AppConstants.platform == "macosx") {
       let reauthLabel = typeof reauth == "string" ? reauth : "";
       // On Windows, this promise rejects when the user cancels login dialog, see bug 1502121.
       // On macOS this resolves to false, so we would need to check it.
@@ -247,8 +247,10 @@ XPCOMUtils.defineLazyGetter(this, "log",
   let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
   return new ConsoleAPI({
     maxLogLevelPref: "extensions.formautofill.loglevel",
     prefix: "OSKeyStore",
   });
 });
 
 XPCOMUtils.defineLazyPreferenceGetter(OSKeyStore, "_testReauth", TEST_ONLY_REAUTH, "");
+XPCOMUtils.defineLazyPreferenceGetter(OSKeyStore, "_reauthEnabledByUser",
+                                      "extensions.formautofill.reauth.enabled", false);
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 head = head.js
 prefs =
   extensions.formautofill.creditCards.available=true
+  extensions.formautofill.reauth.enabled=true
 support-files =
   ../fixtures/autocomplete_basic.html
   ../fixtures/autocomplete_simple_basic.html
   ../fixtures/autocomplete_creditcard_basic.html
 
 [browser_autocomplete_footer.js]
 skip-if = verify
 [browser_autocomplete_marked_back_forward.js]
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 prefs =
   extensions.formautofill.creditCards.available=true
+  extensions.formautofill.reauth.enabled=true
 support-files =
   ../../../../../toolkit/components/satchel/test/satchel_common.js
   ../../../../../toolkit/components/satchel/test/parent_utils.js
   formautofill_common.js
   formautofill_parent_utils.js
 
 [test_address_level_1_submission.html]
 [test_autofocus_form.html]
--- a/browser/extensions/formautofill/test/unit/test_osKeyStore.js
+++ b/browser/extensions/formautofill/test/unit/test_osKeyStore.js
@@ -1,16 +1,18 @@
 /**
  * Tests of OSKeyStore.jsm
  */
 
 "use strict";
 
 let OSKeyStore;
 add_task(async function setup() {
+  Services.prefs.setBoolPref("extensions.formautofill.reauth.enabled", true);
+
   ({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", {}));
 });
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 
 const testText = "test string";
 let cipherText;
@@ -54,16 +56,21 @@ add_task(async function test_reauth() {
   let plainText2 = await OSKeyStore.decrypt(cipherText, true);
   await reauthObserved;
   Assert.equal(testText, plainText2);
 
   reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
   await new Promise(resolve => TestUtils.executeSoon(resolve));
   Assert.equal(await OSKeyStore.ensureLoggedIn(true), true, "Reauth logged in.");
   await reauthObserved;
+
+  Services.prefs.setBoolPref("extensions.formautofill.reauth.enabled", false);
+  Assert.equal(await OSKeyStore.ensureLoggedIn(true), true,
+               "Reauth disabled so logged in without prompt");
+  Services.prefs.setBoolPref("extensions.formautofill.reauth.enabled", true);
 });
 
 add_task(async function test_decryption_failure() {
   try {
     await OSKeyStore.decrypt("Malformed cipher text");
     throw new Error("Not receiving decryption error");
   } catch (ex) {
     Assert.notEqual(ex.result, Cr.NS_ERROR_ABORT);
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -526,17 +526,20 @@ BrowserTabList.prototype.receiveMessage 
     }
   });
 
 /**
  * Implement nsIDOMEventListener.
  */
 BrowserTabList.prototype.handleEvent =
 DevToolsUtils.makeInfallible(function(event) {
-  const browser = event.target.linkedBrowser;
+  // If event target has `linkedBrowser`, the event target can be assumed <tab> element.
+  // Else (in Android case), because event target is assumed <browser> element,
+  // use the target as it is.
+  const browser = event.target.linkedBrowser || event.target;
   switch (event.type) {
     case "TabOpen":
     case "TabSelect": {
       /* Don't create a new actor; iterate will take care of that. Just notify. */
       this._notifyListChanged();
       this._checkListening();
       break;
     }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -786,19 +786,19 @@ nsDocShell::LoadURI(nsDocShellLoadState*
                       nullptr, // no nsIDocShell
                       nullptr); // no nsIRequest
 }
 
 void
 nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
 {
   // First, verify if this is a subframe.
-    nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
-    GetSameTypeParent(getter_AddRefs(parentAsItem));
-    nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+  nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+  GetSameTypeParent(getter_AddRefs(parentAsItem));
+  nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
 
   if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
     // This is the root docshell. If we got here while
     // executing an onLoad Handler,this load will not go
     // into session history.
     bool inOnLoadHandler = false;
     GetIsExecutingOnLoadHandler(&inOnLoadHandler);
     if (inOnLoadHandler) {
@@ -808,68 +808,67 @@ nsDocShell::MaybeHandleSubframeHistory(n
   }
 
   /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
    * loaded through a history mechanism, then get the SH entry for the child from
    * the parent. This is done to restore frameset navigation while going
    * back/forward. If the parent was loaded through any other loadType, set the
    * child's loadType too accordingly, so that session history does not get
    * confused.
-       */
-
-      // Get the parent's load type
+   */
+
+  // Get the parent's load type
   uint32_t parentLoadType;
-      parentDS->GetLoadType(&parentLoadType);
-
-      // Get the ShEntry for the child from the parent
-      nsCOMPtr<nsISHEntry> currentSH;
-      bool oshe = false;
-      parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
-      bool dynamicallyAddedChild = mDynamicallyCreated;
-
-      if (!dynamicallyAddedChild && !oshe && currentSH) {
-        currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
-      }
-
-      if (!dynamicallyAddedChild) {
-        // Only use the old SHEntry, if we're sure enough that
-        // it wasn't originally for some other frame.
+  parentDS->GetLoadType(&parentLoadType);
+
+  // Get the ShEntry for the child from the parent
+  nsCOMPtr<nsISHEntry> currentSH;
+  bool oshe = false;
+  parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+  bool dynamicallyAddedChild = mDynamicallyCreated;
+
+  if (!dynamicallyAddedChild && !oshe && currentSH) {
+    currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+  }
+
+  if (!dynamicallyAddedChild) {
+    // Only use the old SHEntry, if we're sure enough that
+    // it wasn't originally for some other frame.
     nsCOMPtr<nsISHEntry> shEntry;
-        parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
+    parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
     aLoadState->SetSHEntry(shEntry);
-      }
-
-      // Make some decisions on the child frame's loadType based on the
-      // parent's loadType, if the subframe hasn't loaded anything into it.
-      //
-      // In some cases privileged scripts may try to get the DOMWindow
-      // reference of this docshell before the loading starts, causing the
-      // initial about:blank content viewer being created and mCurrentURI being
-      // set. To handle this case we check if mCurrentURI is about:blank and
-      // currentSHEntry is null.
-      nsCOMPtr<nsISHEntry> currentChildEntry;
-      GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
+  }
+
+  // Make some decisions on the child frame's loadType based on the
+  // parent's loadType, if the subframe hasn't loaded anything into it.
+  //
+  // In some cases privileged scripts may try to get the DOMWindow
+  // reference of this docshell before the loading starts, causing the
+  // initial about:blank content viewer being created and mCurrentURI being
+  // set. To handle this case we check if mCurrentURI is about:blank and
+  // currentSHEntry is null.
+  nsCOMPtr<nsISHEntry> currentChildEntry;
+  GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
 
   if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) {
-        // This is a pre-existing subframe. If
-        // 1. The load of this frame was not originally initiated by session
-        //    history directly (i.e. (!shEntry) condition succeeded, but it can
-        //    still be a history load on parent which causes this frame being
+    // This is a pre-existing subframe. If
+    // 1. The load of this frame was not originally initiated by session
+    //    history directly (i.e. (!shEntry) condition succeeded, but it can
+    //    still be a history load on parent which causes this frame being
     //    loaded), which we checked with the above assert, and
-        // 2. mCurrentURI is not null, nor the initial about:blank,
-        // it is possible that a parent's onLoadHandler or even self's
-        // onLoadHandler is loading a new page in this child. Check parent's and
-        // self's busy flag and if it is set, we don't want this onLoadHandler
-        // load to get in to session history.
-        uint32_t parentBusy = BUSY_FLAGS_NONE;
-        uint32_t selfBusy = BUSY_FLAGS_NONE;
-        parentDS->GetBusyFlags(&parentBusy);
-        GetBusyFlags(&selfBusy);
-        if (parentBusy & BUSY_FLAGS_BUSY ||
-            selfBusy & BUSY_FLAGS_BUSY) {
+    // 2. mCurrentURI is not null, nor the initial about:blank,
+    // it is possible that a parent's onLoadHandler or even self's
+    // onLoadHandler is loading a new page in this child. Check parent's and
+    // self's busy flag and if it is set, we don't want this onLoadHandler
+    // load to get in to session history.
+    BusyFlags parentBusy = parentDS->GetBusyFlags();
+    BusyFlags selfBusy = GetBusyFlags();
+
+    if (parentBusy & BUSY_FLAGS_BUSY ||
+        selfBusy & BUSY_FLAGS_BUSY) {
       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
       aLoadState->SetSHEntry(nullptr);
     }
     return;
   }
 
   // This is a newly created frame. Check for exception cases first.
   // By default the subframe will inherit the parent's loadType.
@@ -877,19 +876,19 @@ nsDocShell::MaybeHandleSubframeHistory(n
                                 parentLoadType == LOAD_LINK   ||
                                 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
     // The parent was loaded normally. In this case, this *brand new*
     // child really shouldn't have a SHEntry. If it does, it could be
     // because the parent is replacing an existing frame with a new frame,
     // in the onLoadHandler. We don't want this url to get into session
     // history. Clear off shEntry, and set load type to
     // LOAD_BYPASS_HISTORY.
-      bool inOnLoadHandler = false;
+    bool inOnLoadHandler = false;
     parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
-      if (inOnLoadHandler) {
+    if (inOnLoadHandler) {
       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
       aLoadState->SetSHEntry(nullptr);
     }
   } else if (parentLoadType == LOAD_REFRESH) {
     // Clear shEntry. For refresh loads, we have to load
     // what comes through the pipe, not what's in history.
     aLoadState->SetSHEntry(nullptr);
   } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
@@ -2005,17 +2004,17 @@ nsDocShell::GetMayEnableCharacterEncodin
     return NS_OK;
   }
 
   *aMayEnableCharacterEncodingMenu = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection,
+nsDocShell::GetDocShellEnumerator(int32_t aItemType, DocShellEnumeratorDirection aDirection,
                                   nsISimpleEnumerator** aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = nullptr;
 
   RefPtr<nsDocShellEnumerator> docShellEnum;
   if (aDirection == ENUMERATE_FORWARDS) {
     docShellEnum = new nsDocShellForwardsEnumerator;
@@ -2040,24 +2039,24 @@ nsDocShell::GetDocShellEnumerator(int32_
 
   rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
                                     (void**)aResult);
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetAppType(uint32_t* aAppType)
+nsDocShell::GetAppType(AppType* aAppType)
 {
   *aAppType = mAppType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetAppType(uint32_t aAppType)
+nsDocShell::SetAppType(AppType aAppType)
 {
   mAppType = aAppType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAllowAuth(bool* aAllowAuth)
 {
@@ -2114,17 +2113,17 @@ nsDocShell::GetMarginHeight(int32_t* aHe
 NS_IMETHODIMP
 nsDocShell::SetMarginHeight(int32_t aHeight)
 {
   mMarginHeight = aHeight;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
+nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags)
 {
   NS_ENSURE_ARG_POINTER(aBusyFlags);
 
   *aBusyFlags = mBusyFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2516,58 +2515,62 @@ nsDocShell::SetCustomUserAgent(const nsA
     if (childShell) {
       childShell->SetCustomUserAgent(aCustomUserAgent);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride)
+nsDocShell::GetTouchEventsOverride(TouchEventsOverride* aTouchEventsOverride)
 {
   *aTouchEventsOverride = mTouchEventsOverride;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride)
-{
-  if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
-        aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
-        aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
+nsDocShell::SetTouchEventsOverride(TouchEventsOverride aTouchEventsOverride)
+{
+  // We don't have a way to verify this coming from Javascript, so this check is
+  // still needed.
+  if (!(aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_NONE ||
+        aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_ENABLED ||
+        aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_DISABLED)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   mTouchEventsOverride = aTouchEventsOverride;
 
   uint32_t childCount = mChildList.Length();
   for (uint32_t i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
     if (childShell) {
       childShell->SetTouchEventsOverride(aTouchEventsOverride);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetMetaViewportOverride(uint32_t* aMetaViewportOverride)
+nsDocShell::GetMetaViewportOverride(MetaViewportOverride* aMetaViewportOverride)
 {
   NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
 
   *aMetaViewportOverride = mMetaViewportOverride;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetMetaViewportOverride(uint32_t aMetaViewportOverride)
-{
-  if (!(aMetaViewportOverride == nsIDocShell::META_VIEWPORT_OVERRIDE_NONE ||
-        aMetaViewportOverride == nsIDocShell::META_VIEWPORT_OVERRIDE_ENABLED ||
-        aMetaViewportOverride == nsIDocShell::META_VIEWPORT_OVERRIDE_DISABLED)) {
+nsDocShell::SetMetaViewportOverride(MetaViewportOverride aMetaViewportOverride)
+{
+  // We don't have a way to verify this coming from Javascript, so this check is
+  // still needed.
+  if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
+        aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
+        aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   mMetaViewportOverride = aMetaViewportOverride;
 
   // Inform our presShell that it needs to re-check its need for a viewport
   // override.
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
@@ -2822,20 +2825,19 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
     if (mInheritPrivateBrowsingId) {
       value = parentAsDocShell->GetAffectPrivateSessionLifetime();
       SetAffectPrivateSessionLifetime(value);
     }
     uint32_t flags;
     if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
       SetDefaultLoadFlags(flags);
     }
-    uint32_t touchEventsOverride;
-    if (NS_SUCCEEDED(parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
-      SetTouchEventsOverride(touchEventsOverride);
-    }
+
+    SetTouchEventsOverride(parentAsDocShell->GetTouchEventsOverride());
+
     // We don't need to inherit metaViewportOverride, because the viewport
     // is only relevant for the outermost nsDocShell, not for any iframes
     // like this that might be embedded within it.
   }
 
   nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
   if (parentAsLoadContext && mInheritPrivateBrowsingId &&
       NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
@@ -6109,18 +6111,17 @@ nsDocShell::RefreshURI(nsIURI* aURI, nsI
   }
   if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsITimerCallback> refreshTimer =
     new nsRefreshTimer(this, aURI, aPrincipal, aDelay, aRepeat, aMetaRefresh);
 
-  uint32_t busyFlags = 0;
-  GetBusyFlags(&busyFlags);
+  BusyFlags busyFlags = GetBusyFlags();
 
   if (!mRefreshURIList) {
     mRefreshURIList = nsArray::Create();
   }
 
   if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
     // We don't  want to create the timer right now. Instead queue up the request
     // and trigger the timer in EndPageLoad() or whenever we become active.
@@ -6793,31 +6794,31 @@ nsDocShell::OnStateChange(nsIWebProgress
         // Save history state of the previous page
         PersistLayoutHistoryState();
         // We'll never get an Embed() for this load, so just go ahead
         // and SetHistoryEntry now.
         SetHistoryEntry(&mOSHE, mLSHE);
       }
     }
     // Page has begun to load
-    mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
+    mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
 
     if ((aStateFlags & STATE_RESTORING) == 0) {
       // Show the progress cursor if the pref is set
       if (nsContentUtils::UseActivityCursor()) {
         nsCOMPtr<nsIWidget> mainWidget;
         GetMainWidget(getter_AddRefs(mainWidget));
         if (mainWidget) {
           mainWidget->SetCursor(eCursor_spinning);
         }
       }
     }
   } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
     // Page is loading
-    mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
+    mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
     // Page has finished loading
     mBusyFlags = BUSY_FLAGS_NONE;
 
     // Hide the progress cursor if the pref is set
     if (nsContentUtils::UseActivityCursor()) {
       nsCOMPtr<nsIWidget> mainWidget;
       GetMainWidget(getter_AddRefs(mainWidget));
@@ -13569,24 +13570,24 @@ unsigned long nsDocShell::gNumberOfDocSh
 NS_IMETHODIMP
 nsDocShell::GetCanExecuteScripts(bool* aResult)
 {
   *aResult = mCanExecuteScripts;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
-nsDocShell::SetFrameType(uint32_t aFrameType)
+nsDocShell::SetFrameType(FrameType aFrameType)
 {
   mFrameType = aFrameType;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
-nsDocShell::GetFrameType(uint32_t* aFrameType)
+nsDocShell::GetFrameType(FrameType* aFrameType)
 {
   *aFrameType = mFrameType;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsMozBrowser(bool* aIsMozBrowser)
 {
@@ -14059,25 +14060,27 @@ nsIDocShell::GetHTMLEditor()
 nsresult
 nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor)
 {
   nsDocShell* docShell = static_cast<nsDocShell*>(this);
   return docShell->SetHTMLEditorInternal(aHTMLEditor);
 }
 
 NS_IMETHODIMP
-nsDocShell::GetDisplayMode(uint32_t* aDisplayMode)
+nsDocShell::GetDisplayMode(DisplayMode* aDisplayMode)
 {
   *aDisplayMode = mDisplayMode;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetDisplayMode(uint32_t aDisplayMode)
-{
+nsDocShell::SetDisplayMode(DisplayMode aDisplayMode)
+{
+  // We don't have a way to verify this coming from Javascript, so this check is
+  // still needed.
   if (!(aDisplayMode == nsIDocShell::DISPLAY_MODE_BROWSER ||
         aDisplayMode == nsIDocShell::DISPLAY_MODE_STANDALONE ||
         aDisplayMode == nsIDocShell::DISPLAY_MODE_FULLSCREEN ||
         aDisplayMode == nsIDocShell::DISPLAY_MODE_MINIMAL_UI)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (aDisplayMode != mDisplayMode) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -127,16 +127,50 @@ class nsDocShell final
   , public nsILoadContext
   , public nsILinkHandler
   , public nsIDOMStorageManager
   , public nsINetworkInterceptController
   , public nsIDeprecationWarner
   , public mozilla::SupportsWeakPtr<nsDocShell>
 {
 public:
+  enum InternalLoad : uint32_t {
+    INTERNAL_LOAD_FLAGS_NONE                    = 0x0,
+    INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL       = 0x1,
+    INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER      = 0x2,
+    INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x4,
+
+    // This flag marks the first load in this object
+    // @see nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD
+    INTERNAL_LOAD_FLAGS_FIRST_LOAD              = 0x8,
+
+
+    // The set of flags that should not be set before calling into
+    // nsDocShell::LoadURI and other nsDocShell loading functions.
+    INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS     = 0xf,
+
+
+    INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER       = 0x10,
+    INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES     = 0x20,
+
+    // Whether the load should be treated as srcdoc load, rather than a URI one.
+    INTERNAL_LOAD_FLAGS_IS_SRCDOC               = 0x40,
+
+    // Whether this is the load of a frame's original src attribute
+    INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC      = 0x80,
+
+    INTERNAL_LOAD_FLAGS_NO_OPENER               = 0x100,
+
+    // Whether a top-level data URI navigation is allowed for that load
+    INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI    = 0x200,
+
+    // Whether the load was triggered by user interaction.
+    INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED       = 0x1000,
+  };
+
   // Event type dispatched by RestorePresentation
   class RestorePresentationEvent : public mozilla::Runnable
   {
   public:
     NS_DECL_NSIRUNNABLE
     explicit RestorePresentationEvent(nsDocShell* aDs)
       : mozilla::Runnable("nsDocShell::RestorePresentationEvent")
       , mDocShell(aDs)
@@ -1050,56 +1084,48 @@ private: // data members
   int32_t mPreviousEntryIndex;
   int32_t mLoadedEntryIndex;
 
   // Offset in the parent's child list.
   // -1 if the docshell is added dynamically to the parent shell.
   int32_t mChildOffset;
 
   uint32_t mSandboxFlags;
-  uint32_t mBusyFlags;
-  uint32_t mAppType;
+  BusyFlags mBusyFlags;
+  AppType mAppType;
   uint32_t mLoadType;
   uint32_t mDefaultLoadFlags;
   uint32_t mReferrerPolicy;
   uint32_t mFailedLoadType;
 
   // Are we a regular frame, a browser frame, or an app frame?
-  uint32_t mFrameType;
+  FrameType mFrameType;
 
   // This represents the state of private browsing in the docshell.
   // Currently treated as a binary value: 1 - in private mode, 0 - not private mode
   // On content docshells mPrivateBrowsingId == mOriginAttributes.mPrivateBrowsingId
   // On chrome docshells this value will be set, but not have the corresponding
   // origin attribute set.
   uint32_t mPrivateBrowsingId;
 
-  // This represents the CSS display-mode we are currently using.
-  // It can be any of the following values from nsIDocShell.idl:
-  //
-  // DISPLAY_MODE_BROWSER = 0
-  // DISPLAY_MODE_MINIMAL_UI = 1
-  // DISPLAY_MODE_STANDALONE = 2
-  // DISPLAY_MODE_FULLSCREEN = 3
-  //
-  // This is mostly used for media queries. The integer values above
-  // match those used in nsStyleConsts.h
-  uint32_t mDisplayMode;
+  // This represents the CSS display-mode we are currently using. This is mostly
+  // used for media queries.
+  DisplayMode mDisplayMode;
 
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
   // Whether or not touch events are overridden. Possible values are defined
   // as constants in the nsIDocShell.idl file.
-  uint32_t mTouchEventsOverride;
+  TouchEventsOverride mTouchEventsOverride;
 
   // Whether or not handling of the <meta name="viewport"> tag is overridden.
   // Possible values are defined as constants in nsIDocShell.idl.
-  uint32_t mMetaViewportOverride;
+  MetaViewportOverride mMetaViewportOverride;
 
   // mFullscreenAllowed stores how we determine whether fullscreen is allowed
   // when GetFullscreenAllowed() is called. Fullscreen is allowed in a
   // docshell when all containing iframes have the allowfullscreen
   // attribute set to true. When mFullscreenAllowed is CHECK_ATTRIBUTES
   // we check this docshell's containing frame for the allowfullscreen
   // attribute, and recurse onto the parent docshell to ensure all containing
   // frames also have the allowfullscreen attribute. If we find an ancestor
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -510,49 +510,49 @@ nsDocShellLoadState::CalculateDocShellIn
 {
   MOZ_ASSERT(mDocShellInternalLoadFlags == 0,
              "Shouldn't have any load flags set at this point.");
 
   if (mInheritPrincipal) {
     MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mPrincipalToInherit),
                "Should not inherit SystemPrincipal");
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+      nsDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
   }
 
   if (!mSendReferrer) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
+      nsDocShell::INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
   }
 
   if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+      nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
   }
 
   if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) {
-    mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+    mDocShellInternalLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
   }
 
   if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CLASSIFIER) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
+      nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
   }
 
   if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
+      nsDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
   }
 
   if (mIsSrcdocLoad) {
-    mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+    mDocShellInternalLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
   }
 
   if (mForceAllowDataURI) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+      nsDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
   }
 
   if (mOriginalFrameSrc) {
     mDocShellInternalLoadFlags |=
-      nsIDocShell::INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
+      nsDocShell::INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
   }
 }
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -77,48 +77,16 @@ interface nsIDocShell : nsIDocShellTreeI
    * in the object implementing this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
    * @param loadState   - This is the extended load info for this load.
    */
   [noscript]void loadURI(in nsDocShellLoadStatePtr loadState);
 
-  const long INTERNAL_LOAD_FLAGS_NONE                    = 0x0;
-  const long INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL       = 0x1;
-  const long INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER      = 0x2;
-  const long INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x4;
-
-  // This flag marks the first load in this object
-  // @see nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD
-  const long INTERNAL_LOAD_FLAGS_FIRST_LOAD              = 0x8;
-
-
-  // The set of flags that should not be set before calling into
-  // nsDocShell::LoadURI and other nsDocShell loading functions.
-  const long INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS     = 0xf;
-
-
-  const long INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER       = 0x10;
-  const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES     = 0x20;
-
-  // Whether the load should be treated as srcdoc load, rather than a URI one.
-  const long INTERNAL_LOAD_FLAGS_IS_SRCDOC               = 0x40;
-
-  // Whether this is the load of a frame's original src attribute
-  const long INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC      = 0x80;
-
-  const long INTERNAL_LOAD_FLAGS_NO_OPENER               = 0x100;
-
-  // Whether a top-level data URI navigation is allowed for that load
-  const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI    = 0x200;
-
-  // Whether the load was triggered by user interaction.
-  const long INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED       = 0x1000;
-
   /**
    * Loads the given URI.  This method is identical to loadURI(...) except
    * that its parameter list is broken out instead of being packaged inside
    * of an nsIDocShellLoadInfo object...
    *
    * @param aURI                 - The URI to load.
    * @param aOriginalURI         - The URI to set as the originalURI on the channel
    *                               that does the load. If null, aURI will be set as
@@ -350,30 +318,39 @@ interface nsIDocShell : nsIDocShellTreeI
    * Get an enumerator over this docShell and its children.
    *
    * @param aItemType  - Only include docShells of this type, or if typeAll,
    *                     include all child shells.
    *                     Uses types from nsIDocShellTreeItem.
    * @param aDirection - Whether to enumerate forwards or backwards.
    */
 
-  const long ENUMERATE_FORWARDS  = 0;
-  const long ENUMERATE_BACKWARDS = 1;
+  cenum DocShellEnumeratorDirection : 8 {
+    ENUMERATE_FORWARDS  = 0,
+    ENUMERATE_BACKWARDS = 1
+  };
 
   nsISimpleEnumerator getDocShellEnumerator(in long aItemType,
-                                            in long aDirection);
+                                            in nsIDocShell_DocShellEnumeratorDirection aDirection);
 
   /**
-   * The type of application that created this window
+   * The type of application that created this window.
+   *
+   * DO NOT DELETE, see bug 176166. For firefox, this value will always be
+   * UNKNOWN. However, it is used heavily in Thunderbird/comm-central and we
+   * don't really have a great replacement at the moment, so we'll just leave it
+   * here.
    */
-  const unsigned long APP_TYPE_UNKNOWN  = 0;
-  const unsigned long APP_TYPE_MAIL     = 1;
-  const unsigned long APP_TYPE_EDITOR   = 2;
+  cenum AppType : 8 {
+    APP_TYPE_UNKNOWN = 0,
+    APP_TYPE_MAIL    = 1,
+    APP_TYPE_EDITOR  = 2
+  };
 
-  attribute unsigned long appType;
+  [infallible] attribute nsIDocShell_AppType appType;
 
   /**
    * certain dochshells (like the message pane)
    * should not throw up auth dialogs
    * because it can act as a password trojan
    */
   attribute boolean allowAuth;
 
@@ -411,35 +388,40 @@ interface nsIDocShell : nsIDocShellTreeI
    * next element in the parent should be returned. Returns true if focus was
    * successfully taken by the tree owner.
    */
   bool tabToTreeOwner(in boolean forward, in boolean forDocumentNavigation);
 
   /**
    * Current busy state for DocShell
    */
-  const unsigned long BUSY_FLAGS_NONE             = 0;
-  const unsigned long BUSY_FLAGS_BUSY             = 1;
-  const unsigned long BUSY_FLAGS_BEFORE_PAGE_LOAD = 2;
-  const unsigned long BUSY_FLAGS_PAGE_LOADING     = 4;
+  cenum BusyFlags : 8 {
+    BUSY_FLAGS_NONE             = 0,
+    BUSY_FLAGS_BUSY             = 1,
+    BUSY_FLAGS_BEFORE_PAGE_LOAD = 2,
+    BUSY_FLAGS_PAGE_LOADING     = 4,
+  };
+
+  [infallible] readonly attribute nsIDocShell_BusyFlags busyFlags;
 
   /**
    * Load commands for the document
    */
-  const unsigned long LOAD_CMD_NORMAL  = 0x1;   // Normal load
-  const unsigned long LOAD_CMD_RELOAD  = 0x2;   // Reload
-  const unsigned long LOAD_CMD_HISTORY = 0x4;   // Load from history
-  const unsigned long LOAD_CMD_PUSHSTATE = 0x8; // History.pushState()
-
-  readonly attribute unsigned long busyFlags;
+  cenum LoadCommand : 8 {
+    LOAD_CMD_NORMAL  = 0x1,   // Normal load
+    LOAD_CMD_RELOAD  = 0x2,   // Reload
+    LOAD_CMD_HISTORY = 0x4,   // Load from history
+    LOAD_CMD_PUSHSTATE = 0x8, // History.pushState()
+  };
 
   /*
-   * attribute to access the loadtype  for the document
+   * Attribute to access the loadtype for the document. LoadType Enum is
+   * defined in nsDocShellLoadTypes.h
    */
-  attribute unsigned long  loadType;
+  [infallible] attribute unsigned long loadType;
 
   /*
    * Default load flags (as defined in nsIRequest) that will be set on all
    * requests made by this docShell and propagated to all child docShells and
    * to nsILoadGroup::defaultLoadFlags for the docShell's loadGroup.
    * Default is no flags.  Once set, only future requests initiated by the
    * docShell are affected, so in general, these flags should be set before
    * the docShell loads any content.
@@ -814,20 +796,21 @@ interface nsIDocShell : nsIDocShellTreeI
    * Notify all attached observers that the scroll position of some element
    * has changed.
    */
   [noscript] void notifyScrollObservers();
 
   /**
    * The type of iframe that this docshell lives.
    */
-  const unsigned long FRAME_TYPE_REGULAR = 0;
-  const unsigned long FRAME_TYPE_BROWSER = 1;
-
-  [infallible] attribute unsigned long frameType;
+  cenum FrameType : 8 {
+    FRAME_TYPE_REGULAR = 0,
+    FRAME_TYPE_BROWSER = 1,
+  };
+  [infallible] attribute nsIDocShell_FrameType frameType;
 
   /**
    * Returns true if this docshell corresponds to an <iframe mozbrowser>.
    * <xul:browser> returns false here.
    */
   [infallible] readonly attribute boolean isMozBrowser;
 
   /**
@@ -1126,55 +1109,61 @@ interface nsIDocShell : nsIDocShellTreeI
   /**
    * The tab child for this docshell.
    */
   [binaryname(ScriptableTabChild)] readonly attribute nsITabChild tabChild;
   [noscript,notxpcom,nostdcall] TabChildRef GetTabChild();
 
   [noscript,nostdcall,notxpcom] nsICommandManager GetCommandManager();
 
+  cenum TouchEventsOverride: 8 {
+    /**
+     * Override platform/pref default behaviour and force-disable touch events.
+     */
+    TOUCHEVENTS_OVERRIDE_DISABLED = 0,
+    /**
+     * Override platform/pref default behaviour and force-enable touch events.
+     */
+    TOUCHEVENTS_OVERRIDE_ENABLED = 1,
+    /**
+     * Don't override the platform/pref default behaviour for touch events.
+     */
+    TOUCHEVENTS_OVERRIDE_NONE = 2,
+  };
+
   /**
    * This allows chrome to override the default choice of whether touch events
    * are available on a specific docshell. Possible values are listed below.
    */
-  [infallible] attribute unsigned long touchEventsOverride;
-  /**
-   * Override platform/pref default behaviour and force-disable touch events.
-   */
-  const unsigned long TOUCHEVENTS_OVERRIDE_DISABLED = 0;
-  /**
-   * Override platform/pref default behaviour and force-enable touch events.
-   */
-  const unsigned long TOUCHEVENTS_OVERRIDE_ENABLED = 1;
-  /**
-   * Don't override the platform/pref default behaviour for touch events.
-   */
-  const unsigned long TOUCHEVENTS_OVERRIDE_NONE = 2;
+  [infallible] attribute nsIDocShell_TouchEventsOverride touchEventsOverride;
 
-  /**
-   * Override platform/pref default behaviour and force-disable support for
-   * <meta name="viewport">.
-   */
-  const unsigned long META_VIEWPORT_OVERRIDE_DISABLED = 0;
-  /**
-   * Override platform/pref default behaviour and force-enable support for
-   * <meta name="viewport">.
-   */
-  const unsigned long META_VIEWPORT_OVERRIDE_ENABLED = 1;
-  /**
-   * Don't override the platform/pref default behaviour for support for
-   * <meta name="viewport">.
-   */
-  const unsigned long META_VIEWPORT_OVERRIDE_NONE = 2;
+  cenum MetaViewportOverride: 8 {
+    /**
+     * Override platform/pref default behaviour and force-disable support for
+     * <meta name="viewport">.
+     */
+    META_VIEWPORT_OVERRIDE_DISABLED = 0,
+    /**
+     * Override platform/pref default behaviour and force-enable support for
+     * <meta name="viewport">.
+     */
+    META_VIEWPORT_OVERRIDE_ENABLED = 1,
+    /**
+     * Don't override the platform/pref default behaviour for support for
+     * <meta name="viewport">.
+     */
+    META_VIEWPORT_OVERRIDE_NONE = 2,
+  };
+
   /**
    * This allows chrome to override the default choice of whether the
    * <meta name="viewport"> tag is respected in a specific docshell.
    * Possible values are listed above.
    */
-  attribute unsigned long metaViewportOverride;
+  [infallible] attribute nsIDocShell_MetaViewportOverride metaViewportOverride;
 
   /**
    * This value is `true` if its corresponding unit of related browsing contexts
    * (TabGroup) contains only 1 toplevel window, and that window is the outer
    * window corresponding to this docshell.
    *
    * The value is `false` otherwise. This is the case if the docshell is an
    * iframe, has window.opener set, or another window with window.opener
@@ -1235,28 +1224,30 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * BrowsingContext associated with the DocShell.
    */
   readonly attribute BrowsingContext browsingContext;
 
   /**
    * Allowed CSS display modes. This needs to be kept in
-   * sync with similar values in nsStyleConsts.h
+   * sync with similar values in ServoStyleConsts.h
    */
-  const unsigned long DISPLAY_MODE_BROWSER = 0;
-  const unsigned long DISPLAY_MODE_MINIMAL_UI = 1;
-  const unsigned long DISPLAY_MODE_STANDALONE = 2;
-  const unsigned long DISPLAY_MODE_FULLSCREEN = 3;
+  cenum DisplayMode: 8 {
+    DISPLAY_MODE_BROWSER = 0,
+    DISPLAY_MODE_MINIMAL_UI = 1,
+    DISPLAY_MODE_STANDALONE = 2,
+    DISPLAY_MODE_FULLSCREEN = 3,
+  };
 
   /**
    * Display mode for this docshell. Defaults to DISPLAY_MODE_BROWSER.
    * Media queries only look at the value in the top-most docshell.
    */
-  [infallible] attribute unsigned long displayMode;
+  [infallible] attribute nsIDocShell_DisplayMode displayMode;
 
   /**
    * The message manager for this docshell.  This does not throw, but
    * can return null if the docshell has no message manager.
    */
   [infallible] readonly attribute ContentFrameMessageManager messageManager;
 
   /**
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1889,15 +1889,32 @@ KeyframeEffect::IsMatchForCompositor(
 
   // If we know that the animation is not visible, we don't need to send the
   // animation to the compositor.
   if (!aFrame->IsVisibleOrMayHaveVisibleDescendants() ||
       aFrame->IsScrolledOutOfView()) {
     return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
   }
 
+  if (aProperty == eCSSProperty_background_color) {
+    if (!StaticPrefs::gfx_omta_background_color()) {
+      return KeyframeEffect::MatchForCompositor::No;
+    }
+
+    if (nsIContent* content = aFrame->GetContent()) {
+      RefPtr<layers::LayerManager> layerManager =
+        nsContentUtils::LayerManagerForContent(content);
+      if (layerManager &&
+          layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
+        // Bug 1510030: We don't yet support background-color animations on the
+        // compositor for WebRender.
+        return KeyframeEffect::MatchForCompositor::No;
+      }
+    }
+  }
+
   return mAnimation->IsPlaying()
          ? KeyframeEffect::MatchForCompositor::Yes
          : KeyframeEffect::MatchForCompositor::IfNeeded;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -45,17 +45,19 @@ div {
   target="_blank">Mozilla Bug 1045994</a>
 <div id="log"></div>
 <script>
 'use strict';
 
 /** Test for bug 1045994 - Add a chrome-only property to inspect if an
     animation is running on the compositor or not **/
 
-var omtaEnabled = isOMTAEnabled();
+const omtaEnabled = isOMTAEnabled();
+const isWebRender =
+  SpecialPowers.DOMWindowUtils.layerManagerType == 'WebRender';
 
 function assert_animation_is_running_on_compositor(animation, desc) {
   assert_equals(animation.isRunningOnCompositor, omtaEnabled,
                 desc + ' at ' + animation.currentTime + 'ms');
 }
 
 function assert_animation_is_not_running_on_compositor(animation, desc) {
   assert_equals(animation.isRunningOnCompositor, false,
@@ -983,10 +985,46 @@ promise_test(async t => {
   }
 
   await waitForPaints();
 
   assert_animation_is_running_on_compositor(animation,
     'Transform animation on table element should be running on the compositor');
 }, 'Transform animation on table element runs on the compositor');
 
+promise_test(async t => {
+  const div = addDiv(t);
+  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
+                                100 * MS_PER_SEC);
+
+  await waitForAnimationReadyToRestyle(animation);
+  await waitForPaints();
+
+  if (!isWebRender) {
+    assert_animation_is_running_on_compositor(animation,
+      'background-color animation should be running on the compositor');
+  } else {
+    assert_animation_is_not_running_on_compositor(animation,
+      'background-color animation is not yet able to run on the compositor ' +
+      'on WebRender');
+  }
+}, 'backgound-color animation runs on the compositor');
+
+promise_test(async t => {
+  await SpecialPowers.pushPrefEnv({
+    set: [["gfx.omta.background-color", false]]
+  });
+
+  const div = addDiv(t);
+  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
+                                100 * MS_PER_SEC);
+
+  await waitForAnimationReadyToRestyle(animation);
+  await waitForPaints();
+
+  assert_animation_is_not_running_on_compositor(animation,
+    'background-color animation should NOT be running on the compositor ' +
+    'if the pref is disabled');
+}, 'backgound-color animation does not run on the compositor if the pref ' +
+   'is disabled');
+
 </script>
 </body>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 prefs =
   dom.animations-api.compositing.enabled=true
   dom.animations-api.core.enabled=true
   dom.animations-api.getAnimations.enabled=true
   dom.animations-api.implicit-keyframes.enabled=true
   dom.animations-api.timelines.enabled=true
+  gfx.omta.background-color=true
   layout.css.motion-path.enabled=true
   layout.css.individual-transform.enabled=true
 # Support files for chrome tests that we want to load over HTTP need
 # to go in here, not chrome.ini.
 support-files =
   chrome/file_animate_xrays.html
   mozilla/xhr_doc.html
   mozilla/file_deferred_start.html
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3438,28 +3438,28 @@ nsContentUtils::CanLoadImage(nsIURI* aUR
                              nsIPrincipal* aLoadingPrincipal)
 {
   MOZ_ASSERT(aURI, "Must have a URI");
   MOZ_ASSERT(aLoadingDocument, "Must have a document");
   MOZ_ASSERT(aLoadingPrincipal, "Must have a loading principal");
 
   nsresult rv;
 
-  uint32_t appType = nsIDocShell::APP_TYPE_UNKNOWN;
+  auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
 
   {
     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = aLoadingDocument->GetDocShell();
     if (docShellTreeItem) {
       nsCOMPtr<nsIDocShellTreeItem> root;
       docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
 
       nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
 
-      if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
-        appType = nsIDocShell::APP_TYPE_UNKNOWN;
+      if (docShell) {
+        appType = docShell->GetAppType();
       }
     }
   }
 
   if (appType != nsIDocShell::APP_TYPE_EDITOR) {
     // Editor apps get special treatment here, editors can load images
     // from anywhere.  This allows editor to insert images from file://
     // into documents that are being edited.
@@ -7664,19 +7664,18 @@ nsContentUtils::PrefetchPreloadEnabled(n
   if (!aDocShell) {
     return false;
   }
 
   nsCOMPtr<nsIDocShell> docshell = aDocShell;
   nsCOMPtr<nsIDocShellTreeItem> parentItem;
 
   do {
-    uint32_t appType = 0;
-    nsresult rv = docshell->GetAppType(&appType);
-    if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL) {
+    auto appType = docshell->GetAppType();
+    if (appType == nsIDocShell::APP_TYPE_MAIL) {
       return false; // do not prefetch, preload, preconnect from mailnews
     }
 
     docshell->GetParent(getter_AddRefs(parentItem));
     if (parentItem) {
       docshell = do_QueryInterface(parentItem);
       if (!docshell) {
         NS_ERROR("cannot get a docshell from a treeItem!");
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3728,16 +3728,24 @@ nsDOMWindowUtils::GetOMTAStyle(Element* 
       }
     } else if (aProperty.EqualsLiteral("transform")) {
       OMTAValue value = GetOMTAValue(frame,
                                      DisplayItemType::TYPE_TRANSFORM,
                                      GetWebRenderBridge());
       if (value.type() == OMTAValue::TMatrix4x4) {
         cssValue = nsComputedDOMStyle::MatrixToCSSValue(value.get_Matrix4x4());
       }
+    } else if (aProperty.EqualsLiteral("background-color")) {
+      OMTAValue value = GetOMTAValue(frame,
+                                     DisplayItemType::TYPE_BACKGROUND_COLOR,
+                                     GetWebRenderBridge());
+      if (value.type() == OMTAValue::Tnscolor) {
+        cssValue = new nsROCSSPrimitiveValue;
+        nsComputedDOMStyle::SetToRGBAColor(cssValue, value.get_nscolor());
+      }
     }
   }
 
   if (cssValue) {
     nsString text;
     ErrorResult rv;
     cssValue->GetCssText(text, rv);
     aResult.Assign(text);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3124,19 +3124,18 @@ nsGlobalWindowOuter::GetSanitizedOpener(
   // So, we look in the opener's root docshell to see if it's a mail window.
   nsCOMPtr<nsIDocShell> openerDocShell = aOpener->GetDocShell();
 
   if (openerDocShell) {
     nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
     openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
     nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
     if (openerRootDocShell) {
-      uint32_t appType;
-      nsresult rv = openerRootDocShell->GetAppType(&appType);
-      if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) {
+      nsIDocShell::AppType appType = openerRootDocShell->GetAppType();
+      if (appType != nsIDocShell::APP_TYPE_MAIL) {
         return aOpener;
       }
     }
   }
 
   return nullptr;
 }
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3880,23 +3880,22 @@ EventStateManager::UpdateCursor(nsPresCo
     hotspotX = framecursor.mHotspotX;
     hotspotY = framecursor.mHotspotY;
   }
 
   if (nsContentUtils::UseActivityCursor()) {
     // Check whether or not to show the busy cursor
     nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
     if (!docShell) return;
-    uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
-    docShell->GetBusyFlags(&busyFlags);
+    auto busyFlags = docShell->GetBusyFlags();
 
     // Show busy cursor everywhere before page loads
     // and just replace the arrow cursor after page starts loading
     if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY &&
-          (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
+        (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
     {
       cursor = NS_STYLE_CURSOR_SPINNING;
       container = nullptr;
     }
   }
 
   if (aTargetFrame) {
     SetCursor(cursor, container, haveHotspot, hotspotX, hotspotY,
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -248,19 +248,19 @@ TouchEvent::PlatformSupportsTouch()
 
 // static
 bool
 TouchEvent::PrefEnabled(nsIDocShell* aDocShell)
 {
   static bool sPrefCached = false;
   static int32_t sPrefCacheValue = 0;
 
-  uint32_t touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE;
+  auto touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE;
   if (aDocShell) {
-    aDocShell->GetTouchEventsOverride(&touchEventsOverride);
+    touchEventsOverride = aDocShell->GetTouchEventsOverride();
   }
 
   if (!sPrefCached) {
     sPrefCached = true;
     Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled");
   }
 
   bool enabled = false;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4689,41 +4689,40 @@ HTMLInputElement::UnbindFromTree(bool aD
   // nsGenericHTMLFormElementWithState::UnbindFromTree() will unset the form and
   // that takes care of form's WillRemove so we just have to take care
   // of the case where we're removing from the document and we don't
   // have a form
   if (!mForm && mType == NS_FORM_INPUT_RADIO) {
     WillRemoveFromRadioGroup();
   }
 
+  if (GetShadowRoot() && IsInComposedDoc()) {
+    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
+      "HTMLInputElement::UnbindFromTree::UAWidgetUnbindFromTree",
+      [self = RefPtr<Element>(this)]() {
+        nsContentUtils::DispatchChromeEvent(
+          self->OwnerDoc(), self,
+          NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
+          CanBubble::eYes, Cancelable::eNo);
+        self->UnattachShadow();
+      })
+    );
+  }
+
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
 
   // GetCurrentDoc is returning nullptr so we can update the value
   // missing validity state to reflect we are no longer into a doc.
   UpdateValueMissingValidityState();
   // We might be no longer disabled because of parent chain changed.
   UpdateBarredFromConstraintValidation();
 
   // And now make sure our state is up to date
   UpdateState(false);
-
-  if (GetShadowRoot() && IsInComposedDoc()) {
-    RefPtr<Element> self = this;
-    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
-      "HTMLInputElement::UnbindFromTree::UAWidgetUnbindFromTree",
-      [self]() {
-        nsContentUtils::DispatchChromeEvent(
-          self->OwnerDoc(), self,
-          NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
-          CanBubble::eYes, Cancelable::eNo);
-        self->UnattachShadow();
-      })
-    );
-  }
 }
 
 void
 HTMLInputElement::HandleTypeChange(uint8_t aNewType, bool aNotify)
 {
   uint8_t oldType = mType;
   MOZ_ASSERT(oldType != aNewType);
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4688,28 +4688,34 @@ HTMLMediaElement::ReportTelemetry()
 }
 
 void
 HTMLMediaElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   mUnboundFromTree = true;
   mVisibilityState = Visibility::UNTRACKED;
 
+  if (GetShadowRoot() && IsInComposedDoc()) {
+    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
+      "HTMLMediaElement::UnbindFromTree::UAWidgetUnbindFromTree",
+      [self = RefPtr<Element>(this)]() {
+        nsContentUtils::DispatchChromeEvent(
+          self->OwnerDoc(), self,
+          NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
+          CanBubble::eYes, Cancelable::eNo);
+        self->UnattachShadow();
+      })
+    );
+  }
+
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   MOZ_ASSERT(IsHidden());
   NotifyDecoderActivityChanges();
 
-  AsyncEventDispatcher* dispatcher =
-    new AsyncEventDispatcher(this,
-                             NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
-                             CanBubble::eYes,
-                             ChromeOnlyDispatch::eYes);
-  dispatcher->RunDOMEventWhenSafe();
-
   RefPtr<HTMLMediaElement> self(this);
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction("dom::HTMLMediaElement::UnbindFromTree", [self]() {
       if (self->mUnboundFromTree) {
         self->Pause();
       }
     });
   RunInStableState(task);
--- a/dom/media/doctor/DecoderDoctorLogger.h
+++ b/dom/media/doctor/DecoderDoctorLogger.h
@@ -365,17 +365,17 @@ class DecoderDoctorLogger {
 };
 
 // Base class to automatically record a class lifetime. Usage:
 //   class SomeClass : public DecoderDoctorLifeLogger<SomeClass>
 //   {
 //     ...
 template <typename T>
 class DecoderDoctorLifeLogger {
- public:
+ protected:
   DecoderDoctorLifeLogger() {
     DecoderDoctorLogger::LogConstruction(NonDereferenceable<const T>(this));
   }
   ~DecoderDoctorLifeLogger() {
     DecoderDoctorLogger::LogDestruction(NonDereferenceable<const T>(this));
   }
 };
 
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -174,21 +174,17 @@ PresentationResponderLoadingCallback::~P
 nsresult
 PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell)
 {
   mProgress = do_GetInterface(aDocShell);
   if (NS_WARN_IF(!mProgress)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
-  nsresult rv = aDocShell->GetBusyFlags(&busyFlags);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  auto busyFlags = aDocShell->GetBusyFlags();
 
   if ((busyFlags == nsIDocShell::BUSY_FLAGS_NONE) ||
       (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) {
     // The docshell has finished loading or is receiving data (|STATE_TRANSFERRING|
     // has already been fired), so the page is ready for presentation use.
     return NotifyReceiverReady(/* isLoading = */ true);
   }
 
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -265,17 +265,17 @@ static bool IsImageLoadInEditorAppType(n
   nsContentPolicyType type = aLoadInfo->InternalContentPolicyType();
   if (type != nsIContentPolicy::TYPE_INTERNAL_IMAGE  &&
       type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD &&
       type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON &&
       type != nsIContentPolicy::TYPE_IMAGESET) {
     return false;
   }
 
-  uint32_t appType = nsIDocShell::APP_TYPE_UNKNOWN;
+  auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
   nsINode* node = aLoadInfo->LoadingNode();
   if (!node) {
     return false;
   }
   nsIDocument* doc = node->OwnerDoc();
   if (!doc) {
     return false;
   }
@@ -283,18 +283,18 @@ static bool IsImageLoadInEditorAppType(n
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
   if (!docShellTreeItem) {
     return false;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> root;
   docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
-  if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
-    appType = nsIDocShell::APP_TYPE_UNKNOWN;
+  if (docShell) {
+    appType = docShell->GetAppType();
   }
 
   return appType == nsIDocShell::APP_TYPE_EDITOR;
 }
 
 static nsresult
 DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
 {
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -483,20 +483,19 @@ TextEditor::IsSafeToInsertData(nsIDocume
   nsCOMPtr<nsIDocument> destdoc = GetDocument();
   NS_ASSERTION(destdoc, "Where is our destination doc?");
   nsCOMPtr<nsIDocShellTreeItem> dsti = destdoc->GetDocShell();
   nsCOMPtr<nsIDocShellTreeItem> root;
   if (dsti) {
     dsti->GetRootTreeItem(getter_AddRefs(root));
   }
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(root);
-  uint32_t appType;
-  if (docShell && NS_SUCCEEDED(docShell->GetAppType(&appType))) {
-    isSafe = appType == nsIDocShell::APP_TYPE_EDITOR;
-  }
+
+  isSafe = docShell && docShell->GetAppType() == nsIDocShell::APP_TYPE_EDITOR;
+
   if (!isSafe && aSourceDoc) {
     nsIPrincipal* srcPrincipal = aSourceDoc->NodePrincipal();
     nsIPrincipal* destPrincipal = destdoc->NodePrincipal();
     NS_ASSERTION(srcPrincipal && destPrincipal, "How come we don't have a principal?");
     srcPrincipal->Subsumes(destPrincipal, &isSafe);
   }
 
   return isSafe;
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -50,16 +50,19 @@ CompositorAnimationStorage::GetOMTAValue
 {
   OMTAValue omtaValue = mozilla::null_t();
   auto animatedValue = GetAnimatedValue(aId);
   if (!animatedValue) {
     return omtaValue;
   }
 
   switch (animatedValue->mType) {
+    case AnimatedValue::COLOR:
+      omtaValue = animatedValue->mColor;
+      break;
     case AnimatedValue::OPACITY:
       omtaValue = animatedValue->mOpacity;
       break;
     case AnimatedValue::TRANSFORM: {
       gfx::Matrix4x4 transform = animatedValue->mTransform.mFrameTransform;
       const TransformData& data = animatedValue->mTransform.mData;
       float scale = data.appUnitsPerDevPixel();
       gfx::Point3D transformOrigin = data.transformOrigin();
@@ -114,16 +117,28 @@ CompositorAnimationStorage::SetAnimatedV
   const TransformData dontCare = {};
   SetAnimatedValue(aId,
                    std::move(aTransformInDevSpace),
                    gfx::Matrix4x4(),
                    dontCare);
 }
 
 void
+CompositorAnimationStorage::SetAnimatedValue(uint64_t aId, nscolor aColor)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  auto count = mAnimatedValues.Count();
+  AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId, aColor);
+  if (count == mAnimatedValues.Count()) {
+    MOZ_ASSERT(value->mType == AnimatedValue::COLOR);
+    value->mColor = aColor;
+  }
+}
+
+void
 CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
                                              const float& aOpacity)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   auto count = mAnimatedValues.Count();
   AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId, aOpacity);
   if (count == mAnimatedValues.Count()) {
     MOZ_ASSERT(value->mType == AnimatedValue::OPACITY);
@@ -514,17 +529,17 @@ CreateCSSValueList(const InfallibleTArra
   if (aFunctions.Length() == 0) {
     result = new nsCSSValueList();
     result->mValue.SetNoneValue();
   }
   return new nsCSSValueSharedList(result.forget());
 }
 
 static already_AddRefed<RawServoAnimationValue>
-ToAnimationValue(const Animatable& aAnimatable)
+ToAnimationValue(nsCSSPropertyID aProperty, const Animatable& aAnimatable)
 {
   RefPtr<RawServoAnimationValue> result;
 
   switch (aAnimatable.type()) {
     case Animatable::Tnull_t:
       break;
     case Animatable::TArrayOfTransformFunction: {
       const InfallibleTArray<TransformFunction>& transforms =
@@ -535,16 +550,20 @@ ToAnimationValue(const Animatable& aAnim
         MOZ_ASSERT(list, "Transform list should be non null");
         result = Servo_AnimationValue_Transform(*list).Consume();
       }
       break;
     }
     case Animatable::Tfloat:
       result = Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
       break;
+    case Animatable::Tnscolor:
+      result = Servo_AnimationValue_Color(aProperty,
+                                          aAnimatable.get_nscolor()).Consume();
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported type");
   }
   return result.forget();
 }
 
 void
 AnimationHelper::SetAnimations(
@@ -575,17 +594,18 @@ AnimationHelper::SetAnimations(
           animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
         }
         break;
       default:
         break;
     }
 
     if (animation.baseStyle().type() != Animatable::Tnull_t) {
-      aBaseAnimationStyle = ToAnimationValue(animation.baseStyle());
+      aBaseAnimationStyle = ToAnimationValue(animation.property(),
+                                             animation.baseStyle());
     }
 
     AnimData* data = aAnimData.AppendElement();
 
     data->mTiming = TimingParams {
       animation.duration(),
       animation.delay(),
       animation.endDelay(),
@@ -600,18 +620,20 @@ AnimationHelper::SetAnimations(
       data->mFunctions;
     InfallibleTArray<RefPtr<RawServoAnimationValue>>& startValues =
       data->mStartValues;
     InfallibleTArray<RefPtr<RawServoAnimationValue>>& endValues =
       data->mEndValues;
 
     const InfallibleTArray<AnimationSegment>& segments = animation.segments();
     for (const AnimationSegment& segment : segments) {
-      startValues.AppendElement(ToAnimationValue(segment.startState()));
-      endValues.AppendElement(ToAnimationValue(segment.endState()));
+      startValues.AppendElement(ToAnimationValue(animation.property(),
+                                                 segment.startState()));
+      endValues.AppendElement(ToAnimationValue(animation.property(),
+                                               segment.endState()));
 
       TimingFunction tf = segment.sampleFn();
       Maybe<ComputedTimingFunction> ctf =
         AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
       functions.AppendElement(ctf);
     }
   }
 }
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -50,22 +50,24 @@ struct AnimationTransform {
   gfx::Matrix4x4 mFrameTransform;
   TransformData mData;
 };
 
 struct AnimatedValue {
   enum {
     TRANSFORM,
     OPACITY,
+    COLOR,
     NONE
   } mType {NONE};
 
   union {
     AnimationTransform mTransform;
     float mOpacity;
+    nscolor mColor;
   };
 
   AnimatedValue(gfx::Matrix4x4&& aTransformInDevSpace,
                 gfx::Matrix4x4&& aFrameTransform,
                 const TransformData& aData)
     : mType(AnimatedValue::TRANSFORM)
     , mOpacity(0.0)
   {
@@ -75,16 +77,22 @@ struct AnimatedValue {
   }
 
   explicit AnimatedValue(const float& aValue)
     : mType(AnimatedValue::OPACITY)
     , mOpacity(aValue)
   {
   }
 
+  explicit AnimatedValue(nscolor aValue)
+    : mType(AnimatedValue::COLOR)
+    , mColor(aValue)
+  {
+  }
+
   ~AnimatedValue() {}
 
 private:
   AnimatedValue() = delete;
 };
 
 // CompositorAnimationStorage stores the animations and animated values
 // keyed by a CompositorAnimationsId. The "animations" are a representation of
@@ -124,16 +132,21 @@ public:
                         gfx::Matrix4x4&& aTransformInDevSpace);
 
   /**
    * Set the animation opacity based on the unique id
    */
   void SetAnimatedValue(uint64_t aId, const float& aOpacity);
 
   /**
+   * Set the animation color based on the unique id
+   */
+  void SetAnimatedValue(uint64_t aId, nscolor aColor);
+
+  /**
    * Return the animated value if a given id can map to its animated value
    */
   AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
 
   OMTAValue GetOMTAValue(const uint64_t& aId) const;
 
   /**
    * Return the iterator of animated value table
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -628,16 +628,29 @@ ApplyAnimatedValue(Layer* aLayer,
 {
   if (!aValue) {
     // Return gracefully if we have no valid AnimationValue.
     return;
   }
 
   HostLayer* layerCompositor = aLayer->AsHostLayer();
   switch (aProperty) {
+    case eCSSProperty_background_color: {
+      // We don't support 'color' animations on the compositor yet so we never
+      // meet currentColor on the compositor.
+      nscolor color = Servo_AnimationValue_GetColor(aValue, NS_RGBA(0, 0, 0, 0));
+      aLayer->AsColorLayer()->SetColor(gfx::Color::FromABGR(color));
+      aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(), color);
+
+      layerCompositor->SetShadowOpacity(aLayer->GetOpacity());
+      layerCompositor->SetShadowOpacitySetByAnimation(false);
+      layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
+      layerCompositor->SetShadowTransformSetByAnimation(false);
+      break;
+    }
     case eCSSProperty_opacity: {
       float opacity = Servo_AnimationValue_GetOpacity(aValue);
       layerCompositor->SetShadowOpacity(opacity);
       layerCompositor->SetShadowOpacitySetByAnimation(true);
       aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(), opacity);
 
       layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
       layerCompositor->SetShadowTransformSetByAnimation(false);
@@ -704,29 +717,32 @@ SampleAnimations(Layer* aLayer,
                                aStorage,
                                animation.property(),
                                animation.data(),
                                animationValue);
             break;
           }
           case AnimationHelper::SampleResult::Skipped:
             switch (animations[0].property()) {
+              case eCSSProperty_background_color:
               case eCSSProperty_opacity: {
-                MOZ_ASSERT(
-                  layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
+                if (animations[0].property() == eCSSProperty_opacity) {
+                  MOZ_ASSERT(
+                    layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
 #ifdef DEBUG
-                // Disable this assertion until the root cause is fixed in bug
-                // 1459775.
-                // MOZ_ASSERT(FuzzyEqualsMultiplicative(
-                //   Servo_AnimationValue_GetOpacity(animationValue),
-                //   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
+                  // Disable this assertion until the root cause is fixed in bug
+                  // 1459775.
+                  // MOZ_ASSERT(FuzzyEqualsMultiplicative(
+                  //   Servo_AnimationValue_GetOpacity(animationValue),
+                  //   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
 #endif
-                // Even if opacity animation value has unchanged, we have to set
-                // the shadow base transform value here since the value might
-                // have been changed by APZC.
+                }
+                // Even if opacity or background-color  animation value has
+                // unchanged, we have to set the shadow base transform value
+                // here since the value might have been changed by APZC.
                 HostLayer* layerCompositor = layer->AsHostLayer();
                 layerCompositor->SetShadowBaseTransform(
                   layer->GetBaseTransform());
                 layerCompositor->SetShadowTransformSetByAnimation(false);
                 break;
               }
               case eCSSProperty_transform: {
                 MOZ_ASSERT(
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -14,16 +14,17 @@ include "mozilla/GfxMessageUtils.h";
 include "ImageLayers.h";
 
 using mozilla::gfx::Glyph from "mozilla/gfx/2D.h";
 using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Color from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
 using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using nscolor from "nsColor.h";
 using nscoord from "nsCoord.h";
 using struct nsRect from "nsRect.h";
 using struct nsPoint from "nsPoint.h";
 using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
 using nsCSSPropertyID from "nsCSSPropertyID.h";
 using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
@@ -155,16 +156,17 @@ union TransformFunction {
 union MaybeTimeDuration {
   null_t;
   TimeDuration;
 };
 
 union Animatable {
   null_t;
   float;
+  nscolor;
   TransformFunction[];
 };
 
 struct AnimationSegment {
   Animatable startState;
   Animatable endState;
   float startPortion;
   float endPortion;
@@ -560,14 +562,15 @@ struct TransactionInfo
 
 union MaybeTransform {
   Matrix4x4;
   void_t;
 };
 
 union OMTAValue {
   null_t;
+  nscolor;
   float;
   Matrix4x4;
 };
 
 } // namespace
 } // namespace
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3825,19 +3825,18 @@ nsDocumentViewer::Print(nsIPrintSettings
   }
 
   nsCOMPtr<nsIDocShell> docShell(mContainer);
   NS_ENSURE_STATE(docShell);
 
   // Check to see if this document is still busy
   // If it is busy and we aren't already "queued" up to print then
   // Indicate there is a print pending and cache the args for later
-  uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
-  if ((NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
-       (busyFlags != nsIDocShell::BUSY_FLAGS_NONE && busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) &&
+  auto busyFlags = docShell->GetBusyFlags();
+  if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE && busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING &&
       !mPrintDocIsFullyLoaded) {
     if (!mPrintIsPending) {
       mCachedPrintSettings           = aPrintSettings;
       mCachedPrintWebProgressListner = aWebProgressListener;
       mPrintIsPending                = true;
     }
     PR_PL(("Printing Stopped - document is still busy!"));
     return NS_ERROR_GFX_PRINTER_DOC_IS_BUSY;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -10342,20 +10342,20 @@ nsLayoutUtils::ComputeSystemFont(nsFont*
     }
 #endif
   }
 }
 
 /* static */ bool
 nsLayoutUtils::ShouldHandleMetaViewport(nsIDocument* aDocument)
 {
-  uint32_t metaViewportOverride = nsIDocShell::META_VIEWPORT_OVERRIDE_NONE;
+  auto metaViewportOverride = nsIDocShell::META_VIEWPORT_OVERRIDE_NONE;
   if (aDocument) {
     if (nsIDocShell* docShell = aDocument->GetDocShell()) {
-      docShell->GetMetaViewportOverride(&metaViewportOverride);
+      metaViewportOverride = docShell->GetMetaViewportOverride();
     }
   }
   switch (metaViewportOverride) {
     case nsIDocShell::META_VIEWPORT_OVERRIDE_ENABLED:
       return true;
     case nsIDocShell::META_VIEWPORT_OVERRIDE_DISABLED:
       return false;
     default:
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -436,16 +436,33 @@ SetAnimatable(nsCSSPropertyID aProperty,
   MOZ_ASSERT(aFrame);
 
   if (aAnimationValue.IsNull()) {
     aAnimatable = null_t();
     return;
   }
 
   switch (aProperty) {
+    case eCSSProperty_background_color: {
+      // We don't support color animation on the compositor yet so that we can
+      // resolve currentColor at this moment.
+      nscolor foreground;
+      if (aFrame->Style()->RelevantLinkVisited()) {
+        if (ComputedStyle* styleIfVisited =
+              aFrame->Style()->GetStyleIfVisited()) {
+          foreground = styleIfVisited->StyleColor()->mColor;
+        } else {
+          foreground = aFrame->Style()->StyleColor()->mColor;
+        }
+      } else {
+        foreground = aFrame->Style()->StyleColor()->mColor;
+      }
+      aAnimatable = aAnimationValue.GetColor(foreground);
+      break;
+    }
     case eCSSProperty_opacity:
       aAnimatable = aAnimationValue.GetOpacity();
       break;
     case eCSSProperty_transform: {
       aAnimatable = InfallibleTArray<TransformFunction>();
       MOZ_ASSERT(aAnimationValue.mServo);
       RefPtr<nsCSSValueSharedList> list;
       Servo_AnimationValue_GetTransform(aAnimationValue.mServo, &list);
@@ -671,17 +688,17 @@ AddAnimationsForProperty(nsIFrame* aFram
 
     data = TransformData(origin,
                          offsetToTransformOrigin,
                          bounds,
                          devPixelsToAppUnits,
                          scaleX,
                          scaleY,
                          hasPerspectiveParent);
-  } else if (aProperty == eCSSProperty_opacity) {
+  } else {
     data = null_t();
   }
 
   MOZ_ASSERT(
     nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::CanAnimateOnCompositor),
     "inconsistent property flags");
 
   // Add from first to last (since last overrides)
@@ -3527,16 +3544,17 @@ nsDisplaySolidColor::GetBounds(nsDisplay
 LayerState
 nsDisplaySolidColor::GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters)
 {
   if (ForceActiveLayers()) {
     return LAYER_ACTIVE;
   }
+
   return LAYER_NONE;
 }
 
 already_AddRefed<Layer>
 nsDisplaySolidColor::BuildLayer(
   nsDisplayListBuilder* aBuilder,
   LayerManager* aManager,
   const ContainerLayerParameters& aContainerParameters)
@@ -5020,16 +5038,22 @@ nsDisplayBackgroundColor::GetLayerState(
   LayerManager* aManager,
   const ContainerLayerParameters& aParameters)
 {
   StyleGeometryBox clip =
     mBackgroundStyle->StyleBackground()->mImage.mLayers[0].mClip;
   if (ForceActiveLayers() && clip != StyleGeometryBox::Text) {
     return LAYER_ACTIVE;
   }
+
+  if (EffectCompositor::HasAnimationsForCompositor(
+        mFrame, eCSSProperty_background_color)) {
+    return LAYER_ACTIVE_FORCE;
+  }
+
   return LAYER_NONE;
 }
 
 already_AddRefed<Layer>
 nsDisplayBackgroundColor::BuildLayer(
   nsDisplayListBuilder* aBuilder,
   LayerManager* aManager,
   const ContainerLayerParameters& aContainerParameters)
@@ -5047,16 +5071,21 @@ nsDisplayBackgroundColor::BuildLayer(
   }
   layer->SetColor(mColor);
 
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   layer->SetBounds(mBackgroundRect.ToNearestPixels(appUnitsPerDevPixel));
   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
     aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
 
+  nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
+    layer, aBuilder,
+    this, mFrame,
+    eCSSProperty_background_color);
+
   return layer.forget();
 }
 
 bool
 nsDisplayBackgroundColor::CreateWebRenderCommands(
   mozilla::wr::DisplayListBuilder& aBuilder,
   mozilla::wr::IpcResourceUpdateQueue& aResources,
   const StackingContextHelper& aSc,
@@ -8569,16 +8598,24 @@ nsDisplayOpacity::CanUseAsyncAnimations(
 }
 
 bool
 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
 {
   return mAllowAsyncAnimation;
 }
 
+bool
+nsDisplayBackgroundColor::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
+{
+  LayerManager* layerManager = aBuilder->GetWidgetLayerManager();
+  return layerManager &&
+         layerManager->GetBackendType() != layers::LayersBackend::LAYERS_WR;
+}
+
 /* static */ auto
 nsDisplayTransform::ShouldPrerenderTransformedContent(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aFrame,
   nsRect* aDirtyRect) -> PrerenderDecision
 {
   // Elements whose transform has been modified recently, or which
   // have a compositor-animated transform, can be prerendered. An element
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -5069,16 +5069,18 @@ public:
     if (aFrame == mDependentFrame) {
       mDependentFrame = nullptr;
     }
     nsDisplayItem::RemoveFrame(aFrame);
   }
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
+  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
+
 protected:
   const nsRect mBackgroundRect;
   RefPtr<mozilla::ComputedStyle> mBackgroundStyle;
   nsIFrame* mDependentFrame;
   mozilla::gfx::Color mColor;
 
   struct
   {
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -1044,19 +1044,18 @@ nsPrintJob::PrintPreview(nsIPrintSetting
                          mozIDOMWindowProxy* aChildDOMWin,
                          nsIWebProgressListener* aWebProgressListener)
 {
   // Get the DocShell and see if it is busy
   // (We can't Print Preview this document if it is still busy)
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
   NS_ENSURE_STATE(docShell);
 
-  uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
-  if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
-      busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
+  auto busyFlags = docShell->GetBusyFlags();
+  if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
     CloseProgressDialog(aWebProgressListener);
     FirePrintingErrorEvent(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY);
     return NS_ERROR_FAILURE;
   }
 
   auto* window = nsPIDOMWindowOuter::From(aChildDOMWin);
   NS_ENSURE_STATE(window);
   nsCOMPtr<nsIDocument> doc = window->GetDoc();
--- a/layout/reftests/web-animations/child-in-animating-element-display-none-ref.html
+++ b/layout/reftests/web-animations/child-in-animating-element-display-none-ref.html
@@ -1,16 +1,18 @@
 <!DOCTYPE html>
 <html>
 <style>
 div {
   width: 100px;
   height: 100px;
   background-color: blue;
 }
-span {
+#child {
   background-color: green;
+  width: 50px;
+  height: 50px;
 }
 </style>
 <div>
-  <span>child</span>
+  <div id="child"></div>
 </div>
 </html>
--- a/layout/reftests/web-animations/child-in-animating-element-display-none.html
+++ b/layout/reftests/web-animations/child-in-animating-element-display-none.html
@@ -4,22 +4,24 @@
 Child element in animating element that display property is changed from none
 </title>
 <style>
 #test {
   display: none;
   width: 100px;
   height: 100px;
 }
-span {
+#child {
   background-color: green;
+  width: 50px;
+  height: 50px;
 }
 </style>
 <div id="test">
-  <span>child</span>
+  <div id="child"></div>
 </div>
 <script>
   var anim = test.animate({ backgroundColor: [ 'blue', 'blue' ] },
                           { duration: 1000,
                             iterations: Infinity });
 
   requestAnimationFrame(() => {
     requestAnimationFrame(() => {
--- a/layout/style/LayerAnimationInfo.cpp
+++ b/layout/style/LayerAnimationInfo.cpp
@@ -9,24 +9,27 @@
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 #include "nsCSSPropertyIDSet.h" // For nsCSSPropertyIDSet::CompositorAnimatable
 
 namespace mozilla {
 
 /* static */ const Array<DisplayItemType,
                          nsCSSPropertyIDSet::CompositorAnimatableCount()>
   LayerAnimationInfo::sDisplayItemTypes = {
+    DisplayItemType::TYPE_BACKGROUND_COLOR,
     DisplayItemType::TYPE_OPACITY,
     DisplayItemType::TYPE_TRANSFORM,
   };
 
 /* static */ DisplayItemType
 LayerAnimationInfo::GetDisplayItemTypeForProperty(nsCSSPropertyID aProperty)
 {
   switch (aProperty) {
+    case eCSSProperty_background_color:
+      return DisplayItemType::TYPE_BACKGROUND_COLOR;
     case eCSSProperty_opacity:
       return DisplayItemType::TYPE_OPACITY;
     case eCSSProperty_transform:
       return DisplayItemType::TYPE_TRANSFORM;
     default:
       break;
   }
   return DisplayItemType::TYPE_ZERO;
--- a/layout/style/LayerAnimationInfo.h
+++ b/layout/style/LayerAnimationInfo.h
@@ -29,19 +29,23 @@ struct LayerAnimationInfo {
   // be animated on the compositor.
   static inline const nsCSSPropertyIDSet&
   GetCSSPropertiesFor(DisplayItemType aDisplayItemType)
   {
     static const nsCSSPropertyIDSet transformProperties =
       nsCSSPropertyIDSet{ eCSSProperty_transform };
     static const nsCSSPropertyIDSet opacityProperties =
       nsCSSPropertyIDSet{ eCSSProperty_opacity };
+    static const nsCSSPropertyIDSet backgroundColorProperties =
+      nsCSSPropertyIDSet{ eCSSProperty_background_color };
     static const nsCSSPropertyIDSet empty = nsCSSPropertyIDSet();
 
     switch (aDisplayItemType) {
+      case DisplayItemType::TYPE_BACKGROUND_COLOR:
+        return backgroundColorProperties;
       case DisplayItemType::TYPE_OPACITY:
         return opacityProperties;
       case DisplayItemType::TYPE_TRANSFORM:
         return transformProperties;
       default:
         MOZ_ASSERT_UNREACHABLE("Should not be called for display item types "
                                "that are not able to have animations on the "
                                "compositor");
@@ -53,16 +57,18 @@ struct LayerAnimationInfo {
   // |aDisplayItemType|.
   //
   // This function works only for display items tied to CSS properties that can
   // be animated on the compositor.
   static inline nsChangeHint
   GetChangeHintFor(DisplayItemType aDisplayItemType)
   {
     switch (aDisplayItemType) {
+      case DisplayItemType::TYPE_BACKGROUND_COLOR:
+        return nsChangeHint_RepaintFrame;
       case DisplayItemType::TYPE_OPACITY:
         return nsChangeHint_UpdateOpacityLayer;
       case DisplayItemType::TYPE_TRANSFORM:
         return nsChangeHint_UpdateTransformLayer;
       default:
         MOZ_ASSERT_UNREACHABLE("Should not be called for display item types "
                                "that are not able to have animations on the "
                                "compositor");
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -671,16 +671,21 @@ double Servo_AnimationValues_ComputeDist
   RawServoAnimationValueBorrowed from,
   RawServoAnimationValueBorrowed to);
 
 void Servo_AnimationValue_Serialize(
   RawServoAnimationValueBorrowed value,
   nsCSSPropertyID property,
   nsAString* buffer);
 
+nscolor Servo_AnimationValue_GetColor(RawServoAnimationValueBorrowed value,
+                                      nscolor foregroundColor);
+RawServoAnimationValueStrong Servo_AnimationValue_Color(nsCSSPropertyID,
+                                                        nscolor);
+
 float Servo_AnimationValue_GetOpacity(RawServoAnimationValueBorrowed value);
 RawServoAnimationValueStrong Servo_AnimationValue_Opacity(float);
 
 void Servo_AnimationValue_GetTransform(
   RawServoAnimationValueBorrowed value,
   RefPtr<nsCSSValueSharedList>* list);
 
 RawServoAnimationValueStrong Servo_AnimationValue_Transform(
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -115,16 +115,23 @@ AnimationValue::operator!=(const Animati
 
 float
 AnimationValue::GetOpacity() const
 {
   MOZ_ASSERT(mServo);
   return Servo_AnimationValue_GetOpacity(mServo);
 }
 
+nscolor
+AnimationValue::GetColor(nscolor aForegroundColor) const
+{
+  MOZ_ASSERT(mServo);
+  return Servo_AnimationValue_GetColor(mServo, aForegroundColor);
+}
+
 already_AddRefed<const nsCSSValueSharedList>
 AnimationValue::GetTransformList() const
 {
   MOZ_ASSERT(mServo);
 
   RefPtr<nsCSSValueSharedList> transform;
   Servo_AnimationValue_GetTransform(mServo, &transform);
   return transform.forget();
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -73,16 +73,20 @@ struct AnimationValue
 
   bool IsNull() const
   {
     return !mServo;
   }
 
   float GetOpacity() const;
 
+  // Returns nscolor value in this AnimationValue.
+  // Currently only background-color is supported.
+  nscolor GetColor(nscolor aForegroundColor) const;
+
   // Return the transform list as a RefPtr.
   already_AddRefed<const nsCSSValueSharedList> GetTransformList() const;
 
   // Return the scale for mServo, which is calculated with reference to aFrame.
   mozilla::gfx::Size GetScaleValue(const nsIFrame* aFrame) const;
 
   // Uncompute this AnimationValue and then serialize it.
   void SerializeSpecifiedValue(nsCSSPropertyID aProperty,
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1112,17 +1112,17 @@ nsComputedDOMStyle::DoGetBinding()
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetBottom()
 {
   return GetOffsetWidthFor(eSideBottom);
 }
 
-void
+/* static */ void
 nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue,
                                    nscolor aColor)
 {
   nsAutoString string;
   const bool hasAlpha = NS_GET_A(aColor) != 255;
   if (hasAlpha) {
     string.AppendLiteral("rgba(");
   } else {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -126,16 +126,17 @@ public:
                                      mozilla::MutationClosureData*) override;
   virtual nsIDocument* DocToUpdate() override;
 
   nsDOMCSSDeclaration::ParsingEnvironment
   GetParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
 
   static already_AddRefed<nsROCSSPrimitiveValue>
     MatrixToCSSValue(const mozilla::gfx::Matrix4x4& aMatrix);
+  static void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
 
   static void RegisterPrefChangeCallbacks();
   static void UnregisterPrefChangeCallbacks();
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
 
 private:
@@ -423,17 +424,16 @@ private:
   already_AddRefed<CSSValue> DoGetPaintOrder();
 
 
   // For working around a MSVC bug. See related comment in
   // GenerateComputedDOMStyleGenerated.py.
   already_AddRefed<CSSValue> DummyGetter();
 
   /* Helper functions */
-  void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
   void SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue,
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueToPositionCoord(const mozilla::Position::Coord& aCoord,
                                nsROCSSPrimitiveValue* aValue);
   void SetValueToPosition(const mozilla::Position& aPosition,
                           nsDOMCSSValueList* aValueList);
   void SetValueToURLValue(const mozilla::css::URLValue* aURL,
                           nsROCSSPrimitiveValue* aValue);
--- a/layout/style/test/animation_utils.js
+++ b/layout/style/test/animation_utils.js
@@ -417,29 +417,40 @@ const ExpectComparisonTo = {
   };
 
   // Many callers of this method will pass 'undefined' for
   // expectedComparisonResult.
   window.omta_is_approx = function(elem, property, expected, tolerance,
                                    runningOn, desc, expectedComparisonResult,
                                    pseudo) {
     // Check input
-    const omtaProperties = [ "transform", "opacity" ];
+    // FIXME: Auto generate this array.
+    const omtaProperties = [ "transform", "opacity", "background-color" ];
     if (!omtaProperties.includes(property)) {
       ok(false, property + " is not an OMTA property");
       return;
     }
-    var isTransform = property == "transform";
-    var normalize = isTransform ? convertTo3dMatrix : parseFloat;
-    var compare = isTransform ?
-                  matricesRoughlyEqual :
-                  function(a, b, error) { return Math.abs(a - b) <= error; };
-    var normalizedToString = isTransform ?
-                             convert3dMatrixToString :
-                             JSON.stringify;
+    var normalize;
+    var compare;
+    var normalizedToString = JSON.stringify;
+    switch (property) {
+      case "transform":
+        normalize = convertTo3dMatrix;
+        compare = matricesRoughlyEqual;
+        normalizedToString = convert3dMatrixToString;
+        break;
+      case "opacity":
+        normalize = parseFloat;
+        compare = function(a, b, error) { return Math.abs(a - b) <= error; };
+        break;
+      default:
+        normalize = function(value) { return value; };
+        compare = function(a, b, error) { return a == b; };
+        break;
+    }
 
     // Get actual values
     var compositorStr =
       SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property, pseudo);
     var computedStr = window.getComputedStyle(elem, pseudo)[property];
 
     // Prepare expected value
     var expectedValue = normalize(expected);
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 prefs =
   dom.animations-api.compositing.enabled=true
   dom.animations-api.core.enabled=true
   dom.animations-api.getAnimations.enabled=true
   dom.animations-api.implicit-keyframes.enabled=true
   dom.animations-api.timelines.enabled=true
+  gfx.omta.background-color=true
   layout.css.step-position-jump.enabled=true
 support-files =
   animation_utils.js
   ccd-quirks.html
   ccd.sjs
   ccd-standards.html
   chrome/bug418986-2.js
   chrome/match.png
--- a/layout/style/test/test_animations_omta.html
+++ b/layout/style/test/test_animations_omta.html
@@ -2386,10 +2386,127 @@ addAsyncAnimTest(async function() {
   // compositor thread.
   await waitForPaints();
   omta_is("transform", { tx: 200 }, RunningOn.MainThread,
           "transition runs on main thread at end of active interval");
 
   done_div();
 });
 
+if (SpecialPowers.DOMWindowUtils.layerManagerType != 'WebRender') {
+  // Normal background-color animation.
+  addAsyncAnimTest(async function() {
+    new_div("background-color: rgb(255, 0, 0); " +
+            "transition: background-color 10s linear");
+    await waitForPaintsFlushed();
+
+    gDiv.style.backgroundColor = "rgb(0, 255, 0)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color transition runs on compositor thread");
+
+    advance_clock(5000);
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    done_div();
+  });
+
+  // background-color animation with currentColor.
+  addAsyncAnimTest(async function() {
+    new_div("color: rgb(255, 0, 0); " +
+            "background-color: currentColor; " +
+            "transition: background-color 10s linear");
+    await waitForPaintsFlushed();
+
+    gDiv.style.backgroundColor = "rgb(0, 255, 0)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color transition starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(5000);
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    done_div();
+  });
+
+  // Tests that a background-color animation from inherited currentColor to
+  // a normal color on the compositor is updated when the parent color is
+  // changed.
+  addAsyncAnimTest(async function() {
+    new_div("");
+    const parent = document.createElement("div");
+    gDiv.parentNode.insertBefore(parent, gDiv);
+    parent.style.color = "rgb(255, 0, 0)";
+    parent.appendChild(gDiv);
+
+    gDiv.animate({ backgroundColor: [ "currentColor", "rgb(0, 255, 0)" ] }, 1000);
+
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color animation starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(500);
+
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    // Change the parent's color in the middle of the animation.
+    parent.style.color = "rgb(0, 0, 255)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(0, 128, 128)", RunningOn.Compositor,
+            "background-color on compositor is reflected by the parent's " +
+            "color change");
+
+    done_div();
+    parent.remove();
+  });
+
+  // Tests that a background-color animation from currentColor to a normal color
+  // on <a> element is updated when the link is visited.
+  addAsyncAnimTest(async function() {
+    [ gDiv ] = new_element("a", "display: block");
+    gDiv.setAttribute("href", "not-exist.html");
+    gDiv.classList.add("visited");
+
+    const extraStyle = document.createElement('style');
+    document.head.appendChild(extraStyle);
+    extraStyle.sheet.insertRule(".visited:visited { color: rgb(0, 0, 255); }", 0);
+    extraStyle.sheet.insertRule(".visited:link { color: rgb(255, 0, 0); }", 1);
+
+    gDiv.animate({ backgroundColor: [ "currentColor", "rgb(0, 255, 0)" ] }, 1000);
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color animation starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(500);
+
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    gDiv.setAttribute("href", window.top.location.href);
+    await waitForVisitedLinkColoring(gDiv, "color", "rgb(0, 0, 255)");
+    await waitForPaintsFlushed();
+
+    // `omta_is` checks that the result on the compositor equals to the value by
+    // getComputedValue() but getComputedValue lies for visited link values so
+    // we use getOMTAStyle directly instead.
+    is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "background-color"),
+       "rgb(0, 128, 128)",
+       "background-color on <a> element after the link is visited");
+
+    extraStyle.remove();
+    done_element();
+    gDiv = null;
+  });
+}
+
 </script>
 </html>
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -497,16 +497,28 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "gfx.font_ahem_antialias_none",
    gfx_font_ahem_antialias_none,
   RelaxedAtomicBool, false
 )
 
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "gfx.omta.background-color",
+   gfx_omta_background_color,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 //---------------------------------------------------------------------------
 // HTML5 parser prefs
 //---------------------------------------------------------------------------
 
 // Toggle which thread the HTML5 parser uses for stream parsing.
 VARCACHE_PREF(
   "html5.offmainthread",
    html5_offmainthread,
--- a/servo/components/style/properties/longhands/background.mako.rs
+++ b/servo/components/style/properties/longhands/background.mako.rs
@@ -10,17 +10,18 @@
     "background-color",
     "Color",
     "computed_value::T::transparent()",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     allow_quirks=True,
-    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER \
+           CAN_ANIMATE_ON_COMPOSITOR",
 )}
 
 ${helpers.predefined_type(
     "background-image",
     "ImageLayer",
     initial_value="Either::First(None_)",
     initial_specified_value="Either::First(None_)",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -793,31 +793,76 @@ pub extern "C" fn Servo_AnimationValue_S
             buffer,
             None,
             None, /* No extra custom properties */
         );
     debug_assert!(rv.is_ok());
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetColor(
+    value: RawServoAnimationValueBorrowed,
+    foreground_color: structs::nscolor,
+) -> structs::nscolor {
+    use style::gecko::values::convert_nscolor_to_rgba;
+    use style::gecko::values::convert_rgba_to_nscolor;
+    use style::values::animated::ToAnimatedValue;
+    use style::values::computed::color::Color as ComputedColor;
+
+    let value = AnimationValue::as_arc(&value);
+    match **value {
+        AnimationValue::BackgroundColor(color) => {
+            let computed: ComputedColor = ToAnimatedValue::from_animated_value(color);
+            let foreground_color = convert_nscolor_to_rgba(foreground_color);
+            convert_rgba_to_nscolor(&computed.to_rgba(foreground_color))
+        }
+        _ => panic!("Other color properties are not supported yet"),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_GetOpacity(value: RawServoAnimationValueBorrowed) -> f32 {
     let value = AnimationValue::as_arc(&value);
     if let AnimationValue::Opacity(opacity) = **value {
         opacity
     } else {
         panic!("The AnimationValue should be Opacity");
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> RawServoAnimationValueStrong {
     Arc::new(AnimationValue::Opacity(opacity)).into_strong()
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Color(
+    color_property: nsCSSPropertyID,
+    color: structs::nscolor
+) -> RawServoAnimationValueStrong {
+    use style::gecko::values::convert_nscolor_to_rgba;
+    use style::values::animated::color::RGBA as AnimatedRGBA;
+
+    let property = LonghandId::from_nscsspropertyid(color_property)
+        .expect("We don't have shorthand property animation value");
+
+    let rgba = convert_nscolor_to_rgba(color);
+
+    let animatedRGBA = AnimatedRGBA::new(rgba.red_f32(),
+                                         rgba.green_f32(),
+                                         rgba.blue_f32(),
+                                         rgba.alpha_f32());
+    match property {
+        LonghandId::BackgroundColor =>
+            Arc::new(AnimationValue::BackgroundColor(animatedRGBA.into())).into_strong(),
+        _ => panic!("Should be background-color property"),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_GetTransform(
     value: RawServoAnimationValueBorrowed,
     list: *mut structs::RefPtr<nsCSSValueSharedList>,
 ) {
     let value = AnimationValue::as_arc(&value);
     if let AnimationValue::Transform(ref servo_list) = **value {
         let list = unsafe { &mut *list };
         if servo_list.0.is_empty() {
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -58,16 +58,17 @@ jobs:
     definition: debian7-build
     packages:
       - deb7-atk
       - deb7-glib
       - deb7-gdk-pixbuf
       - deb7-gtk3
       - deb7-harfbuzz
       - deb7-libxkbcommon
+      - deb7-nasm
       - deb7-pango
       - deb7-pcre3
       - deb7-valgrind
       - deb7-wayland
     args:
       ARCH: amd64
   debian7-i386-build:
     symbol: I(deb7-32)
@@ -83,16 +84,17 @@ jobs:
       - deb7-32-pango
       - deb7-32-pcre3
       - deb7-32-xkeyboard-config
       - deb7-32-wayland
       - deb7-atk
       - deb7-glib
       - deb7-gtk3
       - deb7-harfbuzz
+      - deb7-nasm
       - deb7-python-defaults
       - deb7-pcre3
       - deb7-valgrind
     args:
       ARCH: i386
   debian7-mozjs-rust-build:
     symbol: I(deb7jsrs)
     parent: debian7-amd64-build
--- a/taskcluster/docker/debian7-build/Dockerfile
+++ b/taskcluster/docker/debian7-build/Dockerfile
@@ -27,16 +27,17 @@ RUN apt-get update && \
       autoconf2.13 \
       automake \
       bzip2 \
       curl \
       file \
       gawk \
       gcc-multilib \
       gnupg \
+      nasm \
       p7zip-full \
       procps \
       python-pip \
       python-setuptools \
       python-virtualenv \
       rsync \
       screen \
       tar \
--- a/toolkit/components/find/nsWebBrowserFind.cpp
+++ b/toolkit/components/find/nsWebBrowserFind.cpp
@@ -122,18 +122,19 @@ nsWebBrowserFind::FindNext(bool* aResult
     return NS_OK;
   }
 
   nsIDocShell* rootDocShell = rootFrame->GetDocShell();
   if (!rootDocShell) {
     return NS_ERROR_FAILURE;
   }
 
-  int32_t enumDirection = mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS :
-                                           nsIDocShell::ENUMERATE_FORWARDS;
+  auto enumDirection =
+    mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS :
+    nsIDocShell::ENUMERATE_FORWARDS;
 
   nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
   rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
                                            enumDirection,
                                            getter_AddRefs(docShellEnumerator));
   if (NS_FAILED(rv)) {
     return rv;
   }
--- a/toolkit/content/tests/widgets/mochitest.ini
+++ b/toolkit/content/tests/widgets/mochitest.ini
@@ -20,42 +20,33 @@ support-files =
   videocontrols_direction-2b.html
   videocontrols_direction-2c.html
   videocontrols_direction-2d.html
   videocontrols_direction-2e.html
   videocontrols_direction_test.js
   videomask.css
 
 [test_audiocontrols_dimensions.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_mousecapture_area.html]
-skip-if = (verify && debug) || toolkit == 'android' # Bug 1483656 (android)
-[test_ua_widget.html]
-skip-if = toolkit == 'android' # Bug 1483656
+skip-if = (verify && debug)
+[test_ua_widget_sandbox.html]
+[test_ua_widget_unbind.html]
 [test_videocontrols.html]
 tags = fullscreen
 skip-if = toolkit == 'android' || (verify && debug && (os == 'linux')) #TIMED_OUT
 [test_videocontrols_keyhandler.html]
 skip-if = (toolkit == 'android') || (os == 'linux') #Bug 1366957
 [test_videocontrols_vtt.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_iframe_fullscreen.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_size.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_audio.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_audio_direction.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_jsdisabled.html]
-skip-if = toolkit == 'android' # bug 1272646 & bug 1483656
+skip-if = toolkit == 'android' # bug 1272646
 [test_videocontrols_standalone.html]
-skip-if = toolkit == 'android' # bug 1075573 & bug 1483656
+skip-if = toolkit == 'android' # bug 1075573
 [test_videocontrols_video_direction.html]
-skip-if = os == 'win' || toolkit == 'android' # Bug 1483656 (android)
+skip-if = os == 'win'
 [test_videocontrols_video_noaudio.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_bug898940.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_error.html]
-skip-if = toolkit == 'android' # Bug 1483656
 [test_videocontrols_orientation.html]
 skip-if = true # Bug 1483656
rename from toolkit/content/tests/widgets/test_ua_widget.html
rename to toolkit/content/tests/widgets/test_ua_widget_sandbox.html
--- a/toolkit/content/tests/widgets/test_ua_widget.html
+++ b/toolkit/content/tests/widgets/test_ua_widget_sandbox.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>UA Widget test</title>
+  <title>UA Widget sandbox test</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="head.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_ua_widget_unbind.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>UA Widget unbind test</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+
+<div id="content">
+</div>
+
+<pre id="test">
+<script class="testbody">
+
+const content = document.getElementById("content");
+
+add_task(function() {
+  const video = document.createElement("video");
+  video.controls = true;
+  ok(!SpecialPowers.wrap(video).openOrClosedShadowRoot, "UA Widget Shadow Root is not created");
+  content.appendChild(video);
+  ok(!!SpecialPowers.wrap(video).openOrClosedShadowRoot, "UA Widget Shadow Root is created");
+  ok(!!SpecialPowers.wrap(video).openOrClosedShadowRoot.firstChild, "Widget is constructed");
+  content.removeChild(video);
+  ok(!SpecialPowers.wrap(video).openOrClosedShadowRoot, "UA Widget Shadow Root is removed");
+});
+
+add_task(function() {
+  const marquee = document.createElement("marquee");
+  ok(!SpecialPowers.wrap(marquee).openOrClosedShadowRoot, "UA Widget Shadow Root is not created");
+  content.appendChild(marquee);
+  ok(!!SpecialPowers.wrap(marquee).openOrClosedShadowRoot, "UA Widget Shadow Root is created");
+  ok(!!SpecialPowers.wrap(marquee).openOrClosedShadowRoot.firstChild, "Widget is constructed");
+  content.removeChild(marquee);
+  ok(SpecialPowers.wrap(marquee).openOrClosedShadowRoot, "UA Widget Shadow Root is not removed for marquee");
+});
+
+add_task(function() {
+  const input = document.createElement("input");
+  input.type = "date";
+  ok(!SpecialPowers.wrap(input).openOrClosedShadowRoot, "UA Widget Shadow Root is not created");
+  content.appendChild(input);
+  ok(!!SpecialPowers.wrap(input).openOrClosedShadowRoot, "UA Widget Shadow Root is created");
+  ok(!!SpecialPowers.wrap(input).openOrClosedShadowRoot.firstChild, "Widget is constructed");
+  content.removeChild(input);
+  ok(!SpecialPowers.wrap(input).openOrClosedShadowRoot, "UA Widget Shadow Root is removed");
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/toolkit/xre/CmdLineAndEnvUtils.h
+++ b/toolkit/xre/CmdLineAndEnvUtils.h
@@ -389,17 +389,17 @@ SetArgv0ToFullBinaryPath(wchar_t* aArgv[
   aArgv[0] = newArgv_0.release();
   MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(aArgv[0]);
   return true;
 }
 
 #endif // defined(XP_WIN)
 
 // Save literal putenv string to environment variable.
-inline void
+MOZ_NEVER_INLINE inline void
 SaveToEnv(const char *aEnvString)
 {
 #if defined(MOZILLA_INTERNAL_API)
   char *expr = strdup(aEnvString);
   if (expr) {
     PR_SetEnv(expr);
   }
 
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -314,17 +314,17 @@ using namespace mozilla;
 using namespace mozilla::startup;
 using mozilla::Unused;
 using mozilla::scache::StartupCache;
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::intl::LocaleService;
 
 // Save the given word to the specified environment variable.
-static void
+static void MOZ_NEVER_INLINE
 SaveWordToEnv(const char *name, const nsACString & word)
 {
   char *expr = Smprintf("%s=%s", name, PromiseFlatCString(word).get()).release();
   if (expr)
     PR_SetEnv(expr);
   // We intentionally leak |expr| here since it is required by PR_SetEnv.
 }