diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h
index f239731e0ff06cb01a4c0e8cf0ba4ff5014f88e0..53447ef12eb59bd065abbfd031bd6336a60020a2 100644
--- a/accessible/base/NotificationController.h
+++ b/accessible/base/NotificationController.h
@@ -284,6 +284,8 @@ class NotificationController final : public EventQueue,
   }
 #endif
 
+  bool IsUpdatePendingForJugglerAccessibility() { return IsUpdatePending(); }
+
  protected:
   virtual ~NotificationController();
 
diff --git a/accessible/interfaces/nsIAccessibleDocument.idl b/accessible/interfaces/nsIAccessibleDocument.idl
index a91df31c96afda66f478a5a38eaa4352039c2a0b..ee777c1746284027fb3aa2f1686f8082af9d89ee 100644
--- a/accessible/interfaces/nsIAccessibleDocument.idl
+++ b/accessible/interfaces/nsIAccessibleDocument.idl
@@ -72,4 +72,9 @@ interface nsIAccessibleDocument : nsISupports
    * Return the child document accessible at the given index.
    */
   nsIAccessibleDocument getChildDocumentAt(in unsigned long index);
+
+  /**
+   * Return whether it is updating.
+   */
+  readonly attribute boolean isUpdatePendingForJugglerAccessibility;
 };
diff --git a/accessible/xpcom/xpcAccessibleDocument.cpp b/accessible/xpcom/xpcAccessibleDocument.cpp
index e3dbe73f22252f11080c3f266b2309f842eba9dc..87f50fe3df7cc8f9bc26dabd5ee571cae270912a 100644
--- a/accessible/xpcom/xpcAccessibleDocument.cpp
+++ b/accessible/xpcom/xpcAccessibleDocument.cpp
@@ -143,6 +143,15 @@ xpcAccessibleDocument::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) {
   return NS_OK;
 }
 
+
+NS_IMETHODIMP
+xpcAccessibleDocument::GetIsUpdatePendingForJugglerAccessibility(bool* updating) {
+  NS_ENSURE_ARG_POINTER(updating);
+  *updating = Intl()->Controller()->IsUpdatePendingForJugglerAccessibility();
+  return NS_OK;
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////
 // xpcAccessibleDocument
 
diff --git a/accessible/xpcom/xpcAccessibleDocument.h b/accessible/xpcom/xpcAccessibleDocument.h
index f042cc1081850ac60e329b70b5569f8b97d4e4dc..65bcff9b41b9471ef1427e3ea330481c194409bc 100644
--- a/accessible/xpcom/xpcAccessibleDocument.h
+++ b/accessible/xpcom/xpcAccessibleDocument.h
@@ -48,6 +48,8 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
                                 nsIAccessibleDocument** aDocument) final;
   NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) final;
 
+  NS_IMETHOD GetIsUpdatePendingForJugglerAccessibility(bool* aUpdating) final;
+
   /**
    * Return XPCOM wrapper for the internal accessible.
    */
diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
index 382881ff9cea80fc1dd1320c2f013c82409bff9a..98efc6a6af65f5a723443bd69d1f31b3666c3683 100644
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -64,6 +64,12 @@ browser/defaults/settings/pinning/pins.json
 browser/defaults/settings/main/example.json
 browser/defaults/settings/main/search-default-override-allowlist.json
 
+# Juggler/marionette files
+chrome/juggler/content/content/floating-scrollbars.css
+browser/chrome/devtools/skin/floating-scrollbars-responsive-design.css
+chrome/juggler/content/server/stream-utils.js
+chrome/marionette/content/stream-utils.js
+
 #ifdef MOZ_EME_WIN32_ARTIFACT
 gmp-clearkey/0.1/manifest.json
 i686/gmp-clearkey/0.1/manifest.json
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index e011b74cc4b4014362397137e92a1f27e29b43c7..c3ac23c2416363ce897741ca457fa9086b4cdd05 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -211,6 +211,11 @@
 @RESPATH@/components/marionette.js
 #endif
 
+@RESPATH@/chrome/juggler@JAREXT@
+@RESPATH@/chrome/juggler.manifest
+@RESPATH@/components/juggler.manifest
+@RESPATH@/components/juggler.js
+
 #if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js
