mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3283 lines
		
	
	
		
			130 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			3283 lines
		
	
	
		
			130 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h
 | |
| index 137963f1170927ae0262e0dc26ef721d496376f4..41fa27bc4a3da41814a7f326792990df3424e81f 100644
 | |
| --- a/accessible/base/NotificationController.h
 | |
| +++ b/accessible/base/NotificationController.h
 | |
| @@ -244,6 +244,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 1886621c373fe1fd5ff54092afc4c64e9ca9a8fd..a0febf72885410b45227171c63e823eca118cb37 100644
 | |
| --- a/accessible/interfaces/nsIAccessibleDocument.idl
 | |
| +++ b/accessible/interfaces/nsIAccessibleDocument.idl
 | |
| @@ -67,4 +67,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 d616e476b2149de5703077563680905e40db0459..7a8a48d5e7303a298a3e2e9fdf64558b3cdbe654 100644
 | |
| --- a/accessible/xpcom/xpcAccessibleDocument.cpp
 | |
| +++ b/accessible/xpcom/xpcAccessibleDocument.cpp
 | |
| @@ -131,6 +131,13 @@ xpcAccessibleDocument::GetChildDocumentAt(uint32_t aIndex,
 | |
|    return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
 | |
|  }
 | |
|  
 | |
| +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 8e9bf2b413585b5a3db9370eee5d57fb6c6716ed..5a3b194b54e3813c89989f13a214c989a409f0f6 100644
 | |
| --- a/accessible/xpcom/xpcAccessibleDocument.h
 | |
| +++ b/accessible/xpcom/xpcAccessibleDocument.h
 | |
| @@ -47,6 +47,8 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
 | |
|    NS_IMETHOD GetChildDocumentAt(uint32_t aIndex,
 | |
|                                  nsIAccessibleDocument** aDocument) final;
 | |
|  
 | |
| +  NS_IMETHOD GetIsUpdatePendingForJugglerAccessibility(bool* aUpdating) final;
 | |
| +
 | |
|    /**
 | |
|     * Return XPCOM wrapper for the internal accessible.
 | |
|     */
 | |
| diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
 | |
| index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e412493e43b14e 100644
 | |
| --- a/browser/app/winlauncher/LauncherProcessWin.cpp
 | |
| +++ b/browser/app/winlauncher/LauncherProcessWin.cpp
 | |
| @@ -22,6 +22,7 @@
 | |
|  #include "mozilla/WinHeaderOnlyUtils.h"
 | |
|  #include "nsWindowsHelpers.h"
 | |
|  
 | |
| +#include <io.h>
 | |
|  #include <windows.h>
 | |
|  #include <processthreadsapi.h>
 | |
|  
 | |
| @@ -421,8 +422,18 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
 | |
|    HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE),
 | |
|                           ::GetStdHandle(STD_OUTPUT_HANDLE),
 | |
|                           ::GetStdHandle(STD_ERROR_HANDLE)};
 | |
| -
 | |
|    attrs.AddInheritableHandles(stdHandles);
 | |
| +  // Playwright pipe installation.
 | |
| +  bool hasJugglerPipe =
 | |
| +      mozilla::CheckArg(argc, argv, "juggler-pipe", nullptr,
 | |
| +                        mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND;
 | |
| +  if (hasJugglerPipe) {
 | |
| +    intptr_t stdio3 = _get_osfhandle(3);
 | |
| +    intptr_t stdio4 = _get_osfhandle(4);
 | |
| +    HANDLE pipeHandles[] = {reinterpret_cast<HANDLE>(stdio3),
 | |
| +                            reinterpret_cast<HANDLE>(stdio4)};
 | |
| +    attrs.AddInheritableHandles(pipeHandles);
 | |
| +  }
 | |
|  
 | |
|    DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
 | |
|  
 | |
| diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
 | |
| index 213a99ed433d5219c2b9a64baad82d14cdbcd432..ee4f6484cdfe80899c28a1d9607494e520bfc93d 100644
 | |
| --- a/browser/installer/allowed-dupes.mn
 | |
| +++ b/browser/installer/allowed-dupes.mn
 | |
| @@ -67,6 +67,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt
 | |
|  removed-files
 | |
|  #endif
 | |
|  
 | |
| +# 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
 | |
| +
 | |
|  # Bug 1606928 - There's no reliable way to connect Top Sites favicons with the favicons in the Search Service
 | |
|  browser/chrome/browser/content/activity-stream/data/content/tippytop/favicons/allegro-pl.ico
 | |
|  browser/defaults/settings/main/search-config-icons/96327a73-c433-5eb4-a16d-b090cadfb80b
 | |
| diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
 | |
| index da760e143740a166df14d055cf3ec7b095b93d10..a7579b3eae69f3b706094693d9b0edaec049e83b 100644
 | |
| --- a/browser/installer/package-manifest.in
 | |
| +++ b/browser/installer/package-manifest.in
 | |
| @@ -189,6 +189,9 @@
 | |
|  @RESPATH@/chrome/remote.manifest
 | |
|  #endif
 | |
|  
 | |
| +@RESPATH@/chrome/juggler@JAREXT@
 | |
| +@RESPATH@/chrome/juggler.manifest
 | |
| +
 | |
|  ; [Extensions]
 | |
|  @RESPATH@/components/extensions-toolkit.manifest
 | |
|  @RESPATH@/browser/components/extensions-browser.manifest
 | |
| diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js
 | |
| index d49c6fbf1bf83b832795fa674f6b41f223eef812..7ea3540947ff5f61b15f27fbf4b955649f8e9ff9 100644
 | |
| --- a/devtools/server/socket/websocket-server.js
 | |
| +++ b/devtools/server/socket/websocket-server.js
 | |
| @@ -134,13 +134,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");
 | |
|    }
 | |
|  
 | |
| @@ -190,13 +189,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, [
 | |
| @@ -218,8 +217,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/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp
 | |
| index db5b5b990727aefcbaa47f89e0f53f4048e60038..bcd2321f46d9bca719fc530054984a2163c21f86 100644
 | |
| --- a/docshell/base/BrowsingContext.cpp
 | |
| +++ b/docshell/base/BrowsingContext.cpp
 | |
| @@ -106,8 +106,15 @@ struct ParamTraits<mozilla::dom::DisplayMode>
 | |
|  
 | |
|  template <>
 | |
|  struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
 | |
| -    : public mozilla::dom::WebIDLEnumSerializer<
 | |
| -          mozilla::dom::PrefersColorSchemeOverride> {};
 | |
| +    : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::PrefersColorSchemeOverride> {};
 | |
| +
 | |
| +template <>
 | |
| +struct ParamTraits<mozilla::dom::PrefersReducedMotionOverride>
 | |
| +    : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::PrefersReducedMotionOverride> {};
 | |
| +
 | |
| +template <>
 | |
| +struct ParamTraits<mozilla::dom::ForcedColorsOverride>
 | |
| +    : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::ForcedColorsOverride> {};
 | |
|  
 | |
|  template <>
 | |
|  struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
 | |
| @@ -2807,6 +2814,40 @@ void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
 | |
|    PresContextAffectingFieldChanged();
 | |
|  }
 | |
|  
 | |
| +void BrowsingContext::DidSet(FieldIndex<IDX_PrefersReducedMotionOverride>,
 | |
| +                             dom::PrefersReducedMotionOverride aOldValue) {
 | |
| +  MOZ_ASSERT(IsTop());
 | |
| +  if (PrefersReducedMotionOverride() == aOldValue) {
 | |
| +    return;
 | |
| +  }
 | |
| +  PreOrderWalk([&](BrowsingContext* aContext) {
 | |
| +    if (nsIDocShell* shell = aContext->GetDocShell()) {
 | |
| +      if (nsPresContext* pc = shell->GetPresContext()) {
 | |
| +        pc->MediaFeatureValuesChanged(
 | |
| +            {MediaFeatureChangeReason::SystemMetricsChange},
 | |
| +            MediaFeatureChangePropagation::JustThisDocument);
 | |
| +      }
 | |
| +    }
 | |
| +  });
 | |
| +}
 | |
| +
 | |
| +void BrowsingContext::DidSet(FieldIndex<IDX_ForcedColorsOverride>,
 | |
| +                             dom::ForcedColorsOverride aOldValue) {
 | |
| +  MOZ_ASSERT(IsTop());
 | |
| +  if (ForcedColorsOverride() == aOldValue) {
 | |
| +    return;
 | |
| +  }
 | |
| +  PreOrderWalk([&](BrowsingContext* aContext) {
 | |
| +    if (nsIDocShell* shell = aContext->GetDocShell()) {
 | |
| +      if (nsPresContext* pc = shell->GetPresContext()) {
 | |
| +        pc->MediaFeatureValuesChanged(
 | |
| +            {MediaFeatureChangeReason::SystemMetricsChange},
 | |
| +            MediaFeatureChangePropagation::JustThisDocument);
 | |
| +      }
 | |
| +    }
 | |
| +  });
 | |
| +}
 | |
| +
 | |
|  void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
 | |
