diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h index 1bcd464d3bd6b8465f78c62b074b0d57dbc6a082..f878ac9db2d800542dabcc2f48e8ae4727ec4b9a 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 8167d2b81c918e02ce757f7f448f22e07c29d140..3ae798880acfd8aa965ae08051f2f81855133711 100644 --- a/browser/app/winlauncher/LauncherProcessWin.cpp +++ b/browser/app/winlauncher/LauncherProcessWin.cpp @@ -23,6 +23,7 @@ #include "mozilla/WinHeaderOnlyUtils.h" #include "nsWindowsHelpers.h" +#include #include #include @@ -422,8 +423,18 @@ Maybe 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(stdio3), + reinterpret_cast(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 a097c2c56a665204ff7b5593c7faf836366801cf..235a4e224e08c22870c6913e335f0b6020b3e7da 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 89cff6b562a82bd3a9a5d268abd4373199b31fac..e58dbf0ab0b06e84f6dac64698d11c6268091204 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -196,6 +196,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 2425779a7767e9350ee2afc4aea111a090c7f909..393eb86bf2d9a8f778bfce560a9fb3bf528ba558 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -108,8 +108,15 @@ struct ParamTraits template <> struct ParamTraits - : public mozilla::dom::WebIDLEnumSerializer< - mozilla::dom::PrefersColorSchemeOverride> {}; + : public mozilla::dom::WebIDLEnumSerializer {}; + +template <> +struct ParamTraits + : public mozilla::dom::WebIDLEnumSerializer {}; + +template <> +struct ParamTraits + : public mozilla::dom::WebIDLEnumSerializer {}; template <> struct ParamTraits @@ -2891,6 +2898,32 @@ void BrowsingContext::DidSet(FieldIndex, PresContextAffectingFieldChanged(); } +void BrowsingContext::DidSet(FieldIndex, + dom::PrefersContrastOverride aOldValue) { + MOZ_ASSERT(IsTop()); + if (PrefersContrastOverride() == aOldValue) { + return; + } + PresContextAffectingFieldChanged(); +} + +void BrowsingContext::DidSet(FieldIndex, + 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, nsString&& aOldValue) { MOZ_ASSERT(IsTop()); diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7e65d06f5 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) \ FIELD(MessageManagerGroup, nsString) \ FIELD(MaxTouchPointsOverride, uint8_t) \ @@ -246,6 +246,9 @@ struct EmbedderColorSchemes { * embedder element. */ \ FIELD(EmbedderColorSchemes, EmbedderColorSchemes) \ FIELD(DisplayMode, dom::DisplayMode) \ + /* playwright addition */ \ + FIELD(PrefersReducedMotionOverride, dom::PrefersReducedMotionOverride) \ + FIELD(PrefersContrastOverride, dom::PrefersContrastOverride) \ /* The number of entries added to the session history because of this \ * browsing context. */ \ FIELD(HistoryEntryCount, uint32_t) \ @@ -950,6 +953,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { return GetForcedColorsOverride(); } + dom::PrefersReducedMotionOverride PrefersReducedMotionOverride() const { + return GetPrefersReducedMotionOverride(); + } + + dom::PrefersContrastOverride PrefersContrastOverride() const { + return GetPrefersContrastOverride(); + } + bool IsInBFCache() const; bool AllowJavascript() const { return GetAllowJavascript(); } @@ -1112,6 +1123,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { return IsTop(); } + bool CanSet(FieldIndex, + dom::PrefersContrastOverride, ContentParent*) { + return IsTop(); + } + bool CanSet(FieldIndex, dom::ForcedColorsOverride, ContentParent*) { return IsTop(); @@ -1130,10 +1146,22 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { void DidSet(FieldIndex, dom::ForcedColorsOverride aOldValue); + void DidSet(FieldIndex, + dom::PrefersContrastOverride aOldValue); + template void WalkPresContexts(Callback&&); void PresContextAffectingFieldChanged(); + bool CanSet(FieldIndex, + dom::PrefersReducedMotionOverride, ContentParent*) { + return IsTop(); + } + + void DidSet(FieldIndex, + dom::PrefersReducedMotionOverride aOldValue); + + void DidSet(FieldIndex, nsString&& aOldValue); bool CanSet(FieldIndex, bool, ContentParent*) { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index bef42c91d6f88922c8c101f3675325d828872aaf..8eb68c441fbef8ecbe5e90c118ccc00813564577 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -324,6 +324,8 @@ void CanonicalBrowsingContext::ReplacedBy( txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart()); txn.SetForceOffline(GetForceOffline()); txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP()); + 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 @@ -1635,6 +1637,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI, return; } + { + nsCOMPtr 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 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0d41c43cb 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -15,6 +15,12 @@ # include // 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/Navigation.h" @@ -94,6 +101,7 @@ #include "mozilla/dom/DocumentBinding.h" #include "mozilla/glean/DocshellMetrics.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" @@ -117,6 +125,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" @@ -211,6 +220,7 @@ #include "nsGlobalWindowInner.h" #include "nsGlobalWindowOuter.h" #include "nsJSEnvironment.h" +#include "nsJSUtils.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsObjectLoadingContent.h" @@ -352,6 +362,14 @@ 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), + mContrastOverride(CONTRAST_OVERRIDE_NONE), mAllowAuth(mItemType == typeContent), mAllowKeywordFixup(false), mDisableMetaRefreshWhenInactive(false), @@ -3024,6 +3042,232 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { return NS_OK; } +// =============== Juggler Begin ======================= + +nsDocShell* nsDocShell::GetRootDocShell() { + nsCOMPtr rootAsItem; + GetInProcessSameTypeRootTreeItem(getter_AddRefs(rootAsItem)); + nsCOMPtr 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 observerService = + mozilla::services::GetObserverService(); + observerService->NotifyObservers( + ToSupports(element), "juggler-file-picker-shown", nullptr); +} + +RefPtr 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 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 presContext = GetPresContext(); + if (presContext) { + presContext->MediaFeatureValuesChanged( + {MediaFeatureChangeReason::SystemMetricsChange}, + MediaFeatureChangePropagation::JustThisDocument); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetContrastOverride(ContrastOverride* aContrastOverride) { + *aContrastOverride = GetRootDocShell()->mContrastOverride; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetContrastOverride(ContrastOverride aContrastOverride) { + mContrastOverride = aContrastOverride; + RefPtr presContext = GetPresContext(); + if (presContext) { + presContext->MediaFeatureValuesChanged( + {MediaFeatureChangeReason::SystemMetricsChange}, + MediaFeatureChangePropagation::JustThisDocument); + } + return NS_OK; +} + +// =============== Juggler End ======================= + NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; @@ -4731,7 +4975,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { } void nsDocShell::ActivenessMaybeChanged() { - const bool isActive = mBrowsingContext->IsActive(); + const bool isActive = mForceActiveState || mBrowsingContext->IsActive(); if (RefPtr presShell = GetPresShell()) { presShell->ActivenessMaybeChanged(); } @@ -6658,6 +6902,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 viewer = mOSHE->GetDocumentViewer(); @@ -8399,6 +8647,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { true, // aForceNoOpener getter_AddRefs(newBC)); MOZ_ASSERT(!newBC); + if (rv == NS_OK) { + nsCOMPtr observerService = mozilla::services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers(GetAsSupports(this), "juggler-window-open-in-new-context", nullptr); + } + } return rv; } @@ -9572,6 +9826,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr); nsCOMPtr req; + + // Juggler: report navigation started for non-same-document and non-javascript + // navigations. + if (!isJavaScript && !sameDocument) { + nsCOMPtr 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)) { @@ -12791,6 +13055,9 @@ class OnLinkClickEvent : public Runnable { mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied, mTriggeringPrincipal); } + nsCOMPtr observerService = mozilla::services::GetObserverService(); + observerService->NotifyObservers(ToSupports(mContent), "juggler-link-click-sync", nullptr); + return NS_OK; } @@ -12877,6 +13144,8 @@ nsresult nsDocShell::OnLinkClick( nsCOMPtr ev = new OnLinkClickEvent( this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal); + nsCOMPtr 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 f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a97978f02 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" @@ -77,6 +78,7 @@ class nsCommandManager; class nsDocShellEditorData; class nsDOMNavigationTiming; class nsDSURIContentListener; +class nsGeolocationService; class nsGlobalWindowOuter; class FramingChecker; @@ -403,6 +405,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 GetGeolocationServiceOverride(); + // Create a content viewer within this nsDocShell for the given // `WindowGlobalChild` actor. nsresult CreateDocumentViewerForActor( @@ -1006,6 +1017,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 @@ -1285,6 +1298,17 @@ 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 mGeolocationServiceOverride; + ReducedMotionOverride mReducedMotionOverride; + ForcedColorsOverride mForcedColorsOverride; + ContrastOverride mContrastOverride; + bool mAllowAuth : 1; bool mAllowKeywordFixup : 1; bool mDisableMetaRefreshWhenInactive : 1; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 84e821e33e8164829dfee4f05340784e189b90ee..aa690eb747cb73bc6bff40a62546037c2e64c485 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,45 @@ 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; + + cenum ContrastOverride : 8 { + CONTRAST_OVERRIDE_LESS, + CONTRAST_OVERRIDE_MORE, + CONTRAST_OVERRIDE_CUSTOM, + CONTRAST_OVERRIDE_NO_PREFERENCE, + CONTRAST_OVERRIDE_NONE, /* This clears the override. */ + }; + [infallible] attribute nsIDocShell_ContrastOverride contrastOverride; + + 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 fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c639a74ac 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -3752,6 +3752,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { } void Document::ApplySettingsFromCSP(bool aSpeculative) { + if (mDocumentContainer && mDocumentContainer->IsBypassCSPEnabled()) + return; + nsresult rv = NS_OK; if (!aSpeculative) { // 1) apply settings from regular CSP @@ -3809,6 +3812,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { MOZ_ASSERT(!mScriptGlobalObject, "CSP must be initialized before mScriptGlobalObject is set!"); + nsCOMPtr 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; @@ -4617,6 +4625,10 @@ bool Document::HasFocus(ErrorResult& rv) const { return false; } + if (IsActive() && mDocumentContainer->ShouldOverrideHasFocus()) { + return true; + } + if (!fm->IsInActiveWindow(bc)) { return false; } @@ -19688,6 +19700,35 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { return PreferenceSheet::PrefsFor(*this).mColorScheme; } +bool Document::PrefersReducedMotion() const { + auto* docShell = static_cast(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::HasRecentlyStartedForegroundLoads() { if (!sLoadingForegroundTopLevelContentDocument) { return false; diff --git a/dom/base/Document.h b/dom/base/Document.h index 622f54e369d324a4cc2800dd4b331bd628339bed..2ef6ed20cf35febeff75b22dfa5bb2fbb4e295fe 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -4140,6 +4140,8 @@ class Document : public nsINode, // color-scheme meta tag. ColorScheme DefaultColorScheme() const; + bool PrefersReducedMotion() const; + static bool HasRecentlyStartedForegroundLoads(); static bool AutomaticStorageAccessPermissionCanBeGranted( diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index a13cae5b990fb2f750e62f5117ad63aa981d787f..bc0f2d674aadd8eba867f56e873595a8885d1798 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& aLanguages) { +void Navigator::GetAcceptLanguages(const nsString* aLanguageOverride, nsTArray& 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& 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 @@ -2298,7 +2308,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& aLanguages); + static void GetAcceptLanguages(const nsString* aLanguageOverride, nsTArray& aLanguages); dom::MediaCapabilities* MediaCapabilities(); dom::MediaSession* MediaSession(); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764cb5c68d11 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -9151,11 +9151,13 @@ nsresult nsContentUtils::SendMouseEvent( int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized, - bool aIsWidgetEventSynthesized) { + bool aIsWidgetEventSynthesized, + bool convertToPointer, uint32_t aJugglerEventId) { MOZ_ASSERT(aWidget); EventMessage msg; Maybe exitFrom; bool contextMenuKey = false; + bool isPWDragEventMessage = false; if (aType.EqualsLiteral("mousedown")) { msg = eMouseDown; } else if (aType.EqualsLiteral("mouseup")) { @@ -9181,6 +9183,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; } @@ -9191,7 +9199,14 @@ nsresult nsContentUtils::SendMouseEvent( Maybe pointerEvent; Maybe mouseEvent; - if (IsPointerEventMessage(msg)) { + Maybe pwDragEvent; + + if (isPWDragEventMessage) { + pwDragEvent.emplace(true, msg, aWidget); + 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)) { @@ -9210,8 +9225,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; @@ -9224,6 +9242,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 779cd9e544bfb2d135f12c3558c0ca8164b37029..041eba4bcbf40f51fc40ce7609ea81408e6a788d 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -3015,8 +3015,9 @@ class nsContentUtils { int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, - bool* aPreventDefault, bool aIsDOMEventSynthesized, - bool aIsWidgetEventSynthesized); + bool* aPreventDefault, + 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 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee204fcabde 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -710,6 +710,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, @@ -724,7 +744,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 @@ -742,7 +762,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 @@ -751,7 +771,7 @@ 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 = GetPresShell(); if (!presShell) { return NS_ERROR_FAILURE; @@ -768,7 +788,7 @@ nsDOMWindowUtils::SendMouseEventCommon( presShell, widget, aType, refPoint, aButton, aButtons, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, aInputSourceArg, aPointerId, aToWindow, aPreventDefault, aIsDOMEventSynthesized, - aIsWidgetEventSynthesized); + aIsWidgetEventSynthesized, aConvertToPointer, aJugglerEventId); } NS_IMETHODIMP diff --git a/dom/base/nsDOMWindowUtils.h b/dom/base/nsDOMWindowUtils.h index a8a48d28fc4ef612f8234bc2490a41672f1704f5..f702b0c9a0783ec547a41bbefd68e18a27a239fe 100644 --- a/dom/base/nsDOMWindowUtils.h +++ b/dom/base/nsDOMWindowUtils.h @@ -94,7 +94,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 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39ac9b42e51 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1720,6 +1720,10 @@ Maybe 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)) { @@ -2306,6 +2310,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear, bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive, Element* aElementToFocus, uint64_t aActionId) { + LOGFOCUS(("<>", aActionId)); // hold a reference to the focused content, which may be null @@ -2352,6 +2357,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 = docShell->GetPresShell(); @@ -3066,7 +3076,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 99a5049a3aff2efb208895d60622fd9c8d7f337a..9a9b039a46f1294a8b4af0613fb4f4173ac6a8a0 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -2512,10 +2512,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)); + } } } @@ -2635,6 +2641,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() { } } +void nsGlobalWindowOuter::JugglerDispatchDOMWindowReused() { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService && mDoc) { + nsIPrincipal* principal = mDoc->NodePrincipal(); + if (!principal->IsSystemPrincipal()) { + observerService->NotifyObservers(static_cast(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 0453a18ec10c1434d1692f10b1b4acee754e6b7e..ee7bad691515bb51f6b4345e88944b02ad173180 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h @@ -320,6 +320,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 8e2bbed21c13f23745e2eaad4ded831106ebd930..a17b0c7b9730737f178c05703b08d0f5f6d9ecd1 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1449,6 +1449,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 = 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 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 eb75f281630f8ca1b901686207c9fc97336d675f..e607f0ae454d52fc2bfe19046b492352a84b4ebd 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -2397,6 +2397,10 @@ class nsINode : public mozilla::dom::EventTarget { nsTArray>& aResult, ErrorResult& aRv); + void ScrollRectIntoViewIfNeeded(int32_t x, int32_t y, + int32_t w, int32_t h, + ErrorResult& aRv); + already_AddRefed ConvertQuadFromNode( DOMQuad& aQuad, const TextOrElementOrDocument& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index bf7eb34da03c0958de688deecb53b407d430f645..a2ec3b1b7e86f72bee38d890c0834abfe4be8637 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -149,6 +149,11 @@ bool nsJSUtils::GetEnvironmentChainForElement(JSContext* aCx, Element* aElement, 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 f32e21752d5013bf143eb45391ab9218debab08e..83763d2354dade2f8d2b7930ba18ae91c55133ad 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -75,6 +75,7 @@ class nsJSUtils { mozilla::dom::Element* aElement, JS::EnvironmentChain& aEnvChain); + 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 6ec88536141126c97c9b599e3237bb5670d42ce8..41c6cc56738bdaf711adf2cf5b00c7fad5d71ba8 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -61,6 +61,26 @@ enum ForcedColorsOverride { "active", }; +/** + * CSS prefers-reduced-motion values. + */ +enum PrefersReducedMotionOverride { + "none", + "reduce", + "no-preference", +}; + +/** + * CSS prefers-contrast values. + */ +enum PrefersContrastOverride { + "none", + "no-preference", + "more", + "less", + "custom", +}; + /** * Allowed overrides of platform/pref default behaviour for touch events. */ @@ -220,6 +240,12 @@ interface BrowsingContext { // Forced-colors simulation, for DevTools [SetterThrows] attribute ForcedColorsOverride forcedColorsOverride; + // Reduced-Motion simulation, for DevTools. + [SetterThrows] attribute PrefersReducedMotionOverride prefersReducedMotionOverride; + + // Contrast simulation, for DevTools. + [SetterThrows] attribute PrefersContrastOverride prefersContrastOverride; + /** * 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/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 0bd219694282347309680fc9b53b945e1fd0ad92..c5c2e2d32a380ec72379b05a8b84f187431f7309 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -701,6 +701,12 @@ already_AddRefed FetchRequest(nsIGlobalObject* aGlobal, ipcArgs.hasCSPEventListener() = false; ipcArgs.isWorkerRequest() = false; + /* --> Playwright: associate keep-alive fetch with the window */ + BrowsingContext* bc = window ? window->GetBrowsingContext() : nullptr; + if (bc) + ipcArgs.associatedBrowsingContextID() = bc->Id(); + /* <-- Playwright */ + actor->DoFetchOp(ipcArgs); mozilla::glean::networking::fetch_keepalive_request_count.Get("main"_ns) diff --git a/dom/fetch/FetchService.cpp b/dom/fetch/FetchService.cpp index b5e60bbd27fbb2f033d233e9ae2ebc728f442512..0adc568ece34d2c0f35eeacd81e2db9125b7c327 100644 --- a/dom/fetch/FetchService.cpp +++ b/dom/fetch/FetchService.cpp @@ -268,6 +268,14 @@ RefPtr FetchService::FetchInstance::Fetch() { false // IsTrackingFetch ); + /* --> Playwright: associate keep-alive fetch with the window */ + if (mArgsType == FetchArgsType::MainThreadFetch) { + auto& args = mArgs.as(); + mFetchDriver->SetAssociatedBrowsingContextID( + args.mAssociatedBrowsingContextID); + } + /* <-- Playwright */ + if (mArgsType == FetchArgsType::WorkerFetch) { auto& args = mArgs.as(); mFetchDriver->SetWorkerScript(args.mWorkerScript); diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp index 7c653fe131dc34d35ffdc030950071adb31a9fc9..b23442a42ba8ee270e8e38930e59ae15c2a29039 100644 --- a/dom/geolocation/Geolocation.cpp +++ b/dom/geolocation/Geolocation.cpp @@ -28,6 +28,7 @@ #include "nsComponentManagerUtils.h" #include "nsContentPermissionHelper.h" #include "nsContentUtils.h" +#include "nsDocShell.h" #include "nsGlobalWindowInner.h" #include "mozilla/dom/Document.h" #include "nsINamed.h" @@ -428,10 +429,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { return NS_OK; } - RefPtr 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; @@ -639,8 +638,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 gs = - nsGeolocationService::GetGeolocationService(); + nsGeolocationService* gs = mLocator ? mLocator->GetGeolocationService() : nullptr; if (gs) { gs->UpdateAccuracy(); } @@ -957,8 +955,14 @@ void nsGeolocationService::StopDevice() { StaticRefPtr nsGeolocationService::sService; already_AddRefed -nsGeolocationService::GetGeolocationService() { +nsGeolocationService::GetGeolocationService(nsDocShell* docShell) { RefPtr result; + if (docShell) { + result = docShell->GetGeolocationServiceOverride(); + if (result) + return result.forget(); + } + if (nsGeolocationService::sService) { result = nsGeolocationService::sService; @@ -1050,7 +1054,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 doc = aContentDom ? aContentDom->GetDoc() : nullptr; + mService = nsGeolocationService::GetGeolocationService( + doc ? static_cast(doc->GetDocShell()) : nullptr); if (mService) { mService->AddLocator(this); } diff --git a/dom/geolocation/Geolocation.h b/dom/geolocation/Geolocation.h index 992de29b5d2d09c19e55ebb2502215ec9d05a171..cdc20567b693283b0fd5a5923f7ea54210bd1712 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; @@ -51,13 +52,14 @@ struct CachedPositionAndAccuracy { bool isHighAccuracy; }; + /** * Singleton that manages the geolocation provider */ class nsGeolocationService final : public nsIGeolocationUpdate, public nsIObserver { public: - static already_AddRefed GetGeolocationService(); + static already_AddRefed GetGeolocationService(nsDocShell* docShell = nullptr); static mozilla::StaticRefPtr sService; NS_DECL_THREADSAFE_ISUPPORTS @@ -189,6 +191,8 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache { BrowsingContext* aBrowsingContext, geolocation::ParentRequestResolver&& aResolver); + nsGeolocationService* GetGeolocationService() { return mService; }; + private: ~Geolocation(); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index c3e7de8f41e06e11155620b75c4d8a830d908b37..39eb9d31258693dce3a26c3227f28d92bccdb019 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -64,6 +64,7 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLDataListElement.h" #include "mozilla/dom/HTMLOptionElement.h" +#include "nsDocShell.h" #include "nsIFrame.h" #include "nsRangeFrame.h" #include "nsError.h" @@ -790,6 +791,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { return NS_ERROR_FAILURE; } + nsCOMPtr win = doc->GetWindow(); + nsDocShell* docShell = win ? static_cast(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 5e417145c4f21d8f2aa65088611477b681c9c327..bc84c509659c7556077e69c652e5b19639eb88bb 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 93f20a36acef74947d5377df5ff916546218d8b8..22b706b985d22a8c0c278a12ab4944e26ded51a4 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -1676,6 +1676,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, if (postLayerization) { postLayerization->Register(); } + + // Playwright: notify content that mouse event has been received and handled. + nsCOMPtr 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 c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb242b9a4e 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc @@ -52,9 +52,10 @@ namespace webrtc { DesktopCaptureImpl* DesktopCaptureImpl::Create(const int32_t aModuleId, const char* aUniqueId, - const CaptureDeviceType aType) { + const CaptureDeviceType aType, + bool aCaptureCursor) { return new rtc::RefCountedObject(aModuleId, aUniqueId, - aType); + aType, aCaptureCursor); } static DesktopCaptureOptions CreateDesktopCaptureOptions() { @@ -155,8 +156,10 @@ static std::unique_ptr CreateTabCapturer( static std::unique_ptr CreateDesktopCapturerAndThread( CaptureDeviceType aDeviceType, DesktopCapturer::SourceId aSourceId, - nsIThread** aOutThread) { + nsIThread** aOutThread, bool aCaptureCursor) { DesktopCaptureOptions options = CreateDesktopCaptureOptions(); + if (aCaptureCursor) + options.set_prefer_cursor_embedded(aCaptureCursor); auto ensureThread = [&]() { if (*aOutThread) { return *aOutThread; @@ -253,7 +256,8 @@ static std::unique_ptr 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) { @@ -270,6 +274,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") {} @@ -294,6 +299,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(); @@ -333,7 +351,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) { @@ -441,6 +459,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 a76b7de569db1cb42728a5514fb80e5c46e0344e..3d61ad8d3aa4a7eaf96957dcd680e1e1ee8abdf4 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.h +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.h @@ -26,6 +26,7 @@ #include "api/video/video_sink_interface.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/video_capture/video_capture.h" +#include "rtc_base/deprecated/recursive_critical_section.h" #include "mozilla/DataMutex.h" #include "mozilla/Maybe.h" #include "mozilla/TimeStamp.h" @@ -42,17 +43,44 @@ 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; +}; + // Reuses the video engine pipeline for screen sharing. // 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 CreateDeviceInfo(const int32_t aId, @@ -66,6 +94,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback, void DeRegisterCaptureDataCallback( rtc::VideoSinkInterface* 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; @@ -89,7 +119,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: @@ -98,6 +129,9 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback, void InitOnThread(std::unique_ptr aCapturer, int aFramerate); void UpdateOnThread(int aFramerate); void ShutdownOnThread(); + + rtc::RecursiveCriticalSection mApiCs; + std::set _rawFrameCallbacks; // DesktopCapturer::Callback interface. void OnCaptureResult(DesktopCapturer::Result aResult, std::unique_ptr aFrame) override; @@ -105,6 +139,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 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 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 5ec21c1c7f975a372399748e8bab2b21ce347f20..ed16831e549afa3d6623398d35eb61e26ab5f2b0 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -23,6 +23,7 @@ #include "nsSandboxFlags.h" #include "nsServiceManagerUtils.h" #include "nsWhitespaceTokenizer.h" +#include "nsDocShell.h" #include "mozilla/Assertions.h" #include "mozilla/Components.h" @@ -135,6 +136,11 @@ void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc, return; } + if (aDoc.GetDocShell() && + nsDocShell::Cast(aDoc.GetDocShell())->IsBypassCSPEnabled()) { + return; + } + nsAutoString policyStr( nsContentUtils::TrimWhitespace( aPolicyStr)); diff --git a/dom/webidl/GeometryUtils.webidl b/dom/webidl/GeometryUtils.webidl index aee376e971ae01ac1e512c3920b115bfaf06afa8..1701311534bf77e6cd9bafc0e3a283610476aa8f 100644 --- a/dom/webidl/GeometryUtils.webidl +++ b/dom/webidl/GeometryUtils.webidl @@ -17,6 +17,8 @@ dictionary GeometryUtilsOptions { boolean createFramesForSuppressedWhitespace = true; [ChromeOnly] boolean flush = true; + [ChromeOnly] + boolean recurseWhenNoFrame = false; }; dictionary BoxQuadOptions : GeometryUtilsOptions { @@ -35,6 +37,9 @@ interface mixin GeometryUtils { [Throws, Func="nsINode::HasBoxQuadsSupport", NeedsCallerType] sequence 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 a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49d94325f8 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -1026,7 +1026,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { AssertIsOnMainThread(); nsTArray languages; - Navigator::GetAcceptLanguages(languages); + Navigator::GetAcceptLanguages(nullptr, languages); RuntimeService* runtime = RuntimeService::GetService(); if (runtime) { @@ -1214,8 +1214,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; } @@ -1836,6 +1835,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( } } +void RuntimeService::ResetDefaultLocaleInAllWorkers() { + AssertIsOnMainThread(); + BroadcastAllWorkers([](auto& worker) { + worker.ResetDefaultLocale(); + }); +} + template void RuntimeService::BroadcastAllWorkers(const Func& aFunc) { AssertIsOnMainThread(); @@ -2361,6 +2367,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 5d918a82708a26125f7322e43f6436d7eafaa812..b230baead02e05d87a211c276066ec7939ea8251 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -736,6 +736,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 mLanguages; @@ -2159,6 +2171,16 @@ void WorkerPrivate::UpdateContextOptions( } } +void WorkerPrivate::ResetDefaultLocale() { + AssertIsOnParentThread(); + + RefPtr runnable = + new ResetDefaultLocaleRunnable(this); + if (!runnable->Dispatch(this)) { + NS_WARNING("Failed to reset default locale in worker!"); + } +} + void WorkerPrivate::UpdateLanguages(const nsTArray& aLanguages) { AssertIsOnParentThread(); @@ -5946,6 +5968,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& aLanguages) { WorkerGlobalScope* globalScope = GlobalScope(); diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 7bccdc8c0c2cd53f7aa7a6d9a74435344dd27980..86bba2128a7c0f4e5efa4bfbc939937aec146695 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -443,6 +443,8 @@ class WorkerPrivate final void UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions); + void ResetDefaultLocaleInternal(JSContext* aCx); + void UpdateLanguagesInternal(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, @@ -1091,6 +1093,8 @@ class WorkerPrivate final void UpdateContextOptions(const JS::ContextOptions& aContextOptions); + void ResetDefaultLocale(); + void UpdateLanguages(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameter(JSGCParamKey key, Maybe 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, ICUError> TimeZone::TryCreate( Maybe> aTimeZoneOverride) { @@ -318,6 +319,13 @@ static ICUResult SetDefaultTimeZone(TimeZoneIdentifierVector& timeZone) { } #endif +bool TimeZone::IsValidTimeZoneId(const char* timeZoneId) { + // Validate timezone id. + mozilla::UniquePtr timeZone(icu::TimeZone::createTimeZone( + icu::UnicodeString(timeZoneId, -1, US_INV))); + return timeZone && *timeZone != icu::TimeZone::getUnknown(); +} + Result TimeZone::SetDefaultTimeZone( Span 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 b7ea4b6f66d14db0324397cdc1b0ed8c5ea167e2..1c59e328079e7e43b65f7cb7bc31636a48a93263 100644 --- a/js/src/debugger/Object.cpp +++ b/js/src/debugger/Object.cpp @@ -2484,7 +2484,11 @@ Maybe 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 a57b8fefa104f966393a99f5a81876b9a95f9743..adc42dd227e52643b06fb101170aeafb490c0acc 100644 --- a/js/src/vm/DateTime.cpp +++ b/js/src/vm/DateTime.cpp @@ -185,6 +185,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); @@ -528,10 +533,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) { @@ -749,6 +768,15 @@ static bool ReadTimeZoneLink(std::string_view tz, void js::DateTimeInfo::internalResyncICUDefaultTimeZone() { #if JS_HAS_INTL_API + if (!timeZoneOverride_.empty()) { + mozilla::Span 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 @@ -760,7 +788,6 @@ void js::DateTimeInfo::internalResyncICUDefaultTimeZone() { if (const char* tzenv = std::getenv("TZ")) { std::string_view tz(tzenv); - mozilla::Span tzid; # if defined(XP_WIN) diff --git a/js/src/vm/DateTime.h b/js/src/vm/DateTime.h index e3cf82daa3749664aa8ced7e6553b8c6434dfec8..b45b49c4f3bbf12853c4afb12de21d99ac88d77b 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 @@ -253,6 +255,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) { { @@ -352,6 +355,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 @@ -367,6 +372,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 4bfd336ddcbee8004ac538ca7b7d8216d04a61c3..cd22351c4aeacea8afc9828972222aca1b3063bf 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; @@ -265,10 +266,27 @@ static bool CheckFramesInSameTopLevelBrowsingContext(nsIFrame* aFrame1, return false; } +static nsIFrame* GetFrameForNodeRecursive(nsINode* aNode, + const GeometryUtilsOptions& aOptions, + bool aRecurseWhenNoFrame) { + nsIFrame* frame = GetFrameForNode(aNode, aOptions); + if (!frame && aRecurseWhenNoFrame && aNode->IsContent()) { + dom::FlattenedChildIterator iter(aNode->AsContent()); + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { + frame = GetFrameForNodeRecursive(child, aOptions, aRecurseWhenNoFrame); + if (frame) { + break; + } + } + } + return frame; +} + void GetBoxQuads(nsINode* aNode, const dom::BoxQuadOptions& aOptions, nsTArray>& aResult, CallerType aCallerType, ErrorResult& aRv) { - nsIFrame* frame = GetFrameForNode(aNode, aOptions); + nsIFrame* frame = + GetFrameForNodeRecursive(aNode, aOptions, aOptions.mRecurseWhenNoFrame); if (!frame) { // No boxes to return return; @@ -281,7 +299,8 @@ void GetBoxQuads(nsINode* aNode, const dom::BoxQuadOptions& aOptions, // EnsureFrameForTextNode call. We need to get the first frame again // when that happens and re-check it. if (!weakFrame.IsAlive()) { - frame = GetFrameForNode(aNode, aOptions); + frame = + GetFrameForNodeRecursive(aNode, aOptions, aOptions.mRecurseWhenNoFrame); if (!frame) { // No boxes to return return; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index e8fb3a8304b27814e6e84355f24410c820667f9d..211c86fe55b8b650e40275a427b30a1ee8a9a3d7 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -11512,7 +11512,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 315d532eab56dab13b6c8bc2380a5cda2a17ffc1..7c4552137149e8c7fc9ac08a09bd4242952b53b6 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -708,6 +708,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(); @@ -9770,6 +9774,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 c7cf59c2661c7e203384c9b82789879f756b44b7..21e32dab4e60112c073bdd5070a308da2b4e0373 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 ca382a3cfba8ce5839890d6e4cb3cf9789287e3b..5800fc23dc77ee5764beddd6fa48a7fd701d2939 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -264,11 +264,7 @@ 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_PrefersReducedTransparency(const Document* aDocument) { @@ -293,6 +289,20 @@ StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( // as a signal. StylePrefersContrast Gecko_MediaFeatures_PrefersContrast( const Document* aDocument) { + if (auto* bc = aDocument->GetBrowsingContext()) { + switch (bc->Top()->PrefersContrastOverride()) { + case dom::PrefersContrastOverride::No_preference: + return StylePrefersContrast::NoPreference; + case dom::PrefersContrastOverride::Less: + return StylePrefersContrast::Less; + case dom::PrefersContrastOverride::More: + return StylePrefersContrast::More; + case dom::PrefersContrastOverride::Custom: + return StylePrefersContrast::Custom; + } + } + + if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSPrefersContrast)) { return StylePrefersContrast::NoPreference; } diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index 1ec2c64193206d31702e22e5c4783f084b1cff31..fb463eb12ee39cd1e448369f3b47fbcfbb2473b9 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -696,7 +696,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) rhs.mHasInjectedCookieForCookieBannerHandling), mSchemelessInput(rhs.mSchemelessInput), mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry), - mIsNewWindowTarget(rhs.mIsNewWindowTarget) { + mIsNewWindowTarget(rhs.mIsNewWindowTarget), + mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) { } LoadInfo::LoadInfo( @@ -2534,4 +2535,16 @@ LoadInfo::SetSkipHTTPSUpgrade(bool aSkipHTTPSUpgrade) { 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 93cc8d3630f7029303240555ae72d41b68047375..8a09863af399e25ba3f01caff2f6b3af1260e8b8 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -426,6 +426,8 @@ class LoadInfo final : public nsILoadInfo { bool mIsNewWindowTarget = false; bool mSkipHTTPSUpgrade = false; + + 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 d1650595f8cf28a704f94a99c1f6bfe1deb9cc77..2072a3990ff6f4496626dcebb277291ad845cac3 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp @@ -950,5 +950,15 @@ TRRLoadInfo::GetFetchDestination(nsACString& aDestination) { 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 774ec045c0b18310e8cb86e8a9d6b1788d028435..cbf303a0ed872c27d580b4b6615f3dd9c76a8a19 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -1626,4 +1626,6 @@ interface nsILoadInfo : nsISupports return static_cast(userNavigationInvolvement); } %} + + [infallible] attribute unsigned long long jugglerLoadIdentifier; }; diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl index 7f91d2df6f8bb4020c75c132dc8f6bf26625fa1e..aaa5541a17039d6b13ad83ab176fdaaf79edb2a0 100644 --- a/netwerk/base/nsINetworkInterceptController.idl +++ b/netwerk/base/nsINetworkInterceptController.idl @@ -60,6 +60,16 @@ interface nsIInterceptedChannel : nsISupports */ void resetInterception(in boolean bypass); + // ----- Playwright begin ----- + + // Same as resetInterception, but updates the URI. + void resetInterceptionWithURI(in nsIURI aURI); + + // After resetInterception is called, this request will be intercepted again. + void interceptAfterServiceWorkerResets(); + + // ----- Playwright end ------- + /** * Set the status and reason for the forthcoming synthesized response. * Multiple calls overwrite existing values. diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp index 771ae1fbe3d54aa25443eea675cf3abd26a21ce9..a916cb49c16dc6c7809ccbb7c8d4172446a5ac07 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp @@ -177,6 +177,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 fbf4bdf1e24d1102df113984be6c8dc3a7d0d810..787bf014d3bf0b8537f99bf5eb4074e100c78c18 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.cpp +++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp @@ -728,10 +728,33 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor) } // anonymous namespace +NS_IMETHODIMP +InterceptedHttpChannel::InterceptAfterServiceWorkerResets() { + mInterceptAfterServiceWorkerResets = true; + return NS_OK; +} + +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", this, aBypass ? "true" : "false")); + if (mInterceptAfterServiceWorkerResets) { + mInterceptAfterServiceWorkerResets = false; + nsCOMPtr controller; + GetCallback(controller); + if (!controller) + return NS_ERROR_DOM_INVALID_STATE_ERR; + return controller->ChannelIntercepted(this); + } + if (mCanceled) { return mStatus; } @@ -1146,11 +1169,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(this)); diff --git a/netwerk/protocol/http/InterceptedHttpChannel.h b/netwerk/protocol/http/InterceptedHttpChannel.h index 704404c9f094640ad63b685d64bd5a396e733e4b..92bdc21b4d6a015cc2f2bb22781ec6750c7789ec 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.h +++ b/netwerk/protocol/http/InterceptedHttpChannel.h @@ -90,6 +90,11 @@ class InterceptedHttpChannel final Atomic mCallingStatusAndProgress; bool mInterceptionReset{false}; + // ----- Playwright begin ----- + // After resetInterception is called, this request will call into interceptors again. + bool mInterceptAfterServiceWorkerResets{false}; + // ----- Playwright end ------- + /** * InterceptionTimeStamps is used to record the time stamps of the * interception. diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index d3b44cc62d3df49bbf842356cbdb153c82c3163c..23cf9bc83fb1faaf1c7406331b78e522b307cbf0 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -1349,6 +1349,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta( void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (mDocShell && static_cast(mDocShell.get())->IsBypassCSPEnabled()) { + return; + } + nsresult rv = NS_OK; nsCOMPtr preloadCsp = mDocument->GetPreloadCsp(); if (!preloadCsp) { diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp index 8413eb5916f1f857e18972a14292d14f32684aee..66a3c7b01fdc56c29d789ff786aa91d8b0f02cd6 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp @@ -433,7 +433,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; @@ -645,14 +650,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 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 mUserContextIdsWithDisabledSecurityChecks MOZ_GUARDED_BY(mMutex); nsCOMPtr mSettingsFile MOZ_GUARDED_BY(mMutex); nsTHashtable 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 d3643aedf21a26594268a47bc0f6ac53e3977f75..795c6f3b28278b9f65a596799d4e424880fcffa7 100644 --- a/services/settings/Utils.sys.mjs +++ b/services/settings/Utils.sys.mjs @@ -97,7 +97,7 @@ const _cdnURLs = {}; export var Utils = { get SERVER_URL() { - return lazy.allowServerURLOverride + return true || lazy.allowServerURLOverride ? lazy.gServerURL : AppConstants.REMOTE_SETTINGS_SERVER_URL; }, @@ -110,6 +110,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/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 76fb919603e8d2b7864d351eb47be2a38e40e31e..cdfef96e20bea13799751154f4076bbcc2f827d4 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 253171bed4dea54fc28bb4ddc9920823dbd9351c..6dc0e620b399ed9ee6b53f97bc080ec17ee4e1b5 100644 --- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp @@ -490,7 +490,7 @@ void PopulateLanguages() { // sufficient to only collect this information as the other properties are // just reformats of Navigator::GetAcceptLanguages. nsTArray 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 dc0826f72134b91482e30d183ddf52e95146e12f..119a324e162b6965ddd3d6b2d53bd2856a174452 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -361,7 +361,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) { nsCOMPtr windowEnumerator; nsCOMPtr 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 811fb16410e8cf900ad873797269e5fe715579a5..821f5b0c2af8e1dc8754cd023571d1d0ff09eeb6 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -1880,7 +1880,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 40f04aeace0efd701e9454bb8dc6260dec90807e..5b70f65f3e78fc0889b15651ff203bb82e79d202 100644 --- a/toolkit/mozapps/update/UpdateService.sys.mjs +++ b/toolkit/mozapps/update/UpdateService.sys.mjs @@ -3814,6 +3814,8 @@ export class UpdateService { } get disabledForTesting() { + /* playwright */ + return true; return lazy.UpdateServiceStub.updateDisabledForTesting; } diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index c50b7f3932e18da9fad4b673e353974a001e78c4..708e0d75594ddcd62276d4e08c4bd5c64d7f0698 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -152,6 +152,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 #include #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 e5cc386651e192710b61858ab5625c97a02b92da..e560ad4fef232a26ce1e1b244f4ccea05f4aea71 100644 --- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp @@ -812,6 +812,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, ("DocLoader:%p: Firing load event for document.open\n", this)); + nsCOMPtr 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 e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef228ea0e0cd 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" @@ -865,6 +866,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); @@ -1486,7 +1493,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) { // Strip off the ".part" from mTempLeafName mTempLeafName.Truncate(mTempLeafName.Length() - std::size(".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); @@ -1672,7 +1684,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { return NS_OK; } - rv = SetUpTempFile(aChannel); + bool isIntercepted = false; + nsCOMPtr interceptor = mExtProtSvc->mInterceptor; + if (interceptor) { + nsCOMPtr 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; @@ -1733,6 +1774,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 @@ -2249,6 +2293,16 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver, NotifyTransfer(aStatus); } + if (!mCanceled) { + nsCOMPtr 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; } @@ -2732,6 +2786,15 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) { } } + nsCOMPtr 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 2dd4ff87bda3e0ba395cca168c42b37db1713ddf..83e8a3d328e325b3f50f593c9ea71692f9c7d401 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -258,6 +258,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, mozilla::dom::BrowsingContext* aContentContext, bool aForceSave, nsIInterfaceRequestor* aWindowContext, nsIStreamListener** aStreamListener); + + nsCOMPtr mInterceptor; }; /** @@ -455,6 +457,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 53ea934dd4876e4b491b724385c8fbf7d00ee6cd..0b7b88c853b21ce778d8e87fea0a2bfe839ad412 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. @@ -87,6 +101,8 @@ interface nsIExternalHelperAppService : nsISupports * `DownloadIntegration.sys.mjs`, which is implemented on all platforms. */ nsIFile getPreferredDownloadsDirectory(); + + 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::CreateLocal( // do it after the static_cast. nsBaseWidget* widget = static_cast(aWidget); MOZ_RELEASE_ASSERT(widget); + if (aInitData.type() == + CompositorWidgetInitData::THeadlessCompositorWidgetInitData) { + return new HeadlessCompositorWidget( + aInitData.get_HeadlessCompositorWidgetInitData(), aOptions, + static_cast(aWidget)); + } return new InProcessCompositorWidget(aOptions, widget); } #endif diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 5ca1a6fa13233b1bd00ee0467732c5875c51d343..0d3b8ebe127e59516802e8819f4bbed961f0992b 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -368,6 +368,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; @@ -391,6 +394,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, mContextMenuTrigger = aEvent.mContextMenuTrigger; mExitFrom = aEvent.mExitFrom; mClickCount = aEvent.mClickCount; + mJugglerEventId = aEvent.mJugglerEventId; mIgnoreRootScrollFrame = aEvent.mIgnoreRootScrollFrame; mIgnoreCapturingContent = aEvent.mIgnoreCapturingContent; mClickEventPrevented = aEvent.mClickEventPrevented; diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.mm index 24b70173c2e8bb9be9fd6255984a70efe3b14099..75ac367a1c4bb44d4b68b5f4ecc6adf56dbd408e 100644 --- a/widget/cocoa/NativeKeyBindings.mm +++ b/widget/cocoa/NativeKeyBindings.mm @@ -549,6 +549,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())) { @@ -571,6 +578,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())) { @@ -593,6 +607,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()) { @@ -603,7 +621,7 @@ !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveToBeginningOfDocument:)) : ToObjcSelectorPtr( - @selector(moveToBegginingOfDocumentAndModifySelection:)), + @selector(moveToBeginningOfDocumentAndModifySelection:)), aCommands); break; } @@ -630,6 +648,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/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp index f4bded345e95674c66e4d4ad56b50844fce0871b..321e22d334a8bbc6057ee78e77e139a2804b2403 100644 --- a/widget/gtk/nsFilePicker.cpp +++ b/widget/gtk/nsFilePicker.cpp @@ -21,6 +21,7 @@ #include "mozilla/Components.h" #include "mozilla/Preferences.h" #include "mozilla/dom/Promise.h" +#include "gfxPlatform.h" #include "nsArrayEnumerator.h" #include "nsEnumeratorUtils.h" 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 HeadlessCompositorWidget::StartRemoteDrawingInRegion( + const LayoutDeviceIntRegion& aInvalidRegion, + layers::BufferMode* aBufferMode) { + if (!mDrawTarget) + return nullptr; + + *aBufferMode = layers::BufferMode::BUFFER_NONE; + RefPtr result = mDrawTarget; + return result.forget(); +} + void HeadlessCompositorWidget::ObserveVsync(VsyncObserver* aObserver) { if (RefPtr cvd = mWidget->GetCompositorVsyncDispatcher()) { @@ -31,6 +55,59 @@ void HeadlessCompositorWidget::NotifyClientSizeChanged( const LayoutDeviceIntSize& aClientSize) { auto size = mClientSize.Lock(); *size = aClientSize; + layers::CompositorThread()->Dispatch(NewRunnableMethod( + "HeadlessCompositorWidget::UpdateDrawTarget", this, + &HeadlessCompositorWidget::UpdateDrawTarget, + aClientSize)); +} + +void HeadlessCompositorWidget::UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize) { + MOZ_ASSERT(NS_IsInCompositorThread()); + if (aClientSize.IsEmpty()) { + mDrawTarget = nullptr; + return; + } + + RefPtr 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 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 snapshot = mDrawTarget->Snapshot(); + if (!snapshot) { + fprintf(stderr, "Failed to get snapshot of draw target\n"); + return; + } + + RefPtr 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 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 mClientSize; + + HeadlessWidget::SnapshotListener mSnapshotListener; + RefPtr mDrawTarget; }; } // namespace widget diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp index daa2d455374fd9f75a5c6ac9f7b91696d88b065c..f45184137b52db0a5774bf3365b15f784532fbdf 100644 --- a/widget/headless/HeadlessWidget.cpp +++ b/widget/headless/HeadlessWidget.cpp @@ -111,6 +111,8 @@ void HeadlessWidget::Destroy() { } } + SetSnapshotListener(nullptr); + nsBaseWidget::OnDestroy(); nsBaseWidget::Destroy(); @@ -593,5 +595,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 39833c28e40c61e354119cde429b8389056bafac..a638fb7520b857219ce58fcbf9ca0ed939528924 100644 --- a/widget/headless/HeadlessWidget.h +++ b/widget/headless/HeadlessWidget.h @@ -132,6 +132,9 @@ class HeadlessWidget final : public nsBaseWidget { int32_t aModifierFlags, nsIObserver* aObserver) override; + using SnapshotListener = std::function&&)>; + void SetSnapshotListener(SnapshotListener&& listener); + private: ~HeadlessWidget(); bool mEnabled; diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index f7262978239665cbe20470da0790d4d177d4c501..70d11aca3d5b509cf5b37d626299a23fede73ba3 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -244,6 +244,7 @@ struct ParamTraits { aParam.mExitFrom.value())); } WriteParam(aWriter, aParam.mClickCount); + WriteParam(aWriter, aParam.mJugglerEventId); } static bool Read(MessageReader* aReader, paramType* aResult) { @@ -268,6 +269,7 @@ struct ParamTraits { aResult->mExitFrom = Some(static_cast(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 /**