Files

2645 lines
104 KiB
Diff

diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
index 8337336dc894b44ea696bb780e448dfbdd8b6357..9eb83f33bb0415f28d1bcf66507d33d0cb9ad605 100644
--- a/browser/app/winlauncher/LauncherProcessWin.cpp
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
@@ -18,6 +18,7 @@
#include "mozilla/WinHeaderOnlyUtils.h"
#include "nsWindowsHelpers.h"
+#include <io.h>
#include <windows.h>
#include <processthreadsapi.h>
#include <shlwapi.h>
@@ -490,8 +491,18 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[]) {
HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE),
::GetStdHandle(STD_OUTPUT_HANDLE),
::GetStdHandle(STD_ERROR_HANDLE)};
-
attrs.AddInheritableHandles(stdHandles);
+ // Playwright pipe installation.
+ bool hasJugglerPipe =
+ mozilla::CheckArg(argc, argv, "juggler-pipe", nullptr,
+ mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND;
+ if (hasJugglerPipe) {
+ intptr_t stdio3 = _get_osfhandle(3);
+ intptr_t stdio4 = _get_osfhandle(4);
+ HANDLE pipeHandles[] = {reinterpret_cast<HANDLE>(stdio3),
+ reinterpret_cast<HANDLE>(stdio4)};
+ attrs.AddInheritableHandles(pipeHandles);
+ }
DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
index 96706c155f4dd317d93d7e5bf18f67598cce66a4..8f61011841cf06689a59210fb021d23f81db4fa1 100644
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -66,6 +66,12 @@ browser/chrome/browser/builtin-addons/webcompat/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 203811a0fb980b47f113729cb1a16f56f376bfe5..83ac2cc8cdae99006fbc34319ed854c8ef88fcc4 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -204,6 +204,9 @@
@RESPATH@/chrome/remote.manifest
#endif
+@RESPATH@/chrome/juggler@JAREXT@
+@RESPATH@/chrome/juggler.manifest
+
; Modules
@RESPATH@/browser/modules/*
@RESPATH@/modules/*
diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js
index 257e87fe0c618684eb4216c8028ac886ef2d5562..8c13f020100dad9bc4e093d50bd3a1f95bcea982 100644
--- a/devtools/server/socket/websocket-server.js
+++ b/devtools/server/socket/websocket-server.js
@@ -135,13 +135,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");
}
@@ -191,13 +190,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, [
@@ -219,8 +218,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 d7155e79364de64fbf90c7fa37ac1713ed00bfa3..b38aece7733011cad0eac055c560e42b85ad467b 100644
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -118,8 +118,11 @@ struct ParamTraits<mozilla::dom::DisplayMode>
template <>
struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
- : public mozilla::dom::WebIDLEnumSerializer<
- mozilla::dom::PrefersColorSchemeOverride> {};
+ : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::PrefersColorSchemeOverride> {};
+
+template <>
+struct ParamTraits<mozilla::dom::PrefersContrastOverride>
+ : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::PrefersContrastOverride> {};
template <>
struct ParamTraits<mozilla::dom::ForcedColorsOverride>
@@ -489,7 +492,11 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
fields.Get<IDX_UseErrorPages>() = true;
- fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None;
+ // Playwright: make sure touch events override is propagated to the nested
+ // browsing context. See https://bugzilla.mozilla.org/show_bug.cgi?id=2014330
+ fields.Get<IDX_TouchEventsOverrideInternal>() =
+ inherit ? inherit->GetTouchEventsOverrideInternal() :
+ TouchEventsOverride::None;
fields.Get<IDX_AllowJavascript>() =
inherit ? inherit->GetAllowJavascript() : true;
@@ -3544,6 +3551,15 @@ void BrowsingContext::DidSet(FieldIndex<IDX_LanguageOverride>,
});
}
+void BrowsingContext::DidSet(FieldIndex<IDX_PrefersContrastOverride>,
+ dom::PrefersContrastOverride aOldValue) {
+ MOZ_ASSERT(IsTop());
+ if (PrefersContrastOverride() == aOldValue) {
+ return;
+ }
+ PresContextAffectingFieldChanged();
+}
+
void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
nsString&& aOldValue) {
MOZ_ASSERT(IsTop());
@@ -3824,7 +3840,7 @@ void BrowsingContext::SetGeolocationServiceOverride(
if (aGeolocationOverride.WasPassed()) {
if (!mGeolocationServiceOverride) {
mGeolocationServiceOverride = MakeRefPtr<nsGeolocationService>();
- mGeolocationServiceOverride->Init();
+ mGeolocationServiceOverride->Init(true /* isOverride */);
}
mGeolocationServiceOverride->Update(aGeolocationOverride.Value());
} else if (RefPtr<nsGeolocationService> serviceOverride =
diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h
index 6f7f58bba3be6f08b3b27e81f30864c61d683351..ac0d2dc75f3f9e99e97100958108f4504292283f 100644
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -211,11 +211,11 @@ struct EmbedderColorSchemes {
FIELD(HasScreenAreaOverride, bool) \
/* ScreenOrientation-related APIs */ \
FIELD(CurrentOrientationAngle, float) \
- FIELD(CurrentOrientationType, mozilla::dom::OrientationType) \
+ FIELD(CurrentOrientationType, dom::OrientationType) \
FIELD(OrientationLock, mozilla::hal::ScreenOrientation) \
FIELD(HasOrientationOverride, bool) \
FIELD(UserAgentOverride, nsString) \
- FIELD(TouchEventsOverrideInternal, mozilla::dom::TouchEventsOverride) \
+ FIELD(TouchEventsOverrideInternal, dom::TouchEventsOverride) \
FIELD(EmbedderElementType, Maybe<nsString>) \
FIELD(MessageManagerGroup, nsString) \
FIELD(MaxTouchPointsOverride, uint8_t) \
@@ -260,6 +260,8 @@ struct EmbedderColorSchemes {
* <browser> embedder element. */ \
FIELD(EmbedderColorSchemes, EmbedderColorSchemes) \
FIELD(DisplayMode, dom::DisplayMode) \
+ /* playwright addition */ \
+ FIELD(PrefersContrastOverride, dom::PrefersContrastOverride) \
/* The number of entries added to the session history because of this \
* browsing context. */ \
FIELD(HistoryEntryCount, uint32_t) \
@@ -1118,6 +1120,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return Top()->GetAnimationsPlayBackRateMultiplier();
}
+ dom::PrefersContrastOverride PrefersContrastOverride() const {
+ return GetPrefersContrastOverride();
+ }
+
bool IsInBFCache() const;
bool IsEnteringBFCache() const { return mIsEnteringBFCache; }
void DeactivateDocuments();
@@ -1333,6 +1339,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return IsTop();
}
+ bool CanSet(FieldIndex<IDX_PrefersContrastOverride>,
+ dom::PrefersContrastOverride, ContentParent*) {
+ return IsTop();
+ }
+
bool CanSet(FieldIndex<IDX_ForcedColorsOverride>, dom::ForcedColorsOverride,
ContentParent*) {
return IsTop();
@@ -1373,6 +1384,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void DidSet(FieldIndex<IDX_AnimationsPlayBackRateMultiplier>,
double aOldValue);
+ void DidSet(FieldIndex<IDX_PrefersContrastOverride>,
+ dom::PrefersContrastOverride aOldValue);
+
template <typename Callback>
void WalkPresContexts(Callback&&);
void PresContextAffectingFieldChanged();
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
index 00c69a2310f1056e8ef1199156cb593140af8a63..c6f5181e71b952fe36d83640eb776aa55c31eb2c 100644
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -303,6 +303,11 @@ void CanonicalBrowsingContext::ReplacedBy(
txn.SetInnerSizeSpoofedForRFP(GetInnerSizeSpoofedForRFP());
txn.SetIPAddressSpace(GetIPAddressSpace());
txn.SetParentalControlsEnabled(GetParentalControlsEnabled());
+ txn.SetPrefersReducedMotionOverride(GetPrefersReducedMotionOverride());
+ txn.SetForcedColorsOverride(GetForcedColorsOverride());
+ // Playwright: make sure touch events override is propagated to the nested
+ // browsing context. See https://bugzilla.mozilla.org/show_bug.cgi?id=2014330
+ txn.SetTouchEventsOverrideInternal(GetTouchEventsOverrideInternal());
if (!GetLanguageOverride().IsEmpty()) {
// Reapply language override to update the corresponding realm.
@@ -1946,6 +1951,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI,
(void)SetIsCaptivePortalTab(true);
}
+ {
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(ToSupports(this), "juggler-navigation-started-browser", NS_ConvertASCIItoUTF16(nsPrintfCString("%" PRIu64, loadState->GetLoadIdentifier())).get());
+ }
+ }
LoadURI(loadState, true);
}
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 275d0ce1d33e7aa887eebeadac786aac44e0ba10..bff2b58d2169bbd40aa4ac48c656d7f9a0519489 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -16,6 +16,12 @@
#endif
#include "nsDeviceContext.h"
+#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/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/BasePrincipal.h"
@@ -64,6 +70,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"
@@ -96,6 +103,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"
@@ -120,6 +128,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"
@@ -215,6 +224,7 @@
#include "nsGlobalWindowInner.h"
#include "nsGlobalWindowOuter.h"
#include "nsJSEnvironment.h"
+#include "nsJSUtils.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsObjectLoadingContent.h"
@@ -355,6 +365,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),
@@ -2976,6 +2994,174 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
return NS_OK;
}
+// =============== Juggler Begin =======================
+
+nsDocShell* nsDocShell::GetRootDocShell() {
+ nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
+ GetInProcessSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
+ nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
+ return nsDocShell::Cast(rootShell);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetBypassCSPEnabled(bool* aEnabled) {
+ MOZ_ASSERT(aEnabled);
+ *aEnabled = mBypassCSPEnabled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetBypassCSPEnabled(bool aEnabled) {
+ mBypassCSPEnabled = aEnabled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetForceActiveState(bool* aEnabled) {
+ MOZ_ASSERT(aEnabled);
+ *aEnabled = mForceActiveState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetForceActiveState(bool aEnabled) {
+ mForceActiveState = aEnabled;
+ ActivenessMaybeChanged();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDisallowBFCache(bool* aEnabled) {
+ MOZ_ASSERT(aEnabled);
+ *aEnabled = mDisallowBFCache;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDisallowBFCache(bool aEnabled) {
+ mDisallowBFCache = aEnabled;
+ return NS_OK;
+}
+
+bool nsDocShell::IsBypassCSPEnabled() {
+ return GetRootDocShell()->mBypassCSPEnabled;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOverrideHasFocus(bool* aEnabled) {
+ MOZ_ASSERT(aEnabled);
+ *aEnabled = mOverrideHasFocus;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOverrideHasFocus(bool aEnabled) {
+ mOverrideHasFocus = aEnabled;
+ return NS_OK;
+}
+
+bool nsDocShell::ShouldOverrideHasFocus() const {
+ return mOverrideHasFocus;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFileInputInterceptionEnabled(bool* aEnabled) {
+ MOZ_ASSERT(aEnabled);
+ *aEnabled = GetRootDocShell()->mFileInputInterceptionEnabled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFileInputInterceptionEnabled(bool aEnabled) {
+ mFileInputInterceptionEnabled = aEnabled;
+ return NS_OK;
+}
+
+bool nsDocShell::IsFileInputInterceptionEnabled() {
+ return GetRootDocShell()->mFileInputInterceptionEnabled;
+}
+
+void nsDocShell::FilePickerShown(mozilla::dom::Element* element) {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(
+ ToSupports(element), "juggler-file-picker-shown", nullptr);
+}
+
+RefPtr<nsGeolocationService> nsDocShell::GetGeolocationServiceOverride() {
+ return GetRootDocShell()->mGeolocationServiceOverride;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetGeolocationOverride(nsIDOMGeoPosition* aGeolocationOverride) {
+ if (aGeolocationOverride) {
+ if (!mGeolocationServiceOverride) {
+ mGeolocationServiceOverride = new nsGeolocationService();
+ mGeolocationServiceOverride->Init();
+ }
+ mGeolocationServiceOverride->Update(aGeolocationOverride);
+ } else {
+ mGeolocationServiceOverride = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetReducedMotionOverride(ReducedMotionOverride* aReducedMotionOverride) {
+ *aReducedMotionOverride = GetRootDocShell()->mReducedMotionOverride;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetReducedMotionOverride(ReducedMotionOverride aReducedMotionOverride) {
+ mReducedMotionOverride = aReducedMotionOverride;
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (presContext) {
+ presContext->MediaFeatureValuesChanged(
+ {MediaFeatureChangeReason::SystemMetricsChange},
+ MediaFeatureChangePropagation::JustThisDocument);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetForcedColorsOverride(ForcedColorsOverride* aForcedColorsOverride) {
+ *aForcedColorsOverride = GetRootDocShell()->mForcedColorsOverride;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetForcedColorsOverride(ForcedColorsOverride aForcedColorsOverride) {
+ mForcedColorsOverride = aForcedColorsOverride;
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (presContext) {
+ presContext->MediaFeatureValuesChanged(
+ {MediaFeatureChangeReason::SystemMetricsChange},
+ MediaFeatureChangePropagation::JustThisDocument);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetContrastOverride(ContrastOverride* aContrastOverride) {
+ *aContrastOverride = GetRootDocShell()->mContrastOverride;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetContrastOverride(ContrastOverride aContrastOverride) {
+ mContrastOverride = aContrastOverride;
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (presContext) {
+ presContext->MediaFeatureValuesChanged(
+ {MediaFeatureChangeReason::SystemMetricsChange},
+ MediaFeatureChangePropagation::JustThisDocument);
+ }
+ return NS_OK;
+}
+
+// =============== Juggler End =======================
+
NS_IMETHODIMP
nsDocShell::GetIsNavigating(bool* aOut) {
*aOut = mIsNavigating;
@@ -4655,7 +4841,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
}
void nsDocShell::ActivenessMaybeChanged() {
- const bool isActive = mBrowsingContext->IsActive();
+ const bool isActive = mForceActiveState || mBrowsingContext->IsActive();
if (RefPtr<PresShell> presShell = GetPresShell()) {
presShell->ActivenessMaybeChanged();
}
@@ -7651,6 +7837,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
true, // aForceNoOpener
getter_AddRefs(newBC));
MOZ_ASSERT(!newBC);
+ if (rv == NS_OK) {
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(GetAsSupports(this), "juggler-window-open-in-new-context", nullptr);
+ }
+ }
return rv;
}
@@ -8883,6 +9075,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
nsCOMPtr<nsIRequest> req;
+
+ // Juggler: report navigation started for non-same-document and non-javascript
+ // navigations.
+ if (!isJavaScript && !sameDocument) {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(GetAsSupports(this), "juggler-navigation-started-renderer", NS_ConvertASCIItoUTF16(nsPrintfCString("%" PRIu64, aLoadState->GetLoadIdentifier())).get());
+ }
+ }
rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
if (NS_SUCCEEDED(rv)) {
@@ -12019,6 +12221,9 @@ class OnLinkClickEvent : public Runnable {
mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
mTriggeringPrincipal);
}
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ observerService->NotifyObservers(ToSupports(mContent), "juggler-link-click-sync", nullptr);
+
return NS_OK;
}
@@ -12136,6 +12341,8 @@ nsresult nsDocShell::OnLinkClick(
RefPtr ev = MakeRefPtr<OnLinkClickEvent>(
this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal);
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr);
return Dispatch(ev.forget());
}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index aa834b35a1fb2ff6fe3cdd6de9582e48da510be5..ee2f86ddd28a9e18ee33cf509438b5f28cc8c952 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -15,6 +15,7 @@
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/NavigationBinding.h"
#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/Element.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "nsCOMPtr.h"
#include "nsCharsetSource.h"
@@ -84,6 +85,7 @@ class nsCommandManager;
class nsDocShellEditorData;
class nsDOMNavigationTiming;
class nsDSURIContentListener;
+class nsGeolocationService;
class nsGlobalWindowOuter;
class FramingChecker;
@@ -397,6 +399,15 @@ class nsDocShell final : public nsDocLoader,
void SetWillChangeProcess() { mWillChangeProcess = true; }
bool WillChangeProcess() { return mWillChangeProcess; }
+ bool IsFileInputInterceptionEnabled();
+ void FilePickerShown(mozilla::dom::Element* element);
+
+ bool ShouldOverrideHasFocus() const;
+
+ bool IsBypassCSPEnabled();
+
+ RefPtr<nsGeolocationService> GetGeolocationServiceOverride();
+
// Creates a real network channel (not a DocumentChannel) using the specified
// parameters.
// Used by nsDocShell when not using DocumentChannel, by DocumentLoadListener
@@ -988,6 +999,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
@@ -1299,6 +1312,16 @@ class nsDocShell final : public nsDocLoader,
bool mAllowDNSPrefetch : 1;
bool mAllowWindowControl : 1;
bool mCSSErrorReportingEnabled : 1;
+ bool mFileInputInterceptionEnabled: 1;
+ bool mOverrideHasFocus : 1;
+ bool mBypassCSPEnabled : 1;
+ bool mForceActiveState : 1;
+ bool mDisallowBFCache : 1;
+ RefPtr<nsGeolocationService> 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 4aa56b4ce16915d334f32c18953604765699e05a..ef80a46511d870c909dda6182e332c155a8e250c 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -43,6 +43,7 @@ interface nsIURI;
interface nsIChannel;
interface nsIPolicyContainer;
interface nsIDocumentViewer;
+interface nsIDOMGeoPosition;
interface nsIEditor;
interface nsIEditingSession;
interface nsIInputStream;
@@ -692,6 +693,41 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
void synchronizeLayoutHistoryState();
+ attribute boolean fileInputInterceptionEnabled;
+
+ attribute boolean overrideHasFocus;
+
+ attribute boolean bypassCSPEnabled;
+
+ attribute boolean forceActiveState;
+
+ attribute boolean disallowBFCache;
+
+ 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 1081a150609c6068cde605228da6e95f84f06d6b..f51d5f53ea1d8985e7f75e4aba2a6996ac594d21 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3655,6 +3655,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
}
void Document::ApplySettingsFromCSP(bool aSpeculative) {
+ if (mDocumentContainer && mDocumentContainer->IsBypassCSPEnabled())
+ return;
+
nsresult rv = NS_OK;
if (!aSpeculative) {
nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
@@ -3752,6 +3755,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
MOZ_ASSERT(mPolicyContainer,
"Policy container must be initialized before CSP!");
+ nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
+ if (shell && nsDocShell::Cast(shell)->IsBypassCSPEnabled()) {
+ return NS_OK;
+ }
+
// If this is a data document - no need to set CSP.
if (mLoadedAsData) {
return NS_OK;
@@ -4722,6 +4730,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
return false;
}
+ if (IsActive() && mDocumentContainer->ShouldOverrideHasFocus()) {
+ return true;
+ }
+
if (!fm->IsInActiveWindow(bc)) {
return false;
}
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 079746f77996ca03464c8f30b00cd34817b8430d..770a58853159c5d4972aab6002ab68254ee22360 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2381,7 +2381,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/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 55a786dd5bb66d2bb51568407bc942c8ac2e5fe4..c73e34868b03a11b44d195f419fb3fb2e7869965 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9873,6 +9873,7 @@ Result<bool, nsresult> nsContentUtils::SynthesizeMouseEvent(
EventMessage msg;
Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
bool contextMenuKey = false;
+ bool isPWDragEventMessage = false;
if (aType.EqualsLiteral("mousedown")) {
msg = eMouseDown;
} else if (aType.EqualsLiteral("mouseup")) {
@@ -9899,13 +9900,26 @@ Result<bool, nsresult> nsContentUtils::SynthesizeMouseEvent(
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 Err(NS_ERROR_FAILURE);
}
Maybe<WidgetPointerEvent> pointerEvent;
Maybe<WidgetMouseEvent> mouseEvent;
- if (IsPointerEventMessage(msg)) {
+ Maybe<WidgetDragEvent> pwDragEvent;
+
+ if (isPWDragEventMessage) {
+ pwDragEvent.emplace(true, msg, aWidget);
+ pwDragEvent->mReason = aOptions.mIsWidgetEventSynthesized
+ ? WidgetMouseEvent::eSynthesized
+ : WidgetMouseEvent::eReal;
+ } else if (IsPointerEventMessage(msg)) {
if (MOZ_UNLIKELY(aOptions.mIsWidgetEventSynthesized)) {
MOZ_ASSERT_UNREACHABLE(
"The event shouldn't be dispatched as a synthesized event");
@@ -9933,6 +9947,7 @@ Result<bool, nsresult> nsContentUtils::SynthesizeMouseEvent(
mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(callback);
WidgetMouseEvent& mouseOrPointerEvent =
+ pwDragEvent.isSome() ? pwDragEvent.ref() :
pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref();
mouseOrPointerEvent.pointerId = aMouseEventData.mIdentifier;
mouseOrPointerEvent.mModifiers =
@@ -9958,6 +9973,7 @@ Result<bool, nsresult> nsContentUtils::SynthesizeMouseEvent(
aOptions.mIsDOMEventSynthesized;
mouseOrPointerEvent.mExitFrom = std::move(exitFrom);
mouseOrPointerEvent.mCallbackId = notifier.SaveCallback();
+ mouseOrPointerEvent.convertToPointer = aOptions.mJugglerConvertToPointer;
nsPresContext* presContext = aPresShell->GetPresContext();
if (!presContext) {
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index ed255ae7f0f3c3465f4d38a8af2a7899e0dbf3b9..aa60b5bdc059121ac7a9163a40f0d9d23afa5195 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1875,6 +1875,10 @@ Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
(GetActiveBrowsingContext() == newRootBrowsingContext);
}
+ // In Playwright, we want to send focus events even if the element
+ // isn't actually in the active window.
+ isElementInActiveWindow = true;
+
// Exit fullscreen if a website focuses another window
if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
!isElementInActiveWindow && (aFlags & FLAG_RAISE)) {
@@ -2436,6 +2440,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
bool aIsLeavingDocument, bool aAdjustWidget,
bool aRemainActive, Element* aElementToFocus,
uint64_t aActionId) {
+
LOGFOCUS(("<<Blur begin actionid: %" PRIu64 ">>", aActionId));
// hold a reference to the focused content, which may be null
@@ -2479,6 +2484,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
return true;
}
+ // Playwright: emulate focused page by never bluring when leaving document.
+ if (XRE_IsContentProcess() && aIsLeavingDocument && docShell && nsDocShell::Cast(docShell)->ShouldOverrideHasFocus()) {
+ return true;
+ }
+
// Keep a ref to presShell since dispatching the DOM event may cause
// the document to be destroyed.
RefPtr<PresShell> presShell = docShell->GetPresShell();
@@ -3181,7 +3191,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 0fbb9d9e11c72f862d5777a0554a142336f8f17f..80ce0fc7a1b42722eb55a4a2e03af4022126a374 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2535,10 +2535,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
}();
if (!isAboutBlankInChromeDocshell) {
- 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));
+ }
}
}
@@ -2658,6 +2664,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
}
}
+void nsGlobalWindowOuter::JugglerDispatchDOMWindowReused() {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService && mDoc) {
+ nsIPrincipal* principal = mDoc->NodePrincipal();
+ if (!principal->IsSystemPrincipal()) {
+ observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
+ "juggler-dom-window-reused",
+ nullptr);
+ }
+ }
+}
+
void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u""_ns); }
void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h
index fa95821e51984beb7e4672bb82038adb0f8e97d2..08b765dfee64001bde59f99704046246f242e513 100644
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -302,6 +302,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 5b21525de8dc0cc86bd14e2b36fcd9f0059a030a..65c6027e614bb8566a7a24bac6a7b75aa93d4032 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1803,6 +1803,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
}
+static nsIFrame* GetFirstFrame(nsINode* aNode) {
+ if (!aNode->IsContent())
+ return nullptr;
+ nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame(FlushType::Frames);
+ if (!frame) {
+ FlattenedChildIterator iter(aNode->AsContent());
+ for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+ frame = child->GetPrimaryFrame(FlushType::Frames);
+ if (frame) {
+ break;
+ }
+ }
+ }
+ return frame;
+}
+
+void nsINode::ScrollRectIntoViewIfNeeded(int32_t x, int32_t y,
+ int32_t w, int32_t h,
+ ErrorResult& aRv) {
+ aRv = NS_ERROR_UNEXPECTED;
+ nsCOMPtr<Document> document = OwnerDoc();
+ if (!document) {
+ return aRv.ThrowNotFoundError("Node is detached from document");
+ }
+ PresShell* presShell = document->GetPresShell();
+ if (!presShell) {
+ return aRv.ThrowNotFoundError("Node is detached from document");
+ }
+ nsIFrame* primaryFrame = GetFirstFrame(this);
+ if (!primaryFrame) {
+ return aRv.ThrowNotFoundError("Node does not have a layout object");
+ }
+ aRv = NS_OK;
+ nsRect rect;
+ if (x == -1 && y == -1 && w == -1 && h == -1) {
+ rect = primaryFrame->GetRectRelativeToSelf();
+ } else {
+ rect = nsRect(nsPresContext::CSSPixelsToAppUnits(x),
+ nsPresContext::CSSPixelsToAppUnits(y),
+ nsPresContext::CSSPixelsToAppUnits(w),
+ nsPresContext::CSSPixelsToAppUnits(h));
+ }
+ presShell->ScrollFrameIntoView(
+ primaryFrame, Some(rect),
+ AxisScrollParams(WhereToScroll::Center, WhenToScroll::IfNotFullyVisible),
+ AxisScrollParams(WhereToScroll::Center, WhenToScroll::IfNotFullyVisible),
+ ScrollFlags::ScrollOverflowHidden);
+ // If a _visual_ scroll update is pending, cancel it; otherwise, it will
+ // clobber next scroll (e.g. subsequent window.scrollTo(0, 0) wlll break).
+ if (presShell->GetPendingVisualScrollUpdate()) {
+ presShell->AcknowledgePendingVisualScrollUpdate();
+ presShell->ClearPendingVisualScrollUpdate();
+ }
+}
+
already_AddRefed<DOMQuad> nsINode::ConvertQuadFromNode(
DOMQuad& aQuad, const GeometryNode& aFrom,
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
index 4c882db32fb458c4e88dc35cdf6ea4c2ce6675f0..0b5ad5cb09b36bde91ddf5390e2e520c76bf6f49 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -2551,6 +2551,10 @@ class nsINode : public mozilla::dom::EventTarget {
nsTArray<RefPtr<DOMQuad>>& aResult,
ErrorResult& aRv);
+ void ScrollRectIntoViewIfNeeded(int32_t x, int32_t y,
+ int32_t w, int32_t h,
+ ErrorResult& aRv);
+
already_AddRefed<DOMQuad> ConvertQuadFromNode(
DOMQuad& aQuad, const TextOrElementOrDocument& aFrom,
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl
index d0508a8c29e5bd008213f781beb8eb1fcb458fb8..518580aa7a2eb355f690d904d091e80c34a98912 100644
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -72,6 +72,17 @@ enum PrefersReducedMotionOverride {
"no-preference",
};
+/**
+ * CSS prefers-contrast values.
+ */
+enum PrefersContrastOverride {
+ "none",
+ "no-preference",
+ "more",
+ "less",
+ "custom",
+};
+
/**
* Allowed overrides of platform/pref default behaviour for touch events.
*/
@@ -258,6 +269,9 @@ interface BrowsingContext {
// Animation playbackRate multiplier, for Devtools
[SetterThrows] attribute double animationsPlayBackRateMultiplier;
+ // 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/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index f95ba91a36f5b4ac906a2d37a814d6eb42b31151..8e373d2e6eee50f60aa12c2651bf8c3fc7c35b8a 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2104,6 +2104,25 @@ static BrowserParent* GetBrowserParentAncestor(BrowserParent* aBrowserParent) {
return bbp->Manager();
}
+// Playwright: automation can move the mouse between different top-level
+// windows while expecting the previous window to keep its hover state.
+// Suppress the old remote's synthesized exit for that cross-window handoff.
+// See https://github.com/microsoft/playwright/issues/40562
+static bool PlaywrightSuppressMouseExit(
+ const WidgetMouseEvent* aMouseEvent, BrowserParent* aRemoteTarget,
+ BrowserParent* aOldRemoteTarget) {
+ if (!aMouseEvent->mFlags.mIsSynthesizedForTests || !aRemoteTarget ||
+ !aOldRemoteTarget) {
+ return false;
+ }
+
+ nsCOMPtr<nsIWidget> remoteTopLevelWidget = aRemoteTarget->GetTopLevelWidget();
+ nsCOMPtr<nsIWidget> oldRemoteTopLevelWidget =
+ aOldRemoteTarget->GetTopLevelWidget();
+ return remoteTopLevelWidget && oldRemoteTopLevelWidget &&
+ remoteTopLevelWidget != oldRemoteTopLevelWidget;
+}
+
static void DispatchCrossProcessMouseExitEvents(WidgetMouseEvent* aMouseEvent,
BrowserParent* aRemoteTarget,
BrowserParent* aStopAncestor,
@@ -2227,7 +2246,7 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
if (mouseEvent->mReason == WidgetMouseEvent::eReal &&
remote != oldRemote) {
MOZ_ASSERT(mouseEvent->mMessage != eMouseExitFromWidget);
- if (oldRemote) {
+ if (oldRemote && !PlaywrightSuppressMouseExit(mouseEvent, remote, oldRemote)) {
BrowserParent* commonAncestor =
nsContentUtils::GetCommonBrowserParentAncestor(remote, oldRemote);
if (commonAncestor == oldRemote) {
diff --git a/dom/fetch/FetchService.cpp b/dom/fetch/FetchService.cpp
index 180065668131acf3738117c84b6a11117fcb6979..a4a77ca8e006fb298769270bb3342ff48616acb4 100644
--- a/dom/fetch/FetchService.cpp
+++ b/dom/fetch/FetchService.cpp
@@ -266,6 +266,14 @@ RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
net::ClassificationFlags({0, 0}) // TrackingFlags
);
+ /* --> Playwright: associate keep-alive fetch with the window */
+ if (mArgsType == FetchArgsType::MainThreadFetch) {
+ auto& args = mArgs.as<MainThreadFetchArgs>();
+ mFetchDriver->SetAssociatedBrowsingContextID(
+ args.mAssociatedBrowsingContextID);
+ }
+ /* <-- Playwright */
+
if (mArgsType == FetchArgsType::WorkerFetch) {
auto& args = mArgs.as<WorkerFetchArgs>();
mFetchDriver->SetWorkerScript(args.mWorkerScript);
diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp
index 87b78f8d13a1a118eedb032d8feaf7d371e13844..f768f0549659263bc714b6339f9df859a1e33f9b 100644
--- a/dom/geolocation/Geolocation.cpp
+++ b/dom/geolocation/Geolocation.cpp
@@ -120,8 +120,12 @@ class nsGeolocationRequest final : public ContentPermissionRequestBase,
NS_IMETHOD GetIgnoreAllowSitePermission(
bool* aIgnoreAllowSitePermission) override {
+ RefPtr<nsGeolocationService> gs =
+ nsGeolocationService::GetGeolocationService(
+ mLocator->GetBrowsingContext());
*aIgnoreAllowSitePermission =
- mBehavior != geolocation::SystemGeolocationPermissionBehavior::NoPrompt;
+ mBehavior != geolocation::SystemGeolocationPermissionBehavior::NoPrompt &&
+ !gs->IsOverride();
return NS_OK;
}
@@ -410,7 +414,9 @@ nsGeolocationRequest::Allow(JS::Handle<JS::Value> aChoices) {
self->Cancel();
};
- if (mBehavior != SystemGeolocationPermissionBehavior::NoPrompt) {
+ RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService(
+ mLocator->GetBrowsingContext());
+ if (mBehavior != SystemGeolocationPermissionBehavior::NoPrompt && !gs->IsOverride()) {
// Asynchronously present the system dialog or open system preferences
// (RequestGeolocationPermissionFromUser will know which to do), and wait
// for the permission to change or the request to be canceled. If the
@@ -434,8 +440,6 @@ nsGeolocationRequest::Allow(JS::Handle<JS::Value> aChoices) {
return NS_OK;
}
- RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService(
- mLocator->GetBrowsingContext());
bool canUseCache = false;
CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
if (lastPosition.position) {
@@ -722,11 +726,16 @@ NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsGeolocationService)
NS_IMPL_RELEASE(nsGeolocationService)
-nsresult nsGeolocationService::Init() {
+nsresult nsGeolocationService::Init(bool isOverride) {
if (!StaticPrefs::geo_enabled()) {
return NS_ERROR_FAILURE;
}
+ if (isOverride) {
+ mIsOverride = true;
+ mHigherAccuracy = true;
+ }
+
if (XRE_IsContentProcess()) {
return NS_OK;
}
@@ -805,6 +814,10 @@ nsresult nsGeolocationService::Init() {
return NS_OK;
}
+bool nsGeolocationService::IsOverride() {
+ return mIsOverride;
+}
+
nsGeolocationService::~nsGeolocationService() = default;
NS_IMETHODIMP
@@ -948,6 +961,10 @@ bool nsGeolocationService::HighAccuracyRequested() {
}
void nsGeolocationService::UpdateAccuracy(bool aForceHigh) {
+ if (mIsOverride) {
+ return;
+ }
+
bool highRequired = aForceHigh || HighAccuracyRequested();
if (XRE_IsContentProcess()) {
diff --git a/dom/geolocation/Geolocation.h b/dom/geolocation/Geolocation.h
index 0e1d90608d55db6a3d39c730d40efa2e708a4c4a..5d49528d5925b4f9ab55531efd694f355bf5a42d 100644
--- a/dom/geolocation/Geolocation.h
+++ b/dom/geolocation/Geolocation.h
@@ -63,7 +63,7 @@ class nsGeolocationService final : public nsIGeolocationUpdate,
nsGeolocationService() = default;
- nsresult Init();
+ nsresult Init(bool isOverride = false);
// Management of the Geolocation objects
void AddLocator(mozilla::dom::Geolocation* aLocator);
@@ -88,6 +88,8 @@ class nsGeolocationService final : public nsIGeolocationUpdate,
void UpdateAccuracy(bool aForceHigh = false);
bool HighAccuracyRequested();
+ bool IsOverride();
+
private:
~nsGeolocationService();
@@ -114,6 +116,8 @@ class nsGeolocationService final : public nsIGeolocationUpdate,
// Nothing() if not being started, or a boolean reflecting the requested
// accuracy.
mozilla::Maybe<bool> mStarting;
+
+ bool mIsOverride = false;
};
namespace mozilla::dom {
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index 45fa91a01ee24014c33396b4e057a797c72377b2..5e44eea99d06d2688e196ba1eb62eb1766b3cb70 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -60,6 +60,7 @@
#include "nsBaseCommandController.h"
#include "nsCRTGlue.h"
#include "nsColorControlFrame.h"
+#include "nsDocShell.h"
#include "nsError.h"
#include "nsFileControlFrame.h"
#include "nsFocusManager.h"
@@ -929,6 +930,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
return NS_ERROR_FAILURE;
}
+ nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
+ nsDocShell* docShell = win ? static_cast<nsDocShell*>(win->GetDocShell()) : nullptr;
+ if (docShell && docShell->IsFileInputInterceptionEnabled()) {
+ docShell->FilePickerShown(this);
+ return NS_OK;
+ }
+
if (IsPickerBlocked(doc)) {
return NS_OK;
}
diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.cc b/dom/media/systemservices/video_engine/desktop_capture_impl.cc
index 9e1086527c293e95f92bd60b7f39de5e955a36ed..7b3b482cf10e87eb8e9f2423aef7b6ac7f8696bc 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(int32_t aCaptureId,
const char* aUniqueId,
- const CaptureDeviceType aType) {
+ const CaptureDeviceType aType,
+ bool aCaptureCursor) {
return new webrtc::RefCountedObject<DesktopCaptureImpl>(aCaptureId, aUniqueId,
- aType);
+ aType, aCaptureCursor);
}
static DesktopCaptureOptions CreateDesktopCaptureOptions() {
@@ -154,8 +155,10 @@ static std::unique_ptr<DesktopCapturer> CreateTabCapturer(
static std::unique_ptr<DesktopCapturer> 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<DesktopCapturer> CreateDesktopCapturerAndThread(
DesktopCaptureImpl::DesktopCaptureImpl(int32_t aCaptureId,
const char* aUniqueId,
- const CaptureDeviceType aType)
+ const CaptureDeviceType aType,
+ bool aCaptureCursor)
: mTrackingId(mozilla::TrackingId(CaptureEngineToTrackingSourceStr([&] {
switch (aType) {
case CaptureDeviceType::Screen:
@@ -269,9 +273,13 @@ DesktopCaptureImpl::DesktopCaptureImpl(int32_t aCaptureId,
aCaptureId)),
mDeviceUniqueId(aUniqueId),
mDeviceType(aType),
+ capture_cursor_(aCaptureCursor),
mControlThread(mozilla::GetCurrentSerialEventTarget()),
mNextFrameMinimumTime(Timestamp::Zero()),
- mCallback("DesktopCaptureImpl::mCallback"),
+ // Playwright: make sure mCallback is initialized with nullptr instead of
+ // a random garbage; we'll use this to assert existance of data callback.
+ mCallback(static_cast<webrtc::VideoSinkInterface<VideoFrame>*>(nullptr),
+ "DesktopCaptureImpl::mCallback"),
mBufferPool(false, 2) {}
DesktopCaptureImpl::~DesktopCaptureImpl() {
@@ -290,6 +298,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback() {
*callback = nullptr;
}
+void DesktopCaptureImpl::RegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) {
+ webrtc::CritScope lock(&mApiCs);
+ _rawFrameCallbacks.insert(rawFrameCallback);
+}
+
+void DesktopCaptureImpl::DeRegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) {
+ webrtc::CritScope lock(&mApiCs);
+ auto it = _rawFrameCallbacks.find(rawFrameCallback);
+ if (it != _rawFrameCallbacks.end()) {
+ _rawFrameCallbacks.erase(it);
+ }
+}
+
int32_t DesktopCaptureImpl::SetCaptureRotation(VideoRotation aRotation) {
MOZ_ASSERT_UNREACHABLE("Unused");
return -1;
@@ -335,7 +356,7 @@ int32_t DesktopCaptureImpl::StartCapture(
return -1;
}
std::unique_ptr capturer = CreateDesktopCapturerAndThread(
- mDeviceType, sourceId, getter_AddRefs(mCaptureThread));
+ mDeviceType, sourceId, getter_AddRefs(mCaptureThread), capture_cursor_);
MOZ_ASSERT(!capturer == !mCaptureThread);
if (!capturer) {
@@ -445,6 +466,21 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult,
frameInfo.height = aFrame->size().height();
frameInfo.videoType = VideoType::kARGB;
+ {
+ webrtc::CritScope cs(&mApiCs);
+ for (auto rawFrameCallback : _rawFrameCallbacks) {
+ rawFrameCallback->OnRawFrame(videoFrame, aFrame->stride(), frameInfo);
+ }
+ }
+
+ // Playwright: fast-return if only raw callback is registered.
+ {
+ auto callback = mCallback.Lock();
+ if (!*callback) {
+ return;
+ }
+ }
+
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 7f3e2e0360b5fb16265f5581faf3a5ee30f7b94a..7a01c0c3f474feb75d683a482ff08deed7edc98f 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 "common_video/include/video_frame_buffer_pool.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"
@@ -43,18 +44,45 @@ 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 mozilla::DesktopCaptureInterface,
public DesktopCapturer::Callback,
- public VideoCaptureModule {
+ public VideoCaptureModuleEx {
public:
/* Create a screen capture modules object
*/
static DesktopCaptureImpl* Create(
int32_t aCaptureId, const char* aUniqueId,
- const mozilla::camera::CaptureDeviceType aType);
+ const mozilla::camera::CaptureDeviceType aType, bool aCaptureCursor = true);
[[nodiscard]] static std::shared_ptr<VideoCaptureModule::DeviceInfo>
CreateDeviceInfo(const mozilla::camera::CaptureDeviceType aType);
@@ -65,6 +93,8 @@ class DesktopCaptureImpl : public mozilla::DesktopCaptureInterface,
void RegisterCaptureDataCallback(
RawVideoSinkInterface* dataCallback) override {}
void DeRegisterCaptureDataCallback() override;
+ void RegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) override;
+ void DeRegisterRawFrameCallback(RawFrameCallback* rawFrameCallback) override;
int32_t SetCaptureRotation(VideoRotation aRotation) override;
bool SetApplyRotation(bool aEnable) override;
@@ -87,7 +117,8 @@ class DesktopCaptureImpl : public mozilla::DesktopCaptureInterface,
protected:
DesktopCaptureImpl(const int32_t aCaptureId, const char* aUniqueId,
- const mozilla::camera::CaptureDeviceType aType);
+ const mozilla::camera::CaptureDeviceType aType,
+ bool aCaptureCusor);
virtual ~DesktopCaptureImpl();
private:
@@ -96,6 +127,9 @@ class DesktopCaptureImpl : public mozilla::DesktopCaptureInterface,
void InitOnThread(std::unique_ptr<DesktopCapturer> aCapturer, int aFramerate);
void UpdateOnThread(int aFramerate);
void ShutdownOnThread();
+
+ webrtc::RecursiveCriticalSection mApiCs;
+ std::set<RawFrameCallback*> _rawFrameCallbacks;
// DesktopCapturer::Callback interface.
void OnCaptureResult(DesktopCapturer::Result aResult,
std::unique_ptr<DesktopFrame> aFrame) override;
@@ -103,6 +137,8 @@ class DesktopCaptureImpl : public mozilla::DesktopCaptureInterface,
// Notifies all mCallbacks of OnFrame(). mCaptureThread only.
void NotifyOnFrame(const VideoFrame& aFrame);
+ bool capture_cursor_ = true;
+
// Control thread on which the public API is called.
const nsCOMPtr<nsISerialEventTarget> mControlThread;
// Set in StartCapture.
diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp
index 9cdecc7f348068fe01e29b6509f098794f6f0d1a..e3aae194a3f2f86a5498a5fd412c211e17817420 100644
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -146,6 +146,30 @@ ScriptSettingsStackEntry::~ScriptSettingsStackEntry() {
MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->HasJSGlobal());
}
+static nsIGlobalObject* UnwrapSandboxGlobal(nsIGlobalObject* global) {
+ if (!global)
+ return global;
+ JSObject* globalObject = global->GetGlobalJSObject();
+ if (!globalObject)
+ return global;
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ if (!cx)
+ return global;
+ JS::Rooted<JSObject*> proto(cx);
+ JS::RootedObject rootedGlobal(cx, globalObject);
+ if (!JS_GetPrototype(cx, rootedGlobal, &proto))
+ return global;
+ if (!proto || !xpc::IsSandboxPrototypeProxy(proto))
+ return global;
+ // If this is a sandbox associated with a DOMWindow via a
+ // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
+ // and JetPack content scripts.
+ proto = js::CheckedUnwrapDynamic(proto, cx, /* stopAtWindowProxy = */ false);
+ if (!proto)
+ return global;
+ return xpc::WindowGlobalOrNull(proto);
+}
+
// If the entry or incumbent global ends up being something that the subject
// principal doesn't subsume, we don't want to use it. This never happens on
// the web, but can happen with asymmetric privilege relationships (i.e.
@@ -173,7 +197,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 11de03cf6eaf8fa6fff602cc12a78acc08f2e25f..51e5abd2ee8174d672ce70310b9d03fdda170a26 100644
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -31,6 +31,7 @@
#include "nsSandboxFlags.h"
#include "nsServiceManagerUtils.h"
#include "nsWhitespaceTokenizer.h"
+#include "nsDocShell.h"
using namespace mozilla;
using mozilla::dom::SRIMetadata;
@@ -134,6 +135,11 @@ void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc,
return;
}
+ if (aDoc.GetDocShell() &&
+ nsDocShell::Cast(aDoc.GetDocShell())->IsBypassCSPEnabled()) {
+ return;
+ }
+
nsAutoString policyStr(
nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
aPolicyStr));
diff --git a/dom/webidl/GeometryUtils.webidl b/dom/webidl/GeometryUtils.webidl
index 584d39da5f04f6d8fc6a87547557b0eeeb35d168..65eb7014356a90aa01ab517c9937ed650c2ee8fd 100644
--- a/dom/webidl/GeometryUtils.webidl
+++ b/dom/webidl/GeometryUtils.webidl
@@ -16,6 +16,8 @@ dictionary GeometryUtilsOptions {
boolean createFramesForSuppressedWhitespace = true;
[ChromeOnly]
boolean flush = true;
+ [ChromeOnly]
+ boolean recurseWhenNoFrame = false;
};
dictionary BoxQuadOptions : GeometryUtilsOptions {
@@ -34,6 +36,9 @@ interface mixin GeometryUtils {
[Throws, Func="nsINode::HasBoxQuadsSupport", NeedsCallerType]
sequence<DOMQuad> getBoxQuads(optional BoxQuadOptions options = {});
+ [ChromeOnly, Throws, Func="nsINode::HasBoxQuadsSupport"]
+ undefined scrollRectIntoViewIfNeeded(long x, long y, long w, long h);
+
/* getBoxQuadsFromWindowOrigin is similar to getBoxQuads, but the
* returned quads are further translated relative to the window
* origin -- which is not the layout origin. Further translation
diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
index ae2d14e105bc70dc4a5c7f1fa3481c550a74dd4d..83f090b0512dbf2fde42e152838d4bd8315e6616 100644
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -442,6 +442,8 @@ dictionary SynthesizeMouseEventOptions : SynthesizeEventOptions {
boolean ignoreRootScrollFrame = false;
// Controls WidgetMouseEvent.mReason value.
boolean isWidgetEventSynthesized = false;
+ // Playwright
+ boolean jugglerConvertToPointer = true;
};
// Mozilla-specific stuff
diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp
index 57cd28a445025fb9f3a18ce7083ee683069015f0..398b82832bbf373d443b846b7b5cd4d10888d2a3 100644
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -2510,7 +2510,11 @@ Maybe<Completion> DebuggerObject::call(JSContext* cx,
invokeArgs[i].set(args2[i]);
}
+ // Disable CSP for the scope of the call.
+ const JSSecurityCallbacks* securityCallbacks = JS_GetSecurityCallbacks(cx);
+ JS_SetSecurityCallbacks(cx, nullptr);
ok = js::Call(cx, calleev, thisv, invokeArgs, &result);
+ JS_SetSecurityCallbacks(cx, securityCallbacks);
}
}
diff --git a/js/src/vm/DateTime.cpp b/js/src/vm/DateTime.cpp
index 50657afb4aaf2305b01b581ee09eb9ea712b2614..38384eea6216358b40e8440fa2a78a17fca83592 100644
--- a/js/src/vm/DateTime.cpp
+++ b/js/src/vm/DateTime.cpp
@@ -810,7 +810,6 @@ void js::DateTimeInfo::internalResyncICUDefaultTimeZone() {
#if JS_HAS_INTL_API
if (const char* tzenv = std::getenv("TZ")) {
std::string_view tz(tzenv);
-
mozilla::Span<const char> tzid;
# if defined(XP_WIN)
diff --git a/layout/base/GeometryUtils.cpp b/layout/base/GeometryUtils.cpp
index eaaf69687f669a4859a37f3f97b99e6ca519c420..6f965a110db3a1c86466d72f09ead23bef767e5e 100644
--- a/layout/base/GeometryUtils.cpp
+++ b/layout/base/GeometryUtils.cpp
@@ -21,6 +21,7 @@
#include "nsContentUtils.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
+#include "ChildIterator.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -263,10 +264,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<RefPtr<DOMQuad>>& aResult, CallerType aCallerType,
ErrorResult& aRv) {
- nsIFrame* frame = GetFrameForNode(aNode, aOptions);
+ nsIFrame* frame =
+ GetFrameForNodeRecursive(aNode, aOptions, aOptions.mRecurseWhenNoFrame);
if (!frame) {
// No boxes to return
return;
@@ -280,7 +298,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 5b52c35994ce5167fc07b31ed9adb9b2ecc1df32..906f8c6e7a7736eed6ba149fd4a142b53522dbc9 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -11835,7 +11835,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 aa6ef4435df02bad54e195607c5da02bd7ed315a..c7a3f02bf30704c53a3c6a619b2dfbee02edb0c2 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -702,6 +702,7 @@ bool nsLayoutUtils::AllowZoomingForDocument(
!aDocument->GetPresShell()->AsyncPanZoomEnabled()) {
return false;
}
+
// True if we allow zooming for all documents on this platform, or if we are
// in RDM.
BrowsingContext* bc = aDocument->GetBrowsingContext();
diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h
index b8317e006298c1345a6f13d26ca20980875e5bd1..001fd4dbe5612588857924b902cf7ccb359885e5 100644
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -623,6 +623,7 @@ bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*);
bool Gecko_MediaFeatures_PrefersReducedTransparency(
const mozilla::dom::Document*);
bool Gecko_MediaFeatures_MacRTL(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 1f9613b8cd936fa9d884be010a8dd6167251faa6..0346bccc1c469446376c5bee3250e3dcfd9b6d81 100644
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -322,6 +322,20 @@ bool Gecko_MediaFeatures_MacRTL(const Document* aDocument) {
// 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/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 3d33f9916a240af4ef5b0028b23e139dc50e9851..358b8365235dad23c08ad26d7d30cde3fd9b2bc8 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -13315,18 +13315,20 @@
# Use the libwebrtc ScreenCaptureKit desktop capture backend on Mac by default.
# When disabled, or on a host where not supported (< macOS 14), the older
# CoreGraphics backend is used instead.
+# PLAYWRIGHT: disable this preference to avoid this kicking during screencast.
- name: media.getdisplaymedia.screencapturekit.enabled
type: bool
- value: true
+ value: false
mirror: once
# Use SCContentSharingPicker for source picking when the libwebrtc
# ScreenCaptureKit desktop capture backend is used. When this is true and the
# backend supports SCContentSharingPicker, this takes precendence over the
# enumeration pref below.
+# PLAYWRIGHT: disable this preference to avoid this kicking during screencast.
- name: media.getdisplaymedia.screencapturekit.picker.enabled
type: bool
- value: true
+ value: false
mirror: once
# Use the libwebrtc ScreenCaptureKit desktop capture backend on Mac for screen
diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
index a468108e7147e98f1037e751cb25bc5743a276c3..6b9fab4b6a07cdb9a54719bd7007e32368c485f6 100644
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -752,7 +752,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mInterceptionInfo(rhs.mInterceptionInfo),
mSchemelessInput(rhs.mSchemelessInput),
mUserNavigationInvolvement(rhs.mUserNavigationInvolvement),
- mSkipHTTPSUpgrade(rhs.mSkipHTTPSUpgrade) {
+ mSkipHTTPSUpgrade(rhs.mSkipHTTPSUpgrade),
+ mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) {
}
LoadInfo::LoadInfo(
@@ -2116,4 +2117,16 @@ void LoadInfo::UpdateParentAddressSpaceInfo() {
}
}
+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 797517e695981ddddfef253cd5eb227fd08e686d..93927bb84e4a81068b1772a913c4b405605cc063 100644
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -550,6 +550,8 @@ class LoadInfo final : public nsILoadInfo {
dom::UserNavigationInvolvement::None;
bool mSkipHTTPSUpgrade = false;
+
+ uint64_t mJugglerLoadIdentifier = 0;
};
// This is exposed solely for testing purposes and should not be used outside of
// LoadInfo
diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp
index 8acc7468ae67a75c53569c8d9498b0b27d6bcaa2..5cf23f8a3906f923c0368e8cf5b8fc99df4858f5 100644
--- a/netwerk/base/TRRLoadInfo.cpp
+++ b/netwerk/base/TRRLoadInfo.cpp
@@ -555,5 +555,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 f6a22a498d99ee75c7c9985e278b4331ec3eddc4..cbc8f92fabc7272a2f04b0e37214e7a51a9a0dda 100644
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1720,4 +1720,6 @@ interface nsILoadInfo : nsISupports
return static_cast<mozilla::dom::UserNavigationInvolvement>(userNavigationInvolvement);
}
%}
+
+ [infallible] attribute unsigned long long jugglerLoadIdentifier;
};
diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
index 3654f3ed20f6b22d36c4238be40417e77e8f6867..f685e7668ad3310cac8bc8425124a6fe6ed0405d 100644
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -59,6 +59,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 1df2115be38c8e4c71635581f486c0efb0a48d2d..6a8e39d7b3504b05b463ba218b038455ff52bf2a 100644
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -196,6 +196,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
aLoadState->GetTextDirectiveUserActivation() ||
aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_FROM_EXTERNAL));
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 60626cc9b3f7495b3f0ffc13346d1319ffd86dab..99c85a483d9e325bc3f7fd86bf772968ece31724 100644
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -726,10 +726,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<nsINetworkInterceptController> controller;
+ GetCallback(controller);
+ if (!controller)
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ return controller->ChannelIntercepted(this);
+ }
+
if (mCanceled) {
return mStatus;
}
@@ -1143,11 +1166,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-firefox/firefox/commit/bb16ca6496682c3b0ddd452d0dd4c1dd46ff71f8
+ /*
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) {
RefPtr<nsInputStreamPump> pump(mPump);
diff --git a/netwerk/protocol/http/InterceptedHttpChannel.h b/netwerk/protocol/http/InterceptedHttpChannel.h
index 430646881b927d2dddda1e0bcf5fd3427580224f..6b5bbb2a411794e9275d1ab83ea02d4cfca88e0e 100644
--- a/netwerk/protocol/http/InterceptedHttpChannel.h
+++ b/netwerk/protocol/http/InterceptedHttpChannel.h
@@ -89,6 +89,11 @@ class InterceptedHttpChannel final
Atomic<bool> 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/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index c6d926448e399e0cb3032180efad3cddaf785fc0..7148ba816bf0dab3224323736001237a99d3b2ba 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -942,11 +942,9 @@ nsresult nsHttpChannel::OnBeforeConnect() {
// SecurityInfo.sys.mjs
mLoadInfo->SetHstsStatus(isSecureURI);
- RefPtr<mozilla::dom::BrowsingContext> bc;
- mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
// If bypassing the cache and we're forced offline
// we can just return the error here.
- if (bc && bc->Top()->GetForceOffline() &&
+ if (IsForcedOffline() &&
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
return NS_ERROR_OFFLINE;
}
@@ -1066,9 +1064,7 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
return aStatus;
}
- RefPtr<mozilla::dom::BrowsingContext> bc;
- mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
- bool forceOffline = bc && bc->Top()->GetForceOffline();
+ bool forceOffline = IsForcedOffline();
if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC() ||
forceOffline) {
@@ -1531,15 +1527,14 @@ nsresult nsHttpChannel::ContinueConnect() {
"CORS preflight must have been finished by the time we "
"do the rest of ContinueConnect");
- RefPtr<mozilla::dom::BrowsingContext> bc;
- mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
+ bool isForcedOffline = IsForcedOffline();
// we may or may not have a cache entry at this point
if (mCacheEntry) {
// read straight from the cache if possible...
if (CachedContentIsValid()) {
// If we're forced offline, and set to bypass the cache, return offline.
- if (bc && bc->Top()->GetForceOffline() &&
+ if (isForcedOffline &&
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
return NS_ERROR_OFFLINE;
}
@@ -1581,7 +1576,7 @@ nsresult nsHttpChannel::ContinueConnect() {
}
// We're about to hit the network. Don't if we're forced offline.
- if (bc && bc->Top()->GetForceOffline()) {
+ if (isForcedOffline) {
return NS_ERROR_OFFLINE;
}
@@ -1689,12 +1684,9 @@ void nsHttpChannel::SpeculativeConnect() {
// don't speculate if we are offline, when doing http upgrade (i.e.
// websockets bootstrap), or if we can't do keep-alive (because then we
// couldn't reuse the speculative connection anyhow).
- RefPtr<mozilla::dom::BrowsingContext> bc;
- mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
-
if (gIOService->IsOffline() || mUpgradeProtocolCallback ||
!(mCaps & NS_HTTP_ALLOW_KEEPALIVE) ||
- (bc && bc->Top()->GetForceOffline())) {
+ IsForcedOffline()) {
return;
}
@@ -5007,7 +4999,7 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) {
return NS_OK;
}
- bool forceOffline = bc && bc->Top()->GetForceOffline();
+ bool forceOffline = IsForcedOffline();
if (offline || (mLoadFlags & INHIBIT_CACHING) || forceOffline) {
if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()) &&
!offline && !forceOffline) {
@@ -8306,6 +8298,20 @@ void nsHttpChannel::MaybeStartDNSPrefetch() {
}
}
+bool nsHttpChannel::IsForcedOffline() {
+ RefPtr<mozilla::dom::BrowsingContext> bc;
+ mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
+ if (bc && bc->Top()->GetForceOffline())
+ return true;
+
+ RefPtr<mozilla::dom::BrowsingContext> wbc;
+ mLoadInfo->GetAssociatedBrowsingContext(getter_AddRefs(wbc));
+ if (wbc && wbc->Top()->GetForceOffline())
+ return true;
+
+ return false;
+}
+
NS_IMETHODIMP
nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) {
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
index 602a3def6eab873986afc17ec92073eb476a43fb..4b267ff4859ebc3eecb169b7f793e60ac8ba1728 100644
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -306,6 +306,10 @@ class nsHttpChannel final : public HttpBaseChannel,
void MaybeResolveProxyAndBeginConnect();
void MaybeStartDNSPrefetch();
+ // ---- Playwright begin
+ bool IsForcedOffline();
+ // ---- Playwright end
+
// Based on the proxy configuration determine the strategy for resolving the
// end server host name.
nsIHttpChannelInternal::ProxyDNSStrategy ComputeProxyDNSStrategy();
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index ed63fe936c1c0fa38329c19c7adada56fd756f2d..b492aa20dd7ded36472b3816ea181d9a991b8d6f 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -1449,6 +1449,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ if (mDocShell && static_cast<nsDocShell*>(mDocShell.get())->IsBypassCSPEnabled()) {
+ return;
+ }
+
nsresult rv = NS_OK;
nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = mDocument->GetPreloadCsp();
if (!preloadCsp) {
diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp
index 068d9702fd015c974d698d2c8c741b5d2b0d8316..9ff49d6de9d712b7e5ab9711da7f22269dc2fe49 100644
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -624,6 +624,8 @@ void nsCertOverrideService::CountPermanentOverrideTelemetry(
}
static bool IsDebugger() {
+ // In playwright world, this is always enabled.
+ if (1 == 1) return true;
#ifdef ENABLE_WEBDRIVER
nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
if (marionette) {
diff --git a/services/settings/Utils.sys.mjs b/services/settings/Utils.sys.mjs
index 76f69ab74eba9f704b95a7e3c7b27c39a887bf5a..cf0b9223baed50bff6a08497fe571ca153efaeb0 100644
--- a/services/settings/Utils.sys.mjs
+++ b/services/settings/Utils.sys.mjs
@@ -99,7 +99,7 @@ const _cdnURLs = {};
export var Utils = {
get SERVER_URL() {
- return lazy.allowServerURL
+ return true || lazy.allowServerURL
? // eslint-disable-next-line mozilla/valid-lazy
lazy.gServerURL
: AppConstants.REMOTE_SETTINGS_SERVER_URLS[0];
@@ -113,6 +113,9 @@ export var Utils = {
log,
get shouldSkipRemoteActivity() {
+ // Playwright does not set Cu.isInAutomation, hence we just return true
+ // here in order to disable the remote activity.
+ return true;
if (
(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 a665bd039d49aeeb6896e224080a7cc00b0eacbc..099c3b08d7de697cc8edcd5bb1d8000fc28aeefb 100644
--- a/toolkit/components/browser/nsIWebBrowserChrome.idl
+++ b/toolkit/components/browser/nsIWebBrowserChrome.idl
@@ -87,6 +87,9 @@ interface nsIWebBrowserChrome : nsISupports
// Whether this is a Document Picture-in-Picture window
const unsigned long CHROME_DOCUMENT_PIP = 1 << 22;
+ // Whether this window has "width" or "height" defined in features
+ const unsigned long JUGGLER_WINDOW_EXPLICIT_SIZE = 1 << 23;
+
// 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 217d51f4c765e0aac2107246f7142099d21d984e..1b623f956c178904945cddeceec9f737a01c053f 100644
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
@@ -115,7 +115,9 @@ EnterprisePoliciesManager.prototype = {
Services.prefs.clearUserPref(PREF_POLICIES_APPLIED);
}
- let provider = this._chooseProvider();
+ // --- Playwright begin ---
+ let provider = new PlaywrightPoliciesProvider();
+ // --- Playwright end ---
if (provider.failed) {
this.status = Ci.nsIEnterprisePolicies.FAILED;
@@ -736,6 +738,19 @@ class JSONPoliciesProvider {
}
}
+class PlaywrightPoliciesProvider extends JSONPoliciesProvider {
+ _getConfigurationFile() {
+ let prefPath = Services.prefs.getStringPref(PREF_ALTERNATE_PATH, "");
+ if (!prefPath)
+ return null;
+
+ dump(`Playwright: loading enterprise policies from ${prefPath}\n`);
+ let configFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ configFile.initWithPath(prefPath);
+ return configFile;
+ }
+}
+
class WindowsGPOPoliciesProvider {
constructor() {
this._policies = null;
diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp
index d0f9c88dcdaca07c6e10139b50b092bf80fc8c3e..f6d1a5ff8f241abdd931f47508d0fffaadce0391 100644
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -377,7 +377,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
nsCOMPtr<nsIWindowMediator> mediator(
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
- if (mediator) {
+ if (ferocity != eForceQuit && mediator) {
mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
if (windowEnumerator) {
bool more;
diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
index efe8ff5541915c0fc632e75572d1e3968c60139d..e0157515ded811e47d21705f13f475d61998fd78 100644
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -175,8 +175,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 d5ebdf4413568c89e93d89279510ae910bcdf9fd..a1dbe7916e39fd6bc0292d6262787d39a5c8015d 100644
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -1917,7 +1917,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
// behavior of other browsers and avoids breaking sites like Gmail that
// open a Compose popout via Shift+click.
*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 db167b74f040cdb477f68c87c4dfe50ef1173c32..78690b35f701df5a683ea3e392b2f7757f66c73e 100644
--- a/toolkit/mozapps/update/UpdateService.sys.mjs
+++ b/toolkit/mozapps/update/UpdateService.sys.mjs
@@ -4024,6 +4024,8 @@ export class UpdateService {
}
get disabledForTesting() {
+ /* playwright */
+ return true;
return lazy.UpdateServiceStub.updateDisabledForTesting;
}
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
index f3c59b08567c9b4ed1b5ccb3eeea845cf7a4808b..70bf13928f67e96c92369f5df2fee2aee78d000c 100644
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -154,6 +154,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 b7c376eadc1460b0f4e378ca3047eb82a079c19c..44385776349e13f7452d7c1b382cf73880e40c32 100644
--- a/toolkit/xre/nsWindowsWMain.cpp
+++ b/toolkit/xre/nsWindowsWMain.cpp
@@ -14,8 +14,10 @@
#endif
#include "mozilla/Char16.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
#include "nsUTF8Utils.h"
+#include <io.h>
#include <windows.h>
#ifdef __MINGW32__
@@ -114,6 +116,19 @@ static void FreeAllocStrings(int argc, char** argv) {
int wmain(int argc, WCHAR** argv) {
SanitizeEnvironmentVariables();
SetDllDirectoryW(L"");
+ bool hasJugglerPipe =
+ mozilla::CheckArg(argc, argv, "juggler-pipe", nullptr,
+ mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND;
+ if (hasJugglerPipe && !mozilla::EnvHasValue("PW_PIPE_READ")) {
+ intptr_t stdio3 = _get_osfhandle(3);
+ intptr_t stdio4 = _get_osfhandle(4);
+ CHAR stdio3str[20];
+ CHAR stdio4str[20];
+ itoa(stdio3, stdio3str, 10);
+ itoa(stdio4, stdio4str, 10);
+ SetEnvironmentVariableA("PW_PIPE_READ", stdio3str);
+ SetEnvironmentVariableA("PW_PIPE_WRITE", stdio4str);
+ }
// Only run this code if LauncherProcessWin.h was included beforehand, thus
// signalling that the hosting process should support launcher mode.
diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp
index 524451a83e03f8a9a83103b1f3d87850ad411515..a44b3c5da20f6a0bf9c892d4289a07ac698fe56d 100644
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -886,6 +886,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout,
mIsLoadingJavascriptURI ? "javascript URI"
: "document.open"));
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ nsIPrincipal* principal = doc->NodePrincipal();
+ if (!principal->IsSystemPrincipal())
+ os->NotifyObservers(ToSupports(doc), "juggler-document-open-loaded", nullptr);
+ }
// This is a very cut-down version of
// nsDocumentViewer::LoadComplete that doesn't do various things
// that are not relevant here because this wasn't an actual
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
index 1eab401455890aafc73f1da9080415fc035f1c30..d7ce1bccb117cac5d95181f6ab0ca7c3e7e62808 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -111,6 +111,7 @@
#include "mozilla/Components.h"
#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ErrorNames.h"
#include "mozilla/Preferences.h"
#include "mozilla/ipc/URIUtils.h"
@@ -879,6 +880,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);
@@ -1514,7 +1521,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);
@@ -1698,7 +1710,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
return NS_OK;
}
- rv = SetUpTempFile(aChannel);
+ bool isIntercepted = false;
+ nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+ if (interceptor) {
+ nsCOMPtr<nsIFile> fileToUse;
+ rv = interceptor->InterceptDownloadRequest(this, request, mBrowsingContext, getter_AddRefs(fileToUse), &isIntercepted);
+ if (!NS_SUCCEEDED(rv)) {
+ LOG((" failed to call nsIDowloadInterceptor.interceptDownloadRequest"));
+ return rv;
+ }
+ if (isIntercepted) {
+ LOG((" request interceped by nsIDowloadInterceptor"));
+ if (fileToUse) {
+ mTempFile = fileToUse;
+ rv = mTempFile->GetLeafName(mTempLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ Cancel(NS_BINDING_ABORTED);
+ return NS_OK;
+ }
+ }
+ }
+
+ // Temp file is the final destination when download is intercepted. In that
+ // case we only need to create saver (and not create transfer later). Not creating
+ // mTransfer also cuts off all downloads handling logic in the js compoenents and
+ // browser UI.
+ if (isIntercepted)
+ rv = CreateSaverForTempFile();
+ else
+ rv = SetUpTempFile(aChannel);
if (NS_FAILED(rv)) {
nsresult transferError = rv;
@@ -1760,6 +1801,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
@@ -2276,6 +2320,15 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
NotifyTransfer(aStatus);
}
+ if (!mCanceled) {
+ nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+ if (interceptor) {
+ nsCString noError;
+ nsresult rv = interceptor->OnDownloadComplete(this, noError);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to call nsIDowloadInterceptor.OnDownloadComplete");
+ }
+ }
+
return NS_OK;
}
@@ -2761,6 +2814,14 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
}
}
+ nsCOMPtr<nsIDownloadInterceptor> interceptor = mExtProtSvc->mInterceptor;
+ if (interceptor) {
+ nsCString errorName;
+ GetErrorName(aReason, errorName);
+ nsresult rv = interceptor->OnDownloadComplete(this, errorName);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed notify nsIDowloadInterceptor about cancel");
+ }
+
// Break our reference cycle with the helper app dialog (set up in
// OnStartRequest)
mDialog = nullptr;
diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h
index 3f8586ed7ee54af197a82b7a69651b4be2f84dad..929fe5b2b58ceef4055415cfc36a6698b73cba4c 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -269,6 +269,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService,
mozilla::dom::BrowsingContext* aContentContext, bool aForceSave,
nsIInterfaceRequestor* aWindowContext,
nsIStreamListener** aStreamListener);
+
+ nsCOMPtr<nsIDownloadInterceptor> mInterceptor;
};
/**
@@ -467,6 +469,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 f5c40986adad5a9354d1ef1b86e058c83064a7f6..99708fe18ddb939334a9f830ca809b77e8e146bd 100644
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -5,8 +5,11 @@
#include "nsICancelable.idl"
+webidl BrowsingContext;
+interface nsIHelperAppLauncher;
interface nsIURI;
interface nsIChannel;
+interface nsIRequest;
interface nsIStreamListener;
interface nsIFile;
interface nsIMIMEInfo;
@@ -14,6 +17,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.
@@ -86,6 +100,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 777157e17e0db442262b1a9522b0b1b39058789a..54c4dde2ee4847e79b07fffe3ec57a34d86ee468 100644
--- a/widget/InProcessCompositorWidget.cpp
+++ b/widget/InProcessCompositorWidget.cpp
@@ -4,9 +4,12 @@
#include "InProcessCompositorWidget.h"
+#include "HeadlessCompositorWidget.h"
+#include "HeadlessWidget.h"
#include "mozilla/VsyncDispatcher.h"
#include "mozilla/layers/NativeLayer.h"
#include "nsIWidget.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
namespace mozilla {
namespace widget {
@@ -27,6 +30,12 @@ RefPtr<CompositorWidget> CompositorWidget::CreateLocal(
// do it after the static_cast.
nsIWidget* widget = static_cast<nsIWidget*>(aWidget);
MOZ_RELEASE_ASSERT(widget);
+ if (aInitData.type() ==
+ CompositorWidgetInitData::THeadlessCompositorWidgetInitData) {
+ return new HeadlessCompositorWidget(
+ aInitData.get_HeadlessCompositorWidgetInitData(), aOptions,
+ static_cast<HeadlessWidget*>(aWidget));
+ }
return new InProcessCompositorWidget(aOptions, widget);
}
#endif
diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.mm
index 9711cfaf4e3e2974567ea7f7d4074b3541b0248f..34292be04676193f1b8efce45d4fa370c15e970f 100644
--- a/widget/cocoa/NativeKeyBindings.mm
+++ b/widget/cocoa/NativeKeyBindings.mm
@@ -618,6 +618,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()) {
@@ -655,6 +659,10 @@
break;
case KEY_NAME_INDEX_ArrowDown:
if (aEvent.IsControl()) {
+ if (aEvent.IsMeta() || aEvent.IsAlt())
+ break;
+ instance->AppendEditCommandsForSelector(
+ ToObjcSelectorPtr(@selector(scrollPageDown:)), aCommands);
break;
}
if (aEvent.IsMeta()) {
diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp
index cbaa021c9d31cd57e253afd984c5076acab1941e..6cf72e6006e33eac7720958f7d0cd91983f2d782 100644
--- a/widget/headless/HeadlessCompositorWidget.cpp
+++ b/widget/headless/HeadlessCompositorWidget.cpp
@@ -2,6 +2,8 @@
* 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/gfx/2D.h"
+#include "mozilla/layers/CompositorThread.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "HeadlessCompositorWidget.h"
#include "VsyncDispatcher.h"
@@ -14,9 +16,30 @@ HeadlessCompositorWidget::HeadlessCompositorWidget(
const layers::CompositorOptions& aOptions, HeadlessWidget* aWindow)
: CompositorWidget(aOptions),
mWidget(aWindow),
+ mMon("snapshotListener"),
mClientSize(LayoutDeviceIntSize(aInitData.InitialClientSize()),
"HeadlessCompositorWidget::mClientSize") {}
+void HeadlessCompositorWidget::SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ReentrantMonitorAutoEnter lock(mMon);
+ mSnapshotListener = std::move(listener);
+ layers::CompositorThread()->Dispatch(NewRunnableMethod(
+ "HeadlessCompositorWidget::PeriodicSnapshot", this,
+ &HeadlessCompositorWidget::PeriodicSnapshot
+ ));
+}
+
+already_AddRefed<gfx::DrawTarget> HeadlessCompositorWidget::StartRemoteDrawingInRegion(
+ const LayoutDeviceIntRegion& aInvalidRegion) {
+ if (!mDrawTarget)
+ return nullptr;
+
+ RefPtr<gfx::DrawTarget> result = mDrawTarget;
+ return result.forget();
+}
+
void HeadlessCompositorWidget::ObserveVsync(VsyncObserver* aObserver) {
if (RefPtr<CompositorVsyncDispatcher> cvd =
mWidget->GetCompositorVsyncDispatcher()) {
@@ -30,6 +53,59 @@ void HeadlessCompositorWidget::NotifyClientSizeChanged(
const LayoutDeviceIntSize& aClientSize) {
auto size = mClientSize.Lock();
*size = aClientSize;
+ layers::CompositorThread()->Dispatch(NewRunnableMethod<LayoutDeviceIntSize>(
+ "HeadlessCompositorWidget::UpdateDrawTarget", this,
+ &HeadlessCompositorWidget::UpdateDrawTarget,
+ aClientSize));
+}
+
+void HeadlessCompositorWidget::UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize) {
+ MOZ_ASSERT(NS_IsInCompositorThread());
+ if (aClientSize.IsEmpty()) {
+ mDrawTarget = nullptr;
+ return;
+ }
+
+ RefPtr<gfx::DrawTarget> old = std::move(mDrawTarget);
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ gfx::IntSize size = aClientSize.ToUnknownSize();
+ mDrawTarget = mozilla::gfx::Factory::CreateDrawTarget(
+ mozilla::gfx::BackendType::SKIA, size, format);
+ if (old) {
+ RefPtr<gfx::SourceSurface> snapshot = old->Snapshot();
+ if (snapshot)
+ mDrawTarget->CopySurface(snapshot.get(), old->GetRect(), gfx::IntPoint(0, 0));
+ }
+}
+
+void HeadlessCompositorWidget::PeriodicSnapshot() {
+ ReentrantMonitorAutoEnter lock(mMon);
+ if (!mSnapshotListener)
+ return;
+
+ TakeSnapshot();
+ NS_DelayedDispatchToCurrentThread(NewRunnableMethod(
+ "HeadlessCompositorWidget::PeriodicSnapshot", this,
+ &HeadlessCompositorWidget::PeriodicSnapshot), 40);
+}
+
+void HeadlessCompositorWidget::TakeSnapshot() {
+ if (!mDrawTarget)
+ return;
+
+ RefPtr<gfx::SourceSurface> snapshot = mDrawTarget->Snapshot();
+ if (!snapshot) {
+ fprintf(stderr, "Failed to get snapshot of draw target\n");
+ return;
+ }
+
+ RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
+ if (!dataSurface) {
+ fprintf(stderr, "Failed to get data surface from snapshot\n");
+ return;
+ }
+
+ mSnapshotListener(std::move(dataSurface));
}
LayoutDeviceIntSize HeadlessCompositorWidget::GetClientSize() {
diff --git a/widget/headless/HeadlessCompositorWidget.h b/widget/headless/HeadlessCompositorWidget.h
index 8374cd00944eddad27563624cd33bb046b6cf143..0345d19d1da862ccd31afa49b123e9f16755c886 100644
--- a/widget/headless/HeadlessCompositorWidget.h
+++ b/widget/headless/HeadlessCompositorWidget.h
@@ -5,6 +5,7 @@
#ifndef widget_headless_HeadlessCompositorWidget_h
#define widget_headless_HeadlessCompositorWidget_h
+#include "mozilla/ReentrantMonitor.h"
#include "mozilla/widget/CompositorWidget.h"
#include "HeadlessWidget.h"
@@ -22,8 +23,11 @@ class HeadlessCompositorWidget final : public CompositorWidget,
HeadlessWidget* aWindow);
void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize);
+ void SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener);
// CompositorWidget Overrides
+ already_AddRefed<gfx::DrawTarget> StartRemoteDrawingInRegion(
+ const LayoutDeviceIntRegion& aInvalidRegion) override;
uintptr_t GetWidgetKey() override;
@@ -41,10 +45,18 @@ class HeadlessCompositorWidget final : public CompositorWidget,
}
private:
+ void UpdateDrawTarget(const LayoutDeviceIntSize& aClientSize);
+ void PeriodicSnapshot();
+ void TakeSnapshot();
+
HeadlessWidget* mWidget;
+ mozilla::ReentrantMonitor mMon;
// See GtkCompositorWidget for the justification for this mutex.
DataMutex<LayoutDeviceIntSize> mClientSize;
+
+ HeadlessWidget::SnapshotListener mSnapshotListener;
+ RefPtr<gfx::DrawTarget> mDrawTarget;
};
} // namespace widget
diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp
index 34ef4bf32bc4c348bd226f1c3ea5a4f03ad4fac1..394e892cdf35c351b9d8536dcb82c1a130cb2095 100644
--- a/widget/headless/HeadlessLookAndFeelGTK.cpp
+++ b/widget/headless/HeadlessLookAndFeelGTK.cpp
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HeadlessLookAndFeel.h"
+#include "mozilla/ServoTypes.h"
#include "nsStyleConsts.h"
namespace mozilla::widget {
@@ -153,10 +154,9 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
aResult = 0;
break;
case IntID::PrimaryPointerCapabilities:
- aResult = 0;
- break;
case IntID::AllPointerCapabilities:
- aResult = 0;
+ aResult = static_cast<int32_t>(PointerCapabilities::Fine |
+ PointerCapabilities::Hover);
break;
default:
aResult = 0;
diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp
index 0841e299ea23692310db27acfa78f7e2050bb002..3285e16397b14400127c81c0d1a13711e536b073 100644
--- a/widget/headless/HeadlessWidget.cpp
+++ b/widget/headless/HeadlessWidget.cpp
@@ -112,6 +112,8 @@ void HeadlessWidget::Destroy() {
}
}
+ SetSnapshotListener(nullptr);
+
nsIWidget::OnDestroy();
nsIWidget::Destroy();
@@ -573,5 +575,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 9daa3334e7ec6aee433ee7a28e6b86a8548f6226..0aa21a1b0a3b07548d832bfb41f73fe37a174fdc 100644
--- a/widget/headless/HeadlessWidget.h
+++ b/widget/headless/HeadlessWidget.h
@@ -127,6 +127,9 @@ class HeadlessWidget final : public nsIWidget {
double aDeltaX, double aDeltaY, int32_t aModifierFlags,
nsISynthesizedEventCallback* aCallback) override;
+ using SnapshotListener = std::function<void(RefPtr<gfx::DataSourceSurface>&&)>;
+ void SetSnapshotListener(SnapshotListener&& listener);
+
private:
~HeadlessWidget();
bool mEnabled;
diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h
index 2888ffaf432e16d67d348ff008372fbb96c06991..cd4cce73f69bee86d4fb7cb510cd60de0cbcd0b8 100644
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -503,7 +503,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
/**