|                               nsString&& aOldValue) {
 | |
|    MOZ_ASSERT(IsTop());
 | |
| diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h
 | |
| index 61135ab0d7894c500c3c5d80d107e283c01b6830..cc8eb043f1f78214843ec7b335dd9932587d9bbd 100644
 | |
| --- a/docshell/base/BrowsingContext.h
 | |
| +++ b/docshell/base/BrowsingContext.h
 | |
| @@ -203,10 +203,10 @@ struct EmbedderColorSchemes {
 | |
|    FIELD(GVInaudibleAutoplayRequestStatus, GVAutoplayRequestStatus)            \
 | |
|    /* ScreenOrientation-related APIs */                                        \
 | |
|    FIELD(CurrentOrientationAngle, float)                                       \
 | |
| -  FIELD(CurrentOrientationType, mozilla::dom::OrientationType)                \
 | |
| +  FIELD(CurrentOrientationType, dom::OrientationType)                \
 | |
|    FIELD(OrientationLock, mozilla::hal::ScreenOrientation)                     \
 | |
|    FIELD(UserAgentOverride, nsString)                                          \
 | |
| -  FIELD(TouchEventsOverrideInternal, mozilla::dom::TouchEventsOverride)       \
 | |
| +  FIELD(TouchEventsOverrideInternal, dom::TouchEventsOverride)       \
 | |
|    FIELD(EmbedderElementType, Maybe<nsString>)                                 \
 | |
|    FIELD(MessageManagerGroup, nsString)                                        \
 | |
|    FIELD(MaxTouchPointsOverride, uint8_t)                                      \
 | |
| @@ -244,6 +244,10 @@ struct EmbedderColorSchemes {
 | |
|     * <browser> embedder element. */                                           \
 | |
|    FIELD(EmbedderColorSchemes, EmbedderColorSchemes)                           \
 | |
|    FIELD(DisplayMode, dom::DisplayMode)                                        \
 | |
| +  /* playwright addition */                                                   \
 | |
| +  FIELD(PrefersReducedMotionOverride, dom::PrefersReducedMotionOverride)      \
 | |
| +  /* playwright addition */                                                   \
 | |
| +  FIELD(ForcedColorsOverride, dom::ForcedColorsOverride)                      \
 | |
|    /* The number of entries added to the session history because of this       \
 | |
|     * browsing context. */                                                     \
 | |
|    FIELD(HistoryEntryCount, uint32_t)                                          \
 | |
| @@ -937,6 +941,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
 | |
|      return GetPrefersColorSchemeOverride();
 | |
|    }
 | |
|  
 | |
| +  dom::PrefersReducedMotionOverride PrefersReducedMotionOverride() const {
 | |
| +    return GetPrefersReducedMotionOverride();
 | |
| +  }
 | |
| +
 | |
| +  dom::ForcedColorsOverride ForcedColorsOverride() const {
 | |
| +    return GetForcedColorsOverride();
 | |
| +  }
 | |
| +
 | |
|    bool IsInBFCache() const;
 | |
|  
 | |
|    bool AllowJavascript() const { return GetAllowJavascript(); }
 | |
| @@ -1101,6 +1113,23 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
 | |
|    void WalkPresContexts(Callback&&);
 | |
|    void PresContextAffectingFieldChanged();
 | |
|  
 | |
| +  bool CanSet(FieldIndex<IDX_PrefersReducedMotionOverride>,
 | |
| +              dom::PrefersReducedMotionOverride, ContentParent*) {
 | |
| +    return IsTop();
 | |
| +  }
 | |
| +
 | |
| +  void DidSet(FieldIndex<IDX_PrefersReducedMotionOverride>,
 | |
| +              dom::PrefersReducedMotionOverride aOldValue);
 | |
| +
 | |
| +
 | |
| +  bool CanSet(FieldIndex<IDX_ForcedColorsOverride>,
 | |
| +              dom::ForcedColorsOverride, ContentParent*) {
 | |
| +    return IsTop();
 | |
| +  }
 | |
| +
 | |
| +  void DidSet(FieldIndex<IDX_ForcedColorsOverride>,
 | |
| +              dom::ForcedColorsOverride aOldValue);
 | |
| +
 | |
|    void DidSet(FieldIndex<IDX_MediumOverride>, nsString&& aOldValue);
 | |
|  
 | |
|    bool CanSet(FieldIndex<IDX_SuspendMediaWhenInactive>, bool, ContentParent*) {
 | |
| diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
 | |
| index 18b2bde3da2b1e17938fddda486b1bc4ddcf0e79..793a3d002b10298f7a19a2eae4d377f6f022fd36 100644
 | |
| --- a/docshell/base/CanonicalBrowsingContext.cpp
 | |
| +++ b/docshell/base/CanonicalBrowsingContext.cpp
 | |
| @@ -324,6 +324,8 @@ void CanonicalBrowsingContext::ReplacedBy(
 | |
|    txn.SetHasRestoreData(GetHasRestoreData());
 | |
|    txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart());
 | |
|    txn.SetForceOffline(GetForceOffline());
 | |
| +  txn.SetPrefersReducedMotionOverride(GetPrefersReducedMotionOverride());
 | |
| +  txn.SetForcedColorsOverride(GetForcedColorsOverride());
 | |
|  
 | |
|    // Propagate some settings on BrowsingContext replacement so they're not lost
 | |
|    // on bfcached navigations. These are important for GeckoView (see bug
 | |
| @@ -1594,6 +1596,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI,
 | |
|      return;
 | |
|    }
 | |
|  
 | |
| +  {
 | |
| +    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
 | |
| +    if (observerService) {
 | |
| +      observerService->NotifyObservers(ToSupports(this), "juggler-navigation-started-browser", NS_ConvertASCIItoUTF16(nsPrintfCString("%" PRIu64, loadState->GetLoadIdentifier())).get());
 | |
| +    }
 | |
| +  }
 | |
|    LoadURI(loadState, true);
 | |
|  }
 | |
|  
 | |
| diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
 | |
| index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d051577cc5598 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"
 | |
| @@ -66,6 +72,7 @@
 | |
|  #include "mozilla/dom/DocGroup.h"
 | |
|  #include "mozilla/dom/Element.h"
 | |
|  #include "mozilla/dom/FragmentDirective.h"
 | |
| +#include "mozilla/dom/Geolocation.h"
 | |
|  #include "mozilla/dom/HTMLAnchorElement.h"
 | |
|  #include "mozilla/dom/HTMLIFrameElement.h"
 | |
|  #include "mozilla/dom/PerformanceNavigation.h"
 | |
| @@ -90,6 +97,7 @@
 | |
|  #include "mozilla/dom/JSWindowActorChild.h"
 | |
|  #include "mozilla/dom/DocumentBinding.h"
 | |
|  #include "mozilla/ipc/ProtocolUtils.h"
 | |
| +#include "mozilla/dom/WorkerCommon.h"
 | |
|  #include "mozilla/net/DocumentChannel.h"
 | |
|  #include "mozilla/net/DocumentChannelChild.h"
 | |
|  #include "mozilla/net/ParentChannelWrapper.h"
 | |
| @@ -113,6 +121,7 @@
 | |
|  #include "nsIDocumentViewer.h"
 | |
|  #include "mozilla/dom/Document.h"
 | |
|  #include "nsHTMLDocument.h"
 | |
| +#include "mozilla/dom/Element.h"
 | |
|  #include "nsIDocumentLoaderFactory.h"
 | |
|  #include "nsIDOMWindow.h"
 | |
|  #include "nsIEditingSession.h"
 | |
| @@ -207,6 +216,7 @@
 | |
|  #include "nsGlobalWindowInner.h"
 | |
|  #include "nsGlobalWindowOuter.h"
 | |
|  #include "nsJSEnvironment.h"
 | |
| +#include "nsJSUtils.h"
 | |
|  #include "nsNetCID.h"
 | |
|  #include "nsNetUtil.h"
 | |
|  #include "nsObjectLoadingContent.h"
 | |
| @@ -347,6 +357,13 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
 | |
|        mAllowDNSPrefetch(true),
 | |
|        mAllowWindowControl(true),
 | |
|        mCSSErrorReportingEnabled(false),
 | |
| +      mFileInputInterceptionEnabled(false),
 | |
| +      mOverrideHasFocus(false),
 | |
| +      mBypassCSPEnabled(false),
 | |
| +      mForceActiveState(false),
 | |
| +      mDisallowBFCache(false),
 | |
| +      mReducedMotionOverride(REDUCED_MOTION_OVERRIDE_NONE),
 | |
| +      mForcedColorsOverride(FORCED_COLORS_OVERRIDE_NO_OVERRIDE),
 | |
|        mAllowAuth(mItemType == typeContent),
 | |
|        mAllowKeywordFixup(false),
 | |
|        mDisableMetaRefreshWhenInactive(false),
 | |
| @@ -3046,6 +3063,214 @@ 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;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::GetForceActiveState(bool* aEnabled) {
 | |
| +  MOZ_ASSERT(aEnabled);
 | |
| +  *aEnabled = mForceActiveState;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::SetForceActiveState(bool aEnabled) {
 | |
| +  mForceActiveState = aEnabled;
 | |
| +  ActivenessMaybeChanged();
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::GetDisallowBFCache(bool* aEnabled) {
 | |
| +  MOZ_ASSERT(aEnabled);
 | |
| +  *aEnabled = mDisallowBFCache;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::SetDisallowBFCache(bool aEnabled) {
 | |
| +  mDisallowBFCache = 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) {
 | |
| +  aLanguageOverride = GetRootDocShell()->mLanguageOverride;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +static void SetIcuLocale(const nsAString& aLanguageOverride) {
 | |
| +  icu::Locale locale(NS_LossyConvertUTF16toASCII(aLanguageOverride).get());
 | |
| +  if (icu::Locale::getDefault() != locale) {
 | |
| +    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::GetReducedMotionOverride(ReducedMotionOverride* aReducedMotionOverride) {
 | |
| +  *aReducedMotionOverride = GetRootDocShell()->mReducedMotionOverride;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::SetReducedMotionOverride(ReducedMotionOverride aReducedMotionOverride) {
 | |
| +  mReducedMotionOverride = aReducedMotionOverride;
 | |
| +  RefPtr<nsPresContext> presContext = GetPresContext();
 | |
| +  if (presContext) {
 | |
| +    presContext->MediaFeatureValuesChanged(
 | |
| +        {MediaFeatureChangeReason::SystemMetricsChange},
 | |
| +        MediaFeatureChangePropagation::JustThisDocument);
 | |
| +  }
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::GetForcedColorsOverride(ForcedColorsOverride* aForcedColorsOverride) {
 | |
| +  *aForcedColorsOverride = GetRootDocShell()->mForcedColorsOverride;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDocShell::SetForcedColorsOverride(ForcedColorsOverride aForcedColorsOverride) {
 | |
| +  mForcedColorsOverride = aForcedColorsOverride;
 | |
| +  RefPtr<nsPresContext> presContext = GetPresContext();
 | |
| +  if (presContext) {
 | |
| +    presContext->MediaFeatureValuesChanged(
 | |
| +        {MediaFeatureChangeReason::SystemMetricsChange},
 | |
| +        MediaFeatureChangePropagation::JustThisDocument);
 | |
| +  }
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +// =============== Juggler End =======================
 | |
| +
 | |
|  NS_IMETHODIMP
 | |
|  nsDocShell::GetIsNavigating(bool* aOut) {
 | |
|    *aOut = mIsNavigating;
 | |
| @@ -4739,7 +4964,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
 | |
|  }
 | |
|  
 | |
|  void nsDocShell::ActivenessMaybeChanged() {
 | |
| -  const bool isActive = mBrowsingContext->IsActive();
 | |
| +  const bool isActive = mForceActiveState || mBrowsingContext->IsActive();
 | |
|    if (RefPtr<PresShell> presShell = GetPresShell()) {
 | |
|      presShell->ActivenessMaybeChanged();
 | |
|    }
 | |
| @@ -6681,6 +6906,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
 | |
|      return false;  // no entry to save into
 | |
|    }
 | |
|  
 | |
| +  if (mDisallowBFCache) {
 | |
| +    return false;
 | |
| +  }
 | |
| +
 | |
|    MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
 | |
|               "mOSHE cannot be non-null with SHIP");
 | |
|    nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
 | |
| @@ -8413,6 +8642,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;
 | |
|      }
 | |
|  
 | |
| @@ -9549,6 +9784,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
 | |
|                     nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
 | |
|  
 | |
|    nsCOMPtr<nsIRequest> req;
 | |
| +
 | |
| +  // Juggler: report navigation started for non-same-document and non-javascript
 | |
| +  // navigations.
 | |
| +  if (!isJavaScript && !sameDocument) {
 | |
| +    nsCOMPtr<nsIObserverService> observerService =
 | |
| +        mozilla::services::GetObserverService();
 | |
| +    if (observerService) {
 | |
| +      observerService->NotifyObservers(GetAsSupports(this), "juggler-navigation-started-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("%" PRIu64, aLoadState->GetLoadIdentifier())).get());
 | |
| +    }
 | |
| +  }
 | |
|    rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
 | |
|  
 | |
|    if (NS_SUCCEEDED(rv)) {
 | |
| @@ -12747,6 +12992,9 @@ class OnLinkClickEvent : public Runnable {
 | |
|        mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
 | |
|                                  mTriggeringPrincipal);
 | |
|      }
 | |
| +    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
 | |
| +    observerService->NotifyObservers(ToSupports(mContent), "juggler-link-click-sync", nullptr);
 | |
| +
 | |
|      return NS_OK;
 | |
|    }
 | |
|  
 | |
| @@ -12836,6 +13084,8 @@ nsresult nsDocShell::OnLinkClick(
 | |
|    nsCOMPtr<nsIRunnable> ev =
 | |
|        new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
 | |
|                             aIsTrusted, aTriggeringPrincipal);
 | |
| +  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
 | |
| +  observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr);
 | |
|    return Dispatch(ev.forget());
 | |
|  }
 | |
|  
 | |
| diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
 | |
| index 0ea84df4dde885fd8e7f7e6045db56a0fa6b2691..b00bc444a5a25e63f98e583959d5f6812caf1815 100644
 | |
| --- a/docshell/base/nsDocShell.h
 | |
| +++ b/docshell/base/nsDocShell.h
 | |
| @@ -15,6 +15,7 @@
 | |
|  #include "mozilla/UniquePtr.h"
 | |
|  #include "mozilla/WeakPtr.h"
 | |
|  #include "mozilla/dom/BrowsingContext.h"
 | |
| +#include "mozilla/dom/Element.h"
 | |
|  #include "mozilla/dom/WindowProxyHolder.h"
 | |
|  #include "nsCOMPtr.h"
 | |
|  #include "nsCharsetSource.h"
 | |
| @@ -76,6 +77,7 @@ class nsCommandManager;
 | |
|  class nsDocShellEditorData;
 | |
|  class nsDOMNavigationTiming;
 | |
|  class nsDSURIContentListener;
 | |
| +class nsGeolocationService;
 | |
|  class nsGlobalWindowOuter;
 | |
|  
 | |
|  class FramingChecker;
 | |
| @@ -401,6 +403,15 @@ class nsDocShell final : public nsDocLoader,
 | |
|    void SetWillChangeProcess() { mWillChangeProcess = true; }
 | |
|    bool WillChangeProcess() { return mWillChangeProcess; }
 | |
|  
 | |
| +  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 CreateDocumentViewerForActor(
 | |
| @@ -1004,6 +1015,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
 | |
| @@ -1291,6 +1304,16 @@ class nsDocShell final : public nsDocLoader,
 | |
|    bool mAllowDNSPrefetch : 1;
 | |
|    bool mAllowWindowControl : 1;
 | |
|    bool mCSSErrorReportingEnabled : 1;
 | |
| +  bool mFileInputInterceptionEnabled: 1;
 | |
| +  bool mOverrideHasFocus : 1;
 | |
| +  bool mBypassCSPEnabled : 1;
 | |
| +  bool mForceActiveState : 1;
 | |
| +  bool mDisallowBFCache : 1;
 | |
| +  nsString mLanguageOverride;
 | |
| +  RefPtr<nsGeolocationService> mGeolocationServiceOverride;
 | |
| +  ReducedMotionOverride mReducedMotionOverride;
 | |
| +  ForcedColorsOverride mForcedColorsOverride;
 | |
| +
 | |
|    bool mAllowAuth : 1;
 | |
|    bool mAllowKeywordFixup : 1;
 | |
|    bool mDisableMetaRefreshWhenInactive : 1;
 | |
| diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
 | |
| index fdc04f16c6f547077ad8c872f9357d85d4513c50..199f8fdb0670265c715f99f5cac1a2b2f22c963d 100644
 | |
| --- a/docshell/base/nsIDocShell.idl
 | |
| +++ b/docshell/base/nsIDocShell.idl
 | |
| @@ -44,6 +44,7 @@ interface nsIURI;
 | |
|  interface nsIChannel;
 | |
|  interface nsIContentSecurityPolicy;
 | |
|  interface nsIDocumentViewer;
 | |
| +interface nsIDOMGeoPosition;
 | |
|  interface nsIEditor;
 | |
|  interface nsIEditingSession;
 | |
|  interface nsIInputStream;
 | |
| @@ -719,6 +720,36 @@ interface nsIDocShell : nsIDocShellTreeItem
 | |
|     */
 | |
|    void synchronizeLayoutHistoryState();
 | |
|  
 | |
| +  attribute boolean fileInputInterceptionEnabled;
 | |
| +
 | |
| +  attribute boolean overrideHasFocus;
 | |
| +
 | |
| +  attribute boolean bypassCSPEnabled;
 | |
| +
 | |
| +  attribute boolean forceActiveState;
 | |
| +
 | |
| +  attribute boolean disallowBFCache;
 | |
| +
 | |
| +  attribute AString languageOverride;
 | |
| +
 | |
| +  boolean overrideTimezone(in AString timezoneId);
 | |
| +
 | |
| +  cenum ReducedMotionOverride : 8 {
 | |
| +    REDUCED_MOTION_OVERRIDE_REDUCE,
 | |
| +    REDUCED_MOTION_OVERRIDE_NO_PREFERENCE,
 | |
| +    REDUCED_MOTION_OVERRIDE_NONE, /* This clears the override. */
 | |
| +  };
 | |
| +  [infallible] attribute nsIDocShell_ReducedMotionOverride reducedMotionOverride;
 | |
| +
 | |
| +  cenum ForcedColorsOverride : 8 {
 | |
| +    FORCED_COLORS_OVERRIDE_ACTIVE,
 | |
| +    FORCED_COLORS_OVERRIDE_NONE,
 | |
| +    FORCED_COLORS_OVERRIDE_NO_OVERRIDE, /* This clears the override. */
 | |
| +  };
 | |
| +  [infallible] attribute nsIDocShell_ForcedColorsOverride forcedColorsOverride;
 | |
| +
 | |
| +  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 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0dcf54fd2 100644
 | |
| --- a/dom/base/Document.cpp
 | |
| +++ b/dom/base/Document.cpp
 | |
| @@ -3757,6 +3757,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
 | |
| @@ -3814,6 +3817,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
 | |
|    MOZ_ASSERT(!mScriptGlobalObject,
 | |
|               "CSP must be initialized before mScriptGlobalObject is set!");
 | |
|  
 | |
| +  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;
 | |
| @@ -4613,6 +4621,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
 | |
|      return false;
 | |
|    }
 | |
|  
 | |
| +  if (IsActive() && mDocumentContainer->ShouldOverrideHasFocus()) {
 | |
| +    return true;
 | |
| +  }
 | |
| +
 | |
|    if (!fm->IsInActiveWindow(bc)) {
 | |
|      return false;
 | |
|    }
 | |
| @@ -19080,6 +19092,66 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
 | |
|    return PreferenceSheet::PrefsFor(*this).mColorScheme;
 | |
|  }
 | |
|  
 | |
| +bool Document::PrefersReducedMotion() const {
 | |
| +  auto* docShell = static_cast<nsDocShell*>(GetDocShell());
 | |
| +  nsIDocShell::ReducedMotionOverride reducedMotion;
 | |
| +  if (docShell && docShell->GetReducedMotionOverride(&reducedMotion) == NS_OK &&
 | |
| +      reducedMotion != nsIDocShell::REDUCED_MOTION_OVERRIDE_NONE) {
 | |
| +    switch (reducedMotion) {
 | |
| +      case nsIDocShell::REDUCED_MOTION_OVERRIDE_REDUCE:
 | |
| +        return true;
 | |
| +      case nsIDocShell::REDUCED_MOTION_OVERRIDE_NO_PREFERENCE:
 | |
| +        return false;
 | |
| +      case nsIDocShell::REDUCED_MOTION_OVERRIDE_NONE:
 | |
| +        break;
 | |
| +    };
 | |
| +  }
 | |
| +
 | |
| +  if (auto* bc = GetBrowsingContext()) {
 | |
| +    switch (bc->Top()->PrefersReducedMotionOverride()) {
 | |
| +      case dom::PrefersReducedMotionOverride::Reduce:
 | |
| +        return true;
 | |
| +      case dom::PrefersReducedMotionOverride::No_preference:
 | |
| +        return false;
 | |
| +      case dom::PrefersReducedMotionOverride::None:
 | |
| +        break;
 | |
| +    }
 | |
| +  }
 | |
| +
 | |
| +  return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
 | |
| +}
 | |
| +
 | |
| +bool Document::ForcedColors() const {
 | |
| +  auto* docShell = static_cast<nsDocShell*>(GetDocShell());
 | |
| +  nsIDocShell::ForcedColorsOverride forcedColors;
 | |
| +  if (docShell && docShell->GetForcedColorsOverride(&forcedColors) == NS_OK) {
 | |
| +    switch (forcedColors) {
 | |
| +      case nsIDocShell::FORCED_COLORS_OVERRIDE_ACTIVE:
 | |
| +        return true;
 | |
| +      case nsIDocShell::FORCED_COLORS_OVERRIDE_NONE:
 | |
| +        return false;
 | |
| +      case nsIDocShell::FORCED_COLORS_OVERRIDE_NO_OVERRIDE:
 | |
| +        break;
 | |
| +    };
 | |
| +  }
 | |
| +
 | |
| +  if (auto* bc = GetBrowsingContext()) {
 | |
| +    switch (bc->Top()->ForcedColorsOverride()) {
 | |
| +      case dom::ForcedColorsOverride::Active:
 | |
| +        return true;
 | |
| +      case dom::ForcedColorsOverride::None:
 | |
| +        return false;
 | |
| +      case dom::ForcedColorsOverride::No_override:
 | |
| +        break;
 | |
| +    }
 | |
| +  }
 | |
| +
 | |
| +  if (mIsBeingUsedAsImage) {
 | |
| +    return false;
 | |
| +  }
 | |
| +  return !PreferenceSheet::PrefsFor(*this).mUseDocumentColors;
 | |
| +}
 | |
| +
 | |
|  bool Document::HasRecentlyStartedForegroundLoads() {
 | |
|    if (!sLoadingForegroundTopLevelContentDocument) {
 | |
|      return false;
 | |
| diff --git a/dom/base/Document.h b/dom/base/Document.h
 | |
| index 0021e452414f9b7dc7b32a1065a82986d12dfdd7..2325b7d65bc1fb98b1dce994724c8e75c902834e 100644
 | |
| --- a/dom/base/Document.h
 | |
| +++ b/dom/base/Document.h
 | |
| @@ -4053,6 +4053,9 @@ class Document : public nsINode,
 | |
|    // color-scheme meta tag.
 | |
|    ColorScheme DefaultColorScheme() const;
 | |
|  
 | |
| +  bool PrefersReducedMotion() const;
 | |
| +  bool ForcedColors() const;
 | |
| +
 | |
|    static bool HasRecentlyStartedForegroundLoads();
 | |
|  
 | |
|    static bool AutomaticStorageAccessPermissionCanBeGranted(
 | |
| diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
 | |
| index e26e0968c11905a39bfcfeea60b4989126780084..376165771df0e215d9e1c78ae5d3669e525bcf31 100644
 | |
| --- a/dom/base/Navigator.cpp
 | |
| +++ b/dom/base/Navigator.cpp
 | |
| @@ -344,14 +344,18 @@ void Navigator::GetAppName(nsAString& aAppName) 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.
 | |
|    for (nsDependentSubstring lang :
 | |
| @@ -403,7 +407,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) {
 | |
|  }
 | |
|  
 | |
|  void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
 | |
| -  GetAcceptLanguages(aLanguages);
 | |
| +  if (mWindow && mWindow->GetDocShell()) {
 | |
| +    nsString languageOverride;
 | |
| +    mWindow->GetDocShell()->GetLanguageOverride(languageOverride);
 | |
| +    GetAcceptLanguages(&languageOverride, aLanguages);
 | |
| +  } else {
 | |
| +    GetAcceptLanguages(nullptr, 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
 | |
| @@ -2307,7 +2317,8 @@ bool Navigator::Webdriver() {
 | |
|    }
 | |
|  #endif
 | |
|  
 | |
| -  return false;
 | |
| +  // Playwright is automating the browser, so we should pretend to be a webdriver
 | |
| +  return true;
 | |
|  }
 | |
|  
 | |
|  AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) {
 | |
| diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
 | |
| index 6abf6cef230c97815f17f6b7abf9f1b1de274a6f..46ead1f32e0d710b5b32e61dff72a4f772d5421e 100644
 | |
| --- a/dom/base/Navigator.h
 | |
| +++ b/dom/base/Navigator.h
 | |
| @@ -218,7 +218,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/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 | |
| index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a69257799fec 100644
 | |
| --- a/dom/base/nsContentUtils.cpp
 | |
| +++ b/dom/base/nsContentUtils.cpp
 | |
| @@ -8829,7 +8829,8 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|      bool aIgnoreRootScrollFrame, float aPressure,
 | |
|      unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
 | |
|      PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
 | |
| -    bool aIsWidgetEventSynthesized) {
 | |
| +    bool aIsWidgetEventSynthesized,
 | |
| +    bool convertToPointer, uint32_t aJugglerEventId) {
 | |
|    nsPoint offset;
 | |
|    nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
 | |
|    if (!widget) return NS_ERROR_FAILURE;
 | |
| @@ -8837,6 +8838,7 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|    EventMessage msg;
 | |
|    Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
 | |
|    bool contextMenuKey = false;
 | |
| +  bool isPWDragEventMessage = false;
 | |
|    if (aType.EqualsLiteral("mousedown")) {
 | |
|      msg = eMouseDown;
 | |
|    } else if (aType.EqualsLiteral("mouseup")) {
 | |
| @@ -8861,6 +8863,12 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|      msg = eMouseHitTest;
 | |
|    } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
 | |
|      msg = eMouseExploreByTouch;
 | |
| +  } else if (aType.EqualsLiteral("dragover")) {
 | |
| +    msg = eDragOver;
 | |
| +    isPWDragEventMessage = true;
 | |
| +  } else if (aType.EqualsLiteral("drop")) {
 | |
| +    msg = eDrop;
 | |
| +    isPWDragEventMessage = true;
 | |
|    } else {
 | |
|      return NS_ERROR_FAILURE;
 | |
|    }
 | |
| @@ -8871,7 +8879,14 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|  
 | |
|    Maybe<WidgetPointerEvent> pointerEvent;
 | |
|    Maybe<WidgetMouseEvent> mouseEvent;
 | |
| -  if (IsPointerEventMessage(msg)) {
 | |
| +  Maybe<WidgetDragEvent> pwDragEvent;
 | |
| +
 | |
| +  if (isPWDragEventMessage) {
 | |
| +    pwDragEvent.emplace(true, msg, widget);
 | |
| +    pwDragEvent->mReason = aIsWidgetEventSynthesized
 | |
| +                             ? WidgetMouseEvent::eSynthesized
 | |
| +                             : WidgetMouseEvent::eReal;
 | |
| +  } else if (IsPointerEventMessage(msg)) {
 | |
|      MOZ_ASSERT(!aIsWidgetEventSynthesized,
 | |
|                 "The event shouldn't be dispatched as a synthesized event");
 | |
|      if (MOZ_UNLIKELY(aIsWidgetEventSynthesized)) {
 | |
| @@ -8890,8 +8905,11 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|                         contextMenuKey ? WidgetMouseEvent::eContextMenuKey
 | |
|                                        : WidgetMouseEvent::eNormal);
 | |
|    }
 | |
| +
 | |
|    WidgetMouseEvent& mouseOrPointerEvent =
 | |
| +      pwDragEvent.isSome() ? pwDragEvent.ref() :
 | |
|        pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref();
 | |
| +
 | |
|    mouseOrPointerEvent.pointerId = aIdentifier;
 | |
|    mouseOrPointerEvent.mModifiers = GetWidgetModifiers(aModifiers);
 | |
|    mouseOrPointerEvent.mButton = aButton;
 | |
| @@ -8904,6 +8922,8 @@ nsresult nsContentUtils::SendMouseEvent(
 | |
|    mouseOrPointerEvent.mClickCount = aClickCount;
 | |
|    mouseOrPointerEvent.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
 | |
|    mouseOrPointerEvent.mExitFrom = exitFrom;
 | |
| +  mouseOrPointerEvent.mJugglerEventId = aJugglerEventId;
 | |
| +  mouseOrPointerEvent.convertToPointer = convertToPointer;
 | |
|  
 | |
|    nsPresContext* presContext = aPresShell->GetPresContext();
 | |
|    if (!presContext) return NS_ERROR_FAILURE;
 | |
| diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
 | |
| index 3837cce20cfb7cc3c5a93e7b595dee632739de5c..81ccfbe139e7041eb862ab3b085f1dae76bf0a5c 100644
 | |
| --- a/dom/base/nsContentUtils.h
 | |
| +++ b/dom/base/nsContentUtils.h
 | |
| @@ -3093,7 +3093,8 @@ class nsContentUtils {
 | |
|        int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure,
 | |
|        unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
 | |
|        mozilla::PreventDefaultResult* aPreventDefault,
 | |
| -      bool aIsDOMEventSynthesized, bool aIsWidgetEventSynthesized);
 | |
| +      bool aIsDOMEventSynthesized, bool aIsWidgetEventSynthesized,
 | |
| +      bool convertToPointer = true, uint32_t aJugglerEventId = 0);
 | |
|  
 | |
|    static void FirePageShowEventForFrameLoaderSwap(
 | |
|        nsIDocShellTreeItem* aItem,
 | |
| diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
 | |
| index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4127e6462 100644
 | |
| --- a/dom/base/nsDOMWindowUtils.cpp
 | |
| +++ b/dom/base/nsDOMWindowUtils.cpp
 | |
| @@ -684,6 +684,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) {
 | |
|    return NS_ERROR_FAILURE;
 | |
|  }
 | |
|  
 | |
| +static uint32_t sJugglerEventId = 1000;
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +nsDOMWindowUtils::JugglerSendMouseEvent(
 | |
| +    const nsAString& aType, float aX, float aY, int32_t aButton,
 | |
| +    int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
 | |
| +    float aPressure, unsigned short aInputSourceArg,
 | |
| +    bool aIsDOMEventSynthesized, bool aIsWidgetEventSynthesized,
 | |
| +    int32_t aButtons, uint32_t aIdentifier, bool aDisablePointerEvent,
 | |
| +    uint32_t* aJugglerEventId) {
 | |
| +  *aJugglerEventId = ++sJugglerEventId;
 | |
| +  return SendMouseEventCommon(
 | |
| +      aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame,
 | |
| +      aPressure, aInputSourceArg,
 | |
| +      aIdentifier, false,
 | |
| +      nullptr, aIsDOMEventSynthesized,
 | |
| +      aIsWidgetEventSynthesized,
 | |
| +      aButtons, !aDisablePointerEvent, *aJugglerEventId);
 | |
| +}
 | |
| +
 | |
|  NS_IMETHODIMP
 | |
|  nsDOMWindowUtils::SendMouseEvent(
 | |
|      const nsAString& aType, float aX, float aY, int32_t aButton,
 | |
| @@ -698,7 +718,7 @@ nsDOMWindowUtils::SendMouseEvent(
 | |
|        aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, false,
 | |
|        aPreventDefault, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
 | |
|        aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
 | |
| -      aOptionalArgCount >= 6 ? aButtons : MOUSE_BUTTONS_NOT_SPECIFIED);
 | |
| +      aOptionalArgCount >= 6 ? aButtons : MOUSE_BUTTONS_NOT_SPECIFIED, true, 0);
 | |
|  }
 | |
|  
 | |
|  NS_IMETHODIMP
 | |
| @@ -716,7 +736,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(
 | |
|        aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, true,
 | |
|        nullptr, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
 | |
|        aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
 | |
| -      aOptionalArgCount >= 6 ? aButtons : MOUSE_BUTTONS_NOT_SPECIFIED);
 | |
| +      aOptionalArgCount >= 6 ? aButtons : MOUSE_BUTTONS_NOT_SPECIFIED, 0);
 | |
|  }
 | |
|  
 | |
|  NS_IMETHODIMP
 | |
| @@ -725,13 +745,13 @@ nsDOMWindowUtils::SendMouseEventCommon(
 | |
|      int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
 | |
|      float aPressure, unsigned short aInputSourceArg, uint32_t aPointerId,
 | |
|      bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized,
 | |
| -    bool aIsWidgetEventSynthesized, int32_t aButtons) {
 | |
| +    bool aIsWidgetEventSynthesized, int32_t aButtons, bool aConvertToPointer, uint32_t aJugglerEventId) {
 | |
|    RefPtr<PresShell> presShell = GetPresShell();
 | |
|    PreventDefaultResult preventDefaultResult;
 | |
|    nsresult rv = nsContentUtils::SendMouseEvent(
 | |
|        presShell, aType, aX, aY, aButton, aButtons, aClickCount, aModifiers,
 | |
|        aIgnoreRootScrollFrame, aPressure, aInputSourceArg, aPointerId, aToWindow,
 | |
| -      &preventDefaultResult, aIsDOMEventSynthesized, aIsWidgetEventSynthesized);
 | |
| +      &preventDefaultResult, aIsDOMEventSynthesized, aIsWidgetEventSynthesized, aConvertToPointer, aJugglerEventId);
 | |
|  
 | |
|    if (aPreventDefault) {
 | |
|      *aPreventDefault = preventDefaultResult != PreventDefaultResult::No;
 | |
| diff --git a/dom/base/nsDOMWindowUtils.h b/dom/base/nsDOMWindowUtils.h
 | |
| index 47ff326b202266b1d7d6af8bdfb72776df8a6a93..b8e084b0c788c46345b1455b8257f1719c851404 100644
 | |
| --- a/dom/base/nsDOMWindowUtils.h
 | |
| +++ b/dom/base/nsDOMWindowUtils.h
 | |
| @@ -93,7 +93,7 @@ class nsDOMWindowUtils final : public nsIDOMWindowUtils,
 | |
|        int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
 | |
|        float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier,
 | |
|        bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized,
 | |
| -      bool aIsWidgetEventSynthesized, int32_t aButtons);
 | |
| +      bool aIsWidgetEventSynthesized, int32_t aButtons, bool aConvertToPointer = true, uint32_t aJugglerEventId = 0);
 | |
|  
 | |
|    MOZ_CAN_RUN_SCRIPT
 | |
|    nsresult SendTouchEventCommon(
 | |
| diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
 | |
| index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e0606715e3acdb 100644
 | |
| --- a/dom/base/nsFocusManager.cpp
 | |
| +++ b/dom/base/nsFocusManager.cpp
 | |
| @@ -1684,6 +1684,10 @@ Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
 | |
|          (GetActiveBrowsingContext() == newRootBrowsingContext);
 | |
|    }
 | |
|  
 | |
| +  // In Playwright, we want to send focus events even if the element
 | |
| +  // isn't actually in the active window.
 | |
| +  isElementInActiveWindow = true;
 | |
| +
 | |
|    // Exit fullscreen if a website focuses another window
 | |
|    if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
 | |
|        !isElementInActiveWindow && (aFlags & FLAG_RAISE)) {
 | |
| @@ -2269,6 +2273,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
 | |
|                                bool aIsLeavingDocument, bool aAdjustWidget,
 | |
|                                bool aRemainActive, Element* aElementToFocus,
 | |
|                                uint64_t aActionId) {
 | |
| +
 | |
|    LOGFOCUS(("<<Blur begin actionid: %" PRIu64 ">>", aActionId));
 | |
|  
 | |
|    // hold a reference to the focused content, which may be null
 | |
| @@ -2315,6 +2320,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
 | |
|      return true;
 | |
|    }
 | |
|  
 | |
| +  // Playwright: emulate focused page by never bluring when leaving document.
 | |
| +  if (XRE_IsContentProcess() && aIsLeavingDocument && docShell && nsDocShell::Cast(docShell)->ShouldOverrideHasFocus()) {
 | |
| +    return true;
 | |
| +  }
 | |
| +
 | |
|    // Keep a ref to presShell since dispatching the DOM event may cause
 | |
|    // the document to be destroyed.
 | |
|    RefPtr<PresShell> presShell = docShell->GetPresShell();
 | |
| @@ -2992,7 +3002,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
 | |
|      }
 | |
|    }
 | |
|  
 | |
| -  if (sTestMode) {
 | |
| +  // In Playwright, we still want to execte the embedder functions
 | |
| +  // to actually show / focus windows.
 | |
| +  if (false && sTestMode) {
 | |
|      // In test mode, emulate raising the window. WindowRaised takes
 | |
|      // care of lowering the present active window. This happens in
 | |
|      // a separate runnable to avoid touching multiple windows in
 | |
| diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
 | |
| index e47d4979078343102f00e93df913ff778b841804..360ab27a8f3394d18b558de80b5d0bbb963c1391 100644
 | |
| --- a/dom/base/nsGlobalWindowOuter.cpp
 | |
| +++ b/dom/base/nsGlobalWindowOuter.cpp
 | |
| @@ -2514,10 +2514,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));
 | |
| +      }
 | |
|      }
 | |
|    }
 | |