index 040c7b124dec6bb254563bbe74fe50012cb077a3..b4e6b8132786af70e8ad0dce88b67c2835307f88 100644
--- a/devtools/server/socket/websocket-server.js
+++ b/devtools/server/socket/websocket-server.js
@@ -133,13 +133,12 @@ function writeHttpResponse(output, response) {
  * Process the WebSocket handshake headers and return the key to be sent in
  * Sec-WebSocket-Accept response header.
  */
-function processRequest({ requestLine, headers }) {
+function processRequest({ requestLine, headers }, expectedPath) {
   const [method, path] = requestLine.split(" ");
   if (method !== "GET") {
     throw new Error("The handshake request must use GET method");
   }
-
-  if (path !== "/") {
+  if (path !== expectedPath) {
     throw new Error("The handshake request has unknown path");
   }
 
@@ -189,13 +188,13 @@ function computeKey(key) {
 /**
  * Perform the server part of a WebSocket opening handshake on an incoming connection.
  */
-const serverHandshake = async function(input, output) {
+const serverHandshake = async function(input, output, expectedPath) {
   // Read the request
   const request = await readHttpRequest(input);
 
   try {
     // Check and extract info from the request
-    const { acceptKey } = processRequest(request);
+    const { acceptKey } = processRequest(request, expectedPath);
 
     // Send response headers
     await writeHttpResponse(output, [
@@ -217,8 +216,8 @@ const serverHandshake = async function(input, output) {
  * Performs the WebSocket handshake and waits for the WebSocket to open.
  * Returns Promise with a WebSocket ready to send and receive messages.
  */
-const accept = async function(transport, input, output) {
-  await serverHandshake(input, output);
+const accept = async function(transport, input, output, expectedPath) {
+  await serverHandshake(input, output, expectedPath || "/");
 
   const transportProvider = {
     setListener(upgradeListener) {
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 2424029f2783fe2af012f033c75c497b3c493bd6..56b0aa8f51798666f80cdfce5ace18260185c6ee 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -15,6 +15,12 @@
 #  include <unistd.h>  // for getpid()
 #endif
 
+#if JS_HAS_INTL_API && !MOZ_SYSTEM_ICU
+#  include "unicode/locid.h"
+#endif /* JS_HAS_INTL_API && !MOZ_SYSTEM_ICU */
+
+#include "js/LocaleSensitive.h"
+
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
@@ -57,6 +63,7 @@
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/Geolocation.h"
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/PerformanceNavigation.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
@@ -77,6 +84,7 @@
 #include "mozilla/dom/LoadURIOptionsBinding.h"
 #include "mozilla/dom/JSWindowActorChild.h"
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/net/DocumentChannel.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "ReferrerInfo.h"
@@ -100,6 +108,7 @@
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsIDOMWindow.h"
 #include "nsIEditingSession.h"
@@ -190,6 +199,7 @@
 #include "nsGlobalWindow.h"
 #include "nsISearchService.h"
 #include "nsJSEnvironment.h"
+#include "nsJSUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsObjectLoadingContent.h"
@@ -379,6 +389,11 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
       mAllowWindowControl(true),
       mUseErrorPages(true),
       mCSSErrorReportingEnabled(false),
+      mFileInputInterceptionEnabled(false),
+      mOverrideHasFocus(false),
+      mBypassCSPEnabled(false),
+      mOnlineOverride(nsIDocShell::ONLINE_OVERRIDE_NONE),
+      mColorSchemeOverride(COLOR_SCHEME_OVERRIDE_NONE),
       mAllowAuth(mItemType == typeContent),
       mAllowKeywordFixup(false),
       mIsOffScreenBrowser(false),
@@ -1249,6 +1264,7 @@ bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
     isSubFrame = mLSHE->GetIsSubFrame();
   }
 
+  FireOnFrameLocationChange(this, aRequest, aURI, aLocationFlags);
   if (!isSubFrame && !isRoot) {
     /*
      * We don't want to send OnLocationChange notifications when
@@ -3079,6 +3095,184 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
   return NS_OK;
 }
 
+// =============== Juggler Begin =======================
+
+nsDocShell* nsDocShell::GetRootDocShell() {
+  nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
+  GetInProcessSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
+  nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
+  return nsDocShell::Cast(rootShell);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetBypassCSPEnabled(bool* aEnabled) {
+  MOZ_ASSERT(aEnabled);
+  *aEnabled = mBypassCSPEnabled;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetBypassCSPEnabled(bool aEnabled) {
+  mBypassCSPEnabled = aEnabled;
+  return NS_OK;
+}
+
+bool nsDocShell::IsBypassCSPEnabled() {
+  return GetRootDocShell()->mBypassCSPEnabled;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOverrideHasFocus(bool* aEnabled) {
+  MOZ_ASSERT(aEnabled);
+  *aEnabled = mOverrideHasFocus;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOverrideHasFocus(bool aEnabled) {
+  mOverrideHasFocus = aEnabled;
+  return NS_OK;
+}
+
+bool nsDocShell::ShouldOverrideHasFocus() const {
+  return mOverrideHasFocus;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLanguageOverride(nsAString& aLanguageOverride) {
+  MOZ_ASSERT(aEnabled);
+  aLanguageOverride = GetRootDocShell()->mLanguageOverride;
+  return NS_OK;
+}
+
+
+static void SetIcuLocale(const nsAString& aLanguageOverride) {
+  icu::Locale locale(NS_LossyConvertUTF16toASCII(aLanguageOverride).get());
+  if (icu::Locale::getDefault() == locale)
+    return;
+  UErrorCode error_code = U_ZERO_ERROR;
+  const char* lang = locale.getLanguage();
+  if (lang != nullptr && *lang != '\0') {
+    icu::Locale::setDefault(locale, error_code);
+  } else {
+    fprintf(stderr, "SetIcuLocale Failed to set the ICU default locale to %s\n", NS_LossyConvertUTF16toASCII(aLanguageOverride).get());
+  }
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSContext* cx = jsapi.cx();
+  JS_ResetDefaultLocale(JS_GetRuntime(cx));
+
+  ResetDefaultLocaleInAllWorkers();
+}
+
+NS_IMETHODIMP
+nsDocShell::SetLanguageOverride(const nsAString& aLanguageOverride) {
+  mLanguageOverride = aLanguageOverride;
+  SetIcuLocale(aLanguageOverride);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OverrideTimezone(const nsAString& aTimezoneOverride,
+                             bool* aSuccess) {
+  NS_ENSURE_ARG(aSuccess);
+  NS_LossyConvertUTF16toASCII timeZoneId(aTimezoneOverride);
+  *aSuccess = nsJSUtils::SetTimeZoneOverride(timeZoneId.get());
+
+  // Set TZ which affects localtime_s().
+  auto setTimeZoneEnv = [](const char* value) {
+#if defined(_WIN32)
+    return _putenv_s("TZ", value) == 0;
+#else
+    return setenv("TZ", value, true) == 0;
+#endif /* _WIN32 */
+  };
+  if (*aSuccess) {
+    *aSuccess = setTimeZoneEnv(timeZoneId.get());
+    if (!*aSuccess) {
+      fprintf(stderr, "Failed to set 'TZ' to '%s'\n", timeZoneId.get());
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFileInputInterceptionEnabled(bool* aEnabled) {
+  MOZ_ASSERT(aEnabled);
+  *aEnabled = GetRootDocShell()->mFileInputInterceptionEnabled;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFileInputInterceptionEnabled(bool aEnabled) {
+  mFileInputInterceptionEnabled = aEnabled;
+  return NS_OK;
+}
+
+bool nsDocShell::IsFileInputInterceptionEnabled() {
+  return GetRootDocShell()->mFileInputInterceptionEnabled;
+}
+
+void nsDocShell::FilePickerShown(mozilla::dom::Element* element) {
+  nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+  observerService->NotifyObservers(
+      ToSupports(element), "juggler-file-picker-shown", nullptr);
+}
+
+RefPtr<nsGeolocationService> nsDocShell::GetGeolocationServiceOverride() {
+  return GetRootDocShell()->mGeolocationServiceOverride;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetGeolocationOverride(nsIDOMGeoPosition* aGeolocationOverride) {
+  if (aGeolocationOverride) {
+    if (!mGeolocationServiceOverride) {
+      mGeolocationServiceOverride = new nsGeolocationService();
+      mGeolocationServiceOverride->Init();
+    }
+    mGeolocationServiceOverride->Update(aGeolocationOverride);
+  } else {
+    mGeolocationServiceOverride = nullptr;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOnlineOverride(OnlineOverride* aOnlineOverride) {
+  *aOnlineOverride = GetRootDocShell()->mOnlineOverride;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOnlineOverride(OnlineOverride aOnlineOverride) {
+  // We don't have a way to verify this coming from Javascript, so this check is
+  // still needed.
+  if (!(aOnlineOverride == ONLINE_OVERRIDE_NONE ||
+        aOnlineOverride == ONLINE_OVERRIDE_ONLINE ||
+        aOnlineOverride == ONLINE_OVERRIDE_OFFLINE)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mOnlineOverride = aOnlineOverride;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetColorSchemeOverride(ColorSchemeOverride* aColorSchemeOverride) {
+  *aColorSchemeOverride = GetRootDocShell()->mColorSchemeOverride;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetColorSchemeOverride(ColorSchemeOverride aColorSchemeOverride) {
+  mColorSchemeOverride = aColorSchemeOverride;
+  return NS_OK;
+}
+
+// =============== Juggler End =======================
+
 NS_IMETHODIMP
 nsDocShell::GetIsNavigating(bool* aOut) {
   *aOut = mIsNavigating;
@@ -8195,6 +8389,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState,
                      true,  // aForceNoOpener
                      getter_AddRefs(newBC));
       MOZ_ASSERT(!newBC);
+      if (rv == NS_OK) {
+        nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+        if (observerService) {
+          observerService->NotifyObservers(GetAsSupports(this), "juggler-window-open-in-new-context", nullptr);
+        }
+      }
       return rv;
     }
 
@@ -11698,6 +11898,9 @@ class OnLinkClickEvent : public Runnable {
                                 mNoOpenerImplied, nullptr, nullptr,
                                 mIsUserTriggered, mTriggeringPrincipal, mCsp);
     }
+    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+    observerService->NotifyObservers(ToSupports(mContent), "juggler-link-click-sync", nullptr);
+
     return NS_OK;
   }
 
@@ -11787,6 +11990,8 @@ nsresult nsDocShell::OnLinkClick(
       this, aContent, aURI, target, aFileName, aPostDataStream,
       aHeadersDataStream, noOpenerImplied, aIsUserTriggered, aIsTrusted,
       aTriggeringPrincipal, aCsp);
+  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+  observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr);
   return Dispatch(TaskCategory::UI, ev.forget());
 }
 
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index dcffed8e55371a4f0aeae408de3a94e4e3a9ab95..5f6b2243ac054f414573736fc23b75509b2a8f5b 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -13,6 +13,7 @@
 #include "Units.h"
 #include "jsapi.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/Geolocation.h"
 #include "mozilla/HalScreenConfiguration.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
@@ -25,6 +26,7 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/dom/ChildSHistory.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/WindowProxyHolder.h"
@@ -479,6 +481,15 @@ class nsDocShell final : public nsDocLoader,
 
   void SetWillChangeProcess() { mWillChangeProcess = true; }
 
+  bool IsFileInputInterceptionEnabled();
+  void FilePickerShown(mozilla::dom::Element* element);
+
+  bool ShouldOverrideHasFocus() const;
+
+  bool IsBypassCSPEnabled();
+
+  RefPtr<nsGeolocationService> GetGeolocationServiceOverride();
+
   // Create a content viewer within this nsDocShell for the given
   // `WindowGlobalChild` actor.
   nsresult CreateContentViewerForActor(
@@ -1020,6 +1031,8 @@ class nsDocShell final : public nsDocLoader,
 
   bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; }
 
+  nsDocShell* GetRootDocShell();
+
   // Handles retrieval of subframe session history for nsDocShell::LoadURI. If a
   // load is requested in a subframe of the current DocShell, the subframe
   // loadType may need to reflect the loadType of the parent document, or in
@@ -1262,6 +1275,14 @@ class nsDocShell final : public nsDocLoader,
   bool mAllowWindowControl : 1;
   bool mUseErrorPages : 1;
   bool mCSSErrorReportingEnabled : 1;
+  bool mFileInputInterceptionEnabled: 1;
+  bool mOverrideHasFocus : 1;
+  bool mBypassCSPEnabled : 1;
+  nsString mLanguageOverride;
+  RefPtr<nsGeolocationService> mGeolocationServiceOverride;
+  OnlineOverride mOnlineOverride;
+  ColorSchemeOverride mColorSchemeOverride;
+
   bool mAllowAuth : 1;
   bool mAllowKeywordFixup : 1;
   bool mIsOffScreenBrowser : 1;
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 01dc4734eb191adf6572b9b628b4682eda017e19..0929fa0c324d5bc6e09a9d71ac4d8f4fbacca946 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -44,6 +44,7 @@ interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIContentSecurityPolicy;
+interface nsIDOMGeoPosition;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
 interface nsIEditingSession;
@@ -1010,6 +1011,33 @@ interface nsIDocShell : nsIDocShellTreeItem
    */
   void synchronizeLayoutHistoryState();
 
+  attribute boolean fileInputInterceptionEnabled;
+
+  attribute boolean overrideHasFocus;
+
+  attribute boolean bypassCSPEnabled;
+
+  attribute AString languageOverride;
+
+  boolean overrideTimezone(in AString timezoneId);
+
+  cenum OnlineOverride: 8 {
+    ONLINE_OVERRIDE_NONE = 0,
+    ONLINE_OVERRIDE_ONLINE = 1,
+    ONLINE_OVERRIDE_OFFLINE = 2,
+  };
+  [infallible] attribute nsIDocShell_OnlineOverride onlineOverride;
+
+  cenum ColorSchemeOverride : 8 {
+    COLOR_SCHEME_OVERRIDE_LIGHT,
+    COLOR_SCHEME_OVERRIDE_DARK,
+    COLOR_SCHEME_OVERRIDE_NO_PREFERENCE,
+    COLOR_SCHEME_OVERRIDE_NONE, /* This clears the override. */
+  };
+  [infallible] attribute nsIDocShell_ColorSchemeOverride colorSchemeOverride;
+
+  void setGeolocationOverride(in nsIDOMGeoPosition position);
+
   /**
    * This attempts to save any applicable layout history state (like
    * scroll position) in the nsISHEntry. This is normally done
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 3ea8d9829cbfb59e7095a21ff53993cb7716e9f2..b882a58f17a701cbced7b38f959a40e9e964b96a 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3216,6 +3216,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
 }
 
 void Document::ApplySettingsFromCSP(bool aSpeculative) {
+  if (mDocumentContainer && mDocumentContainer->IsBypassCSPEnabled())
+    return;
+
   nsresult rv = NS_OK;
   if (!aSpeculative) {
     // 1) apply settings from regular CSP
@@ -3270,6 +3273,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
     return NS_OK;
   }
 
+  nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
+  if (shell && nsDocShell::Cast(shell)->IsBypassCSPEnabled()) {
+    return NS_OK;
+  }
+
   // If this is a data document - no need to set CSP.
   if (mLoadedAsData) {
     return NS_OK;
@@ -4030,6 +4038,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
     return false;
   }
 
+  if (IsActive() && mDocumentContainer->ShouldOverrideHasFocus()) {
+    return true;
+  }
+
   // Is there a focused DOMWindow?
   nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
@@ -16322,6 +16334,19 @@ void Document::RemoveToplevelLoadingDocument(Document* aDoc) {
 
 StylePrefersColorScheme Document::PrefersColorScheme(
     IgnoreRFP aIgnoreRFP) const {
+  auto* docShell = static_cast<nsDocShell*>(GetDocShell());
+  nsIDocShell::ColorSchemeOverride colorScheme;
+  if (docShell->GetColorSchemeOverride(&colorScheme) == NS_OK &&
+      colorScheme != nsIDocShell::COLOR_SCHEME_OVERRIDE_NONE) {
+    switch (colorScheme) {
+      case nsIDocShell::COLOR_SCHEME_OVERRIDE_LIGHT:
+        return StylePrefersColorScheme::Light;
+      case nsIDocShell::COLOR_SCHEME_OVERRIDE_DARK:
+        return StylePrefersColorScheme::Dark;
+      case nsIDocShell::COLOR_SCHEME_OVERRIDE_NO_PREFERENCE:
+        return StylePrefersColorScheme::NoPreference;
+    };
+  }
   if (aIgnoreRFP == IgnoreRFP::No &&
       nsContentUtils::ShouldResistFingerprinting(this)) {
     return StylePrefersColorScheme::Light;
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index aa3fa6719f05a1aeb45f03f85a7e169d42f96bb8..4f3131229fe053a2b125327839fc64178fc15b62 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -327,14 +327,18 @@ void Navigator::GetAppName(nsAString& aAppName, CallerType aCallerType) const {
  * for more detail.
  */
 /* static */
-void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages) {
+void Navigator::GetAcceptLanguages(const nsString* aLanguageOverride, nsTArray<nsString>& aLanguages) {
   MOZ_ASSERT(NS_IsMainThread());
 
   aLanguages.Clear();
 
   // E.g. "de-de, en-us,en".
   nsAutoString acceptLang;
-  Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
+  if (aLanguageOverride && aLanguageOverride->Length())
+    acceptLang = *aLanguageOverride;
+  else
+    Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
+    
 
   // Split values on commas.
   nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
@@ -390,7 +394,9 @@ void Navigator::GetLanguage(nsAString& aLanguage) {
 }
 
 void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
-  GetAcceptLanguages(aLanguages);
+  nsString languageOverride;
+  mWindow->GetDocShell()->GetLanguageOverride(languageOverride);
+  GetAcceptLanguages(&languageOverride, aLanguages);
 
   // The returned value is cached by the binding code. The window listens to the
   // accept languages change and will clear the cache when needed. It has to
@@ -541,7 +547,13 @@ bool Navigator::CookieEnabled() {
   return granted;
 }
 
-bool Navigator::OnLine() { return !NS_IsOffline(); }
+bool Navigator::OnLine() {
+  nsDocShell* docShell = static_cast<nsDocShell*>(GetDocShell());
+  nsIDocShell::OnlineOverride onlineOverride;
+  if (!docShell || docShell->GetOnlineOverride(&onlineOverride) != NS_OK || onlineOverride == nsIDocShell::ONLINE_OVERRIDE_NONE)
+    return !NS_IsOffline();
+  return onlineOverride == nsIDocShell::ONLINE_OVERRIDE_ONLINE;
+}
 
 void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                            ErrorResult& aRv) const {
diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
index e268e2bbe8add1b43f6e4d6507cc7810d707a344..a34a7a292a02ea8d94042475a43ae3a05710c207 100644
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -216,7 +216,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
 
   StorageManager* Storage();
 
-  static void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
+  static void GetAcceptLanguages(const nsString* aLanguageOverride, nsTArray<nsString>& aLanguages);
 
   dom::MediaCapabilities* MediaCapabilities();
   dom::MediaSession* MediaSession();
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index da9d56e843a2c762dc7d5527712cdd3d30418f7f..aa3d2d6022b60ac3a263c2d1df4eb12714d8e728 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2430,7 +2430,7 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
         &nsGlobalWindowInner::FireOnNewGlobalObject));
   }
 
-  if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
+  if (newInnerWindow && mDoc) {
     // We should probably notify. However if this is the, arguably bad,
     // situation when we're creating a temporary non-chrome-about-blank
     // document in a chrome docshell, don't notify just yet. Instead wait
@@ -2449,10 +2449,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
     }();
 
     if (!isContentAboutBlankInChromeDocshell) {
-      newInnerWindow->mHasNotifiedGlobalCreated = true;
-      nsContentUtils::AddScriptRunner(NewRunnableMethod(
-          "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
-          &nsGlobalWindowOuter::DispatchDOMWindowCreated));
+      if (!newInnerWindow->mHasNotifiedGlobalCreated) {
+        newInnerWindow->mHasNotifiedGlobalCreated = true;
+        nsContentUtils::AddScriptRunner(NewRunnableMethod(
+            "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
+            &nsGlobalWindowOuter::DispatchDOMWindowCreated));
+      } else if (!reUseInnerWindow) {
+        nsContentUtils::AddScriptRunner(NewRunnableMethod(
+            "nsGlobalWindowOuter::JugglerDispatchDOMWindowReused", this,
+            &nsGlobalWindowOuter::JugglerDispatchDOMWindowReused));
+      }
     }
   }
 
@@ -2609,6 +2615,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
   }
 }
 
+void nsGlobalWindowOuter::JugglerDispatchDOMWindowReused() {
+  nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+  if (observerService && mDoc) {
+    nsIPrincipal* principal = mDoc->NodePrincipal();
+    if (!principal->IsSystemPrincipal()) {
+      observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
+                                      "juggler-dom-window-reused",
+                                      nullptr);
+    }
+  }
+}
+
 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(EmptyString()); }
 
 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
@@ -3861,6 +3880,14 @@ Maybe<CSSIntSize> nsGlobalWindowOuter::GetRDMDeviceSize(
       }
     }
   }
+  if (topInProcessContentDoc) {
+    nsIDocShell* docShell = topInProcessContentDoc->GetDocShell();
+    if (docShell && docShell->GetDeviceSizeIsPageSize()) {
+      nsPresContext* presContext = docShell->GetPresContext();
+      if (presContext)
+        return Some(CSSPixel::FromAppUnitsRounded(presContext->GetVisibleArea().Size()));
+    }
+  }
   return Nothing();
 }
 
diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h
index d2a8f0219f21800ef7800ed637c1f61d454a99a6..7e88989437613cec54d6bbf551afd572e28c4221 100644
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -319,6 +319,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
 
   // Outer windows only.
   void DispatchDOMWindowCreated();
+  void JugglerDispatchDOMWindowReused();
 
   // Outer windows only.
   virtual void EnsureSizeAndPositionUpToDate() override;
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
index 7c0d4bd261bf053c7cab4091522dc1e52118eba1..e320e6bfb0402eb07ae53272264278bad9638519 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1260,6 +1260,48 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
   mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
 }
 
+void nsINode::ScrollRectIntoViewIfNeeded(int32_t x, int32_t y,
+                                         int32_t w, int32_t h,
+                                         ErrorResult& aRv) {
+  aRv = NS_ERROR_UNEXPECTED;
+  nsCOMPtr<Document> document = OwnerDoc();
+  if (!document) {
+    return aRv.ThrowNotFoundError("Node is detached from document");
+  }
+  PresShell* presShell = document->GetPresShell();
+  if (!presShell) {
+    return aRv.ThrowNotFoundError("Node is detached from document");
+  }
+  if (!IsContent()) {
+    return aRv.ThrowNotFoundError("Node does not have a layout object");
+  }
+  aRv = NS_OK;
+  nsIFrame* primaryFrame = AsContent()->GetPrimaryFrame(FlushType::Frames);
+  if (!primaryFrame) {
+    return aRv.ThrowNotFoundError("Node does not have a layout object");
+  }
+  nsRect rect;
+  if (x == -1 && y == -1 && w == -1 && h == -1) {
+    rect = primaryFrame->GetRectRelativeToSelf();
+  } else {
+    rect = nsRect(nsPresContext::CSSPixelsToAppUnits(x),
+                  nsPresContext::CSSPixelsToAppUnits(y),
+                  nsPresContext::CSSPixelsToAppUnits(w),
+                  nsPresContext::CSSPixelsToAppUnits(h));
+  }
+  presShell->ScrollFrameRectIntoView(
+      primaryFrame, rect,
+      ScrollAxis(kScrollToCenter, WhenToScroll::Always),
+      ScrollAxis(kScrollToCenter, WhenToScroll::Always),
+      ScrollFlags::ScrollOverflowHidden);
+  // If a _visual_ scroll update is pending, cancel it; otherwise, it will
+  // clobber next scroll (e.g. subsequent window.scrollTo(0, 0) wlll break).
+  if (presShell->GetPendingVisualScrollUpdate()) {
+    presShell->AcknowledgePendingVisualScrollUpdate();
+    presShell->ClearPendingVisualScrollUpdate();
+  }
+}
+
 already_AddRefed<DOMQuad> nsINode::ConvertQuadFromNode(
     DOMQuad& aQuad, const GeometryNode& aFrom,
     const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
index 8a58b9e21cb6c79a8d28104fef866c9809776bce..1e58cfbfa6ef9f3f5f80055321f21980cda5c093 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -2044,6 +2044,10 @@ class nsINode : public mozilla::dom::EventTarget {
                                    nsTArray<RefPtr<DOMQuad>>& aResult,
                                    ErrorResult& aRv);
 
+  void ScrollRectIntoViewIfNeeded(int32_t x, int32_t y,
+                                  int32_t w, int32_t h,
+                                  ErrorResult& aRv);
+
   already_AddRefed<DOMQuad> ConvertQuadFromNode(
       DOMQuad& aQuad, const TextOrElementOrDocument& aFrom,
       const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp
index 048563cc8e58e282d94bbbd568a7aaa8f655b84f..37ab6d489c1e079bdd5398323db5541cdc88cde7 100644
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -564,6 +564,11 @@ bool nsJSUtils::GetScopeChainForElement(
   return true;
 }
 
+/* static */
+bool nsJSUtils::SetTimeZoneOverride(const char* timezoneId) {
+  return JS::SetTimeZoneOverride(timezoneId);
+}
+
 /* static */
 void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
 
diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
index cd7b816d011e236895cdeff88a96bf75334726d3..60d62ac411b4c4c17bf860f2a29b71802133abb2 100644
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -237,6 +237,7 @@ class nsJSUtils {
       JSContext* aCx, mozilla::dom::Element* aElement,
       JS::MutableHandleVector<JSObject*> aScopeChain);
 
+  static bool SetTimeZoneOverride(const char* timezoneId);
   static void ResetTimeZone();
 
   static bool DumpEnabled();
diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp
index 51c04d2f40f51c9163183559d6a92ea7b0179e17..72084201c77a4dfeabb9a2a6d42a3348b5aa6485 100644
--- a/dom/geolocation/Geolocation.cpp
+++ b/dom/geolocation/Geolocation.cpp
@@ -23,6 +23,7 @@
 #include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
+#include "nsDocShell.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/Document.h"
 #include "nsINamed.h"
@@ -294,10 +295,8 @@ nsGeolocationRequest::Allow(JS::HandleValue aChoices) {
     return NS_OK;
   }
 
-  RefPtr<nsGeolocationService> gs =
-      nsGeolocationService::GetGeolocationService();
-
-  bool canUseCache = false;
+  nsGeolocationService* gs = mLocator->GetGeolocationService();
+  bool canUseCache = gs != nsGeolocationService::sService.get();
   CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
   if (lastPosition.position) {
     DOMTimeStamp cachedPositionTime_ms;
@@ -467,8 +466,7 @@ void nsGeolocationRequest::Shutdown() {
   // If there are no other high accuracy requests, the geolocation service will
   // notify the provider to switch to the default accuracy.
   if (mOptions && mOptions->mEnableHighAccuracy) {
-    RefPtr<nsGeolocationService> gs =
-        nsGeolocationService::GetGeolocationService();
+    nsGeolocationService* gs = mLocator ? mLocator->GetGeolocationService() : nullptr;
     if (gs) {
       gs->UpdateAccuracy();
     }
@@ -745,8 +743,14 @@ void nsGeolocationService::StopDevice() {
 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
 
 already_AddRefed<nsGeolocationService>
-nsGeolocationService::GetGeolocationService() {
+nsGeolocationService::GetGeolocationService(nsDocShell* docShell) {
   RefPtr<nsGeolocationService> result;
+  if (docShell) {
+    result = docShell->GetGeolocationServiceOverride();
+    if (result)
+      return result.forget();
+  }
+
   if (nsGeolocationService::sService) {
     result = nsGeolocationService::sService;
 
@@ -838,7 +842,9 @@ nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) {
   // If no aContentDom was passed into us, we are being used
   // by chrome/c++ and have no mOwner, no mPrincipal, and no need
   // to prompt.
-  mService = nsGeolocationService::GetGeolocationService();
+  nsCOMPtr<Document> doc = aContentDom ? aContentDom->GetDoc() : nullptr;
+  mService = nsGeolocationService::GetGeolocationService(
+      doc ? static_cast<nsDocShell*>(doc->GetDocShell()) : nullptr);
   if (mService) {
     mService->AddLocator(this);
   }
diff --git a/dom/geolocation/Geolocation.h b/dom/geolocation/Geolocation.h
index d92bd1c738016f93c66dbdc449c70937c37b6f9a..a4c1f0ca974470342cb8136705d78cfc00e35083 100644
--- a/dom/geolocation/Geolocation.h
+++ b/dom/geolocation/Geolocation.h
@@ -57,7 +57,7 @@ struct CachedPositionAndAccuracy {
 class nsGeolocationService final : public nsIGeolocationUpdate,
                                    public nsIObserver {
  public:
-  static already_AddRefed<nsGeolocationService> GetGeolocationService();
+  static already_AddRefed<nsGeolocationService> GetGeolocationService(nsDocShell* docShell = nullptr);
   static mozilla::StaticRefPtr<nsGeolocationService> sService;
 
   NS_DECL_THREADSAFE_ISUPPORTS
@@ -182,6 +182,8 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache {
   // null.
   static already_AddRefed<Geolocation> NonWindowSingleton();
 
+  nsGeolocationService* GetGeolocationService() { return mService; };
+
  private:
   ~Geolocation();
 
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index 82a7d54d6e7ae6949422ac82ab36d2d88f407a7c..595067b6954ed53ffe0fb266e7666cc7ec1fd3f1 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -44,6 +44,7 @@
 #include "nsMappedAttributes.h"
 #include "nsIFormControl.h"
 #include "mozilla/dom/Document.h"
+#include "nsDocShell.h"
 #include "nsIFormControlFrame.h"
 #include "nsITextControlFrame.h"
 #include "nsIFrame.h"
@@ -706,6 +707,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
     return NS_ERROR_FAILURE;
   }
 
+  nsDocShell* docShell = static_cast<nsDocShell*>(win->GetDocShell());
+  if (docShell && docShell->IsFileInputInterceptionEnabled()) {
+    docShell->FilePickerShown(this);
+    return NS_OK;
+  }
+
   if (IsPopupBlocked()) {
     return NS_OK;
   }
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
index 2b17323d8a939a18a788d34367a8c427740c0f17..b4764f1c0406b4f93b6936b32104f3de1a813233 100644
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -3569,6 +3569,13 @@ NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress,
   return NS_OK;
 }
 
+NS_IMETHODIMP BrowserChild::OnFrameLocationChange(nsIWebProgress *aWebProgress,
+                                             nsIRequest *aRequest,
+                                             nsIURI *aLocation,
+                                             uint32_t aFlags) {
+  return NS_OK;
+}
+
 NS_IMETHODIMP BrowserChild::OnProgressChange(nsIWebProgress* aWebProgress,
                                              nsIRequest* aRequest,
                                              int32_t aCurSelfProgress,
diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp
index 3f69c0f398ab6d849fe011e836343c09110673ef..4c06a9100a1d41119cb2984ee3734a1043a9c50a 100644
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -142,6 +142,30 @@ ScriptSettingsStackEntry::~ScriptSettingsStackEntry() {
   MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->HasJSGlobal());
 }
 
+static nsIGlobalObject* UnwrapSandboxGlobal(nsIGlobalObject* global) {
+  if (!global)
+    return global;
+  JSObject* globalObject = global->GetGlobalJSObject();
+  if (!globalObject)
+    return global;
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (!cx)
+    return global;
+  JS::Rooted<JSObject*> proto(cx);
+  JS::RootedObject rootedGlobal(cx, globalObject);
+  if (!JS_GetPrototype(cx, rootedGlobal, &proto))
+    return global;
+  if (!proto || !xpc::IsSandboxPrototypeProxy(proto))
+    return global;
+  // If this is a sandbox associated with a DOMWindow via a
+  // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
+  // and JetPack content scripts.
+  proto = js::CheckedUnwrapDynamic(proto, cx, /* stopAtWindowProxy = */ false);
+  if (!proto)
+    return global;
+  return xpc::WindowGlobalOrNull(proto);
+}
+
 // If the entry or incumbent global ends up being something that the subject
 // principal doesn't subsume, we don't want to use it. This never happens on
 // the web, but can happen with asymmetric privilege relationships (i.e.
@@ -169,7 +193,7 @@ static nsIGlobalObject* ClampToSubject(nsIGlobalObject* aGlobalOrNull) {
   NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal());
   if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()
            ->SubsumesConsideringDomain(globalPrin)) {
-    return GetCurrentGlobal();
+    return UnwrapSandboxGlobal(GetCurrentGlobal());
   }
 
   return aGlobalOrNull;
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp
index c553ad7e85e0afe1225a1d349523d067e19d5846..0318ac69ce03a37f0138ea563b11aa567bd56454 100644
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -121,6 +121,11 @@ void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc,
     return;
   }
 
+  if (aDoc.GetDocShell() &&
+      nsDocShell::Cast(aDoc.GetDocShell())->IsBypassCSPEnabled()) {
+    return;
+  }
+
   nsAutoString policyStr(
       nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
           aPolicyStr));
diff --git a/dom/webidl/GeometryUtils.webidl b/dom/webidl/GeometryUtils.webidl
index 2f71b284ee5f7e11f117c447834b48355784448c..d996e0a3cbbb19c1dc320c305c6d74037bffa0d3 100644
--- a/dom/webidl/GeometryUtils.webidl
+++ b/dom/webidl/GeometryUtils.webidl
@@ -27,6 +27,9 @@ interface mixin GeometryUtils {
   [Throws, Func="nsINode::HasBoxQuadsSupport", NeedsCallerType]
   sequence<DOMQuad> getBoxQuads(optional BoxQuadOptions options = {});
 
+  [ChromeOnly, Throws, Func="nsINode::HasBoxQuadsSupport"]
+  void scrollRectIntoViewIfNeeded(long x, long y, long w, long h);
+
   /* getBoxQuadsFromWindowOrigin is similar to getBoxQuads, but the
    * returned quads are further translated relative to the window
    * origin -- which is not the layout origin. Further translation
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 50db4fa62bc99a71c4f1e37f622dd71b5bbdc6cd..3b0bc9f7633212a27a43bafcb4e3c53709dee990 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1019,7 +1019,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
   AssertIsOnMainThread();
 
   nsTArray<nsString> languages;
-  Navigator::GetAcceptLanguages(languages);
+  Navigator::GetAcceptLanguages(nullptr, languages);
 
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
@@ -1218,8 +1218,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate* aWorkerPrivate) {
       }
 
       // The navigator overridden properties should have already been read.
-
-      Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
+      Navigator::GetAcceptLanguages(nullptr, mNavigatorProperties.mLanguages);
       mNavigatorPropertiesLoaded = true;
     }
 
@@ -1969,6 +1968,11 @@ void RuntimeService::PropagateFirstPartyStorageAccessGranted(
   }
 }
 
+void RuntimeService::ResetDefaultLocaleInAllWorkers() {
+  AssertIsOnMainThread();
+  BROADCAST_ALL_WORKERS(ResetDefaultLocale);
+}
+
 void RuntimeService::NoteIdleThread(WorkerThread* aThread) {
   AssertIsOnMainThread();
   MOZ_ASSERT(aThread);
@@ -2383,6 +2387,14 @@ void PropagateFirstPartyStorageAccessGrantedToWorkers(
   }
 }
 
+void ResetDefaultLocaleInAllWorkers() {
+  AssertIsOnMainThread();
+  RuntimeService* runtime = RuntimeService::GetService();
+  if (runtime) {
+    runtime->ResetDefaultLocaleInAllWorkers();
+  }
+}
+
 WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aCx);
diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h
index 9321a7abfe68483dbf013ce05418a6474d20103e..4800763806d8d3bb665d4ae912cf916292838ffc 100644
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -117,6 +117,8 @@ class RuntimeService final : public nsIObserver {
 
   void PropagateFirstPartyStorageAccessGranted(nsPIDOMWindowInner* aWindow);
 
+  void ResetDefaultLocaleInAllWorkers();
+
   const NavigatorProperties& GetNavigatorProperties() const {
     return mNavigatorProperties;
   }
diff --git a/dom/workers/WorkerCommon.h b/dom/workers/WorkerCommon.h
index f5e5c232d424e25607fb2fcf089c747708e02104..ada9c56f9d31d8d1c7c4c918403f14279358a4a8 100644
--- a/dom/workers/WorkerCommon.h
+++ b/dom/workers/WorkerCommon.h
@@ -47,6 +47,8 @@ void ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow);
 void PropagateFirstPartyStorageAccessGrantedToWorkers(
     nsPIDOMWindowInner* aWindow);
 
+void ResetDefaultLocaleInAllWorkers();
+
 // All of these are implemented in WorkerScope.cpp
 
 bool IsWorkerGlobal(JSObject* global);
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index a0ab199295e766ff336840cba9644fd44b9011a6..add07f04046105b96bcade37980444982660af47 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -651,6 +651,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
   }
 };
 
+class ResetDefaultLocaleRunnable final : public WorkerControlRunnable {
+ public:
+  explicit ResetDefaultLocaleRunnable(WorkerPrivate* aWorkerPrivate)
+      : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
+
+  virtual bool WorkerRun(JSContext* aCx,
+                         WorkerPrivate* aWorkerPrivate) override {
+    aWorkerPrivate->ResetDefaultLocaleInternal(aCx);
+    return true;
+  }
+};
+
 class UpdateLanguagesRunnable final : public WorkerRunnable {
   nsTArray<nsString> mLanguages;
 
@@ -1830,6 +1842,16 @@ void WorkerPrivate::UpdateContextOptions(
   }
 }
 
+void WorkerPrivate::ResetDefaultLocale() {
+  AssertIsOnParentThread();
+
+  RefPtr<ResetDefaultLocaleRunnable> runnable =
+      new ResetDefaultLocaleRunnable(this);
+  if (!runnable->Dispatch()) {
+    NS_WARNING("Failed to reset default locale in worker!");
+  }
+}
+
 void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
   AssertIsOnParentThread();
 
@@ -4790,6 +4812,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
   }
 }
 
+void WorkerPrivate::ResetDefaultLocaleInternal(JSContext* aCx) {
+  JS_ResetDefaultLocale(JS_GetRuntime(aCx));
+
+  MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
+  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
+    data->mChildWorkers[index]->ResetDefaultLocale();
+  }
+}
+
 void WorkerPrivate::UpdateLanguagesInternal(
     const nsTArray<nsString>& aLanguages) {
   WorkerGlobalScope* globalScope = GlobalScope();
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index 82d04ec3a72f3390a43c98eb2ae91f0d15a83631..b197bb0718ed712af2a7c08d2ef691de1bde1d98 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -297,6 +297,8 @@ class WorkerPrivate : public RelativeTimeline {
   void UpdateContextOptionsInternal(JSContext* aCx,
                                     const JS::ContextOptions& aContextOptions);
 
+  void ResetDefaultLocaleInternal(JSContext* aCx);
+
   void UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
 
   void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key,
@@ -875,6 +877,8 @@ class WorkerPrivate : public RelativeTimeline {
 
   void UpdateContextOptions(const JS::ContextOptions& aContextOptions);
 
+  void ResetDefaultLocale();
+
   void UpdateLanguages(const nsTArray<nsString>& aLanguages);
 
   void UpdateJSWorkerMemoryParameter(JSGCParamKey key, Maybe<uint32_t> value);
diff --git a/js/public/Date.h b/js/public/Date.h
index e7a54d86c44499a3ec2adf1c156b9f9dfb0bc6b4..f56c1b419c4cb52bc371f6b8dbfffba464326fc4 100644
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -56,6 +56,8 @@ namespace JS {
  */
 extern JS_PUBLIC_API void ResetTimeZone();
 
+extern JS_PUBLIC_API bool SetTimeZoneOverride(const char* timezoneId);
+
 class ClippedTime;
 inline ClippedTime TimeClip(double time);
 
diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp
index add42a1c2da3fca6a4469435498a4c6d824d8f47..72a7f8eff3302586d6567287380ec5de9c1c5165 100644
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -2385,7 +2385,11 @@ Maybe<Completion> DebuggerObject::call(JSContext* cx,
         invokeArgs[i].set(args2[i]);
       }
 
+      // Disable CSP for the scope of the call.
+      const JSSecurityCallbacks* securityCallbacks = JS_GetSecurityCallbacks(cx);
+      JS_SetSecurityCallbacks(cx, nullptr);
       ok = js::Call(cx, calleev, thisv, invokeArgs, &result);
+      JS_SetSecurityCallbacks(cx, securityCallbacks);
     }
   }
 
diff --git a/js/src/vm/DateTime.cpp b/js/src/vm/DateTime.cpp
index d27ba46016e0a01bd57dde7acc219eaaee9e65ca..3cfa6ec99272339238a4494b62b008de7036d426 100644
--- a/js/src/vm/DateTime.cpp
+++ b/js/src/vm/DateTime.cpp
@@ -168,6 +168,11 @@ void js::DateTimeInfo::internalResetTimeZone(ResetTimeZoneMode mode) {
   }
 }
 
+void js::DateTimeInfo::internalSetTimeZoneOverride(mozilla::UniquePtr<icu::TimeZone> timeZone) {
+  timeZoneOverride_ = std::move(timeZone);
+  internalResetTimeZone(ResetTimeZoneMode::ResetEvenIfOffsetUnchanged);
+}
+
 void js::DateTimeInfo::updateTimeZone() {
   MOZ_ASSERT(timeZoneStatus_ != TimeZoneStatus::Valid);
 
@@ -528,10 +533,27 @@ void js::ResetTimeZoneInternal(ResetTimeZoneMode mode) {
   js::DateTimeInfo::resetTimeZone(mode);
 }
 
+void js::SetTimeZoneOverrideInternal(mozilla::UniquePtr<icu::TimeZone> timeZone) {
+  auto guard = js::DateTimeInfo::instance->lock();
+  guard->internalSetTimeZoneOverride(std::move(timeZone));
+}
+
 JS_PUBLIC_API void JS::ResetTimeZone() {
   js::ResetTimeZoneInternal(js::ResetTimeZoneMode::ResetEvenIfOffsetUnchanged);
 }
 
+JS_PUBLIC_API bool JS::SetTimeZoneOverride(const char* timeZoneId) {
+  // Validate timezone id.
+  mozilla::UniquePtr<icu::TimeZone> timeZone(icu::TimeZone::createTimeZone(
+      icu::UnicodeString(timeZoneId, -1, US_INV)));
+  if (!timeZone || *timeZone == icu::TimeZone::getUnknown()) {
+    fprintf(stderr, "Invalid timezone id: %s\n", timeZoneId);
+    return false;
+  }
+  js::SetTimeZoneOverrideInternal(std::move(timeZone));
+  return true;
+}
+
 #if defined(XP_WIN)
 static bool IsOlsonCompatibleWindowsTimeZoneId(const char* tz) {
   // ICU ignores the TZ environment variable on Windows and instead directly
@@ -726,6 +748,11 @@ void js::ResyncICUDefaultTimeZone() {
 
 void js::DateTimeInfo::internalResyncICUDefaultTimeZone() {
 #if JS_HAS_INTL_API && !MOZ_SYSTEM_ICU
+  if (timeZoneOverride_) {
+    icu::TimeZone::setDefault(*timeZoneOverride_);
+    return;
+  }
+
   if (const char* tz = std::getenv("TZ")) {
     icu::UnicodeString tzid;
 
diff --git a/js/src/vm/DateTime.h b/js/src/vm/DateTime.h
index 25c5b01fc54c8d45da8ceb7cf6ba163bee3c5361..490c5ce49cd9b5f804df59abbfb0450fb9d1f877 100644
--- a/js/src/vm/DateTime.h
+++ b/js/src/vm/DateTime.h
@@ -67,6 +67,8 @@ enum class ResetTimeZoneMode : bool {
  */
 extern void ResetTimeZoneInternal(ResetTimeZoneMode mode);
 
+extern void SetTimeZoneOverrideInternal(mozilla::UniquePtr<icu::TimeZone> timeZone);
+
 /**
  * ICU's default time zone, used for various date/time formatting operations
  * that include the local time in the representation, is allowed to go stale
@@ -206,6 +208,7 @@ class DateTimeInfo {
   // and js::ResyncICUDefaultTimeZone().
   friend void js::ResetTimeZoneInternal(ResetTimeZoneMode);
   friend void js::ResyncICUDefaultTimeZone();
+  friend void js::SetTimeZoneOverrideInternal(mozilla::UniquePtr<icu::TimeZone>);
 
   static void resetTimeZone(ResetTimeZoneMode mode) {
     auto guard = instance->lock();
@@ -302,6 +305,8 @@ class DateTimeInfo {
   JS::UniqueChars locale_;
   JS::UniqueTwoByteChars standardName_;
   JS::UniqueTwoByteChars daylightSavingsName_;
+
+  mozilla::UniquePtr<icu::TimeZone> timeZoneOverride_;
 #else
   // Restrict the data-time range to the minimum required time_t range as
   // specified in POSIX. Most operating systems support 64-bit time_t
@@ -317,6 +322,8 @@ class DateTimeInfo {
 
   void internalResetTimeZone(ResetTimeZoneMode mode);
 
+  void internalSetTimeZoneOverride(mozilla::UniquePtr<icu::TimeZone> timeZone);
+
   void updateTimeZone();
 
   void internalResyncICUDefaultTimeZone();
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index d315012c8ae284fdb0df50efd672ec4cbd77f2ad..baddcde3fd380c886a53056917841cf281d12277 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -1091,9 +1091,12 @@ void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
   if (!StaticPrefs::security_csp_enable()) {
     return;
   }
-
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
+  if (mDocShell && static_cast<nsDocShell*>(mDocShell.get())->IsBypassCSPEnabled()) {
+    return;
+  }
+
   nsresult rv = NS_OK;
   nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = mDocument->GetPreloadCsp();
   if (!preloadCsp) {
diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp
index 429b216be8b54b7c1a47d41df7d78953899a61ae..a5afb34a3df138d37df9be80e1fd40d2b7a0f98c 100644
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1185,8 +1185,8 @@ PRErrorCode AuthCertificateParseResults(
         return SEC_ERROR_NO_MEMORY;
       }
       nsresult rv = overrideService->HasMatchingOverride(
-          aHostName, aPort, nssCert, &overrideBits, &isTemporaryOverride,
-          &haveOverride);
+          aHostName, aPort, aOriginAttributes.mUserContextId, nssCert,
+          &overrideBits, &isTemporaryOverride, &haveOverride);
       if (NS_SUCCEEDED(rv) && haveOverride) {
         // remove the errors that are already overriden
         remainingDisplayErrors &= ~overrideBits;
diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp
index d6b9e8f606b4d4871793d4c8ca85d7bfe0e86f37..fed251dec07057bd9a9398023e47bef5bd2ef6c4 100644
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -413,13 +413,20 @@ nsCertOverrideService::RememberTemporaryValidityOverrideUsingFingerprint(
 
 NS_IMETHODIMP
 nsCertOverrideService::HasMatchingOverride(const nsACString& aHostName,
-                                           int32_t aPort, nsIX509Cert* aCert,
+                                           int32_t aPort,
+                                           uint32_t aUserContextId,
+                                           nsIX509Cert* aCert,
                                            uint32_t* aOverrideBits,
                                            bool* aIsTemporary, bool* _retval) {
   bool disableAllSecurityCheck = false;
   {
     MutexAutoLock lock(mMutex);
-    disableAllSecurityCheck = mDisableAllSecurityCheck;
+    if (aUserContextId) {
+      disableAllSecurityCheck = mUserContextIdsWithDisabledSecurityChecks.has(
+          aUserContextId);
+    } else {
+      disableAllSecurityCheck = mDisableAllSecurityCheck;
+    }
   }
   if (disableAllSecurityCheck) {
     nsCertOverride::OverrideBits all = nsCertOverride::OverrideBits::Untrusted |
@@ -632,12 +639,21 @@ static bool IsDebugger() {
 
 NS_IMETHODIMP
 nsCertOverrideService::
-    SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable) {
-  if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
+    SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(
+      bool aDisable, uint32_t aUserContextId) {
+  if (false /* juggler hacks */ && !(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   MutexAutoLock lock(mMutex);
+  if (aUserContextId) {
+    if (aDisable) {
+      mozilla::Unused << mUserContextIdsWithDisabledSecurityChecks.put(aUserContextId);
+    } else {
+      mUserContextIdsWithDisabledSecurityChecks.remove(aUserContextId);
+    }
+    return NS_OK;
+  }
   mDisableAllSecurityCheck = aDisable;
   return NS_OK;
 }
diff --git a/security/manager/ssl/nsCertOverrideService.h b/security/manager/ssl/nsCertOverrideService.h
index a9fd392c18e330c107a5b2cf9720a9b6f6abd3db..ac2e2739ab2fffddea417dd73fba76d3dc639490 100644
--- a/security/manager/ssl/nsCertOverrideService.h
+++ b/security/manager/ssl/nsCertOverrideService.h
@@ -120,6 +120,7 @@ class nsCertOverrideService final : public nsICertOverrideService,
   ~nsCertOverrideService();
 
   bool mDisableAllSecurityCheck;
+  mozilla::HashSet<uint32_t> mUserContextIdsWithDisabledSecurityChecks;
   mozilla::Mutex mMutex;
   nsCOMPtr<nsIFile> mSettingsFile;
   nsTHashtable<nsCertOverrideEntry> mSettingsTable;
diff --git a/security/manager/ssl/nsICertOverrideService.idl b/security/manager/ssl/nsICertOverrideService.idl
index 6f0f8259b309c0a299c9c80b2943a498b0f1b0e6..03d17899be96bc87dc78f06277e1bd9eb93d08f8 100644
--- a/security/manager/ssl/nsICertOverrideService.idl
+++ b/security/manager/ssl/nsICertOverrideService.idl
@@ -98,6 +98,7 @@ interface nsICertOverrideService : nsISupports {
   [must_use]
   boolean hasMatchingOverride(in AUTF8String aHostName,
                               in int32_t aPort,
+                              in uint32_t aUserContextId,
                               in nsIX509Cert aCert,
                               out uint32_t aOverrideBits,
                               out boolean aIsTemporary);
@@ -137,5 +138,7 @@ interface nsICertOverrideService : nsISupports {
    *  @param aDisable If true, disable all security check and make
    *                  hasMatchingOverride always return true.
    */
-  void setDisableAllSecurityChecksAndLetAttackersInterceptMyData(in boolean aDisable);
+  void setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
+      in boolean aDisable,
+      [optional] in uint32_t aUserContextId);
 };
diff --git a/services/settings/Utils.jsm b/services/settings/Utils.jsm
index be2dafd39c1e9be513e910359841ffa6b3374ec7..665a7ccbbf5d5fb6ccb77da30c3e23ebf691b940 100644
--- a/services/settings/Utils.jsm
+++ b/services/settings/Utils.jsm
@@ -55,7 +55,7 @@ var Utils = {
       Ci.nsIEnvironment
     );
     const isXpcshell = env.exists("XPCSHELL_TEST_PROFILE_DIR");
-    return AppConstants.RELEASE_OR_BETA && !Cu.isInAutomation && !isXpcshell
+    return false && !Cu.isInAutomation && !isXpcshell
       ? "https://firefox.settings.services.mozilla.com/v1"
       : gServerURL;
   },
diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp
index 73d33f54032c0ec785145214fe572d6655ca12b2..87daad40c1d811b75e3b8acab6837469e8443383 100644
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -335,7 +335,7 @@ nsAppStartup::Quit(uint32_t aMode) {
     nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
     nsCOMPtr<nsIWindowMediator> mediator(
         do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
-    if (mediator) {
+    if (ferocity != eForceQuit && mediator) {
       mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
       if (windowEnumerator) {
         bool more;
diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
index ba7205c529c402512532c840225a535ce14fed5d..4597d7e950313dff1888aaf35cab49f04ae506bd 100644
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -176,8 +176,16 @@ nsBrowserStatusFilter::OnStateChange(nsIWebProgress* aWebProgress,
 }
 
 NS_IMETHODIMP
-nsBrowserStatusFilter::OnProgressChange(nsIWebProgress* aWebProgress,
-                                        nsIRequest* aRequest,
+nsBrowserStatusFilter::OnFrameLocationChange(nsIWebProgress *aWebProgress,
+                                             nsIRequest *aRequest,
+                                             nsIURI *aLocation,
+                                             uint32_t aFlags) {
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBrowserStatusFilter::OnProgressChange(nsIWebProgress *aWebProgress,
+                                        nsIRequest *aRequest,
                                         int32_t aCurSelfProgress,
                                         int32_t aMaxSelfProgress,
                                         int32_t aCurTotalProgress,
diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm
index c01d6aadef1ff34ac255d0ad5851b32f4c7a7f0b..452d5ccb8f0480d82f04793f200ea903b869cca7 100644
--- a/toolkit/mozapps/update/UpdateService.jsm
+++ b/toolkit/mozapps/update/UpdateService.jsm
@@ -3076,7 +3076,7 @@ UpdateService.prototype = {
       ).running;
     }
 
-    return (
+    return true || (
       (Cu.isInAutomation || marionetteRunning) &&
       Services.prefs.getBoolPref(PREF_APP_UPDATE_DISABLEDFORTESTING, false)
     );
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
index d092b8b223464ce9218272c81e75a77d38730dbb..bf80011e2cd59bd58b4f23788a56756d04b58a35 100644
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -167,6 +167,7 @@ if CONFIG['ENABLE_MARIONETTE']:
     DIRS += [
         '/testing/firefox-ui',
         '/testing/marionette',
+        '/juggler',
         '/toolkit/components/telemetry/tests/marionette',
     ]
 
diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp
index 06b2b7936d1833c482d894f3efa085173278a8da..f66866c8ff2918d7c8c5be9fc100d00b84f95db9 100644
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -779,6 +779,13 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout) {
                         ("DocLoader:%p: Firing load event for document.open\n",
                          this));
 
+                nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+                if (os) {
+                  nsIPrincipal* principal = doc->NodePrincipal();
+                  if (!principal->IsSystemPrincipal())
+                    os->NotifyObservers(ToSupports(doc), "juggler-document-open-loaded", nullptr);
+                }
+
                 // This is a very cut-down version of
                 // nsDocumentViewer::LoadComplete that doesn't do various things
                 // that are not relevant here because this wasn't an actual
@@ -1385,6 +1392,24 @@ void nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
   }
 }
 
+void nsDocLoader::FireOnFrameLocationChange(nsIWebProgress* aWebProgress,
+                                       nsIRequest* aRequest,
+                                       nsIURI *aUri,
+                                       uint32_t aFlags) {
+  NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_FRAME_LOCATION,
+    nsCOMPtr<nsIWebProgressListener2> listener2 =
+      do_QueryReferent(info.mWeakListener);
+    if (!listener2)
+      continue;
+    listener2->OnFrameLocationChange(aWebProgress, aRequest, aUri, aFlags);
+  );
+
+  // Pass the notification up to the parent...
+  if (mParent) {
+    mParent->FireOnFrameLocationChange(aWebProgress, aRequest, aUri, aFlags);
+  }
+}
+
 void nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
                                      nsIRequest* aRequest, nsresult aStatus,
                                      const char16_t* aMessage) {
diff --git a/uriloader/base/nsDocLoader.h b/uriloader/base/nsDocLoader.h
index 5b735c57cedcec62d185a256c945c72371822df6..079245720df84c9f7722ceae6502db58479454ae 100644
--- a/uriloader/base/nsDocLoader.h
+++ b/uriloader/base/nsDocLoader.h
@@ -209,6 +209,11 @@ class nsDocLoader : public nsIDocumentLoader,
                                       nsIURI* aURI, int32_t aDelay,
                                       bool aSameURI);
 
+  void FireOnFrameLocationChange(nsIWebProgress* aWebProgress,
+                               nsIRequest* aRequest,
+                               nsIURI *aUri,
+                               uint32_t aFlags);
+
   // this function is overridden by the docshell, it is provided so that we
   // can pass more information about redirect state (the normal OnStateChange
   // doesn't get the new channel).
diff --git a/uriloader/base/nsIWebProgress.idl b/uriloader/base/nsIWebProgress.idl
index b0cde5026dc7c414e8f20300ac2b7d735dbd846e..09ebb0ef6799cf6a74fe529d4d000c6bed2c9497 100644
--- a/uriloader/base/nsIWebProgress.idl
+++ b/uriloader/base/nsIWebProgress.idl
@@ -87,6 +87,10 @@ interface nsIWebProgress : nsISupports
    * NOTIFY_REFRESH
    *   Receive onRefreshAttempted events.
    *   This is defined on nsIWebProgressListener2.
+   *
+   * NOTIFY_FRAME_LOCATION
+   *   Receive onFrameLocationChange events.
+   *   This is defined on nsIWebProgressListener2.
    */
   const unsigned long NOTIFY_PROGRESS         = 0x00000010;
   const unsigned long NOTIFY_STATUS           = 0x00000020;
@@ -94,11 +98,12 @@ interface nsIWebProgress : nsISupports
   const unsigned long NOTIFY_LOCATION         = 0x00000080;
   const unsigned long NOTIFY_REFRESH          = 0x00000100;
   const unsigned long NOTIFY_CONTENT_BLOCKING = 0x00000200;
+  const unsigned long NOTIFY_FRAME_LOCATION   = 0x00000400;
 
   /**
    * This flag enables all notifications.
    */
-  const unsigned long NOTIFY_ALL              = 0x000003ff;
+  const unsigned long NOTIFY_ALL              = 0x000007ff;
 
   /**
    * Registers a listener to receive web progress events.
diff --git a/uriloader/base/nsIWebProgressListener2.idl b/uriloader/base/nsIWebProgressListener2.idl
index 87701f8d2cfee8bd84acd28c62b3be4989c9474c..ae1aa85c019cb21d4f7e79c35e8afe72709468a1 100644
--- a/uriloader/base/nsIWebProgressListener2.idl
+++ b/uriloader/base/nsIWebProgressListener2.idl
@@ -66,4 +66,27 @@ interface nsIWebProgressListener2 : nsIWebProgressListener {
                              in nsIURI aRefreshURI,
                              in long aMillis,
                              in boolean aSameURI);
+
+  /**
+   * Called when the location of the window or its subframes changes.  This is not
+   * when a load is requested, but rather once it is verified that the load is
+   * going to occur in the given window.  For instance, a load that starts in a
+   * window might send progress and status messages for the new site, but it
+   * will not send the onLocationChange until we are sure that we are loading
+   * this new page here.
+   *
+   * @param aWebProgress
+   *        The nsIWebProgress instance that fired the notification.
+   * @param aRequest
+   *        The associated nsIRequest.  This may be null in some cases.
+   * @param aLocation
+   *        The URI of the location that is being loaded.
+   * @param aFlags
+   *        This is a value which explains the situation or the reason why
+   *        the location has changed.
+   */
+  void onFrameLocationChange(in nsIWebProgress aWebProgress,
+                             in nsIRequest aRequest,
+                             in nsIURI aLocation,
+                             [optional] in unsigned long aFlags);
 };
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
index 0808085752eab5ad43bc54d2fac8771fc6d2a1d1..cea94060a58b6c58848d6c6beb54d1108eba168e 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -99,6 +99,7 @@
 
 #include "mozilla/Components.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ErrorNames.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ipc/URIUtils.h"
 
@@ -840,6 +841,12 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(
   return NS_OK;
 }
 
+NS_IMETHODIMP nsExternalHelperAppService::SetDownloadInterceptor(
+    nsIDownloadInterceptor* interceptor) {
+  mInterceptor = interceptor;
+  return NS_OK;
+}
+
 nsresult nsExternalHelperAppService::GetFileTokenForPath(
     const char16_t* aPlatformAppPath, nsIFile** aFile) {
   nsDependentString platformAppPath(aPlatformAppPath);
@@ -1428,7 +1435,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) {
   // Strip off the ".part" from mTempLeafName
   mTempLeafName.Truncate(mTempLeafName.Length() - ArrayLength(".part") + 1);
 
+  return CreateSaverForTempFile();
+}
+
+nsresult nsExternalAppHandler::CreateSaverForTempFile() {
   MOZ_ASSERT(!mSaver, "Output file initialization called more than once!");
+  nsresult rv;
   mSaver =
       do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1588,7 +1600,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
     return NS_OK;
   }
 
-  rv = SetUpTempFile(aChannel);
+  bool isIntercepted = false;
+  nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+  if (interceptor) {
+    nsCOMPtr<nsIFile> fileToUse;
+    rv = interceptor->InterceptDownloadRequest(this, request, mBrowsingContext, getter_AddRefs(fileToUse), &isIntercepted);
+    if (!NS_SUCCEEDED(rv)) {
+      LOG(("    failed to call nsIDowloadInterceptor.interceptDownloadRequest"));
+      return rv;
+    }
+    if (isIntercepted) {
+      LOG(("    request interceped by nsIDowloadInterceptor"));
+      if (fileToUse) {
+        mTempFile = fileToUse;
+        rv = mTempFile->GetLeafName(mTempLeafName);
+        NS_ENSURE_SUCCESS(rv, rv);
+      } else {
+        Cancel(NS_BINDING_ABORTED);
+        return NS_OK;
+      }
+    }
+  }
+
+  // Temp file is the final destination when download is intercepted. In that
+  // case we only need to create saver (and not create transfer later). Not creating
+  // mTransfer also cuts off all downloads handling logic in the js compoenents and
+  // browser UI.
+  if (isIntercepted)
+    rv = CreateSaverForTempFile();
+  else
+    rv = SetUpTempFile(aChannel);
   if (NS_FAILED(rv)) {
     nsresult transferError = rv;
 
@@ -1636,6 +1677,11 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
   mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
   nsAutoCString MIMEType;
   mMimeInfo->GetMIMEType(MIMEType);
+
+  if (isIntercepted) {
+    return NS_OK;
+  }
+
   if (alwaysAsk) {
     // But we *don't* ask if this mimeInfo didn't come from
     // our user configuration datastore and the user has said
@@ -2043,6 +2089,15 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
     NotifyTransfer(aStatus);
   }
 
+  if (!mCanceled) {
+    nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+    if (interceptor) {
+      nsCString noError;
+      nsresult rv = interceptor->OnDownloadComplete(this, noError);
+      MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to call nsIDowloadInterceptor.OnDownloadComplete");
+    }
+  }
+
   return NS_OK;
 }
 
@@ -2422,6 +2477,14 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
     }
   }
 
+  nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+  if (interceptor) {
+    nsCString errorName;
+    GetErrorName(aReason, errorName);
+    nsresult rv = interceptor->OnDownloadComplete(this, errorName);
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed notify nsIDowloadInterceptor about cancel");
+  }
+
   // Break our reference cycle with the helper app dialog (set up in
   // OnStartRequest)
   mDialog = nullptr;
diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h
index 566d14ee83adfedd3fe7a0f985356602f131241b..51eb63f74a171370a1b40a4c978c50d6ca2c288e 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -190,6 +190,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService,
       mozilla::dom::BrowsingContext* aContentContext, bool aForceSave,
       nsIInterfaceRequestor* aWindowContext,
       nsIStreamListener** aStreamListener);
+
+  nsCOMPtr<nsIDownloadInterceptor> mInterceptor;
 };
 
 /**
@@ -372,6 +374,9 @@ class nsExternalAppHandler final : public nsIStreamListener,
    * Upon successful return, both mTempFile and mSaver will be valid.
    */
   nsresult SetUpTempFile(nsIChannel* aChannel);
+
+  nsresult CreateSaverForTempFile();
+
   /**
    * When we download a helper app, we are going to retarget all load
    * notifications into our own docloader and load group instead of
diff --git a/uriloader/exthandler/nsIExternalHelperAppService.idl b/uriloader/exthandler/nsIExternalHelperAppService.idl
index ea8b9b08f3e6f6e99b8a4fa3fa427beb8c5f5945..a7ec2bd3afe53d500f0cd8f800223ee291340c18 100644
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -6,6 +6,8 @@
 
 #include "nsICancelable.idl"
 
+webidl BrowsingContext;
+interface nsIHelperAppLauncher;
 interface nsIURI;
 interface nsIRequest;
 interface nsIStreamListener;
@@ -20,6 +22,17 @@ webidl BrowsingContext;
 class nsExternalAppHandler;
 %}
 
+/**
+ * Interceptor interface used by Juggler.
+ */
+[scriptable, uuid(9a20e9b0-75d0-11ea-bc55-0242ac130003)]
+interface nsIDownloadInterceptor : nsISupports
+{
+  bool interceptDownloadRequest(in nsIHelperAppLauncher aHandler, in nsIRequest aRequest, in BrowsingContext aBrowsingContext, out nsIFile file);
+
+  void onDownloadComplete(in nsIHelperAppLauncher aHandler, in ACString aErrorName);
+};
+
 /**
  * The external helper app service is used for finding and launching
  * platform specific external applications for a given mime content type.
@@ -48,7 +61,7 @@ interface nsIExternalHelperAppService : nsISupports
                                in nsIInterfaceRequestor aContentContext,
                                in boolean aForceSave,
                                [optional] in nsIInterfaceRequestor aWindowContext);
-  
+
   /**
    * Binds an external helper application to a stream listener. The caller
    * should pump data into the returned stream listener. When the OnStopRequest
@@ -81,6 +94,7 @@ interface nsIExternalHelperAppService : nsISupports
   boolean applyDecodingForExtension(in AUTF8String aExtension,
                                     in ACString aEncodingType);
 
+  void setDownloadInterceptor(in nsIDownloadInterceptor interceptor);
 };
 
 /**
