diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h index 0a5ebee659a06e3d3fdcd6fc88f749dfda617af5..cd3f3085560d91ada10ea92ebaf2c76412e69579 100644 --- a/accessible/base/NotificationController.h +++ b/accessible/base/NotificationController.h @@ -284,6 +284,8 @@ class NotificationController final : public EventQueue, } #endif + bool IsUpdatePendingForJugglerAccessibility() { return IsUpdatePending(); } + protected: virtual ~NotificationController(); diff --git a/accessible/interfaces/nsIAccessibleDocument.idl b/accessible/interfaces/nsIAccessibleDocument.idl index a91df31c96afda66f478a5a38eaa4352039c2a0b..ee777c1746284027fb3aa2f1686f8082af9d89ee 100644 --- a/accessible/interfaces/nsIAccessibleDocument.idl +++ b/accessible/interfaces/nsIAccessibleDocument.idl @@ -72,4 +72,9 @@ interface nsIAccessibleDocument : nsISupports * Return the child document accessible at the given index. */ nsIAccessibleDocument getChildDocumentAt(in unsigned long index); + + /** + * Return whether it is updating. + */ + readonly attribute boolean isUpdatePendingForJugglerAccessibility; }; diff --git a/accessible/xpcom/xpcAccessibleDocument.cpp b/accessible/xpcom/xpcAccessibleDocument.cpp index e3dbe73f22252f11080c3f266b2309f842eba9dc..87f50fe3df7cc8f9bc26dabd5ee571cae270912a 100644 --- a/accessible/xpcom/xpcAccessibleDocument.cpp +++ b/accessible/xpcom/xpcAccessibleDocument.cpp @@ -143,6 +143,15 @@ xpcAccessibleDocument::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) { return NS_OK; } + +NS_IMETHODIMP +xpcAccessibleDocument::GetIsUpdatePendingForJugglerAccessibility(bool* updating) { + NS_ENSURE_ARG_POINTER(updating); + *updating = Intl()->Controller()->IsUpdatePendingForJugglerAccessibility(); + return NS_OK; +} + + //////////////////////////////////////////////////////////////////////////////// // xpcAccessibleDocument diff --git a/accessible/xpcom/xpcAccessibleDocument.h b/accessible/xpcom/xpcAccessibleDocument.h index f042cc1081850ac60e329b70b5569f8b97d4e4dc..65bcff9b41b9471ef1427e3ea330481c194409bc 100644 --- a/accessible/xpcom/xpcAccessibleDocument.h +++ b/accessible/xpcom/xpcAccessibleDocument.h @@ -48,6 +48,8 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText, nsIAccessibleDocument** aDocument) final; NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) final; + NS_IMETHOD GetIsUpdatePendingForJugglerAccessibility(bool* aUpdating) final; + /** * Return XPCOM wrapper for the internal accessible. */ diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp index faf53eff9a46f958890d2c50de4c741fce75f1b1..7ad4b9f2203dbf1706518a0ba372d424673c9d64 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 @@ -324,8 +325,25 @@ 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, L"juggler-pipe", + static_cast(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); + SetEnvironmentVariable("PW_PIPE_READ", stdio3str); + SetEnvironmentVariable("PW_PIPE_WRITE", stdio4str); + 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 8c549f08dc64e8bc0b12a7ec236c9c9d97a1db3c..30a969a134823b6df5a0442969a73ff7352ce074 100644 --- a/browser/installer/allowed-dupes.mn +++ b/browser/installer/allowed-dupes.mn @@ -64,6 +64,12 @@ browser/defaults/settings/pinning/pins.json browser/defaults/settings/main/example.json browser/defaults/settings/main/search-default-override-allowlist.json +# Juggler/marionette files +chrome/juggler/content/content/floating-scrollbars.css +browser/chrome/devtools/skin/floating-scrollbars-responsive-design.css +chrome/juggler/content/server/stream-utils.js +chrome/marionette/content/stream-utils.js + #ifdef MOZ_EME_WIN32_ARTIFACT gmp-clearkey/0.1/manifest.json i686/gmp-clearkey/0.1/manifest.json diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 75c79a7168a631f31f751f971b04f9f05f6b8954..4f25debd2a9a6e877394917d43a0bb2f1946969d 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -215,6 +215,11 @@ @RESPATH@/components/marionette.js #endif +@RESPATH@/chrome/juggler@JAREXT@ +@RESPATH@/chrome/juggler.manifest +@RESPATH@/components/juggler.manifest +@RESPATH@/components/juggler.js + #if defined(ENABLE_TESTS) && defined(MOZ_DEBUG) @RESPATH@/components/TestInterfaceJS.js @RESPATH@/components/TestInterfaceJS.manifest diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js index 040c7b124dec6bb254563bbe74fe50012cb077a3..b4e6b8132786af70e8ad0dce88b67c2835307f88 100644 --- a/devtools/server/socket/websocket-server.js +++ b/devtools/server/socket/websocket-server.js @@ -133,13 +133,12 @@ function writeHttpResponse(output, response) { * Process the WebSocket handshake headers and return the key to be sent in * Sec-WebSocket-Accept response header. */ -function processRequest({ requestLine, headers }) { +function processRequest({ requestLine, headers }, expectedPath) { const [method, path] = requestLine.split(" "); if (method !== "GET") { throw new Error("The handshake request must use GET method"); } - - if (path !== "/") { + if (path !== expectedPath) { throw new Error("The handshake request has unknown path"); } @@ -189,13 +188,13 @@ function computeKey(key) { /** * Perform the server part of a WebSocket opening handshake on an incoming connection. */ -const serverHandshake = async function(input, output) { +const serverHandshake = async function(input, output, expectedPath) { // Read the request const request = await readHttpRequest(input); try { // Check and extract info from the request - const { acceptKey } = processRequest(request); + const { acceptKey } = processRequest(request, expectedPath); // Send response headers await writeHttpResponse(output, [ @@ -217,8 +216,8 @@ const serverHandshake = async function(input, output) { * Performs the WebSocket handshake and waits for the WebSocket to open. * Returns Promise with a WebSocket ready to send and receive messages. */ -const accept = async function(transport, input, output) { - await serverHandshake(input, output); +const accept = async function(transport, input, output, expectedPath) { + await serverHandshake(input, output, expectedPath || "/"); const transportProvider = { setListener(upgradeListener) { diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 5bb0e61df8197a8fe3ff4603fb1cb0d97a327141..67eb81b0c38828b38fdc062a1ea36d92ce0a4c2f 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" @@ -58,6 +64,7 @@ #include "mozilla/dom/ContentFrameMessageManager.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/Geolocation.h" #include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/PerformanceNavigation.h" @@ -79,6 +86,7 @@ #include "mozilla/dom/LoadURIOptionsBinding.h" #include "mozilla/dom/JSWindowActorChild.h" #include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/dom/WorkerCommon.h" #include "mozilla/net/DocumentChannel.h" #include "mozilla/net/ParentChannelWrapper.h" #include "mozilla/net/UrlClassifierFeatureFactory.h" @@ -103,6 +111,7 @@ #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeOwner.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/Element.h" #include "nsIDocumentLoaderFactory.h" #include "nsIDOMWindow.h" #include "nsIEditingSession.h" @@ -195,6 +204,7 @@ #include "nsGlobalWindow.h" #include "nsISearchService.h" #include "nsJSEnvironment.h" +#include "nsJSUtils.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsObjectLoadingContent.h" @@ -389,6 +399,11 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, mAllowDNSPrefetch(true), mAllowWindowControl(true), mCSSErrorReportingEnabled(false), + mFileInputInterceptionEnabled(false), + mOverrideHasFocus(false), + mBypassCSPEnabled(false), + mOnlineOverride(nsIDocShell::ONLINE_OVERRIDE_NONE), + mColorSchemeOverride(COLOR_SCHEME_OVERRIDE_NONE), mAllowAuth(mItemType == typeContent), mAllowKeywordFixup(false), mIsOffScreenBrowser(false), @@ -1418,6 +1433,7 @@ bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest, } } + FireOnFrameLocationChange(this, aRequest, aURI, aLocationFlags); if (!isSubFrame && !isRoot) { /* * We don't want to send OnLocationChange notifications when @@ -3229,6 +3245,189 @@ 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; +} + +bool nsDocShell::IsBypassCSPEnabled() { + return GetRootDocShell()->mBypassCSPEnabled; +} + +NS_IMETHODIMP +nsDocShell::GetOverrideHasFocus(bool* aEnabled) { + MOZ_ASSERT(aEnabled); + *aEnabled = mOverrideHasFocus; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetOverrideHasFocus(bool aEnabled) { + mOverrideHasFocus = aEnabled; + return NS_OK; +} + +bool nsDocShell::ShouldOverrideHasFocus() const { + return mOverrideHasFocus; +} + +NS_IMETHODIMP +nsDocShell::GetLanguageOverride(nsAString& aLanguageOverride) { + MOZ_ASSERT(aEnabled); + aLanguageOverride = GetRootDocShell()->mLanguageOverride; + return NS_OK; +} + + +static void SetIcuLocale(const nsAString& aLanguageOverride) { + icu::Locale locale(NS_LossyConvertUTF16toASCII(aLanguageOverride).get()); + if (icu::Locale::getDefault() == locale) + return; + UErrorCode error_code = U_ZERO_ERROR; + const char* lang = locale.getLanguage(); + if (lang != nullptr && *lang != '\0') { + icu::Locale::setDefault(locale, error_code); + } else { + fprintf(stderr, "SetIcuLocale Failed to set the ICU default locale to %s\n", NS_LossyConvertUTF16toASCII(aLanguageOverride).get()); + } + + AutoJSAPI jsapi; + jsapi.Init(); + JSContext* cx = jsapi.cx(); + JS_ResetDefaultLocale(JS_GetRuntime(cx)); + + ResetDefaultLocaleInAllWorkers(); +} + +NS_IMETHODIMP +nsDocShell::SetLanguageOverride(const nsAString& aLanguageOverride) { + mLanguageOverride = aLanguageOverride; + SetIcuLocale(aLanguageOverride); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::OverrideTimezone(const nsAString& aTimezoneOverride, + bool* aSuccess) { + NS_ENSURE_ARG(aSuccess); + NS_LossyConvertUTF16toASCII timeZoneId(aTimezoneOverride); + *aSuccess = nsJSUtils::SetTimeZoneOverride(timeZoneId.get()); + + // Set TZ which affects localtime_s(). + auto setTimeZoneEnv = [](const char* value) { +#if defined(_WIN32) + return _putenv_s("TZ", value) == 0; +#else + return setenv("TZ", value, true) == 0; +#endif /* _WIN32 */ + }; + if (*aSuccess) { + *aSuccess = setTimeZoneEnv(timeZoneId.get()); + if (!*aSuccess) { + fprintf(stderr, "Failed to set 'TZ' to '%s'\n", timeZoneId.get()); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetFileInputInterceptionEnabled(bool* aEnabled) { + MOZ_ASSERT(aEnabled); + *aEnabled = GetRootDocShell()->mFileInputInterceptionEnabled; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetFileInputInterceptionEnabled(bool aEnabled) { + mFileInputInterceptionEnabled = aEnabled; + return NS_OK; +} + +bool nsDocShell::IsFileInputInterceptionEnabled() { + return GetRootDocShell()->mFileInputInterceptionEnabled; +} + +void nsDocShell::FilePickerShown(mozilla::dom::Element* element) { + nsCOMPtr 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::GetOnlineOverride(OnlineOverride* aOnlineOverride) { + *aOnlineOverride = GetRootDocShell()->mOnlineOverride; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetOnlineOverride(OnlineOverride aOnlineOverride) { + // We don't have a way to verify this coming from Javascript, so this check is + // still needed. + if (!(aOnlineOverride == ONLINE_OVERRIDE_NONE || + aOnlineOverride == ONLINE_OVERRIDE_ONLINE || + aOnlineOverride == ONLINE_OVERRIDE_OFFLINE)) { + return NS_ERROR_INVALID_ARG; + } + + mOnlineOverride = aOnlineOverride; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetColorSchemeOverride(ColorSchemeOverride* aColorSchemeOverride) { + *aColorSchemeOverride = GetRootDocShell()->mColorSchemeOverride; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::SetColorSchemeOverride(ColorSchemeOverride aColorSchemeOverride) { + mColorSchemeOverride = aColorSchemeOverride; + RefPtr presContext = GetPresContext(); + if (presContext) { + presContext->MediaFeatureValuesChanged( + {MediaFeatureChangeReason::SystemMetricsChange}); + } + return NS_OK; +} + +// =============== Juggler End ======================= + NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; @@ -8539,6 +8738,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; } @@ -12341,6 +12546,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; } @@ -12426,6 +12634,8 @@ nsresult nsDocShell::OnLinkClick( nsCOMPtr ev = new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied, aIsTrusted, aTriggeringPrincipal); + nsCOMPtr observerService = mozilla::services::GetObserverService(); + observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr); return Dispatch(TaskCategory::UI, ev.forget()); } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 06ffb9408a43db3968bb01952399bd461855b181..7b3fb6b5223815a1b2279f68564305a1b787418e 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -13,6 +13,7 @@ #include "Units.h" #include "jsapi.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/dom/Geolocation.h" #include "mozilla/HalScreenConfiguration.h" #include "mozilla/LinkedList.h" #include "mozilla/Maybe.h" @@ -25,6 +26,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/ChildSHistory.h" #include "mozilla/dom/ProfileTimelineMarkerBinding.h" #include "mozilla/dom/WindowProxyHolder.h" @@ -416,6 +418,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 CreateContentViewerForActor( @@ -1030,6 +1041,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 @@ -1263,6 +1276,14 @@ class nsDocShell final : public nsDocLoader, bool mAllowDNSPrefetch : 1; bool mAllowWindowControl : 1; bool mCSSErrorReportingEnabled : 1; + bool mFileInputInterceptionEnabled: 1; + bool mOverrideHasFocus : 1; + bool mBypassCSPEnabled : 1; + nsString mLanguageOverride; + RefPtr mGeolocationServiceOverride; + OnlineOverride mOnlineOverride; + ColorSchemeOverride mColorSchemeOverride; + bool mAllowAuth : 1; bool mAllowKeywordFixup : 1; bool mIsOffScreenBrowser : 1; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 5102fd30129a96d4a3ac2a9490ec10f31e6ceafc..c30710d04be5ad3bd0fbb946e9bc311b881bfd2a 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -44,6 +44,7 @@ interface nsIURI; interface nsIChannel; interface nsIContentViewer; interface nsIContentSecurityPolicy; +interface nsIDOMGeoPosition; interface nsIDocShellLoadInfo; interface nsIEditor; interface nsIEditingSession; @@ -945,6 +946,33 @@ interface nsIDocShell : nsIDocShellTreeItem */ void synchronizeLayoutHistoryState(); + attribute boolean fileInputInterceptionEnabled; + + attribute boolean overrideHasFocus; + + attribute boolean bypassCSPEnabled; + + attribute AString languageOverride; + + boolean overrideTimezone(in AString timezoneId); + + cenum OnlineOverride: 8 { + ONLINE_OVERRIDE_NONE = 0, + ONLINE_OVERRIDE_ONLINE = 1, + ONLINE_OVERRIDE_OFFLINE = 2, + }; + [infallible] attribute nsIDocShell_OnlineOverride onlineOverride; + + cenum ColorSchemeOverride : 8 { + COLOR_SCHEME_OVERRIDE_LIGHT, + COLOR_SCHEME_OVERRIDE_DARK, + COLOR_SCHEME_OVERRIDE_NO_PREFERENCE, + COLOR_SCHEME_OVERRIDE_NONE, /* This clears the override. */ + }; + [infallible] attribute nsIDocShell_ColorSchemeOverride colorSchemeOverride; + + void setGeolocationOverride(in nsIDOMGeoPosition position); + /** * This attempts to save any applicable layout history state (like * scroll position) in the nsISHEntry. This is normally done diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index dd39e33c72269c6eb2b9e09e36bd11b4a3ee5980..d09d6124dd9fb3633d15edd8059f7470471c4e10 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -3278,6 +3278,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 @@ -3332,6 +3335,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { return NS_OK; } + 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; @@ -4103,6 +4111,10 @@ bool Document::HasFocus(ErrorResult& rv) const { return false; } + if (IsActive() && mDocumentContainer->ShouldOverrideHasFocus()) { + return true; + } + // Is there a focused DOMWindow? nsCOMPtr focusedWindow; fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); @@ -16743,6 +16755,19 @@ void Document::RemoveToplevelLoadingDocument(Document* aDoc) { StylePrefersColorScheme Document::PrefersColorScheme( IgnoreRFP aIgnoreRFP) const { + auto* docShell = static_cast(GetDocShell()); + nsIDocShell::ColorSchemeOverride colorScheme; + if (docShell->GetColorSchemeOverride(&colorScheme) == NS_OK && + colorScheme != nsIDocShell::COLOR_SCHEME_OVERRIDE_NONE) { + switch (colorScheme) { + case nsIDocShell::COLOR_SCHEME_OVERRIDE_LIGHT: + return StylePrefersColorScheme::Light; + case nsIDocShell::COLOR_SCHEME_OVERRIDE_DARK: + return StylePrefersColorScheme::Dark; + case nsIDocShell::COLOR_SCHEME_OVERRIDE_NO_PREFERENCE: + return StylePrefersColorScheme::NoPreference; + }; + } if (aIgnoreRFP == IgnoreRFP::No && nsContentUtils::ShouldResistFingerprinting(this)) { return StylePrefersColorScheme::Light; diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 3ec7aa3c8e5973a6e6c042c7bbf18b571bfffc33..6169c56fd6de7c3452384b76979f5d1faabc8686 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -326,14 +326,18 @@ void Navigator::GetAppName(nsAString& aAppName, CallerType aCallerType) 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. nsCharSeparatedTokenizer langTokenizer(acceptLang, ','); @@ -389,7 +393,9 @@ void Navigator::GetLanguage(nsAString& aLanguage) { } void Navigator::GetLanguages(nsTArray& aLanguages) { - GetAcceptLanguages(aLanguages); + nsString languageOverride; + mWindow->GetDocShell()->GetLanguageOverride(languageOverride); + GetAcceptLanguages(&languageOverride, aLanguages); // The returned value is cached by the binding code. The window listens to the // accept languages change and will clear the cache when needed. It has to @@ -553,7 +559,13 @@ bool Navigator::CookieEnabled() { return granted; } -bool Navigator::OnLine() { return !NS_IsOffline(); } +bool Navigator::OnLine() { + nsDocShell* docShell = static_cast(GetDocShell()); + nsIDocShell::OnlineOverride onlineOverride; + if (!docShell || docShell->GetOnlineOverride(&onlineOverride) != NS_OK || onlineOverride == nsIDocShell::ONLINE_OVERRIDE_NONE) + return !NS_IsOffline(); + return onlineOverride == nsIDocShell::ONLINE_OVERRIDE_ONLINE; +} void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType, ErrorResult& aRv) const { diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 9e898052f8eebd8331b46b105bc8b6eeff3a2e84..994243e99814a3f8882c9fe425ecab521b9820e4 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -220,7 +220,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 dbe02dd6082c7fea5d161328d55fd003adbfe853..ba410cdc1677234665d95f6ea984b7af8b9d34d2 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7837,7 +7837,8 @@ nsresult nsContentUtils::SendMouseEvent( bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized, - bool aIsWidgetEventSynthesized) { + bool aIsWidgetEventSynthesized, + bool convertToPointer) { nsPoint offset; nsCOMPtr widget = GetWidget(aPresShell, &offset); if (!widget) return NS_ERROR_FAILURE; @@ -7893,6 +7894,7 @@ nsresult nsContentUtils::SendMouseEvent( event.mTime = PR_IntervalNow(); event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized; event.mExitFrom = exitFrom; + event.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 f4b25288a8183ceaa92c39ba6f55ae6d91948b96..1c5645c4f4bc81d79e815d9fb0520c0b0410c8b7 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2897,7 +2897,7 @@ class nsContentUtils { 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 = true); static void FirePageShowEventForFrameLoaderSwap( nsIDocShellTreeItem* aItem, diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 657577f2bdfcacbcc2f038080a9c3bf10ed46c24..11b26095373d848862bed1d77f3b8e527de68199 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -647,7 +647,7 @@ nsDOMWindowUtils::SendMouseEvent( int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, bool aIsDOMEventSynthesized, bool aIsWidgetEventSynthesized, - int32_t aButtons, uint32_t aIdentifier, uint8_t aOptionalArgCount, + int32_t aButtons, uint32_t aIdentifier, bool aDisablePointerEvent, uint8_t aOptionalArgCount, bool* aPreventDefault) { return SendMouseEventCommon( aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, @@ -655,7 +655,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, !aDisablePointerEvent); } NS_IMETHODIMP @@ -682,12 +682,12 @@ 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) { RefPtr presShell = GetPresShell(); return nsContentUtils::SendMouseEvent( presShell, aType, aX, aY, aButton, aButtons, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, aInputSourceArg, aPointerId, aToWindow, - aPreventDefault, aIsDOMEventSynthesized, aIsWidgetEventSynthesized); + aPreventDefault, aIsDOMEventSynthesized, aIsWidgetEventSynthesized, aConvertToPointer); } NS_IMETHODIMP diff --git a/dom/base/nsDOMWindowUtils.h b/dom/base/nsDOMWindowUtils.h index 08e81b1c24a17729ec7b6c9e048c2febe57e18dc..cb09fe30de0a42c89da220e3bf8afe5f05923084 100644 --- a/dom/base/nsDOMWindowUtils.h +++ b/dom/base/nsDOMWindowUtils.h @@ -93,7 +93,7 @@ class nsDOMWindowUtils final : public nsIDOMWindowUtils, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized, - bool aIsWidgetEventSynthesized, int32_t aButtons); + bool aIsWidgetEventSynthesized, int32_t aButtons, bool aConvertToPointer = true); MOZ_CAN_RUN_SCRIPT nsresult SendTouchEventCommon( diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 264b2509a71d85124079f6855c5ac19ce72082ef..3c45db6ad2151bc1454a5b1eb570774c81a2c7d8 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -2806,7 +2806,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 8a671defd79d57d94333ed7f5285f07f1a426269..fa87f7590fd13cdb43a28a6527f9f136a55e42c9 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -2463,7 +2463,7 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, &nsGlobalWindowInner::FireOnNewGlobalObject)); } - if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { + if (newInnerWindow && mDoc) { // We should probably notify. However if this is the, arguably bad, // situation when we're creating a temporary non-chrome-about-blank // document in a chrome docshell, don't notify just yet. Instead wait @@ -2482,10 +2482,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)); + } } } @@ -2605,6 +2611,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(EmptyString()); } void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) { @@ -3865,6 +3884,14 @@ Maybe nsGlobalWindowOuter::GetRDMDeviceSize( } } } + if (topInProcessContentDoc) { + nsIDocShell* docShell = topInProcessContentDoc->GetDocShell(); + if (docShell && docShell->GetDeviceSizeIsPageSize()) { + nsPresContext* presContext = docShell->GetPresContext(); + if (presContext) + return Some(CSSPixel::FromAppUnitsRounded(presContext->GetVisibleArea().Size())); + } + } return Nothing(); } diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h index 63d65fa057ea13362b60aaf5277cf5a638a8aaac..73a7ac2c3624110c7992dcbeb90427052d4af2ff 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h @@ -324,6 +324,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 f5d900d618657b601baf079d8a9bc3ad265ef498..06e4d027929dcaccf188da2dd2e46ddc38603411 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1258,6 +1258,48 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv); } +void nsINode::ScrollRectIntoViewIfNeeded(int32_t x, int32_t y, + int32_t w, int32_t h, + ErrorResult& aRv) { + aRv = NS_ERROR_UNEXPECTED; + nsCOMPtr document = OwnerDoc(); + if (!document) { + return aRv.ThrowNotFoundError("Node is detached from document"); + } + PresShell* presShell = document->GetPresShell(); + if (!presShell) { + return aRv.ThrowNotFoundError("Node is detached from document"); + } + if (!IsContent()) { + return aRv.ThrowNotFoundError("Node does not have a layout object"); + } + aRv = NS_OK; + nsIFrame* primaryFrame = AsContent()->GetPrimaryFrame(FlushType::Frames); + if (!primaryFrame) { + return aRv.ThrowNotFoundError("Node does not have a layout object"); + } + nsRect rect; + if (x == -1 && y == -1 && w == -1 && h == -1) { + rect = primaryFrame->GetRectRelativeToSelf(); + } else { + rect = nsRect(nsPresContext::CSSPixelsToAppUnits(x), + nsPresContext::CSSPixelsToAppUnits(y), + nsPresContext::CSSPixelsToAppUnits(w), + nsPresContext::CSSPixelsToAppUnits(h)); + } + presShell->ScrollFrameRectIntoView( + primaryFrame, rect, + ScrollAxis(kScrollToCenter, WhenToScroll::Always), + ScrollAxis(kScrollToCenter, WhenToScroll::Always), + ScrollFlags::ScrollOverflowHidden); + // If a _visual_ scroll update is pending, cancel it; otherwise, it will + // clobber next scroll (e.g. subsequent window.scrollTo(0, 0) wlll break). + if (presShell->GetPendingVisualScrollUpdate()) { + presShell->AcknowledgePendingVisualScrollUpdate(); + presShell->ClearPendingVisualScrollUpdate(); + } +} + already_AddRefed 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 5aca0ee79b59e912447f3797c4598ab5b603b95a..5c1ab22a9548295a41caf0f5ea781a1256107ad5 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -2052,6 +2052,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 b518b30c78a25bfe02471632eea4aabf13b0c074..10dda5cfe2982e2b97d8258bf0181ce23e033db7 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -507,6 +507,11 @@ bool nsJSUtils::GetScopeChainForElement( return true; } +/* static */ +bool nsJSUtils::SetTimeZoneOverride(const char* timezoneId) { + return JS::SetTimeZoneOverride(timezoneId); +} + /* static */ void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); } diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index dd1bc7262365f55531c22200c2bb359abebf18e1..552e1ec925c3eb2eb863ec0e99f5bbbabd4b6958 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -232,6 +232,7 @@ class nsJSUtils { JSContext* aCx, mozilla::dom::Element* aElement, JS::MutableHandleVector aScopeChain); + static bool SetTimeZoneOverride(const char* timezoneId); static void ResetTimeZone(); static bool DumpEnabled(); diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp index e1b1075e6318d63c7b7d6d60b419d5fe6b630652..c02ae426052f22997fa8513f20bc3dc05b14726a 100644 --- a/dom/geolocation/Geolocation.cpp +++ b/dom/geolocation/Geolocation.cpp @@ -23,6 +23,7 @@ #include "nsComponentManagerUtils.h" #include "nsContentPermissionHelper.h" #include "nsContentUtils.h" +#include "nsDocShell.h" #include "nsGlobalWindow.h" #include "mozilla/dom/Document.h" #include "nsINamed.h" @@ -291,10 +292,8 @@ nsGeolocationRequest::Allow(JS::HandleValue 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) { DOMTimeStamp cachedPositionTime_ms; @@ -468,8 +467,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(); } @@ -746,8 +744,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; @@ -839,7 +843,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 d92bd1c738016f93c66dbdc449c70937c37b6f9a..a4c1f0ca974470342cb8136705d78cfc00e35083 100644 --- a/dom/geolocation/Geolocation.h +++ b/dom/geolocation/Geolocation.h @@ -57,7 +57,7 @@ struct CachedPositionAndAccuracy { class nsGeolocationService final : public nsIGeolocationUpdate, public nsIObserver { public: - static already_AddRefed GetGeolocationService(); + static already_AddRefed GetGeolocationService(nsDocShell* docShell = nullptr); static mozilla::StaticRefPtr sService; NS_DECL_THREADSAFE_ISUPPORTS @@ -182,6 +182,8 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache { // null. static already_AddRefed NonWindowSingleton(); + nsGeolocationService* GetGeolocationService() { return mService; }; + private: ~Geolocation(); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index ad9ad3692022c7eb5e38fac5242b41943ee05dee..b2d34d4daa28a1f6ebff083452196820c31ff006 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -47,6 +47,7 @@ #include "nsMappedAttributes.h" #include "nsIFormControl.h" #include "mozilla/dom/Document.h" +#include "nsDocShell.h" #include "nsIFormControlFrame.h" #include "nsITextControlFrame.h" #include "nsIFrame.h" @@ -703,6 +704,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { return NS_ERROR_FAILURE; } + nsDocShell* docShell = static_cast(win->GetDocShell()); + if (docShell && docShell->IsFileInputInterceptionEnabled()) { + docShell->FilePickerShown(this); + return NS_OK; + } + if (IsPopupBlocked()) { return NS_OK; } diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 6728f7f4e4fbc6ace1a674bebfe179af0da087e1..e1147cb7e810c8fac743145aae10937c7fbc8f4b 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -347,7 +347,8 @@ interface nsIDOMWindowUtils : nsISupports { [optional] in boolean aIsDOMEventSynthesized, [optional] in boolean aIsWidgetEventSynthesized, [optional] in long aButtons, - [optional] in unsigned long aIdentifier); + [optional] in unsigned long aIdentifier, + [optional] 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 bb336902d2303223d14b07cb7dbc7aba1347490b..ac37b7e8b09683d077d4467c74487b137e541ebb 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -3574,6 +3574,13 @@ NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress, return NS_OK; } +NS_IMETHODIMP BrowserChild::OnFrameLocationChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *aLocation, + uint32_t aFlags) { + return NS_OK; +} + NS_IMETHODIMP BrowserChild::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, int32_t aCurSelfProgress, diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.cc b/dom/media/systemservices/video_engine/desktop_capture_impl.cc index 76dbf6ef6bed843568c45606fe825233c6cd77a5..a43e1977b278c52ee0611f387dd726bf7ffb8e7c 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc @@ -125,8 +125,9 @@ int32_t ScreenDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8, VideoCaptureModule* DesktopCaptureImpl::Create(const int32_t id, const char* uniqueId, - const CaptureDeviceType type) { - return new rtc::RefCountedObject(id, uniqueId, type); + const CaptureDeviceType type, + bool captureCursor) { + return new rtc::RefCountedObject(id, uniqueId, type, captureCursor); } int32_t WindowDeviceInfoImpl::Init() { @@ -360,12 +361,16 @@ int32_t DesktopCaptureImpl::Init() { DesktopCapturer::SourceId sourceId = atoi(_deviceUniqueId.c_str()); pWindowCapturer->SelectSource(sourceId); - MouseCursorMonitor* pMouseCursorMonitor = - MouseCursorMonitor::CreateForWindow( - webrtc::DesktopCaptureOptions::CreateDefault(), sourceId); - desktop_capturer_cursor_composer_ = - std::unique_ptr(new DesktopAndCursorComposer( - pWindowCapturer.release(), pMouseCursorMonitor)); + if (capture_cursor_) { + MouseCursorMonitor* pMouseCursorMonitor = + MouseCursorMonitor::CreateForWindow( + webrtc::DesktopCaptureOptions::CreateDefault(), sourceId); + desktop_capturer_cursor_composer_ = + std::unique_ptr(new DesktopAndCursorComposer( + pWindowCapturer.release(), pMouseCursorMonitor)); + } else { + desktop_capturer_cursor_composer_ = std::move(pWindowCapturer); + } } else if (_deviceType == CaptureDeviceType::Browser) { // XXX We don't capture cursors, so avoid the extra indirection layer. We // could also pass null for the pMouseCursorMonitor. @@ -382,7 +387,8 @@ int32_t DesktopCaptureImpl::Init() { } DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id, const char* uniqueId, - const CaptureDeviceType type) + const CaptureDeviceType type, + bool captureCursor) : _id(id), _deviceUniqueId(uniqueId), _deviceType(type), @@ -393,6 +399,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id, const char* uniqueId, delta_ntp_internal_ms_( Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() - last_capture_time_), + capture_cursor_(captureCursor), time_event_(EventWrapper::Create()), #if defined(_WIN32) capturer_thread_( diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.h b/dom/media/systemservices/video_engine/desktop_capture_impl.h index e1f28e16da200a7727c6cb85ab41e843d81ce802..9e27e14f235e56b2b231060738c1422a79379b75 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.h +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.h @@ -159,7 +159,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback, /* Create a screen capture modules object */ static VideoCaptureModule* Create(const int32_t id, const char* uniqueId, - const CaptureDeviceType type); + const CaptureDeviceType type, + bool captureCursor = true); static VideoCaptureModule::DeviceInfo* CreateDeviceInfo( const int32_t id, const CaptureDeviceType type); @@ -191,7 +192,7 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback, protected: DesktopCaptureImpl(const int32_t id, const char* uniqueId, - const CaptureDeviceType type); + const CaptureDeviceType type, bool captureCursor); virtual ~DesktopCaptureImpl(); int32_t DeliverCapturedFrame(webrtc::VideoFrame& captureFrame, int64_t capture_time); @@ -239,6 +240,7 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback, void process(); private: + bool capture_cursor_ = true; // This is created on the main thread and accessed on both the main thread // and the capturer thread. It is created prior to the capturer thread // starting and is destroyed after it is stopped. diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp index 28b14a75e98c453ca6ed26f188a09c275c296a54..db904ecd71b8cc2eef6becc9117d711a95935c9a 100644 --- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -142,6 +142,30 @@ ScriptSettingsStackEntry::~ScriptSettingsStackEntry() { MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->HasJSGlobal()); } +static nsIGlobalObject* UnwrapSandboxGlobal(nsIGlobalObject* global) { + if (!global) + return global; + JSObject* globalObject = global->GetGlobalJSObject(); + if (!globalObject) + return global; + JSContext* cx = nsContentUtils::GetCurrentJSContext(); + if (!cx) + return global; + JS::Rooted proto(cx); + JS::RootedObject rootedGlobal(cx, globalObject); + if (!JS_GetPrototype(cx, rootedGlobal, &proto)) + return global; + if (!proto || !xpc::IsSandboxPrototypeProxy(proto)) + return global; + // If this is a sandbox associated with a DOMWindow via a + // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey + // and JetPack content scripts. + proto = js::CheckedUnwrapDynamic(proto, cx, /* stopAtWindowProxy = */ false); + if (!proto) + return global; + return xpc::WindowGlobalOrNull(proto); +} + // If the entry or incumbent global ends up being something that the subject // principal doesn't subsume, we don't want to use it. This never happens on // the web, but can happen with asymmetric privilege relationships (i.e. @@ -169,7 +193,7 @@ static nsIGlobalObject* ClampToSubject(nsIGlobalObject* aGlobalOrNull) { NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal()); if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller() ->SubsumesConsideringDomain(globalPrin)) { - return GetCurrentGlobal(); + return UnwrapSandboxGlobal(GetCurrentGlobal()); } return aGlobalOrNull; diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index 1da8659fa5bd4def23569c9b2fe31b0821dfdb69..6dd64c94ab29f4c0ea1bc080b07249968509f32d 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -121,6 +121,11 @@ void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc, return; } + if (aDoc.GetDocShell() && + nsDocShell::Cast(aDoc.GetDocShell())->IsBypassCSPEnabled()) { + return; + } + nsAutoString policyStr( nsContentUtils::TrimWhitespace( aPolicyStr)); diff --git a/dom/webidl/GeometryUtils.webidl b/dom/webidl/GeometryUtils.webidl index 2f71b284ee5f7e11f117c447834b48355784448c..d996e0a3cbbb19c1dc320c305c6d74037bffa0d3 100644 --- a/dom/webidl/GeometryUtils.webidl +++ b/dom/webidl/GeometryUtils.webidl @@ -27,6 +27,9 @@ interface mixin GeometryUtils { [Throws, Func="nsINode::HasBoxQuadsSupport", NeedsCallerType] sequence getBoxQuads(optional BoxQuadOptions options = {}); + [ChromeOnly, Throws, Func="nsINode::HasBoxQuadsSupport"] + void scrollRectIntoViewIfNeeded(long x, long y, long w, long h); + /* getBoxQuadsFromWindowOrigin is similar to getBoxQuads, but the * returned quads are further translated relative to the window * origin -- which is not the layout origin. Further translation diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 673292a812b425b56fb29b20ada100b44c656822..61649b077e01efde275d0e00296f86b6fdea6c5c 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -1000,7 +1000,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { AssertIsOnMainThread(); nsTArray languages; - Navigator::GetAcceptLanguages(languages); + Navigator::GetAcceptLanguages(nullptr, languages); RuntimeService* runtime = RuntimeService::GetService(); if (runtime) { @@ -1199,8 +1199,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; } @@ -1900,6 +1899,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( } } +void RuntimeService::ResetDefaultLocaleInAllWorkers() { + AssertIsOnMainThread(); + BroadcastAllWorkers([](auto& worker) { + worker.ResetDefaultLocale(); + }); +} + void RuntimeService::NoteIdleThread(SafeRefPtr aThread) { AssertIsOnMainThread(); MOZ_ASSERT(aThread); @@ -2337,6 +2343,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 67eac10dabd93da344a1366dee5d1c4428a4352c..1c31651d8087c90983c08fe184b344853af87033 100644 --- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h @@ -121,6 +121,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 0eeb8a65c328a2d5de0ec62cd94af1b249a101af..4625535b61d1ccd92da3e3bd0993b9a89d614747 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 59e80edf620554547a88aaa72107345110ee7148..c8351661b43f43651002f67ff46fa4635c61b8e8 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -653,6 +653,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { } }; +class ResetDefaultLocaleRunnable final : public WorkerControlRunnable { + public: + explicit ResetDefaultLocaleRunnable(WorkerPrivate* aWorkerPrivate) + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {} + + virtual bool WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) override { + aWorkerPrivate->ResetDefaultLocaleInternal(aCx); + return true; + } +}; + class UpdateLanguagesRunnable final : public WorkerRunnable { nsTArray mLanguages; @@ -1831,6 +1843,16 @@ void WorkerPrivate::UpdateContextOptions( } } +void WorkerPrivate::ResetDefaultLocale() { + AssertIsOnParentThread(); + + RefPtr runnable = + new ResetDefaultLocaleRunnable(this); + if (!runnable->Dispatch()) { + NS_WARNING("Failed to reset default locale in worker!"); + } +} + void WorkerPrivate::UpdateLanguages(const nsTArray& aLanguages) { AssertIsOnParentThread(); @@ -4800,6 +4822,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 bc31b18cf9021f753a0f8b5a80dfb32041721113..200792e7266745642589104d02856468ddd5235a 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -297,6 +297,8 @@ class WorkerPrivate : public RelativeTimeline { void UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions); + void ResetDefaultLocaleInternal(JSContext* aCx); + void UpdateLanguagesInternal(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, @@ -886,6 +888,8 @@ class WorkerPrivate : public RelativeTimeline { void UpdateContextOptions(const JS::ContextOptions& aContextOptions); + void ResetDefaultLocale(); + void UpdateLanguages(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameter(JSGCParamKey key, Maybe value); diff --git a/js/public/Date.h b/js/public/Date.h index e7a54d86c44499a3ec2adf1c156b9f9dfb0bc6b4..f56c1b419c4cb52bc371f6b8dbfffba464326fc4 100644 --- a/js/public/Date.h +++ b/js/public/Date.h @@ -56,6 +56,8 @@ namespace JS { */ extern JS_PUBLIC_API void ResetTimeZone(); +extern JS_PUBLIC_API bool SetTimeZoneOverride(const char* timezoneId); + class ClippedTime; inline ClippedTime TimeClip(double time); diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp index 7cd45e2e2fc3854f6e1e5e004f7dd46a1a674bb0..49430e8f78a4b6014fb4c976748dd1f1f80a0834 100644 --- a/js/src/debugger/Object.cpp +++ b/js/src/debugger/Object.cpp @@ -2354,7 +2354,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 2077eca87c1deb890491723eb6a3650497b71227..78f33af48cd62d77be1c359903aec4db51f38bcf 100644 --- a/js/src/vm/DateTime.cpp +++ b/js/src/vm/DateTime.cpp @@ -168,6 +168,11 @@ void js::DateTimeInfo::internalResetTimeZone(ResetTimeZoneMode mode) { } } +void js::DateTimeInfo::internalSetTimeZoneOverride(mozilla::UniquePtr timeZone) { + timeZoneOverride_ = std::move(timeZone); + internalResetTimeZone(ResetTimeZoneMode::ResetEvenIfOffsetUnchanged); +} + void js::DateTimeInfo::updateTimeZone() { MOZ_ASSERT(timeZoneStatus_ != TimeZoneStatus::Valid); @@ -528,10 +533,27 @@ void js::ResetTimeZoneInternal(ResetTimeZoneMode mode) { js::DateTimeInfo::resetTimeZone(mode); } +void js::SetTimeZoneOverrideInternal(mozilla::UniquePtr timeZone) { + auto guard = js::DateTimeInfo::instance->lock(); + guard->internalSetTimeZoneOverride(std::move(timeZone)); +} + JS_PUBLIC_API void JS::ResetTimeZone() { js::ResetTimeZoneInternal(js::ResetTimeZoneMode::ResetEvenIfOffsetUnchanged); } +JS_PUBLIC_API bool JS::SetTimeZoneOverride(const char* timeZoneId) { + // Validate timezone id. + mozilla::UniquePtr timeZone(icu::TimeZone::createTimeZone( + icu::UnicodeString(timeZoneId, -1, US_INV))); + if (!timeZone || *timeZone == icu::TimeZone::getUnknown()) { + fprintf(stderr, "Invalid timezone id: %s\n", timeZoneId); + return false; + } + js::SetTimeZoneOverrideInternal(std::move(timeZone)); + return true; +} + #if defined(XP_WIN) static bool IsOlsonCompatibleWindowsTimeZoneId(const char* tz) { // ICU ignores the TZ environment variable on Windows and instead directly @@ -733,6 +755,11 @@ void js::ResyncICUDefaultTimeZone() { void js::DateTimeInfo::internalResyncICUDefaultTimeZone() { #if JS_HAS_INTL_API && !MOZ_SYSTEM_ICU + if (timeZoneOverride_) { + icu::TimeZone::setDefault(*timeZoneOverride_); + return; + } + if (const char* tz = std::getenv("TZ")) { icu::UnicodeString tzid; diff --git a/js/src/vm/DateTime.h b/js/src/vm/DateTime.h index 25c5b01fc54c8d45da8ceb7cf6ba163bee3c5361..490c5ce49cd9b5f804df59abbfb0450fb9d1f877 100644 --- a/js/src/vm/DateTime.h +++ b/js/src/vm/DateTime.h @@ -67,6 +67,8 @@ enum class ResetTimeZoneMode : bool { */ extern void ResetTimeZoneInternal(ResetTimeZoneMode mode); +extern void SetTimeZoneOverrideInternal(mozilla::UniquePtr timeZone); + /** * ICU's default time zone, used for various date/time formatting operations * that include the local time in the representation, is allowed to go stale @@ -206,6 +208,7 @@ class DateTimeInfo { // and js::ResyncICUDefaultTimeZone(). friend void js::ResetTimeZoneInternal(ResetTimeZoneMode); friend void js::ResyncICUDefaultTimeZone(); + friend void js::SetTimeZoneOverrideInternal(mozilla::UniquePtr); static void resetTimeZone(ResetTimeZoneMode mode) { auto guard = instance->lock(); @@ -302,6 +305,8 @@ class DateTimeInfo { JS::UniqueChars locale_; JS::UniqueTwoByteChars standardName_; JS::UniqueTwoByteChars daylightSavingsName_; + + mozilla::UniquePtr timeZoneOverride_; #else // Restrict the data-time range to the minimum required time_t range as // specified in POSIX. Most operating systems support 64-bit time_t @@ -317,6 +322,8 @@ class DateTimeInfo { void internalResetTimeZone(ResetTimeZoneMode mode); + void internalSetTimeZoneOverride(mozilla::UniquePtr timeZone); + void updateTimeZone(); void internalResyncICUDefaultTimeZone(); diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 8da3746d98f90ded2ea85caf7008ad2061f64677..32851ac4b1a4217eb99bbbfc09fa9d317c831d56 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -1090,9 +1090,12 @@ void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) { if (!StaticPrefs::security_csp_enable()) { return; } - 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/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index 0112bec9faca84427ed4b96f751d7043b8935930..d23fb33d999e2c4ca18925171ccbbad1114d372d 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -1185,8 +1185,8 @@ PRErrorCode AuthCertificateParseResults( return SEC_ERROR_NO_MEMORY; } nsresult rv = overrideService->HasMatchingOverride( - aHostName, aPort, nssCert, &overrideBits, &isTemporaryOverride, - &haveOverride); + aHostName, aPort, aOriginAttributes.mUserContextId, nssCert, + &overrideBits, &isTemporaryOverride, &haveOverride); if (NS_SUCCEEDED(rv) && haveOverride) { // remove the errors that are already overriden remainingDisplayErrors &= ~overrideBits; diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp index 72b3995b0dfae04756c70ba1a79499222ca2ef5d..4498ca0651e4cc70ac36eb2e4e8581db15e1b8b1 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp @@ -494,13 +494,20 @@ nsCertOverrideService::RememberTemporaryValidityOverrideUsingFingerprint( NS_IMETHODIMP nsCertOverrideService::HasMatchingOverride(const nsACString& aHostName, - int32_t aPort, nsIX509Cert* aCert, + int32_t aPort, + uint32_t aUserContextId, + nsIX509Cert* aCert, uint32_t* aOverrideBits, bool* aIsTemporary, bool* _retval) { bool disableAllSecurityCheck = false; { MutexAutoLock lock(mMutex); - disableAllSecurityCheck = mDisableAllSecurityCheck; + if (aUserContextId) { + disableAllSecurityCheck = mUserContextIdsWithDisabledSecurityChecks.has( + aUserContextId); + } else { + disableAllSecurityCheck = mDisableAllSecurityCheck; + } } if (disableAllSecurityCheck) { nsCertOverride::OverrideBits all = nsCertOverride::OverrideBits::Untrusted | @@ -715,12 +722,21 @@ static bool IsDebugger() { NS_IMETHODIMP nsCertOverrideService:: - SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable) { - if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) { + SetDisableAllSecurityChecksAndLetAttackersInterceptMyData( + bool aDisable, uint32_t aUserContextId) { + if (false /* juggler hacks */ && !(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) { return NS_ERROR_NOT_AVAILABLE; } MutexAutoLock lock(mMutex); + if (aUserContextId) { + if (aDisable) { + mozilla::Unused << mUserContextIdsWithDisabledSecurityChecks.put(aUserContextId); + } else { + mUserContextIdsWithDisabledSecurityChecks.remove(aUserContextId); + } + return NS_OK; + } mDisableAllSecurityCheck = aDisable; return NS_OK; } diff --git a/security/manager/ssl/nsCertOverrideService.h b/security/manager/ssl/nsCertOverrideService.h index f85a699824e921fec72d114fd9f522dba08d0532..efb8f0abe0ec7796d88c611b6067d0965f9796c3 100644 --- a/security/manager/ssl/nsCertOverrideService.h +++ b/security/manager/ssl/nsCertOverrideService.h @@ -134,6 +134,7 @@ class nsCertOverrideService final : public nsICertOverrideService, ~nsCertOverrideService(); bool mDisableAllSecurityCheck; + mozilla::HashSet mUserContextIdsWithDisabledSecurityChecks; mozilla::Mutex mMutex; nsCOMPtr mSettingsFile; nsTHashtable mSettingsTable; diff --git a/security/manager/ssl/nsICertOverrideService.idl b/security/manager/ssl/nsICertOverrideService.idl index 23276fbe1933b87eca13f41550c4a9ec78b1c76b..02ad890e2a884f9988ec02eef88727836a92e8d2 100644 --- a/security/manager/ssl/nsICertOverrideService.idl +++ b/security/manager/ssl/nsICertOverrideService.idl @@ -130,6 +130,7 @@ interface nsICertOverrideService : nsISupports { [must_use] boolean hasMatchingOverride(in AUTF8String aHostName, in int32_t aPort, + in uint32_t aUserContextId, in nsIX509Cert aCert, out uint32_t aOverrideBits, out boolean aIsTemporary); @@ -171,5 +172,7 @@ interface nsICertOverrideService : nsISupports { * @param aDisable If true, disable all security check and make * hasMatchingOverride always return true. */ - void setDisableAllSecurityChecksAndLetAttackersInterceptMyData(in boolean aDisable); + void setDisableAllSecurityChecksAndLetAttackersInterceptMyData( + in boolean aDisable, + [optional] in uint32_t aUserContextId); }; diff --git a/services/settings/Utils.jsm b/services/settings/Utils.jsm index 66df8509044600a0d71eb36bb838f96087a53ef1..e4558874434e3aa57bc26344f0ca89b3ebb837bf 100644 --- a/services/settings/Utils.jsm +++ b/services/settings/Utils.jsm @@ -59,7 +59,7 @@ var Utils = { Ci.nsIEnvironment ); const isXpcshell = env.exists("XPCSHELL_TEST_PROFILE_DIR"); - return AppConstants.RELEASE_OR_BETA && !Cu.isInAutomation && !isXpcshell + return false && !Cu.isInAutomation && !isXpcshell ? "https://firefox.settings.services.mozilla.com/v1" : gServerURL; }, diff --git a/toolkit/components/browser/nsIWebBrowserChrome.idl b/toolkit/components/browser/nsIWebBrowserChrome.idl index 1e9bea1655af731fc003f8d0cab3ad4d2ad29f5d..5081c0e1ee0c41c6a79bd2ed358a57442e3baa6b 100644 --- a/toolkit/components/browser/nsIWebBrowserChrome.idl +++ b/toolkit/components/browser/nsIWebBrowserChrome.idl @@ -70,6 +70,9 @@ interface nsIWebBrowserChrome : nsISupports // Whether this window should use out-of-process cross-origin subframes. const unsigned long CHROME_FISSION_WINDOW = 0x00200000; + // 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 = 0x01000000; diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp index aaaa367cc4a64682b8a0419afbaebaa8854e15f7..0941495a99ebffb4b991c2e018770d6b607de1b6 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -340,7 +340,7 @@ nsAppStartup::Quit(uint32_t aMode, 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 318037b12e9ea7b8bad92498950ac48ff936fb3c..44db941025a5253da38572600cd0fc57f54ee6bf 100644 --- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp @@ -162,8 +162,16 @@ nsBrowserStatusFilter::OnStateChange(nsIWebProgress* aWebProgress, } NS_IMETHODIMP -nsBrowserStatusFilter::OnProgressChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, +nsBrowserStatusFilter::OnFrameLocationChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *aLocation, + uint32_t aFlags) { + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnProgressChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp index 4145a4b509f1908ed66a65b35715903aa25c07a5..3df59466a9bc2ad55322e8759f23f350d368219c 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -1815,6 +1815,10 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent( uint32_t chromeFlags = CalculateChromeFlagsHelper( nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, aSizeSpec); + if (aFeatures.Exists("width") || aFeatures.Exists("height")) { + chromeFlags |= nsIWebBrowserChrome::JUGGLER_WINDOW_EXPLICIT_SIZE; + } + return EnsureFlagsSafeForContent(chromeFlags); } diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm index 2ea1ae85ff68f2b499aab4634539ab826a509942..9b40b61c9c6921668fd81e8af7927deedd88f966 100644 --- a/toolkit/mozapps/update/UpdateService.jsm +++ b/toolkit/mozapps/update/UpdateService.jsm @@ -3134,7 +3134,7 @@ UpdateService.prototype = { ).running; } - return ( + return true || ( (Cu.isInAutomation || marionetteRunning) && Services.prefs.getBoolPref(PREF_APP_UPDATE_DISABLEDFORTESTING, false) ); diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index 93df016f7d0529f8c336defa4ed08233c7548887..95876d7df4e74d73100a4ebed2bd70e6db0a6e60 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -167,6 +167,7 @@ if CONFIG['ENABLE_MARIONETTE']: DIRS += [ '/testing/firefox-ui', '/testing/marionette', + '/juggler', '/toolkit/components/telemetry/tests/marionette', ] diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp index bf7902f5dd642e2800b3fa83f4d379ab1ac9fda0..b94c24f9534cc19a5c2828e035e33c95f671d181 100644 --- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp @@ -793,6 +793,13 @@ 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 @@ -1366,6 +1373,24 @@ void nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress, } } +void nsDocLoader::FireOnFrameLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *aUri, + uint32_t aFlags) { + NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_FRAME_LOCATION, + nsCOMPtr listener2 = + do_QueryReferent(info.mWeakListener); + if (!listener2) + continue; + listener2->OnFrameLocationChange(aWebProgress, aRequest, aUri, aFlags); + ); + + // Pass the notification up to the parent... + if (mParent) { + mParent->FireOnFrameLocationChange(aWebProgress, aRequest, aUri, aFlags); + } +} + void nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsresult aStatus, const char16_t* aMessage) { diff --git a/uriloader/base/nsDocLoader.h b/uriloader/base/nsDocLoader.h index 0994d81476b242d59f235da2e84aea1bf8b1df86..be6dc9faa29183d4709ee92f75d015e917dd5435 100644 --- a/uriloader/base/nsDocLoader.h +++ b/uriloader/base/nsDocLoader.h @@ -209,6 +209,11 @@ class nsDocLoader : public nsIDocumentLoader, nsIURI* aURI, int32_t aDelay, bool aSameURI); + void FireOnFrameLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *aUri, + uint32_t aFlags); + // this function is overridden by the docshell, it is provided so that we // can pass more information about redirect state (the normal OnStateChange // doesn't get the new channel). diff --git a/uriloader/base/nsIWebProgress.idl b/uriloader/base/nsIWebProgress.idl index 70079adfff8a1b625ffd5aad5572c960cec5b646..5822ca46fb4022b0b31288f5492fcb4a2ac9baa1 100644 --- a/uriloader/base/nsIWebProgress.idl +++ b/uriloader/base/nsIWebProgress.idl @@ -87,6 +87,10 @@ interface nsIWebProgress : nsISupports * NOTIFY_REFRESH * Receive onRefreshAttempted events. * This is defined on nsIWebProgressListener2. + * + * NOTIFY_FRAME_LOCATION + * Receive onFrameLocationChange events. + * This is defined on nsIWebProgressListener2. */ const unsigned long NOTIFY_PROGRESS = 0x00000010; const unsigned long NOTIFY_STATUS = 0x00000020; @@ -94,11 +98,12 @@ interface nsIWebProgress : nsISupports const unsigned long NOTIFY_LOCATION = 0x00000080; const unsigned long NOTIFY_REFRESH = 0x00000100; const unsigned long NOTIFY_CONTENT_BLOCKING = 0x00000200; + const unsigned long NOTIFY_FRAME_LOCATION = 0x00000400; /** * This flag enables all notifications. */ - const unsigned long NOTIFY_ALL = 0x000003ff; + const unsigned long NOTIFY_ALL = 0x000007ff; /** * Registers a listener to receive web progress events. diff --git a/uriloader/base/nsIWebProgressListener2.idl b/uriloader/base/nsIWebProgressListener2.idl index 87701f8d2cfee8bd84acd28c62b3be4989c9474c..ae1aa85c019cb21d4f7e79c35e8afe72709468a1 100644 --- a/uriloader/base/nsIWebProgressListener2.idl +++ b/uriloader/base/nsIWebProgressListener2.idl @@ -66,4 +66,27 @@ interface nsIWebProgressListener2 : nsIWebProgressListener { in nsIURI aRefreshURI, in long aMillis, in boolean aSameURI); + + /** + * Called when the location of the window or its subframes changes. This is not + * when a load is requested, but rather once it is verified that the load is + * going to occur in the given window. For instance, a load that starts in a + * window might send progress and status messages for the new site, but it + * will not send the onLocationChange until we are sure that we are loading + * this new page here. + * + * @param aWebProgress + * The nsIWebProgress instance that fired the notification. + * @param aRequest + * The associated nsIRequest. This may be null in some cases. + * @param aLocation + * The URI of the location that is being loaded. + * @param aFlags + * This is a value which explains the situation or the reason why + * the location has changed. + */ + void onFrameLocationChange(in nsIWebProgress aWebProgress, + in nsIRequest aRequest, + in nsIURI aLocation, + [optional] in unsigned long aFlags); }; diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 01fd8246430fa9ed00da4e14fb66ccc731d2c490..1868c097ec7da33f472b4afc1445103c921cadbd 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -101,6 +101,7 @@ #include "mozilla/Components.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/ErrorNames.h" #include "mozilla/Preferences.h" #include "mozilla/ipc/URIUtils.h" @@ -871,6 +872,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); @@ -1504,7 +1511,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) { // Strip off the ".part" from mTempLeafName mTempLeafName.Truncate(mTempLeafName.Length() - ArrayLength(".part") + 1); + return CreateSaverForTempFile(); +} + +nsresult nsExternalAppHandler::CreateSaverForTempFile() { MOZ_ASSERT(!mSaver, "Output file initialization called more than once!"); + nsresult rv; mSaver = do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -1680,7 +1692,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; @@ -1726,6 +1767,11 @@ 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 @@ -2133,6 +2179,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; } @@ -2514,6 +2570,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 1edde606dc657334024f5bc0ea13feea6ad2e3b2..1a4862f51cc71365c8f61b67d8dee0dfae287d55 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -207,6 +207,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, mozilla::dom::BrowsingContext* aContentContext, bool aForceSave, nsIInterfaceRequestor* aWindowContext, nsIStreamListener** aStreamListener); + + nsCOMPtr mInterceptor; }; /** @@ -398,6 +400,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 657e15bc07426745b9488b903c5a53b8d977fb2d..4f61835e64d537ab7a35c2c2fb059e67cd7cd0fc 100644 --- a/uriloader/exthandler/nsIExternalHelperAppService.idl +++ b/uriloader/exthandler/nsIExternalHelperAppService.idl @@ -6,6 +6,8 @@ #include "nsICancelable.idl" +webidl BrowsingContext; +interface nsIHelperAppLauncher; interface nsIURI; interface nsIRequest; interface nsIStreamListener; @@ -15,6 +17,17 @@ interface nsIWebProgressListener2; interface nsIInterfaceRequestor; webidl BrowsingContext; +/** + * Interceptor interface used by Juggler. + */ +[scriptable, uuid(9a20e9b0-75d0-11ea-bc55-0242ac130003)] +interface nsIDownloadInterceptor : nsISupports +{ + bool interceptDownloadRequest(in nsIHelperAppLauncher aHandler, in nsIRequest aRequest, in BrowsingContext aBrowsingContext, out nsIFile file); + + void onDownloadComplete(in nsIHelperAppLauncher aHandler, in ACString aErrorName); +}; + /** * The external helper app service is used for finding and launching * platform specific external applications for a given mime content type. @@ -43,7 +56,7 @@ interface nsIExternalHelperAppService : nsISupports in nsIInterfaceRequestor aContentContext, in boolean aForceSave, [optional] in nsIInterfaceRequestor aWindowContext); - + /** * Binds an external helper application to a stream listener. The caller * should pump data into the returned stream listener. When the OnStopRequest @@ -76,6 +89,7 @@ interface nsIExternalHelperAppService : nsISupports boolean applyDecodingForExtension(in AUTF8String aExtension, in ACString aEncodingType); + void setDownloadInterceptor(in nsIDownloadInterceptor interceptor); }; /** diff --git a/widget/InProcessCompositorWidget.cpp b/widget/InProcessCompositorWidget.cpp index 38b94ac0b4bc259c3c18c7e2a73c59945828b284..64cf4f20439c6d111215551716edbf690270b2db 100644 --- a/widget/InProcessCompositorWidget.cpp +++ b/widget/InProcessCompositorWidget.cpp @@ -4,6 +4,8 @@ #include "InProcessCompositorWidget.h" +#include "HeadlessCompositorWidget.h" +#include "HeadlessWidget.h" #include "mozilla/VsyncDispatcher.h" #include "nsBaseWidget.h" @@ -22,6 +24,12 @@ RefPtr CompositorWidget::CreateLocal( const CompositorWidgetInitData& aInitData, const layers::CompositorOptions& aOptions, nsIWidget* aWidget) { MOZ_ASSERT(aWidget); + if (aInitData.type() == + CompositorWidgetInitData::THeadlessCompositorWidgetInitData) { + return new HeadlessCompositorWidget( + aInitData.get_HeadlessCompositorWidgetInitData(), aOptions, + static_cast(aWidget)); + } # ifdef MOZ_WIDGET_ANDROID return new AndroidCompositorWidget(aOptions, static_cast(aWidget)); diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp index b31a969b7ab3d0fc80912b110d91dfdf3e5991f4..45456bd34713a32695c0fe6a84588f31e40c137d 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" @@ -17,6 +18,32 @@ HeadlessCompositorWidget::HeadlessCompositorWidget( mClientSize = aInitData.InitialClientSize(); } +void HeadlessCompositorWidget::SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener) { + MOZ_ASSERT(NS_IsMainThread()); + + layers::CompositorThread()->Dispatch(NewRunnableMethod( + "HeadlessCompositorWidget::SetSnapshotListener", this, + &HeadlessCompositorWidget::SetSnapshotListenerOnCompositorThread, + std::move(listener))); +} + +void HeadlessCompositorWidget::SetSnapshotListenerOnCompositorThread( + HeadlessWidget::SnapshotListener&& listener) { + MOZ_ASSERT(NS_IsInCompositorThread()); + mSnapshotListener = std::move(listener); + PeriodicSnapshot(); +} + +already_AddRefed HeadlessCompositorWidget::StartRemoteDrawingInRegion( + 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()) { @@ -29,6 +56,58 @@ nsIWidget* HeadlessCompositorWidget::RealWidget() { return mWidget; } void HeadlessCompositorWidget::NotifyClientSizeChanged( const LayoutDeviceIntSize& aClientSize) { mClientSize = 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() { + 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 7f91de9e67d7ffa02de3eef1d760e5cfd05e7ad6..b0e3572413f80e5bd125f777c3247b10b8521a73 100644 --- a/widget/headless/HeadlessCompositorWidget.h +++ b/widget/headless/HeadlessCompositorWidget.h @@ -23,9 +23,13 @@ class HeadlessCompositorWidget final : public CompositorWidget, HeadlessWidget* aWindow); void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize); + void SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener); // CompositorWidget Overrides + already_AddRefed StartRemoteDrawingInRegion( + LayoutDeviceIntRegion& aInvalidRegion, layers::BufferMode* aBufferMode) override; + uintptr_t GetWidgetKey() override; LayoutDeviceIntSize GetClientSize() override; @@ -42,9 +46,18 @@ class HeadlessCompositorWidget final : public CompositorWidget, } private: + void SetSnapshotListenerOnCompositorThread( + HeadlessWidget::SnapshotListener&& listener); + void UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize); + void PeriodicSnapshot(); + void TakeSnapshot(); + HeadlessWidget* mWidget; LayoutDeviceIntSize mClientSize; + + HeadlessWidget::SnapshotListener mSnapshotListener; + RefPtr mDrawTarget; }; } // namespace widget diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp index f4f3acda7e645bf3c97b4bdd50eed6b429a7e74a..c1bbd3e07397b7b341538f78e18db987ae0d244e 100644 --- a/widget/headless/HeadlessWidget.cpp +++ b/widget/headless/HeadlessWidget.cpp @@ -104,6 +104,8 @@ void HeadlessWidget::Destroy() { } } + SetSnapshotListener(nullptr); + nsBaseWidget::OnDestroy(); nsBaseWidget::Destroy(); @@ -499,5 +501,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchPoint( 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 c375629d4a954f872a2abdd6983ae38dbb98f4ca..1857a4874ac9f8a3d7e402b5707a9ea58f241eb9 100644 --- a/widget/headless/HeadlessWidget.h +++ b/widget/headless/HeadlessWidget.h @@ -153,6 +153,9 @@ class HeadlessWidget : public nsBaseWidget { uint32_t aPointerOrientation, nsIObserver* aObserver) override; + using SnapshotListener = std::function&&)>; + void SetSnapshotListener(SnapshotListener&& listener); + private: ~HeadlessWidget(); bool mEnabled; diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h index 33b1f25411fd6a8d02edca9198054347289a1501..ee6ea48f3986a8d7c0e2f351b6d30b9fb706524e 100644 --- a/xpcom/reflect/xptinfo/xptinfo.h +++ b/xpcom/reflect/xptinfo/xptinfo.h @@ -513,7 +513,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 /**