|  
 | |
| @@ -2637,6 +2643,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(u""_ns); }
 | |
|  
 | |
|  void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
 | |
| diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h
 | |
| index 0039d6d91b23953afbd6aec2b4d1f064db3c3b1c..7a6c5da16651d34ea60c69331365d94886da1993 100644
 | |
| --- a/dom/base/nsGlobalWindowOuter.h
 | |
| +++ b/dom/base/nsGlobalWindowOuter.h
 | |
| @@ -314,6 +314,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 4b54dcd5b4fc9c575552ae82d5ed66f313cdeb72..e75b5f148d55d8f7d7e098a84930fec0e28aa01d 100644
 | |
| --- a/dom/base/nsINode.cpp
 | |
| +++ b/dom/base/nsINode.cpp
 | |
| @@ -1402,6 +1402,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
 | |
|    mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
 | |
|  }
 | |
|  
 | |
| +static nsIFrame* GetFirstFrame(nsINode* aNode) {
 | |
| +  if (!aNode->IsContent())
 | |
| +    return nullptr;
 | |
| +  nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame(FlushType::Frames);
 | |
| +  if (!frame) {
 | |
| +    FlattenedChildIterator iter(aNode->AsContent());
 | |
| +    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
 | |
| +      frame = child->GetPrimaryFrame(FlushType::Frames);
 | |
| +      if (frame) {
 | |
| +        break;
 | |
| +      }
 | |
| +    }
 | |
| +  }
 | |
| +  return frame;
 | |
| +}
 | |
| +
 | |
| +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");
 | |
| +  }
 | |
| +  nsIFrame* primaryFrame = GetFirstFrame(this);
 | |
| +  if (!primaryFrame) {
 | |
| +    return aRv.ThrowNotFoundError("Node does not have a layout object");
 | |
| +  }
 | |
| +  aRv = NS_OK;
 | |
| +  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->ScrollFrameIntoView(
 | |
| +      primaryFrame, Some(rect),
 | |
| +      ScrollAxis(WhereToScroll::Center, WhenToScroll::IfNotFullyVisible),
 | |
| +      ScrollAxis(WhereToScroll::Center, WhenToScroll::IfNotFullyVisible),
 | |
| +      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 6f980f472aefe147de47212717ca300e62e02952..3d60daf88196ed502fc647cc7b51d2eb70a281ef 100644
 | |
| --- a/dom/base/nsINode.h
 | |
| +++ b/dom/base/nsINode.h
 | |
| @@ -2303,6 +2303,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 cf8037cd580013efe5eb578c43f45c0d21946c6a..583460796fdef633e8075013597f7c315ce4ab06 100644
 | |
| --- a/dom/base/nsJSUtils.cpp
 | |
| +++ b/dom/base/nsJSUtils.cpp
 | |
| @@ -177,6 +177,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 cceb725d393d5e5f83c8f87491089c3fa1d57cc3..e906a7fb7c3fd72554613f640dcc272e6984d929 100644
 | |
| --- a/dom/base/nsJSUtils.h
 | |
| +++ b/dom/base/nsJSUtils.h
 | |
| @@ -79,6 +79,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/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl
 | |
| index 864890f6a23b21a2a59687e4e2873b6837c05fbb..a34005c323d4b8e35b5bdb2b6eec2a268f8adc4b 100644
 | |
| --- a/dom/chrome-webidl/BrowsingContext.webidl
 | |
| +++ b/dom/chrome-webidl/BrowsingContext.webidl
 | |
| @@ -53,6 +53,24 @@ enum PrefersColorSchemeOverride {
 | |
|    "dark",
 | |
|  };
 | |
|  
 | |
| +/**
 | |
| + * CSS prefers-reduced-motion values.
 | |
| + */
 | |
| +enum PrefersReducedMotionOverride {
 | |
| +  "none",
 | |
| +  "reduce",
 | |
| +  "no-preference",
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * CSS forced-colors values.
 | |
| + */
 | |
| +enum ForcedColorsOverride {
 | |
| +  "none",
 | |
| +  "active",
 | |
| +  "no-override",  /* This clears the override. */
 | |
| +};
 | |
| +
 | |
|  /**
 | |
|   * Allowed overrides of platform/pref default behaviour for touch events.
 | |
|   */
 | |
| @@ -209,6 +227,12 @@ interface BrowsingContext {
 | |
|    // Color-scheme simulation, for DevTools.
 | |
|    [SetterThrows] attribute PrefersColorSchemeOverride prefersColorSchemeOverride;
 | |
|  
 | |
| +  // Reduced-Motion simulation, for DevTools.
 | |
| +  [SetterThrows] attribute PrefersReducedMotionOverride prefersReducedMotionOverride;
 | |
| +
 | |
| +  // Forced-Colors simulation, for DevTools.
 | |
| +  [SetterThrows] attribute ForcedColorsOverride forcedColorsOverride;
 | |
| +
 | |
|    /**
 | |
|     * A unique identifier for the browser element that is hosting this
 | |
|     * BrowsingContext tree. Every BrowsingContext in the element's tree will
 | |
| diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp
 | |
| index 21717aba5547b973e439ae9ba525f358d044d3f8..274cdebc2e0a2eb9f8b7743d24921204a417f76d 100644
 | |
| --- a/dom/geolocation/Geolocation.cpp
 | |
| +++ b/dom/geolocation/Geolocation.cpp
 | |
| @@ -24,6 +24,7 @@
 | |
|  #include "nsComponentManagerUtils.h"
 | |
|  #include "nsContentPermissionHelper.h"
 | |
|  #include "nsContentUtils.h"
 | |
| +#include "nsDocShell.h"
 | |
|  #include "nsGlobalWindowInner.h"
 | |
|  #include "mozilla/dom/Document.h"
 | |
|  #include "nsINamed.h"
 | |
| @@ -264,10 +265,8 @@ nsGeolocationRequest::Allow(JS::Handle<JS::Value> 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) {
 | |
|      EpochTimeStamp cachedPositionTime_ms;
 | |
| @@ -475,8 +474,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();
 | |
|      }
 | |
| @@ -785,8 +783,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;
 | |
|  
 | |
| @@ -878,7 +882,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 7e1af00d05fbafa2d828e2c7e4dcc5c82d115f5b..e85af9718d064e4d2865bc944e9d4ba1efb9a5d7 100644
 | |
| --- a/dom/geolocation/Geolocation.h
 | |
| +++ b/dom/geolocation/Geolocation.h
 | |
| @@ -31,6 +31,7 @@
 | |
|  
 | |
|  #include "nsIGeolocationProvider.h"
 | |
|  #include "mozilla/Attributes.h"
 | |
| +#include "nsDocShell.h"
 | |
|  
 | |
|  class nsGeolocationService;
 | |
|  class nsGeolocationRequest;
 | |
| @@ -48,13 +49,14 @@ struct CachedPositionAndAccuracy {
 | |
|    bool isHighAccuracy;
 | |
|  };
 | |
|  
 | |
| +
 | |
|  /**
 | |
|   * Singleton that manages the geolocation provider
 | |
|   */
 | |
|  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
 | |
| @@ -179,6 +181,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 e2a77a11435a80abbb6381ffabbb5711eca0ac0d..a614efef052ca7c39457726d1f1e66f7cff777f8 100644
 | |
| --- a/dom/html/HTMLInputElement.cpp
 | |
| +++ b/dom/html/HTMLInputElement.cpp
 | |
| @@ -59,6 +59,7 @@
 | |
|  #include "mozilla/dom/Document.h"
 | |
|  #include "mozilla/dom/HTMLDataListElement.h"
 | |
|  #include "mozilla/dom/HTMLOptionElement.h"
 | |
| +#include "nsDocShell.h"
 | |
|  #include "nsIFormControlFrame.h"
 | |
|  #include "nsITextControlFrame.h"
 | |
|  #include "nsIFrame.h"
 | |
| @@ -784,6 +785,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
 | |
|      return NS_ERROR_FAILURE;
 | |
|    }
 | |
|  
 | |
| +  nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
 | |
| +  nsDocShell* docShell = win ? static_cast<nsDocShell*>(win->GetDocShell()) : nullptr;
 | |
| +  if (docShell && docShell->IsFileInputInterceptionEnabled()) {
 | |
| +    docShell->FilePickerShown(this);
 | |
| +    return NS_OK;
 | |
| +  }
 | |
| +
 | |
|    if (IsPickerBlocked(doc)) {
 | |
|      return NS_OK;
 | |
|    }
 | |
| diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl
 | |
| index ac0251b4989799e9bb370a8066d10f13154bbc7c..184f4d980c35652c67da06e917e9d0b85ff34cea 100644
 | |
| --- a/dom/interfaces/base/nsIDOMWindowUtils.idl
 | |
| +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
 | |
| @@ -374,6 +374,26 @@ interface nsIDOMWindowUtils : nsISupports {
 | |
|                           [optional] in long aButtons,
 | |
|                           [optional] in unsigned long aIdentifier);
 | |
|  
 | |
| +  /**
 | |
| +   * Playwright: a separate method to dispatch mouse event with a
 | |
| +   * specific `jugglerEventId`.
 | |
| +   */
 | |
| +  [can_run_script]
 | |
| +  unsigned long jugglerSendMouseEvent(in AString aType,
 | |
| +                         in float aX,
 | |
| +                         in float aY,
 | |
| +                         in long aButton,
 | |
| +                         in long aClickCount,
 | |
| +                         in long aModifiers,
 | |
| +                         in boolean aIgnoreRootScrollFrame,
 | |
| +                         in float aPressure,
 | |
| +                         in unsigned short aInputSourceArg,
 | |
| +                         in boolean aIsDOMEventSynthesized,
 | |
| +                         in boolean aIsWidgetEventSynthesized,
 | |
| +                         in long aButtons,
 | |
| +                         in unsigned long aIdentifier,
 | |
| +                         in boolean aDisablePointerEvent);
 | |
| +
 | |
|    /** Synthesize a touch event. The event types supported are:
 | |
|     *    touchstart, touchend, touchmove, and touchcancel
 | |
|     *
 | |
| diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
 | |
| index 204ee71ece1afa8b416caafcb4bdd242344f1a26..8597f2d0c4bd7a6fbfed9f29d002d0c59c8f8ae9 100644
 | |
| --- a/dom/ipc/BrowserChild.cpp
 | |
| +++ b/dom/ipc/BrowserChild.cpp
 | |
| @@ -1656,6 +1656,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
 | |
|    if (postLayerization) {
 | |
|      postLayerization->Register();
 | |
|    }
 | |
| +
 | |
| +  // Playwright: notify content that mouse event has been received and handled.
 | |
| +  nsCOMPtr<nsIObserverService> observerService =
 | |
| +      mozilla::services::GetObserverService();
 | |
| +  if (observerService && aEvent.mJugglerEventId) {
 | |
| +    if (aEvent.mMessage == eMouseUp) {
 | |
| +      observerService->NotifyObservers(nullptr, "juggler-mouse-event-hit-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("mouseup %" PRIu32, aEvent.mJugglerEventId)).get());
 | |
| +    } else if (aEvent.mMessage == eMouseDown) {
 | |
| +      observerService->NotifyObservers(nullptr, "juggler-mouse-event-hit-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("mousedown %" PRIu32, aEvent.mJugglerEventId)).get());
 | |
| +    } else if (aEvent.mMessage == eMouseMove) {
 | |
| +      observerService->NotifyObservers(nullptr, "juggler-mouse-event-hit-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("mousemove %" PRIu32, aEvent.mJugglerEventId)).get());
 | |
| +    } else if (aEvent.mMessage == eContextMenu) {
 | |
| +      observerService->NotifyObservers(nullptr, "juggler-mouse-event-hit-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("contextmenu %" PRIu32, aEvent.mJugglerEventId)).get());
 | |
| +    }
 | |
| +  }
 | |
|  }
 | |
|  
 | |
|  mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent(
 | |
| diff --git a/dom/ipc/CoalescedMouseData.cpp b/dom/ipc/CoalescedMouseData.cpp
 | |
| index 5aa445d2e0a6169e57c44569974d557b3baf7064..671f71979b407f0ca17c66f13805e851ba30479e 100644
 | |
| --- a/dom/ipc/CoalescedMouseData.cpp
 | |
| +++ b/dom/ipc/CoalescedMouseData.cpp
 | |
| @@ -67,6 +67,9 @@ bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
 | |
|            mCoalescedInputEvent->pointerId == aEvent.pointerId &&
 | |
|            mCoalescedInputEvent->mButton == aEvent.mButton &&
 | |
|            mCoalescedInputEvent->mButtons == aEvent.mButtons && mGuid == aGuid &&
 | |
| +          // `mJugglerEventId` is 0 for non-juggler events and a unique number for
 | |
| +          // juggler-emitted events.
 | |
| +          mCoalescedInputEvent->mJugglerEventId == aEvent.mJugglerEventId &&
 | |
|            mInputBlockId == aInputBlockId);
 | |
|  }
 | |
|  
 | |
| diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.cc b/dom/media/systemservices/video_engine/desktop_capture_impl.cc
 | |
| index d4b40fda96ea759eb92e1351e1046a9e0b85689b..b2123a3be05b2622a5e07d5ee32752d0feaaa57f 100644
 | |
| --- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc
 | |
| +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc
 | |
| @@ -137,9 +137,10 @@ int32_t ScreenDeviceInfoImpl::GetOrientation(const char* aDeviceUniqueIdUTF8,
 | |
|  
 | |
|  DesktopCaptureImpl* DesktopCaptureImpl::Create(const int32_t aModuleId,
 | |
|                                                 const char* aUniqueId,
 | |
| -                                               const CaptureDeviceType aType) {
 | |
| +                                               const CaptureDeviceType aType,
 | |
| +                                               bool aCaptureCursor) {
 | |
|    return new rtc::RefCountedObject<DesktopCaptureImpl>(aModuleId, aUniqueId,
 | |
| -                                                       aType);
 | |
| +                                                       aType, aCaptureCursor);
 | |
|  }
 | |
|  
 | |
|  int32_t WindowDeviceInfoImpl::Init() {
 | |
| @@ -412,7 +413,7 @@ static bool UsePipewire() {
 | |
|  
 | |
|  static std::unique_ptr<DesktopCapturer> CreateDesktopCapturerAndThread(
 | |
|      CaptureDeviceType aDeviceType, DesktopCapturer::SourceId aSourceId,
 | |
| -    nsIThread** aOutThread) {
 | |
| +    nsIThread** aOutThread, bool aCaptureCursor) {
 | |
|    DesktopCaptureOptions options = CreateDesktopCaptureOptions();
 | |
|    std::unique_ptr<DesktopCapturer> capturer;
 | |
|  
 | |
| @@ -462,8 +463,10 @@ static std::unique_ptr<DesktopCapturer> CreateDesktopCapturerAndThread(
 | |
|  
 | |
|      capturer->SelectSource(aSourceId);
 | |
|  
 | |
| -    capturer = std::make_unique<DesktopAndCursorComposer>(std::move(capturer),
 | |
| -                                                          options);
 | |
| +    if (aCaptureCursor) {
 | |
| +      capturer = std::make_unique<DesktopAndCursorComposer>(
 | |
| +          std::move(capturer), options);
 | |
| +    }
 | |
|    } else if (aDeviceType == CaptureDeviceType::Browser) {
 | |
|      // XXX We don't capture cursors, so avoid the extra indirection layer. We
 | |
|      // could also pass null for the pMouseCursorMonitor.
 | |
| @@ -480,7 +483,8 @@ static std::unique_ptr<DesktopCapturer> CreateDesktopCapturerAndThread(
 | |
|  }
 | |
|  
 | |
|  DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId,
 | |
| -                                       const CaptureDeviceType aType)
 | |
| +                                       const CaptureDeviceType aType,
 | |
| +                                       bool aCaptureCursor)
 | |
|      : mModuleId(aId),
 | |
|        mTrackingId(mozilla::TrackingId(CaptureEngineToTrackingSourceStr([&] {
 | |
|                                          switch (aType) {
 | |
| @@ -497,6 +501,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId,
 | |
|                                        aId)),
 | |
|        mDeviceUniqueId(aUniqueId),
 | |
|        mDeviceType(aType),
 | |
| +      capture_cursor_(aCaptureCursor),
 | |
|        mControlThread(mozilla::GetCurrentSerialEventTarget()),
 | |
|        mNextFrameMinimumTime(Timestamp::Zero()),
 | |
|        mCallbacks("DesktopCaptureImpl::mCallbacks") {}
 | |
| @@ -521,6 +526,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback(
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +void DesktopCaptureImpl::RegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) {
 | |
| +  rtc::CritScope lock(&mApiCs);
 | |
| +  _rawFrameCallbacks.insert(rawFrameCallback);
 | |
| +}
 | |
| +
 | |
| +void DesktopCaptureImpl::DeRegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) {
 | |
| +  rtc::CritScope lock(&mApiCs);
 | |
| +  auto it = _rawFrameCallbacks.find(rawFrameCallback);
 | |
| +  if (it != _rawFrameCallbacks.end()) {
 | |
| +    _rawFrameCallbacks.erase(it);
 | |
| +  }
 | |
| +}
 | |
| +
 | |
|  int32_t DesktopCaptureImpl::StopCaptureIfAllClientsClose() {
 | |
|    {
 | |
|      auto callbacks = mCallbacks.Lock();
 | |
| @@ -553,7 +571,7 @@ int32_t DesktopCaptureImpl::StartCapture(
 | |
|  
 | |
|    DesktopCapturer::SourceId sourceId = std::stoi(mDeviceUniqueId);
 | |
|    std::unique_ptr capturer = CreateDesktopCapturerAndThread(
 | |
| -      mDeviceType, sourceId, getter_AddRefs(mCaptureThread));
 | |
| +      mDeviceType, sourceId, getter_AddRefs(mCaptureThread), capture_cursor_);
 | |
|  
 | |
|    MOZ_ASSERT(!capturer == !mCaptureThread);
 | |
|    if (!capturer) {
 | |
| @@ -663,6 +681,15 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult,
 | |
|    frameInfo.height = aFrame->size().height();
 | |
|    frameInfo.videoType = VideoType::kARGB;
 | |
|  
 | |
| +  size_t videoFrameStride =
 | |
| +      frameInfo.width * DesktopFrame::kBytesPerPixel;
 | |
| +  {
 | |
| +    rtc::CritScope cs(&mApiCs);
 | |
| +    for (auto rawFrameCallback : _rawFrameCallbacks) {
 | |
| +      rawFrameCallback->OnRawFrame(videoFrame, videoFrameStride, frameInfo);
 | |
| +    }
 | |
| +  }
 | |
| +
 | |
|    size_t videoFrameLength =
 | |
|        frameInfo.width * frameInfo.height * DesktopFrame::kBytesPerPixel;
 | |
|  
 | |
| diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.h b/dom/media/systemservices/video_engine/desktop_capture_impl.h
 | |
| index 9aebaa39321839eb3beb503fc4ed33e303bb0deb..1dd75f3cdb8078b01c4d43a0ac3d8a6ea3ec47ab 100644
 | |
| --- a/dom/media/systemservices/video_engine/desktop_capture_impl.h
 | |
| +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.h
 | |
| @@ -25,6 +25,7 @@
 | |
|  #include "modules/desktop_capture/desktop_capturer.h"
 | |
|  #include "modules/video_capture/video_capture.h"
 | |
|  #include "rtc_base/synchronization/mutex.h"
 | |
| +#include "rtc_base/deprecated/recursive_critical_section.h"
 | |
|  
 | |
|  #include "desktop_device_info.h"
 | |
|  #include "MediaEngineSource.h"
 | |
| @@ -45,6 +46,33 @@ namespace webrtc {
 | |
|  
 | |
|  class VideoCaptureEncodeInterface;
 | |
|  
 | |
| +class RawFrameCallback {
 | |
| + public:
 | |
| +  virtual ~RawFrameCallback() {}
 | |
| +
 | |
| +  virtual void OnRawFrame(uint8_t* videoFrame, size_t videoFrameLength, const VideoCaptureCapability& frameInfo) = 0;
 | |
| +};
 | |
| +
 | |
| +class VideoCaptureModuleEx : public VideoCaptureModule {
 | |
| + public:
 | |
| +  virtual ~VideoCaptureModuleEx() {}
 | |
| +
 | |
| +  virtual void RegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) = 0;
 | |
| +  virtual void DeRegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) = 0;
 | |
| +  int32_t StartCaptureCounted(const VideoCaptureCapability& aCapability) {
 | |
| +    ++capture_counter_;
 | |
| +    return capture_counter_ == 1 ? StartCapture(aCapability) : 0;
 | |
| +  }
 | |
| +
 | |
| +  int32_t StopCaptureCounted() {
 | |
| +    --capture_counter_;
 | |
| +    return capture_counter_ == 0 ? StopCapture() : 0;
 | |
| +  }
 | |
| +
 | |
| + private:
 | |
| +  int32_t capture_counter_ = 0;
 | |
| +};
 | |
| +
 | |
|  // simulate deviceInfo interface for video engine, bridge screen/application and
 | |
|  // real screen/application device info
 | |
|  
 | |
| @@ -160,13 +188,13 @@ class BrowserDeviceInfoImpl : public VideoCaptureModule::DeviceInfo {
 | |
|  // As with video, DesktopCaptureImpl is a proxy for screen sharing
 | |
|  // and follows the video pipeline design
 | |
|  class DesktopCaptureImpl : public DesktopCapturer::Callback,
 | |
| -                           public VideoCaptureModule {
 | |
| +                           public VideoCaptureModuleEx {
 | |
|   public:
 | |
|    /* Create a screen capture modules object
 | |
|     */
 | |
|    static DesktopCaptureImpl* Create(
 | |
|        const int32_t aModuleId, const char* aUniqueId,
 | |
| -      const mozilla::camera::CaptureDeviceType aType);
 | |
| +      const mozilla::camera::CaptureDeviceType aType, bool aCaptureCursor = true);
 | |
|  
 | |
|    [[nodiscard]] static std::shared_ptr<VideoCaptureModule::DeviceInfo>
 | |
|    CreateDeviceInfo(const int32_t aId,
 | |
| @@ -180,6 +208,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
 | |
|    void DeRegisterCaptureDataCallback(
 | |
|        rtc::VideoSinkInterface<VideoFrame>* aCallback) override;
 | |
|    int32_t StopCaptureIfAllClientsClose() override;
 | |
| +  void RegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) override;
 | |
| +  void DeRegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) override;
 | |
|  
 | |
|    int32_t SetCaptureRotation(VideoRotation aRotation) override;
 | |
|    bool SetApplyRotation(bool aEnable) override;
 | |
| @@ -203,7 +233,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
 | |
|  
 | |
|   protected:
 | |
|    DesktopCaptureImpl(const int32_t aId, const char* aUniqueId,
 | |
| -                     const mozilla::camera::CaptureDeviceType aType);
 | |
| +                     const mozilla::camera::CaptureDeviceType aType,
 | |
| +                     bool aCaptureCusor);
 | |
|    virtual ~DesktopCaptureImpl();
 | |
|  
 | |
|   private:
 | |
| @@ -211,6 +242,9 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
 | |
|    static constexpr uint32_t kMaxDesktopCaptureCpuUsage = 50;
 | |
|    void InitOnThread(std::unique_ptr<DesktopCapturer> aCapturer, int aFramerate);
 | |
|    void ShutdownOnThread();
 | |
| +
 | |
| +  rtc::RecursiveCriticalSection mApiCs;
 | |
| +  std::set<RawFrameCallback*> _rawFrameCallbacks;
 | |
|    // DesktopCapturer::Callback interface.
 | |
|    void OnCaptureResult(DesktopCapturer::Result aResult,
 | |
|                         std::unique_ptr<DesktopFrame> aFrame) override;
 | |
| @@ -218,6 +252,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
 | |
|    // Notifies all mCallbacks of OnFrame(). mCaptureThread only.
 | |
|    void NotifyOnFrame(const VideoFrame& aFrame);
 | |
|  
 | |
| +  bool capture_cursor_ = true;
 | |
| +
 | |
|    // Control thread on which the public API is called.
 | |
|    const nsCOMPtr<nsISerialEventTarget> mControlThread;
 | |
|    // Set in StartCapture.
 | |
| diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp
 | |
| index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc9b5e445f 100644
 | |
| --- a/dom/script/ScriptSettings.cpp
 | |
| +++ b/dom/script/ScriptSettings.cpp
 | |
| @@ -150,6 +150,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.
 | |
| @@ -177,7 +201,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 4eafb2247d5aa8e989c0359d6d9d864edf73759d..e0d0b5bc78537c6fa8d0cf02cc6c772993008b91 100644
 | |
| --- a/dom/security/nsCSPUtils.cpp
 | |
| +++ b/dom/security/nsCSPUtils.cpp
 | |
| @@ -22,6 +22,7 @@
 | |
|  #include "nsSandboxFlags.h"
 | |
|  #include "nsServiceManagerUtils.h"
 | |
|  #include "nsWhitespaceTokenizer.h"
 | |
| +#include "nsDocShell.h"
 | |
|  
 | |
|  #include "mozilla/Assertions.h"
 | |
|  #include "mozilla/Components.h"
 | |
| @@ -133,6 +134,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..2640bd57123c2b03bf4b06a2419cd020ba95f155 100644
 | |
| --- a/dom/webidl/GeometryUtils.webidl
 | |
| +++ b/dom/webidl/GeometryUtils.webidl
 | |
| @@ -16,6 +16,8 @@ dictionary BoxQuadOptions {
 | |
|    GeometryNode relativeTo;
 | |
|    [ChromeOnly]
 | |
|    boolean createFramesForSuppressedWhitespace = true;
 | |
| +  [ChromeOnly]
 | |
| +  boolean recurseWhenNoFrame = false;
 | |
|  };
 | |
|  
 | |
|  dictionary ConvertCoordinateOptions {
 | |
| @@ -27,6 +29,9 @@ interface mixin GeometryUtils {
 | |
|    [Throws, Func="nsINode::HasBoxQuadsSupport", NeedsCallerType]
 | |
|    sequence<DOMQuad> getBoxQuads(optional BoxQuadOptions options = {});
 | |
|  
 | |
| +  [ChromeOnly, Throws, Func="nsINode::HasBoxQuadsSupport"]
 | |
| +  undefined 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 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30ed33396b 100644
 | |
| --- a/dom/workers/RuntimeService.cpp
 | |
| +++ b/dom/workers/RuntimeService.cpp
 | |
| @@ -998,7 +998,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) {
 | |
| @@ -1185,8 +1185,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;
 | |
|      }
 | |
|  
 | |
| @@ -1808,6 +1807,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted(
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +void RuntimeService::ResetDefaultLocaleInAllWorkers() {
 | |
| +  AssertIsOnMainThread();
 | |
| +  BroadcastAllWorkers([](auto& worker) {
 | |
| +    worker.ResetDefaultLocale();
 | |
| +  });
 | |
| +}
 | |
| +
 | |
|  template <typename Func>
 | |
|  void RuntimeService::BroadcastAllWorkers(const Func& aFunc) {
 | |
|    AssertIsOnMainThread();
 | |
| @@ -2333,6 +2339,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers(
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +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 534bbe9ec4f0261189eb3322c1229c1eb5d8802e..6aa99b64fdbbff3704602e944b129879fbdf8c15 100644
 | |
| --- a/dom/workers/RuntimeService.h
 | |
| +++ b/dom/workers/RuntimeService.h
 | |
| @@ -112,6 +112,8 @@ class RuntimeService final : public nsIObserver {
 | |
|    void PropagateStorageAccessPermissionGranted(
 | |
|        const nsPIDOMWindowInner& aWindow);
 | |
|  
 | |
| +  void ResetDefaultLocaleInAllWorkers();
 | |
| +
 | |
|    const NavigatorProperties& GetNavigatorProperties() const {
 | |
|      return mNavigatorProperties;
 | |
|    }
 | |
| diff --git a/dom/workers/WorkerCommon.h b/dom/workers/WorkerCommon.h
 | |
| index 58894a8361c7ef1dddd481ca5877a209a8b8ff5c..c481d40d79b6397b7f1d571bd9f6ae5c0a946217 100644
 | |
| --- a/dom/workers/WorkerCommon.h
 | |
| +++ b/dom/workers/WorkerCommon.h
 | |
| @@ -47,6 +47,8 @@ void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow);
 | |
|  void PropagateStorageAccessPermissionGrantedToWorkers(
 | |
|      const 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 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b59662e55f37 100644
 | |
| --- a/dom/workers/WorkerPrivate.cpp
 | |
| +++ b/dom/workers/WorkerPrivate.cpp
 | |
| @@ -700,6 +700,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
 | |
|    }
 | |
|  };
 | |
|  
 | |
| +class ResetDefaultLocaleRunnable final : public WorkerControlRunnable {
 | |
| + public:
 | |
| +  explicit ResetDefaultLocaleRunnable(WorkerPrivate* aWorkerPrivate)
 | |
| +      : WorkerControlRunnable("ResetDefaultLocaleRunnable") {}
 | |
| +
 | |
| +  virtual bool WorkerRun(JSContext* aCx,
 | |
| +                         WorkerPrivate* aWorkerPrivate) override {
 | |
| +    aWorkerPrivate->ResetDefaultLocaleInternal(aCx);
 | |
| +    return true;
 | |
| +  }
 | |
| +};
 | |
| +
 | |
|  class UpdateLanguagesRunnable final : public WorkerThreadRunnable {
 | |
|    nsTArray<nsString> mLanguages;
 | |
|  
 | |
| @@ -2108,6 +2120,16 @@ void WorkerPrivate::UpdateContextOptions(
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +void WorkerPrivate::ResetDefaultLocale() {
 | |
| +  AssertIsOnParentThread();
 | |
| +
 | |
| +  RefPtr<ResetDefaultLocaleRunnable> runnable =
 | |
| +      new ResetDefaultLocaleRunnable(this);
 | |
| +  if (!runnable->Dispatch(this)) {
 | |
| +    NS_WARNING("Failed to reset default locale in worker!");
 | |
| +  }
 | |
| +}
 | |
| +
 | |
|  void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
 | |
|    AssertIsOnParentThread();
 | |
|  
 | |
| @@ -5736,6 +5758,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +void WorkerPrivate::ResetDefaultLocaleInternal(JSContext* aCx) {
 | |
| +  JS_ResetDefaultLocale(JS_GetRuntime(aCx));
 | |
| +  auto data = mWorkerThreadAccessible.Access();
 | |
| +
 | |
| +  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 dfb96b7b798785d7b75c683bc0969e39487137a3..a463eec618af51fdbc25db509870598846c0fd66 100644
 | |
| --- a/dom/workers/WorkerPrivate.h
 | |
| +++ b/dom/workers/WorkerPrivate.h
 | |
| @@ -432,6 +432,8 @@ class WorkerPrivate final
 | |
|    void UpdateContextOptionsInternal(JSContext* aCx,
 | |
|                                      const JS::ContextOptions& aContextOptions);
 | |
|  
 | |
| +  void ResetDefaultLocaleInternal(JSContext* aCx);
 | |
| +
 | |
|    void UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
 | |
|  
 | |
|    void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key,
 | |
| @@ -1059,6 +1061,8 @@ class WorkerPrivate final
 | |
|  
 | |
|    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/intl/components/src/TimeZone.cpp b/intl/components/src/TimeZone.cpp
 | |
| index 7a069ef0c59895cf1f8dc35d612f1494c9c9f1ef..5b09dfdcc5323def65c35b0696141b44eef9dcda 100644
 | |
| --- a/intl/components/src/TimeZone.cpp
 | |
| +++ b/intl/components/src/TimeZone.cpp
 | |
| @@ -16,6 +16,7 @@
 | |
|  
 | |
|  namespace mozilla::intl {
 | |
|  
 | |
| +
 | |
|  /* static */
 | |
|  Result<UniquePtr<TimeZone>, ICUError> TimeZone::TryCreate(
 | |
|      Maybe<Span<const char16_t>> aTimeZoneOverride) {
 | |
| @@ -318,6 +319,13 @@ static ICUResult SetDefaultTimeZone(TimeZoneIdentifierVector& timeZone) {
 | |
|  }
 | |
|  #endif
 | |
|  
 | |
| +bool TimeZone::IsValidTimeZoneId(const char* timeZoneId) {
 | |
| +  // Validate timezone id.
 | |
| +  mozilla::UniquePtr<icu::TimeZone> timeZone(icu::TimeZone::createTimeZone(
 | |
| +      icu::UnicodeString(timeZoneId, -1, US_INV)));
 | |
| +  return timeZone && *timeZone != icu::TimeZone::getUnknown();
 | |
| +}
 | |
| +
 | |
|  Result<bool, ICUError> TimeZone::SetDefaultTimeZone(
 | |
|      Span<const char> aTimeZone) {
 | |
|  #if MOZ_INTL_USE_ICU_CPP_TIMEZONE
 | |
| diff --git a/intl/components/src/TimeZone.h b/intl/components/src/TimeZone.h
 | |
| index 89770839ae108b5f3462a7f20684fdb72c4ab2fb..a7e40d6b7c33c234b41e586eac573ba4ce3a7d18 100644
 | |
| --- a/intl/components/src/TimeZone.h
 | |
| +++ b/intl/components/src/TimeZone.h
 | |
| @@ -191,6 +191,8 @@ class TimeZone final {
 | |
|      return FillBufferWithICUCall(aBuffer, ucal_getHostTimeZone);
 | |
|    }
 | |
|  
 | |
| +  static bool IsValidTimeZoneId(const char* timeZoneId);
 | |
| +
 | |
|    /**
 | |
|     * Set the default time zone.
 | |
|     */
 | |
| diff --git a/js/public/Date.h b/js/public/Date.h
 | |
| index 523e84c8c93f4221701f90f2e8ee146ec8e1adbd..98d5b1176e5378431b859a2dbd4d4e778d236e78 100644
 | |
| --- a/js/public/Date.h
 | |
| +++ b/js/public/Date.h
 | |
| @@ -55,6 +55,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 6ca6c31830066f9677988daca8566e93a8335c26..3f963dbf6127c997810e380802e56b023b5db4c8 100644
 | |
| --- a/js/src/debugger/Object.cpp
 | |
| +++ b/js/src/debugger/Object.cpp
 | |
| @@ -2468,7 +2468,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 623a6863a54fb0d653ebe55fd83356f1a8c8be15..1c0ef7b0d3ee2f61de728a68dd704a5d09757b38 100644
 | |
| --- a/js/src/vm/DateTime.cpp
 | |
| +++ b/js/src/vm/DateTime.cpp
 | |
| @@ -186,6 +186,11 @@ void js::DateTimeInfo::internalResetTimeZone(ResetTimeZoneMode mode) {
 | |
|    }
 | |
|  }
 | |
|  
 | |
| +void js::DateTimeInfo::internalSetTimeZoneOverride(std::string timeZone) {
 | |
| +  timeZoneOverride_ = std::move(timeZone);
 | |
| +  internalResetTimeZone(ResetTimeZoneMode::ResetEvenIfOffsetUnchanged);
 | |
| +}
 | |
| +
 | |
|  void js::DateTimeInfo::updateTimeZone() {
 | |
|    MOZ_ASSERT(timeZoneStatus_ != TimeZoneStatus::Valid);
 | |
|  
 | |
| @@ -529,10 +534,24 @@ void js::ResetTimeZoneInternal(ResetTimeZoneMode mode) {
 | |
|    js::DateTimeInfo::resetTimeZone(mode);
 | |
|  }
 | |
|  
 | |
| +void js::SetTimeZoneOverrideInternal(std::string timeZone) {
 | |
| +  auto guard = js::DateTimeInfo::instance->lock();
 | |
| +  guard->internalSetTimeZoneOverride(timeZone);
 | |
| +}
 | |
| +
 | |
|  JS_PUBLIC_API void JS::ResetTimeZone() {
 | |
|    js::ResetTimeZoneInternal(js::ResetTimeZoneMode::ResetEvenIfOffsetUnchanged);
 | |
|  }
 | |
|  
 | |
| +JS_PUBLIC_API bool JS::SetTimeZoneOverride(const char* timeZoneId) {
 | |
| +  if (!mozilla::intl::TimeZone::IsValidTimeZoneId(timeZoneId)) {
 | |
| +    fprintf(stderr, "Invalid timezone id: %s\n", timeZoneId);
 | |
| +    return false;
 | |
| +  }
 | |
| +  js::SetTimeZoneOverrideInternal(std::string(timeZoneId));
 | |
| +  return true;
 | |
| +}
 | |
| +
 | |
|  #if JS_HAS_INTL_API
 | |
|  #  if defined(XP_WIN)
 | |
|  static bool IsOlsonCompatibleWindowsTimeZoneId(std::string_view tz) {
 | |
| @@ -750,6 +769,15 @@ static bool ReadTimeZoneLink(std::string_view tz,
 | |
|  
 | |
|  void js::DateTimeInfo::internalResyncICUDefaultTimeZone() {
 | |
|  #if JS_HAS_INTL_API
 | |
| +  if (!timeZoneOverride_.empty()) {
 | |
| +    mozilla::Span<const char> tzid = mozilla::Span(timeZoneOverride_.data(), timeZoneOverride_.length());
 | |
| +    auto result = mozilla::intl::TimeZone::SetDefaultTimeZone(tzid);
 | |
| +    if (result.isErr()) {
 | |
| +      fprintf(stderr, "ERROR: failed to setup default time zone\n");
 | |
| +    }
 | |
| +    return;
 | |
| +  }
 | |
| +
 | |
|    // In the future we should not be setting a default ICU time zone at all,
 | |
|    // instead all accesses should go through the appropriate DateTimeInfo
 | |
|    // instance depending on the resist fingerprinting status. For now we return
 | |
| @@ -761,7 +789,6 @@ void js::DateTimeInfo::internalResyncICUDefaultTimeZone() {
 | |
|  
 | |
|    if (const char* tzenv = std::getenv("TZ")) {
 | |
|      std::string_view tz(tzenv);
 | |
| -
 | |
|      mozilla::Span<const char> tzid;
 | |
|  
 | |
|  #  if defined(XP_WIN)
 | |
| diff --git a/js/src/vm/DateTime.h b/js/src/vm/DateTime.h
 | |
| index fd6d7ae078b8f6b3cc46a4a993a1e044a7128c90..4743094e489122dd9ee8ab9a7a175dd7e928859d 100644
 | |
| --- a/js/src/vm/DateTime.h
 | |
| +++ b/js/src/vm/DateTime.h
 | |
| @@ -65,6 +65,8 @@ enum class ResetTimeZoneMode : bool {
 | |
|   */
 | |
|  extern void ResetTimeZoneInternal(ResetTimeZoneMode mode);
 | |
|  
 | |
| +extern void SetTimeZoneOverrideInternal(std::string timeZone);
 | |
| +
 | |
|  /**
 | |
|   * Stores date/time information, particularly concerning the current local
 | |
|   * time zone, and implements a small cache for daylight saving time offset
 | |
| @@ -225,6 +227,7 @@ class DateTimeInfo {
 | |
|   private:
 | |
|    // The method below should only be called via js::ResetTimeZoneInternal().
 | |
|    friend void js::ResetTimeZoneInternal(ResetTimeZoneMode);
 | |
| +  friend void js::SetTimeZoneOverrideInternal(std::string);
 | |
|  
 | |
|    static void resetTimeZone(ResetTimeZoneMode mode) {
 | |
|      {
 | |
| @@ -321,6 +324,8 @@ class DateTimeInfo {
 | |
|    JS::UniqueChars locale_;
 | |
|    JS::UniqueTwoByteChars standardName_;
 | |
|    JS::UniqueTwoByteChars daylightSavingsName_;
 | |
| +
 | |
| +  std::string 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
 | |
| @@ -336,6 +341,8 @@ class DateTimeInfo {
 | |
|  
 | |
|    void internalResetTimeZone(ResetTimeZoneMode mode);
 | |
|  
 | |
| +  void internalSetTimeZoneOverride(std::string timeZone);
 | |
| +
 | |
|    void updateTimeZone();
 | |
|  
 | |
|    void internalResyncICUDefaultTimeZone();
 | |
| diff --git a/layout/base/GeometryUtils.cpp b/layout/base/GeometryUtils.cpp
 | |
| index 0ec6ee3eb37c6493d8a25352fd0e54e1927bceab..885dba71bc5815e5f6f3ec2700c376aa119b30d0 100644
 | |
| --- a/layout/base/GeometryUtils.cpp
 | |
| +++ b/layout/base/GeometryUtils.cpp
 | |
| @@ -23,6 +23,7 @@
 | |
|  #include "nsContentUtils.h"
 | |
|  #include "nsCSSFrameConstructor.h"
 | |
|  #include "nsLayoutUtils.h"
 | |
| +#include "ChildIterator.h"
 | |
|  
 | |
|  using namespace mozilla;
 | |
|  using namespace mozilla::dom;
 | |
| @@ -261,11 +262,27 @@ static bool CheckFramesInSameTopLevelBrowsingContext(nsIFrame* aFrame1,
 | |
|    return false;
 | |
|  }
 | |
|  
 | |
| +static nsIFrame* GetFrameForNode(nsINode* aNode,
 | |
| +                                 bool aCreateFramesForSuppressedWhitespace,
 | |
| +                                 bool aRecurseWhenNoFrame) {
 | |
| +  nsIFrame* frame = GetFrameForNode(aNode, aCreateFramesForSuppressedWhitespace);
 | |
| +  if (!frame && aRecurseWhenNoFrame && aNode->IsContent()) {
 | |
| +    dom::FlattenedChildIterator iter(aNode->AsContent());
 | |
| +    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
 | |
| +      frame = GetFrameForNode(child, aCreateFramesForSuppressedWhitespace, aRecurseWhenNoFrame);
 | |
| +      if (frame) {
 | |
| +        break;
 | |
| +      }
 | |
| +    }
 | |
| +  }
 | |
| +  return frame;
 | |
| +}
 | |
| +
 | |
|  void GetBoxQuads(nsINode* aNode, const dom::BoxQuadOptions& aOptions,
 | |
|                   nsTArray<RefPtr<DOMQuad> >& aResult, CallerType aCallerType,
 | |
|                   ErrorResult& aRv) {
 | |
|    nsIFrame* frame =
 | |
| -      GetFrameForNode(aNode, aOptions.mCreateFramesForSuppressedWhitespace);
 | |
| +      GetFrameForNode(aNode, aOptions.mCreateFramesForSuppressedWhitespace, aOptions.mRecurseWhenNoFrame);
 | |
|    if (!frame) {
 | |
|      // No boxes to return
 | |
|      return;
 | |
| @@ -280,7 +297,7 @@ void GetBoxQuads(nsINode* aNode, const dom::BoxQuadOptions& aOptions,
 | |
|    // when that happens and re-check it.
 | |
|    if (!weakFrame.IsAlive()) {
 | |
|      frame =
 | |
| -        GetFrameForNode(aNode, aOptions.mCreateFramesForSuppressedWhitespace);
 | |
| +        GetFrameForNode(aNode, aOptions.mCreateFramesForSuppressedWhitespace, aOptions.mRecurseWhenNoFrame);
 | |
|      if (!frame) {
 | |
|        // No boxes to return
 | |
|        return;
 | |
| diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
 | |
| index f154e05a8c2e2ebf07565d087a42436feeb17f53..0af8c24c0f391c52fe2acfeb01cacb32358e6861 100644
 | |
| --- a/layout/base/PresShell.cpp
 | |
| +++ b/layout/base/PresShell.cpp
 | |
| @@ -11064,7 +11064,9 @@ bool PresShell::ComputeActiveness() const {
 | |
|      if (!browserChild->IsVisible()) {
 | |
|        MOZ_LOG(gLog, LogLevel::Debug,
 | |
|                (" > BrowserChild %p is not visible", browserChild));
 | |
| -      return false;
 | |
| +      bool isActive;
 | |
| +      root->GetDocShell()->GetForceActiveState(&isActive);
 | |
| +      return isActive;
 | |
|      }
 | |
|  
 | |
|      // If the browser is visible but just due to be preserving layers
 | |
| diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
 | |
| index 0011b1a1a36da0dec7cc6afa6fd689a4c8710d37..25bebd7b03b0b8dc595607bae07f360f3be3f284 100644
 | |
| --- a/layout/base/nsLayoutUtils.cpp
 | |
| +++ b/layout/base/nsLayoutUtils.cpp
 | |
| @@ -698,6 +698,10 @@ bool nsLayoutUtils::AllowZoomingForDocument(
 | |
|        !aDocument->GetPresShell()->AsyncPanZoomEnabled()) {
 | |
|      return false;
 | |
|    }
 | |
| +
 | |
| +  /* Playwright: disable zooming as we don't support meta viewport tag */
 | |
| +  if (1 == 1) return false;
 | |
| +
 | |
|    // True if we allow zooming for all documents on this platform, or if we are
 | |
|    // in RDM.
 | |
|    BrowsingContext* bc = aDocument->GetBrowsingContext();
 | |
| @@ -9791,6 +9795,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
 | |
|  
 | |
|  /* static */
 | |
|  bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) {
 | |
| +  /* Playwright: disable meta viewport handling since we don't require one */
 | |
| +  if (1 == 1) return false;
 | |
| +
 | |
|    BrowsingContext* bc = aDocument->GetBrowsingContext();
 | |
|    return StaticPrefs::dom_meta_viewport_enabled() || (bc && bc->InRDMPane());
 | |
|  }
 | |
| diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h
 | |
| index d273793fc8d92b5c19ec0562730eab249cc41eb8..46b4078c6031318265a8338e01f52ab60bd9c0e8 100644
 | |
| --- a/layout/style/GeckoBindings.h
 | |
| +++ b/layout/style/GeckoBindings.h
 | |
| @@ -596,6 +596,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*);
 | |
|  bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*);
 | |
|  bool Gecko_MediaFeatures_PrefersReducedTransparency(
 | |
|      const mozilla::dom::Document*);
 | |
| +bool Gecko_MediaFeatures_ForcedColors(const mozilla::dom::Document*);
 | |
|  mozilla::StylePrefersContrast Gecko_MediaFeatures_PrefersContrast(
 | |
|      const mozilla::dom::Document*);
 | |
|  mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
 | |
| diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
 | |
| index cc86d1abf6ccfe48530607c41cd675612cbe5582..8cce20c719fee8a0480ae6ea1fd53c6639d0bd7b 100644
 | |
| --- a/layout/style/nsMediaFeatures.cpp
 | |
| +++ b/layout/style/nsMediaFeatures.cpp
 | |
| @@ -260,11 +260,11 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
 | |
|  }
 | |
|  
 | |
|  bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
 | |
| -  if (aDocument->ShouldResistFingerprinting(
 | |
| -          RFPTarget::CSSPrefersReducedMotion)) {
 | |
| -    return false;
 | |
| -  }
 | |
| -  return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
 | |
| +  return aDocument->PrefersReducedMotion();
 | |
| +}
 | |
| +
 | |
| +bool Gecko_MediaFeatures_ForcedColors(const Document* aDocument) {
 | |
| +  return aDocument->ForcedColors();
 | |
|  }
 | |
|  
 | |
|  bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) {
 | |
| diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
 | |
| index 21d5a5e1b4193d058c30268ab73c8d595436b381..11b960ec0ff3ea77857cb915d05bbdbb6772bb37 100644
 | |
| --- a/netwerk/base/LoadInfo.cpp
 | |
| +++ b/netwerk/base/LoadInfo.cpp
 | |
| @@ -693,7 +693,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
 | |
|        mHasInjectedCookieForCookieBannerHandling(
 | |
|            rhs.mHasInjectedCookieForCookieBannerHandling),
 | |
|        mWasSchemelessInput(rhs.mWasSchemelessInput),
 | |
| -      mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry) {
 | |
| +      mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry),
 | |
| +      mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) {
 | |
|  }
 | |
|  
 | |
|  LoadInfo::LoadInfo(
 | |
| @@ -2461,4 +2462,16 @@ LoadInfo::SetHttpsUpgradeTelemetry(
 | |
|    return NS_OK;
 | |
|  }
 | |
|  
 | |
| +NS_IMETHODIMP
 | |
| +LoadInfo::GetJugglerLoadIdentifier(uint64_t* aResult) {
 | |
| +  *aResult = mJugglerLoadIdentifier;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +LoadInfo::SetJugglerLoadIdentifier(uint64_t aID) {
 | |
| +  mJugglerLoadIdentifier = aID;
 | |
| +  return NS_OK;
 | |
| +}
 | |
| +
 | |
|  }  // namespace mozilla::net
 | |
| diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h
 | |
| index 52d867196a459578cbea1a4f626afbe51dd1abd5..2904832cbcad476fdebb54c7e24d5f14b1c0fb4b 100644
 | |
| --- a/netwerk/base/LoadInfo.h
 | |
| +++ b/netwerk/base/LoadInfo.h
 | |
| @@ -413,9 +413,10 @@ class LoadInfo final : public nsILoadInfo {
 | |
|  
 | |
|    bool mHasInjectedCookieForCookieBannerHandling = false;
 | |
|    bool mWasSchemelessInput = false;
 | |
| -
 | |
|    nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry =
 | |
|        nsILoadInfo::NO_UPGRADE;
 | |
| +
 | |
| +  uint64_t mJugglerLoadIdentifier = 0;
 | |
|  };
 | |
|  
 | |
|  // This is exposed solely for testing purposes and should not be used outside of
 | |
| diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp
 | |
| index 9dc2bb0da6871b905abd17d931e555429977c6c2..b71cf6393492346f16417b3ba745a235a483be22 100644
 | |
| --- a/netwerk/base/TRRLoadInfo.cpp
 | |
| +++ b/netwerk/base/TRRLoadInfo.cpp
 | |
| @@ -903,5 +903,15 @@ TRRLoadInfo::SetHttpsUpgradeTelemetry(
 | |
|    return NS_ERROR_NOT_IMPLEMENTED;
 | |
|  }
 | |
|  
 | |
| +NS_IMETHODIMP
 | |
| +TRRLoadInfo::GetJugglerLoadIdentifier(uint64_t* aResult) {
 | |
| +  return NS_ERROR_NOT_IMPLEMENTED;
 | |
| +}
 | |
| +
 | |
| +NS_IMETHODIMP
 | |
| +TRRLoadInfo::SetJugglerLoadIdentifier(uint64_t aResult) {
 | |
| +  return NS_ERROR_NOT_IMPLEMENTED;
 | |
| +}
 | |
| +
 | |
|  }  // namespace net
 | |
|  }  // namespace mozilla
 | |
| diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl
 | |
| index 12f43b911006d5b0bbfa9936070dc0d561bc7bb4..94d20cdca548534ad5e4ef4f937e287c58768870 100644
 | |
| --- a/netwerk/base/nsILoadInfo.idl
 | |
| +++ b/netwerk/base/nsILoadInfo.idl
 | |
| @@ -1586,4 +1586,5 @@ interface nsILoadInfo : nsISupports
 | |
|     */
 | |
|    [infallible] attribute nsILoadInfo_HTTPSUpgradeTelemetryType httpsUpgradeTelemetry;
 | |
|  
 | |
| +  [infallible] attribute unsigned long long jugglerLoadIdentifier;
 | |
|  };
 | |
| diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
 | |
| index 7f91d2df6f8bb4020c75c132dc8f6bf26625fa1e..ba6569f4be8fc54ec96ee44d5de45a0904c077ba 100644
 | |
| --- a/netwerk/base/nsINetworkInterceptController.idl
 | |
| +++ b/netwerk/base/nsINetworkInterceptController.idl
 | |
| @@ -59,6 +59,7 @@ interface nsIInterceptedChannel : nsISupports
 | |
|       * results in the resulting client not being controlled.
 | |
|       */
 | |
|      void resetInterception(in boolean bypass);
 | |
| +    void resetInterceptionWithURI(in nsIURI aURI);
 | |
|  
 | |
|      /**
 | |
|       * Set the status and reason for the forthcoming synthesized response.
 | |
| diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp
 | |
| index 10f65a549ce886bf7f19de02714482e28a8931a5..f41d32ce90f7345ad5a9bd90e420354865f35235 100644
 | |
| --- a/netwerk/ipc/DocumentLoadListener.cpp
 | |
| +++ b/netwerk/ipc/DocumentLoadListener.cpp
 | |
| @@ -171,6 +171,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
 | |
|    loadInfo->SetTextDirectiveUserActivation(
 | |
|        aLoadState->GetTextDirectiveUserActivation());
 | |
|    loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
 | |
| +  loadInfo->SetJugglerLoadIdentifier(aLoadState->GetLoadIdentifier());
 | |
|  
 | |
|    return loadInfo.forget();
 | |
|  }
 | |
| diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp
 | |
| index e81a4538fd45c13aa60d933de5f4f32ce69fb5f2..d7945f81295c497485a09696f06ce041c1cd8079 100644
 | |
| --- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
 | |
| +++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
 | |
| @@ -727,6 +727,14 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor)
 | |
|  
 | |
|  }  // anonymous namespace
 | |
|  
 | |
| +NS_IMETHODIMP
 | |
| +InterceptedHttpChannel::ResetInterceptionWithURI(nsIURI* aURI) {
 | |
| +  if (aURI) {
 | |
| +    mURI = aURI;
 | |
| +  }
 | |
| +  return ResetInterception(true);
 | |
| +}
 | |
| +
 | |
|  NS_IMETHODIMP
 | |
|  InterceptedHttpChannel::ResetInterception(bool aBypass) {
 | |
|    INTERCEPTED_LOG(("InterceptedHttpChannel::ResetInterception [%p] bypass: %s",
 | |
| @@ -1140,11 +1148,18 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
 | |
|      GetCallback(mProgressSink);
 | |
|    }
 | |
|  
 | |
| +  // Playwright: main requests in firefox do not have loading principal.
 | |
| +  // As they are intercepted by Playwright, they don't have
 | |
| +  // serviceWorkerTainting as well.
 | |
| +  // Thus these asserts are wrong for Playwright world.
 | |
| +  // Note: these checks were added in https://github.com/mozilla/gecko-dev/commit/92e2cdde79c11510c3e4192e1b6264d00398ed95
 | |
| +  /*
 | |
|    MOZ_ASSERT_IF(!mLoadInfo->GetServiceWorkerTaintingSynthesized(),
 | |
|                  mLoadInfo->GetLoadingPrincipal());
 | |
|    // No need to do ORB checks if these conditions hold.
 | |
|    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo->GetServiceWorkerTaintingSynthesized() ||
 | |
|                          mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal());
 | |
| +  */
 | |
|  
 | |
|    if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
 | |
|      mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
 | |
| diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
 | |
| index f25949e6cc907ff18a76d68fc2e8005bd40146ea..9be4cb34517b06b94c6e145aef8a8ea5d2687d97 100644
 | |
| --- a/parser/html/nsHtml5TreeOpExecutor.cpp
 | |
| +++ b/parser/html/nsHtml5TreeOpExecutor.cpp
 | |
| @@ -1389,6 +1389,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
 | |
|  void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
 | |
|    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/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp
 | |
| index fcc2a45e6de8eaeb1af2404a69bd3df58cf2aec8..d4c1df007bf5993cf9e0dadbe91aa2c38afc42ec 100644
 | |
| --- a/security/manager/ssl/nsCertOverrideService.cpp
 | |
| +++ b/security/manager/ssl/nsCertOverrideService.cpp
 | |
| @@ -437,7 +437,12 @@ nsCertOverrideService::HasMatchingOverride(
 | |
|    bool disableAllSecurityCheck = false;
 | |
|    {
 | |
|      MutexAutoLock lock(mMutex);
 | |
| -    disableAllSecurityCheck = mDisableAllSecurityCheck;
 | |
| +    if (aOriginAttributes.mUserContextId) {
 | |
| +      disableAllSecurityCheck = mUserContextIdsWithDisabledSecurityChecks.has(
 | |
| +          aOriginAttributes.mUserContextId);
 | |
| +    } else {
 | |
| +      disableAllSecurityCheck = mDisableAllSecurityCheck;
 | |
| +    }
 | |
|    }
 | |
|    if (disableAllSecurityCheck) {
 | |
|      *aIsTemporary = false;
 | |
| @@ -649,14 +654,24 @@ 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);
 | |
| -    mDisableAllSecurityCheck = aDisable;
 | |
| +    if (aUserContextId) {
 | |
| +      if (aDisable) {
 | |
| +        mozilla::Unused << mUserContextIdsWithDisabledSecurityChecks.put(aUserContextId);
 | |
| +      } else {
 | |
| +        mUserContextIdsWithDisabledSecurityChecks.remove(aUserContextId);
 | |
| +      }
 | |
| +      return NS_OK;
 | |
| +    } else {
 | |
| +      mDisableAllSecurityCheck = aDisable;
 | |
| +    }
 | |
|    }
 | |
|  
 | |
|    nsCOMPtr<nsINSSComponent> nss(do_GetService(PSM_COMPONENT_CONTRACTID));
 | |
| diff --git a/security/manager/ssl/nsCertOverrideService.h b/security/manager/ssl/nsCertOverrideService.h
 | |
| index 21cff56300db6490cf9649aa62099cb5525749b3..ce9a7fc16c2d5980be166e0f4ab9a25df300ca2f 100644
 | |
| --- a/security/manager/ssl/nsCertOverrideService.h
 | |
| +++ b/security/manager/ssl/nsCertOverrideService.h
 | |
| @@ -118,6 +118,7 @@ class nsCertOverrideService final : public nsICertOverrideService,
 | |
|  
 | |
|    mozilla::Mutex mMutex;
 | |
|    bool mDisableAllSecurityCheck MOZ_GUARDED_BY(mMutex);
 | |
| +  mozilla::HashSet<uint32_t> mUserContextIdsWithDisabledSecurityChecks MOZ_GUARDED_BY(mMutex);
 | |
|    nsCOMPtr<nsIFile> mSettingsFile MOZ_GUARDED_BY(mMutex);
 | |
|    nsTHashtable<nsCertOverrideEntry> mSettingsTable MOZ_GUARDED_BY(mMutex);
 | |
|  
 | |
| diff --git a/security/manager/ssl/nsICertOverrideService.idl b/security/manager/ssl/nsICertOverrideService.idl
 | |
| index 6dfd07d6b676a99993408921de8dea9d561f201d..e3c6794363cd6336effbeac83a179f3796dd71b0 100644
 | |
| --- a/security/manager/ssl/nsICertOverrideService.idl
 | |
| +++ b/security/manager/ssl/nsICertOverrideService.idl
 | |
| @@ -137,7 +137,9 @@ 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);
 | |
|  
 | |
|    readonly attribute boolean securityCheckDisabled;
 | |
|  };
 | |
| diff --git a/services/settings/Utils.sys.mjs b/services/settings/Utils.sys.mjs
 | |
| index 73c83e526be1a3a252f995d0718e3975d50bffa7..db5977c54221e19e107a8325a0834302c2e84546 100644
 | |
| --- a/services/settings/Utils.sys.mjs
 | |
| +++ b/services/settings/Utils.sys.mjs
 | |
| @@ -95,7 +95,7 @@ function _isUndefined(value) {
 | |
|  
 | |
|  export var Utils = {
 | |
|    get SERVER_URL() {
 | |
| -    return lazy.allowServerURLOverride
 | |
| +    return true || lazy.allowServerURLOverride
 | |
|        ? lazy.gServerURL
 | |
|        : AppConstants.REMOTE_SETTINGS_SERVER_URL;
 | |
|    },
 | |
| @@ -108,6 +108,9 @@ export var Utils = {
 | |
|    log,
 | |
|  
 | |
|    get shouldSkipRemoteActivityDueToTests() {
 | |
| +    // Playwright does not set Cu.isInAutomation, hence we just return true
 | |
| +    // here in order to disable the remote activity.
 | |
| +    return true;
 | |
|      return (
 | |
|        (lazy.isRunningTests || Cu.isInAutomation) &&
 | |
|        this.SERVER_URL == "data:,#remote-settings-dummy/v1"
 | |
| diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs
 | |
| index df1c5e464b845b6a8bfedadb86d0e7aab7fd3ffc..34451e791bb59f635134de702d9e5f641fe8df79 100644
 | |
| --- a/servo/components/style/gecko/media_features.rs
 | |
| +++ b/servo/components/style/gecko/media_features.rs
 | |
| @@ -303,10 +303,16 @@ impl ForcedColors {
 | |
|  
 | |
|  /// https://drafts.csswg.org/mediaqueries-5/#forced-colors
 | |
|  fn eval_forced_colors(context: &Context, query_value: Option<ForcedColors>) -> bool {
 | |
| -    let forced = context.device().forced_colors();
 | |
| +    let prefers_forced_colors =
 | |
| +        unsafe { bindings::Gecko_MediaFeatures_ForcedColors(context.device().document()) };
 | |
| +    let query_value = match query_value {
 | |
| +        Some(v) => v,
 | |
| +        None => return prefers_forced_colors,
 | |
| +    };
 | |
|      match query_value {
 | |
| -        Some(query_value) => query_value == forced,
 | |
| -        None => forced != ForcedColors::None,
 | |
| +        ForcedColors::Active => prefers_forced_colors,
 | |
| +        ForcedColors::Requested => prefers_forced_colors,
 | |
| +        ForcedColors::None => !prefers_forced_colors,
 | |
|      }
 | |
|  }
 | |
|  
 | |
| diff --git a/toolkit/components/browser/nsIWebBrowserChrome.idl b/toolkit/components/browser/nsIWebBrowserChrome.idl
 | |
| index 75555352b8a15a50e4a21e34fc8ede4e9246c7cc..72855a404effa42b6c55cd0c2fcb8bdd6c2b3f9f 100644
 | |
| --- a/toolkit/components/browser/nsIWebBrowserChrome.idl
 | |
| +++ b/toolkit/components/browser/nsIWebBrowserChrome.idl
 | |
| @@ -74,6 +74,9 @@ interface nsIWebBrowserChrome : nsISupports
 | |
|      // Whether this window should use out-of-process cross-origin subframes.
 | |
|      const unsigned long CHROME_FISSION_WINDOW         = 1 << 21;
 | |
|  
 | |
| +    // Whether this window has "width" or "height" defined in features
 | |
| +    const unsigned long JUGGLER_WINDOW_EXPLICIT_SIZE  = 0x00400000;
 | |
| +
 | |
|      // Prevents new window animations on MacOS and Windows. Currently
 | |
|      // ignored for Linux.
 | |
|      const unsigned long CHROME_SUPPRESS_ANIMATION     = 1 << 24;
 | |
| diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
 | |
| index 00a5381133f8cec0de452c31c7151801a1acc0b9..5d3e3d6f566dc724f257beaeb994cedaa7e71139 100644
 | |
| --- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
 | |
| +++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
 | |
| @@ -108,6 +108,12 @@ EnterprisePoliciesManager.prototype = {
 | |
|        Services.prefs.clearUserPref(PREF_POLICIES_APPLIED);
 | |
|      }
 | |
|  
 | |
| +    // Playwright: Disable enterprise policies
 | |
| +    if (true) {
 | |
| +      this.status = Ci.nsIEnterprisePolicies.INACTIVE;
 | |
| +      return;
 | |
| +    }
 | |
| +
 | |
|      let provider = this._chooseProvider();
 | |
|  
 | |
|      if (provider.failed) {
 | |
| diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
 | |
| index 144628a310662eb393d8c1a4fffbec3cf5fd1dff..69fa66f27d0533f3d90801acbfa23039ef81f7df 100644
 | |
| --- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
 | |
| +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
 | |
| @@ -632,7 +632,7 @@ void PopulateLanguages() {
 | |
|    // sufficient to only collect this information as the other properties are
 | |
|    // just reformats of Navigator::GetAcceptLanguages.
 | |
|    nsTArray<nsString> languages;
 | |
| -  dom::Navigator::GetAcceptLanguages(languages);
 | |
| +  dom::Navigator::GetAcceptLanguages(nullptr, languages);
 | |
|    nsCString output = "["_ns;
 | |
|  
 | |
|    for (const auto& language : languages) {
 | |
| diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp
 | |
| index 3314cb813f6ceb67096eeda0864ad3b16c0616cb..5aac63649e186d624a9905a5d16513f8353f5515 100644
 | |
| --- a/toolkit/components/startup/nsAppStartup.cpp
 | |
| +++ b/toolkit/components/startup/nsAppStartup.cpp
 | |
| @@ -371,7 +371,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
 | |
|      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 654903fadb709be976b72f36f155e23bc0622152..815b3dc24c9fda6b1db6c4666ac68904c87ac0ab 100644
 | |
| --- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
 | |
| +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
 | |
| @@ -174,8 +174,8 @@ nsBrowserStatusFilter::OnStateChange(nsIWebProgress* aWebProgress,
 | |
|  }
 | |
|  
 | |
|  NS_IMETHODIMP
 | |
| -nsBrowserStatusFilter::OnProgressChange(nsIWebProgress* aWebProgress,
 | |
| -                                        nsIRequest* aRequest,
 | |
| +nsBrowserStatusFilter::OnProgressChange(nsIWebProgress *aWebProgress,
 | |
| +                                        nsIRequest *aRequest,
 | |
|                                          int32_t aCurSelfProgress,
 | |
|                                          int32_t aMaxSelfProgress,
 | |
|                                          int32_t aCurTotalProgress,
 | |
| diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
 | |
| index cdba76dc8ae2206a58d7e5eb6eba97c2c3732513..266fdc6235363eafc6c7b587d5c0f597deee6e59 100644
 | |
| --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
 | |
| +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
 | |
| @@ -1865,7 +1865,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
 | |
|  
 | |
|    // Open a minimal popup.
 | |
|    *aIsPopupRequested = true;
 | |
| -  return nsIWebBrowserChrome::CHROME_MINIMAL_POPUP;
 | |
| +  uint32_t chromeFlags = 0;
 | |
| +  if (aFeatures.Exists("width") || aFeatures.Exists("height")) {
 | |
| +    chromeFlags |= nsIWebBrowserChrome::JUGGLER_WINDOW_EXPLICIT_SIZE;
 | |
| +  }
 | |
| +  return chromeFlags | nsIWebBrowserChrome::CHROME_MINIMAL_POPUP;
 | |
|  }
 | |
|  
 | |
|  /**
 | |
| diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs
 | |
| index be01248253ee1bcc9435c3e8223ed032f498a023..0f05923c29a023511b72a81ec527300cafa17760 100644
 | |
| --- a/toolkit/mozapps/update/UpdateService.sys.mjs
 | |
| +++ b/toolkit/mozapps/update/UpdateService.sys.mjs
 | |
| @@ -3888,6 +3888,8 @@ export class UpdateService {
 | |
|    }
 | |
|  
 | |
|    get disabledForTesting() {
 | |
| +    /* playwright */
 | |
| +    return true;
 | |
|      return lazy.UpdateServiceStub.updateDisabledForTesting;
 | |
|    }
 | |
|  
 | |
| diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
 | |
| index 8c2b2bf996bd889651dc7fac1dc351b4c47b567e..07d237eb17a657ce051fd0aa5e53449c0c3159f6 100644
 | |
| --- a/toolkit/toolkit.mozbuild
 | |
| +++ b/toolkit/toolkit.mozbuild
 | |
| @@ -155,6 +155,7 @@ if CONFIG["ENABLE_WEBDRIVER"]:
 | |
|          "/remote",
 | |
|          "/testing/firefox-ui",
 | |
|          "/testing/marionette",
 | |
| +        "/juggler",
 | |
|          "/toolkit/components/telemetry/tests/marionette",
 | |
|      ]
 | |
|  
 | |
| diff --git a/toolkit/xre/nsWindowsWMain.cpp b/toolkit/xre/nsWindowsWMain.cpp
 | |
| index 7eb9e1104682d4eb47060654f43a1efa8b2a6bb2..a8315d6decf654b5302bea5beeea34140c300ded 100644
 | |
| --- a/toolkit/xre/nsWindowsWMain.cpp
 | |
| +++ b/toolkit/xre/nsWindowsWMain.cpp
 | |
| @@ -14,8 +14,10 @@
 | |
|  #endif
 | |
|  
 | |
|  #include "mozilla/Char16.h"
 | |
| +#include "mozilla/CmdLineAndEnvUtils.h"
 | |
|  #include "nsUTF8Utils.h"
 | |
|  
 | |
| +#include <io.h>
 | |
|  #include <windows.h>
 | |
|  
 | |
|  #ifdef __MINGW32__
 | |
| @@ -114,6 +116,19 @@ static void FreeAllocStrings(int argc, char** argv) {
 | |
|  int wmain(int argc, WCHAR** argv) {
 | |
|    SanitizeEnvironmentVariables();
 | |
|    SetDllDirectoryW(L"");
 | |
| +  bool hasJugglerPipe =
 | |
| +      mozilla::CheckArg(argc, argv, "juggler-pipe", nullptr,
 | |
| +                        mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND;
 | |
| +  if (hasJugglerPipe && !mozilla::EnvHasValue("PW_PIPE_READ")) {
 | |
| +    intptr_t stdio3 = _get_osfhandle(3);
 | |
| +    intptr_t stdio4 = _get_osfhandle(4);
 | |
| +    CHAR stdio3str[20];
 | |
| +    CHAR stdio4str[20];
 | |
| +    itoa(stdio3, stdio3str, 10);
 | |
| +    itoa(stdio4, stdio4str, 10);
 | |
| +    SetEnvironmentVariableA("PW_PIPE_READ", stdio3str);
 | |
| +    SetEnvironmentVariableA("PW_PIPE_WRITE", stdio4str);
 | |
| +  }
 | |
|  
 | |
|    // Only run this code if LauncherProcessWin.h was included beforehand, thus
 | |
|    // signalling that the hosting process should support launcher mode.
 | |
| diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp
 | |
| index fe72a2715da8846146377e719559c16e6ef1f7ff..a5959143bac8f62ee359fa3883a844f3fe541685 100644
 | |
| --- a/uriloader/base/nsDocLoader.cpp
 | |
| +++ b/uriloader/base/nsDocLoader.cpp
 | |
| @@ -813,6 +813,12 @@ 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
 | |
| diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
 | |
| index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790eafcbc6a 100644
 | |
| --- a/uriloader/exthandler/nsExternalHelperAppService.cpp
 | |
| +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
 | |
| @@ -112,6 +112,7 @@
 | |
|  
 | |
|  #include "mozilla/Components.h"
 | |
|  #include "mozilla/ClearOnShutdown.h"
 | |
| +#include "mozilla/ErrorNames.h"
 | |
|  #include "mozilla/Preferences.h"
 | |
|  #include "mozilla/ipc/URIUtils.h"
 | |
|  
 | |
| @@ -831,6 +832,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);
 | |
| @@ -1441,7 +1448,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);
 | |
| @@ -1630,7 +1642,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;
 | |
|  
 | |
| @@ -1682,6 +1723,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
 | |
|  
 | |
|    bool alwaysAsk = true;
 | |
|    mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
 | |
| +  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
 | |
| @@ -2198,6 +2242,16 @@ 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");
 | |
| +      Unused << rv;
 | |
| +    }
 | |
| +  }
 | |
| +
 | |
|    return NS_OK;
 | |
|  }
 | |
|  
 | |
| @@ -2679,6 +2733,15 @@ 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");
 | |
| +    Unused << rv;
 | |
| +  }
 | |
| +
 | |
|    // 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 1f77e095dbfa3acc046779114007d83fc1cfa087..2354abbab7af6f6bdc3bd628722f03ea401d236a 100644
 | |
| --- a/uriloader/exthandler/nsExternalHelperAppService.h
 | |
| +++ b/uriloader/exthandler/nsExternalHelperAppService.h
 | |
| @@ -257,6 +257,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService,
 | |
|        mozilla::dom::BrowsingContext* aContentContext, bool aForceSave,
 | |
|        nsIInterfaceRequestor* aWindowContext,
 | |
|        nsIStreamListener** aStreamListener);
 | |
| +
 | |
| +  nsCOMPtr<nsIDownloadInterceptor> mInterceptor;
 | |
|  };
 | |
|  
 | |
|  /**
 | |
| @@ -462,6 +464,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 4a399acb72d4fd475c9ae43e9eadbc32f261e290..97ace81c82b16a9a993166dd4b0ddb3a721c9872 100644
 | |
| --- a/uriloader/exthandler/nsIExternalHelperAppService.idl
 | |
| +++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
 | |
| @@ -6,8 +6,11 @@
 | |
|  
 | |
|  #include "nsICancelable.idl"
 | |
|  
 | |
| +webidl BrowsingContext;
 | |
| +interface nsIHelperAppLauncher;
 | |
|  interface nsIURI;
 | |
|  interface nsIChannel;
 | |
| +interface nsIRequest;
 | |
|  interface nsIStreamListener;
 | |
|  interface nsIFile;
 | |
|  interface nsIMIMEInfo;
 | |
| @@ -15,6 +18,17 @@ interface nsIWebProgressListener2;
 | |
|  interface nsIInterfaceRequestor;
 | |
|  webidl BrowsingContext;
 | |
|  
 | |
| +/**
 | |
| + * Interceptor interface used by Juggler.
 | |
| + */
 | |
| +[scriptable, uuid(9a20e9b0-75d0-11ea-bc55-0242ac130003)]
 | |
| +interface nsIDownloadInterceptor : nsISupports
 | |
| +{
 | |
| +  boolean 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.
 | |
| @@ -76,6 +90,7 @@ interface nsIExternalHelperAppService : nsISupports
 | |
|    boolean applyDecodingForExtension(in AUTF8String aExtension,
 | |
|                                      in ACString aEncodingType);
 | |
|  
 | |
| +  void setDownloadInterceptor(in nsIDownloadInterceptor interceptor);
 | |
|  };
 | |
|  
 | |
|  /**
 | |
| diff --git a/widget/InProcessCompositorWidget.cpp b/widget/InProcessCompositorWidget.cpp
 | |
| index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9896abd1c 100644
 | |
| --- a/widget/InProcessCompositorWidget.cpp
 | |
| +++ b/widget/InProcessCompositorWidget.cpp
 | |
| @@ -4,7 +4,10 @@
 | |
|  
 | |
|  #include "InProcessCompositorWidget.h"
 | |
|  
 | |
| +#include "HeadlessCompositorWidget.h"
 | |
| +#include "HeadlessWidget.h"
 | |
|  #include "mozilla/VsyncDispatcher.h"
 | |
| +#include "mozilla/widget/PlatformWidgetTypes.h"
 | |
|  #include "nsBaseWidget.h"
 | |
|  
 | |
|  namespace mozilla {
 | |
| @@ -23,6 +26,12 @@ RefPtr<CompositorWidget> CompositorWidget::CreateLocal(
 | |
|    // do it after the static_cast.
 | |
|    nsBaseWidget* widget = static_cast<nsBaseWidget*>(aWidget);
 | |
|    MOZ_RELEASE_ASSERT(widget);
 | |
| +  if (aInitData.type() ==
 | |
| +      CompositorWidgetInitData::THeadlessCompositorWidgetInitData) {
 | |
| +    return new HeadlessCompositorWidget(
 | |
| +        aInitData.get_HeadlessCompositorWidgetInitData(), aOptions,
 | |
| +        static_cast<HeadlessWidget*>(aWidget));
 | |
| +  }
 | |
|    return new InProcessCompositorWidget(aOptions, widget);
 | |
|  }
 | |
|  #endif
 | |
| diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h
 | |
| index 3d469853bbd30c433ee7b6d2be7175caa568196e..214b92f0a8913fb6667b7554410d4cd58c53cad3 100644
 | |
| --- a/widget/MouseEvents.h
 | |
| +++ b/widget/MouseEvents.h
 | |
| @@ -327,6 +327,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
 | |
|    // Otherwise, this must be 0.
 | |
|    uint32_t mClickCount = 0;
 | |
|  
 | |
| +  // Unique event ID
 | |
| +  uint32_t mJugglerEventId = 0;
 | |
| +
 | |
|    // Whether the event should ignore scroll frame bounds during dispatch.
 | |
|    bool mIgnoreRootScrollFrame = false;
 | |
|  
 | |
| @@ -341,6 +344,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
 | |
|      mContextMenuTrigger = aEvent.mContextMenuTrigger;
 | |
|      mExitFrom = aEvent.mExitFrom;
 | |
|      mClickCount = aEvent.mClickCount;
 | |
| +    mJugglerEventId = aEvent.mJugglerEventId;
 | |
|      mIgnoreRootScrollFrame = aEvent.mIgnoreRootScrollFrame;
 | |
|      mClickEventPrevented = aEvent.mClickEventPrevented;
 | |
|    }
 | |
| diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.mm
 | |
| index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce8050630a1aa 100644
 | |
| --- a/widget/cocoa/NativeKeyBindings.mm
 | |
| +++ b/widget/cocoa/NativeKeyBindings.mm
 | |
| @@ -528,6 +528,13 @@
 | |
|        break;
 | |
|      case KEY_NAME_INDEX_ArrowLeft:
 | |
|        if (aEvent.IsAlt()) {
 | |
| +        if (aEvent.IsMeta() || aEvent.IsControl())
 | |
| +          break;
 | |
| +        instance->AppendEditCommandsForSelector(
 | |
| +            !aEvent.IsShift()
 | |
| +                ? ToObjcSelectorPtr(@selector(moveWordLeft:))
 | |
| +                : ToObjcSelectorPtr(@selector(moveWordLeftAndModifySelection:)),
 | |
| +            aCommands);
 | |
|          break;
 | |
|        }
 | |
|        if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) {
 | |
| @@ -550,6 +557,13 @@
 | |
|        break;
 | |
|      case KEY_NAME_INDEX_ArrowRight:
 | |
|        if (aEvent.IsAlt()) {
 | |
| +        if (aEvent.IsMeta() || aEvent.IsControl())
 | |
| +          break;
 | |
| +        instance->AppendEditCommandsForSelector(
 | |
| +            !aEvent.IsShift()
 | |
| +                ? ToObjcSelectorPtr(@selector(moveWordRight:))
 | |
| +                : ToObjcSelectorPtr(@selector(moveWordRightAndModifySelection:)),
 | |
| +            aCommands);
 | |
|          break;
 | |
|        }
 | |
|        if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) {
 | |
| @@ -572,6 +586,10 @@
 | |
|        break;
 | |
|      case KEY_NAME_INDEX_ArrowUp:
 | |
|        if (aEvent.IsControl()) {
 | |
| +        if (aEvent.IsMeta() || aEvent.IsAlt())
 | |
| +          break;
 | |
| +        instance->AppendEditCommandsForSelector(
 | |
| +          ToObjcSelectorPtr(@selector(scrollPageUp:)), aCommands);
 | |
|          break;
 | |
|        }
 | |
|        if (aEvent.IsMeta()) {
 | |
| @@ -582,7 +600,7 @@
 | |
|              !aEvent.IsShift()
 | |
|                  ? ToObjcSelectorPtr(@selector(moveToBeginningOfDocument:))
 | |
|                  : ToObjcSelectorPtr(
 | |
| -                      @selector(moveToBegginingOfDocumentAndModifySelection:)),
 | |
| +                      @selector(moveToBeginningOfDocumentAndModifySelection:)),
 | |
|              aCommands);
 | |
|          break;
 | |
|        }
 | |
| @@ -609,6 +627,10 @@
 | |
|        break;
 | |
|      case KEY_NAME_INDEX_ArrowDown:
 | |
|        if (aEvent.IsControl()) {
 | |
| +        if (aEvent.IsMeta() || aEvent.IsAlt())
 | |
| +          break;
 | |
| +        instance->AppendEditCommandsForSelector(
 | |
| +          ToObjcSelectorPtr(@selector(scrollPageDown:)), aCommands);
 | |
|          break;
 | |
|        }
 | |
|        if (aEvent.IsMeta()) {
 | |
| diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp
 | |
| index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db5151dd90c1a 100644
 | |
| --- a/widget/headless/HeadlessCompositorWidget.cpp
 | |
| +++ b/widget/headless/HeadlessCompositorWidget.cpp
 | |
| @@ -3,6 +3,7 @@
 | |
|   * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
|  
 | |
| +#include "mozilla/layers/CompositorThread.h"
 | |
|  #include "mozilla/widget/PlatformWidgetTypes.h"
 | |
|  #include "HeadlessCompositorWidget.h"
 | |
|  #include "VsyncDispatcher.h"
 | |
| @@ -15,9 +16,32 @@ HeadlessCompositorWidget::HeadlessCompositorWidget(
 | |
|      const layers::CompositorOptions& aOptions, HeadlessWidget* aWindow)
 | |
|      : CompositorWidget(aOptions),
 | |
|        mWidget(aWindow),
 | |
| +      mMon("snapshotListener"),
 | |
|        mClientSize(LayoutDeviceIntSize(aInitData.InitialClientSize()),
 | |
|                    "HeadlessCompositorWidget::mClientSize") {}
 | |
|  
 | |
| +void HeadlessCompositorWidget::SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener) {
 | |
| +  MOZ_ASSERT(NS_IsMainThread());
 | |
| +
 | |
| +  ReentrantMonitorAutoEnter lock(mMon);
 | |
| +  mSnapshotListener = std::move(listener);
 | |
| +  layers::CompositorThread()->Dispatch(NewRunnableMethod(
 | |
| +      "HeadlessCompositorWidget::PeriodicSnapshot", this,
 | |
| +      &HeadlessCompositorWidget::PeriodicSnapshot
 | |
| +  ));
 | |
| +}
 | |
| +
 | |
| +already_AddRefed<gfx::DrawTarget> HeadlessCompositorWidget::StartRemoteDrawingInRegion(
 | |
| +    const LayoutDeviceIntRegion& aInvalidRegion,
 | |
| +    layers::BufferMode* aBufferMode) {
 | |
| +  if (!mDrawTarget)
 | |
| +    return nullptr;
 | |
| +
 | |
| +  *aBufferMode = layers::BufferMode::BUFFER_NONE;
 | |
| +  RefPtr<gfx::DrawTarget> result = mDrawTarget;
 | |
| +  return result.forget();
 | |
| +}
 | |
| +
 | |
|  void HeadlessCompositorWidget::ObserveVsync(VsyncObserver* aObserver) {
 | |
|    if (RefPtr<CompositorVsyncDispatcher> cvd =
 | |
|            mWidget->GetCompositorVsyncDispatcher()) {
 | |
| @@ -31,6 +55,59 @@ void HeadlessCompositorWidget::NotifyClientSizeChanged(
 | |
|      const LayoutDeviceIntSize& aClientSize) {
 | |
|    auto size = mClientSize.Lock();
 | |
|    *size = aClientSize;
 | |
| +  layers::CompositorThread()->Dispatch(NewRunnableMethod<LayoutDeviceIntSize>(
 | |
| +      "HeadlessCompositorWidget::UpdateDrawTarget", this,
 | |
| +      &HeadlessCompositorWidget::UpdateDrawTarget,
 | |
| +      aClientSize));
 | |
| +}
 | |
| +
 | |
| +void HeadlessCompositorWidget::UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize) {
 | |
| +  MOZ_ASSERT(NS_IsInCompositorThread());
 | |
| +  if (aClientSize.IsEmpty()) {
 | |
| +    mDrawTarget = nullptr;
 | |
| +    return;
 | |
| +  }
 | |
| +
 | |
| +  RefPtr<gfx::DrawTarget> old = std::move(mDrawTarget);
 | |
| +  gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
 | |
| +  gfx::IntSize size = aClientSize.ToUnknownSize();
 | |
| +  mDrawTarget = mozilla::gfx::Factory::CreateDrawTarget(
 | |
| +      mozilla::gfx::BackendType::SKIA, size, format);
 | |
| +  if (old) {
 | |
| +    RefPtr<gfx::SourceSurface> snapshot = old->Snapshot();
 | |
| +    if (snapshot)
 | |
| +      mDrawTarget->CopySurface(snapshot.get(), old->GetRect(), gfx::IntPoint(0, 0));
 | |
| +  }
 | |
| +}
 | |
| +
 | |
| +void HeadlessCompositorWidget::PeriodicSnapshot() {
 | |
| +  ReentrantMonitorAutoEnter lock(mMon);
 | |
| +  if (!mSnapshotListener)
 | |
| +    return;
 | |
| +
 | |
| +  TakeSnapshot();
 | |
| +  NS_DelayedDispatchToCurrentThread(NewRunnableMethod(
 | |
| +      "HeadlessCompositorWidget::PeriodicSnapshot", this,
 | |
| +      &HeadlessCompositorWidget::PeriodicSnapshot), 40);
 | |
| +}
 | |
| +
 | |
| +void HeadlessCompositorWidget::TakeSnapshot() {
 | |
| +  if (!mDrawTarget)
 | |
| +    return;
 | |
| +
 | |
| +  RefPtr<gfx::SourceSurface> snapshot = mDrawTarget->Snapshot();
 | |
| +  if (!snapshot) {
 | |
| +    fprintf(stderr, "Failed to get snapshot of draw target\n");
 | |
| +    return;
 | |
| +  }
 | |
| +
 | |
| +  RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
 | |
| +  if (!dataSurface) {
 | |
| +    fprintf(stderr, "Failed to get data surface from snapshot\n");
 | |
| +    return;
 | |
| +  }
 | |
| +
 | |
| +  mSnapshotListener(std::move(dataSurface));
 | |
|  }
 | |
|  
 | |
|  LayoutDeviceIntSize HeadlessCompositorWidget::GetClientSize() {
 | |
| diff --git a/widget/headless/HeadlessCompositorWidget.h b/widget/headless/HeadlessCompositorWidget.h
 | |
| index facd2bc65afab8ec1aa322faa20a67464964dfb9..d6dea95472bec6006411753c3dfdab2e3659171f 100644
 | |
| --- a/widget/headless/HeadlessCompositorWidget.h
 | |
| +++ b/widget/headless/HeadlessCompositorWidget.h
 | |
| @@ -6,6 +6,7 @@
 | |
|  #ifndef widget_headless_HeadlessCompositorWidget_h
 | |
|  #define widget_headless_HeadlessCompositorWidget_h
 | |
|  
 | |
| +#include "mozilla/ReentrantMonitor.h"
 | |
|  #include "mozilla/widget/CompositorWidget.h"
 | |
|  
 | |
|  #include "HeadlessWidget.h"
 | |
| @@ -23,8 +24,12 @@ class HeadlessCompositorWidget final : public CompositorWidget,
 | |
|                             HeadlessWidget* aWindow);
 | |
|  
 | |
|    void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize);
 | |
| +  void SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener);
 | |
|  
 | |
|    // CompositorWidget Overrides
 | |
| +  already_AddRefed<gfx::DrawTarget> StartRemoteDrawingInRegion(
 | |
| +      const LayoutDeviceIntRegion& aInvalidRegion,
 | |
| +      layers::BufferMode* aBufferMode) override;
 | |
|  
 | |
|    uintptr_t GetWidgetKey() override;
 | |
|  
 | |
| @@ -42,10 +47,18 @@ class HeadlessCompositorWidget final : public CompositorWidget,
 | |
|    }
 | |
|  
 | |
|   private:
 | |
| +  void UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize);
 | |
| +  void PeriodicSnapshot();
 | |
| +  void TakeSnapshot();
 | |
| +
 | |
|    HeadlessWidget* mWidget;
 | |
| +  mozilla::ReentrantMonitor mMon;
 | |
|  
 | |
|    // See GtkCompositorWidget for the justification for this mutex.
 | |
|    DataMutex<LayoutDeviceIntSize> mClientSize;
 | |
| +
 | |
| +  HeadlessWidget::SnapshotListener mSnapshotListener;
 | |
| +  RefPtr<gfx::DrawTarget> mDrawTarget;
 | |
|  };
 | |
|  
 | |
|  }  // namespace widget
 | |
| diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp
 | |
| index c6095751bc1e9bbe907e64fb634b799cac31bb0a..ce1b995015843babeab0e3bf4e357d45066b3cab 100644
 | |
| --- a/widget/headless/HeadlessWidget.cpp
 | |
| +++ b/widget/headless/HeadlessWidget.cpp
 | |
| @@ -111,6 +111,8 @@ void HeadlessWidget::Destroy() {
 | |
|      }
 | |
|    }
 | |
|  
 | |
| +  SetSnapshotListener(nullptr);
 | |
| +
 | |
|    nsBaseWidget::OnDestroy();
 | |
|  
 | |
|    nsBaseWidget::Destroy();
 | |
| @@ -613,5 +615,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchpadPan(
 | |
|    return NS_OK;
 | |
|  }
 | |
|  
 | |
| +void HeadlessWidget::SetSnapshotListener(SnapshotListener&& listener) {
 | |
| +  if (!mCompositorWidget) {
 | |
| +    if (listener)
 | |
| +      fprintf(stderr, "Trying to set SnapshotListener without compositor widget\n");
 | |
| +    return;
 | |
| +  }
 | |
| +  mCompositorWidget->SetSnapshotListener(std::move(listener));
 | |
| +}
 | |
| +
 | |
|  }  // namespace widget
 | |
|  }  // namespace mozilla
 | |
| diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h
 | |
| index 9856991ef32f25f51942f8cd664a09bec2192c70..948947a421179e91c51005aeb83ed0d18cfc84ce 100644
 | |
| --- a/widget/headless/HeadlessWidget.h
 | |
| +++ b/widget/headless/HeadlessWidget.h
 | |
| @@ -141,6 +141,9 @@ class HeadlessWidget : public nsBaseWidget {
 | |
|                                                 int32_t aModifierFlags,
 | |
|                                                 nsIObserver* aObserver) override;
 | |
|  
 | |
| +  using SnapshotListener = std::function<void(RefPtr<gfx::DataSourceSurface>&&)>;
 | |
| +  void SetSnapshotListener(SnapshotListener&& listener);
 | |
| +
 | |
|   private:
 | |
|    ~HeadlessWidget();
 | |
|    bool mEnabled;
 | |
| diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h
 | |
| index 02775a7f27f5697bc33872d997198ce305556970..6c1ae0e371ee012ef47c8e9c74f949da05ad0025 100644
 | |
| --- a/widget/nsGUIEventIPC.h
 | |
| +++ b/widget/nsGUIEventIPC.h
 | |
| @@ -234,6 +234,7 @@ struct ParamTraits<mozilla::WidgetMouseEvent> {
 | |
|                                aParam.mExitFrom.value()));
 | |
|      }
 | |
|      WriteParam(aWriter, aParam.mClickCount);
 | |
| +    WriteParam(aWriter, aParam.mJugglerEventId);
 | |
|    }
 | |
|  
 | |
|    static bool Read(MessageReader* aReader, paramType* aResult) {
 | |
| @@ -258,6 +259,7 @@ struct ParamTraits<mozilla::WidgetMouseEvent> {
 | |
|        aResult->mExitFrom = Some(static_cast<paramType::ExitFrom>(exitFrom));
 | |
|      }
 | |
|      rv = rv && ReadParam(aReader, &aResult->mClickCount);
 | |
| +    rv = rv && ReadParam(aReader, &aResult->mJugglerEventId);
 | |
|      return rv;
 | |
|    }
 | |
|  };
 | |
| diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h
 | |
| index 787d30d881adedd57d2025ca57bff4bc6c57e803..ae1a0172c960ab16919133485722d2ae0cdbcbd4 100644
 | |
| --- a/xpcom/reflect/xptinfo/xptinfo.h
 | |
| +++ b/xpcom/reflect/xptinfo/xptinfo.h
 | |
| @@ -505,7 +505,7 @@ static_assert(sizeof(nsXPTMethodInfo) == 8, "wrong size");
 | |
|  #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
 | |
|  #  define PARAM_BUFFER_COUNT 18
 | |
|  #else
 | |
| -#  define PARAM_BUFFER_COUNT 14
 | |
| +#  define PARAM_BUFFER_COUNT 15
 | |
|  #endif
 | |
|  
 | |
|  /**
 | 
