From 244a1ffdd8746b1418b4eedb95ff72687115ddb4 Mon Sep 17 00:00:00 2001
From: "microsoft-playwright-automation[bot]"
<203992400+microsoft-playwright-automation[bot]@users.noreply.github.com>
Date: Wed, 22 Jul 2026 12:06:26 -0600
Subject: [PATCH 1/3] feat(firefox): roll to r1538 (#41938)
---
README.md | 4 +-
browser_patches/firefox/UPSTREAM_CONFIG.sh | 2 +-
.../firefox/juggler/TargetRegistry.js | 2 +
.../firefox/patches/bootstrap.diff | 214 +++++++++---------
.../isomorphic/deviceDescriptorsSource.json | 4 +-
packages/playwright-core/browsers.json | 4 +-
.../src/server/firefox/ffBrowser.ts | 21 +-
tests/library/browsercontext-locale.spec.ts | 3 +-
.../browsercontext-timezone-id.spec.ts | 3 +-
tests/library/permissions.spec.ts | 2 +-
10 files changed, 130 insertions(+), 129 deletions(-)
diff --git a/README.md b/README.md
index f4f7c49c67cfb..57210c3677139 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# 🎠Playwright
-[](https://www.npmjs.com/package/playwright) [](https://www.chromium.org/Home) [](https://www.mozilla.org/en-US/firefox/new/) [](https://webkit.org/) [](https://aka.ms/playwright/discord)
+[](https://www.npmjs.com/package/playwright) [](https://www.chromium.org/Home) [](https://www.mozilla.org/en-US/firefox/new/) [](https://webkit.org/) [](https://aka.ms/playwright/discord)
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
@@ -298,7 +298,7 @@ The [Playwright VS Code extension](https://marketplace.visualstudio.com/items?it
| :--- | :---: | :---: | :---: |
| Chromium1 151.0.7922.34 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit 26.5 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
-| Firefox 152.0.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| Firefox 153.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless and headed execution on all platforms. 1 Uses [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing) by default.
diff --git a/browser_patches/firefox/UPSTREAM_CONFIG.sh b/browser_patches/firefox/UPSTREAM_CONFIG.sh
index 6a4345ea78398..ab997865af1d1 100644
--- a/browser_patches/firefox/UPSTREAM_CONFIG.sh
+++ b/browser_patches/firefox/UPSTREAM_CONFIG.sh
@@ -1,3 +1,3 @@
REMOTE_URL="https://github.com/mozilla-firefox/firefox"
BASE_BRANCH="release"
-BASE_REVISION="d4faced9e237d6431856c0873cb035cbbc25817b"
+BASE_REVISION="f1b6c0f86b96b7e0688c26f65803576f27cdaf88"
diff --git a/browser_patches/firefox/juggler/TargetRegistry.js b/browser_patches/firefox/juggler/TargetRegistry.js
index e9ceb32fc420d..0511315cf27b0 100644
--- a/browser_patches/firefox/juggler/TargetRegistry.js
+++ b/browser_patches/firefox/juggler/TargetRegistry.js
@@ -17,6 +17,8 @@ const IDENTITY_NAME = 'JUGGLER ';
const ALL_PERMISSIONS = [
'geo',
'desktop-notification',
+ 'local-network',
+ 'loopback-network',
];
let globalTabAndWindowActivationChain = Promise.resolve();
diff --git a/browser_patches/firefox/patches/bootstrap.diff b/browser_patches/firefox/patches/bootstrap.diff
index 2703f59f58001..bf56dc51a6976 100644
--- a/browser_patches/firefox/patches/bootstrap.diff
+++ b/browser_patches/firefox/patches/bootstrap.diff
@@ -31,7 +31,7 @@ index 8337336dc894b44ea696bb780e448dfbdd8b6357..9eb83f33bb0415f28d1bcf66507d33d0
DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
-index 5a388458295a771c0a4e86d7230515e72bc207c2..10a82bb86a590e4c2534ed81d4d3b675baca33ec 100644
+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
@@ -48,7 +48,7 @@ index 5a388458295a771c0a4e86d7230515e72bc207c2..10a82bb86a590e4c2534ed81d4d3b675
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 83e52b260c380b8a4fdcb9146c36a048adffba68..3ab0c47c7153a4226cf1bf0543305365e84f5b14 100644
+index 203811a0fb980b47f113729cb1a16f56f376bfe5..83ac2cc8cdae99006fbc34319ed854c8ef88fcc4 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -204,6 +204,9 @@
@@ -109,10 +109,10 @@ index 257e87fe0c618684eb4216c8028ac886ef2d5562..8c13f020100dad9bc4e093d50bd3a1f9
const transportProvider = {
setListener(upgradeListener) {
diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp
-index e8b50c654849f00d968a43cffd99ef88116bd0c9..e1a54b40fb828dc6917bd4314cf3e27aa801e2a5 100644
+index d7155e79364de64fbf90c7fa37ac1713ed00bfa3..b38aece7733011cad0eac055c560e42b85ad467b 100644
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
-@@ -116,8 +116,11 @@ struct ParamTraits
+@@ -118,8 +118,11 @@ struct ParamTraits
template <>
struct ParamTraits
@@ -126,7 +126,7 @@ index e8b50c654849f00d968a43cffd99ef88116bd0c9..e1a54b40fb828dc6917bd4314cf3e27a
template <>
struct ParamTraits
-@@ -487,7 +490,11 @@ already_AddRefed BrowsingContext::CreateDetached(
+@@ -489,7 +492,11 @@ already_AddRefed BrowsingContext::CreateDetached(
fields.Get() = true;
@@ -139,7 +139,7 @@ index e8b50c654849f00d968a43cffd99ef88116bd0c9..e1a54b40fb828dc6917bd4314cf3e27a
fields.Get() =
inherit ? inherit->GetAllowJavascript() : true;
-@@ -3555,6 +3562,15 @@ void BrowsingContext::DidSet(FieldIndex,
+@@ -3544,6 +3551,15 @@ void BrowsingContext::DidSet(FieldIndex,
});
}
@@ -155,17 +155,17 @@ index e8b50c654849f00d968a43cffd99ef88116bd0c9..e1a54b40fb828dc6917bd4314cf3e27a
void BrowsingContext::DidSet(FieldIndex,
nsString&& aOldValue) {
MOZ_ASSERT(IsTop());
-@@ -3835,7 +3851,7 @@ void BrowsingContext::SetGeolocationServiceOverride(
+@@ -3824,7 +3840,7 @@ void BrowsingContext::SetGeolocationServiceOverride(
if (aGeolocationOverride.WasPassed()) {
if (!mGeolocationServiceOverride) {
- mGeolocationServiceOverride = new nsGeolocationService();
+ mGeolocationServiceOverride = MakeRefPtr();
- mGeolocationServiceOverride->Init();
+ mGeolocationServiceOverride->Init(true /* isOverride */);
}
mGeolocationServiceOverride->Update(aGeolocationOverride.Value());
} else if (RefPtr serviceOverride =
diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h
-index 02abb6ab9562a7b41f4a95a6289c80da8d1176e8..ca04e2314cc16b3e3aa861bea3baaf153c3c274a 100644
+index 6f7f58bba3be6f08b3b27e81f30864c61d683351..ac0d2dc75f3f9e99e97100958108f4504292283f 100644
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -211,11 +211,11 @@ struct EmbedderColorSchemes {
@@ -202,7 +202,7 @@ index 02abb6ab9562a7b41f4a95a6289c80da8d1176e8..ca04e2314cc16b3e3aa861bea3baaf15
bool IsInBFCache() const;
bool IsEnteringBFCache() const { return mIsEnteringBFCache; }
void DeactivateDocuments();
-@@ -1320,6 +1326,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
+@@ -1333,6 +1339,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return IsTop();
}
@@ -214,7 +214,7 @@ index 02abb6ab9562a7b41f4a95a6289c80da8d1176e8..ca04e2314cc16b3e3aa861bea3baaf15
bool CanSet(FieldIndex, dom::ForcedColorsOverride,
ContentParent*) {
return IsTop();
-@@ -1356,6 +1367,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
+@@ -1373,6 +1384,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void DidSet(FieldIndex,
double aOldValue);
@@ -225,7 +225,7 @@ index 02abb6ab9562a7b41f4a95a6289c80da8d1176e8..ca04e2314cc16b3e3aa861bea3baaf15
void WalkPresContexts(Callback&&);
void PresContextAffectingFieldChanged();
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
-index 6a5e8fba0a0cd96834ebcbd9f8b5ba8ea5266972..f5a174d14ba1e6f8482956dd18da5bd4651511b1 100644
+index 00c69a2310f1056e8ef1199156cb593140af8a63..c6f5181e71b952fe36d83640eb776aa55c31eb2c 100644
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -303,6 +303,11 @@ void CanonicalBrowsingContext::ReplacedBy(
@@ -254,7 +254,7 @@ index 6a5e8fba0a0cd96834ebcbd9f8b5ba8ea5266972..f5a174d14ba1e6f8482956dd18da5bd4
}
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
-index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aacc9dafd0 100644
+index 275d0ce1d33e7aa887eebeadac786aac44e0ba10..bff2b58d2169bbd40aa4ac48c656d7f9a0519489 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -16,6 +16,12 @@
@@ -270,7 +270,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
#include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/BasePrincipal.h"
-@@ -63,6 +69,7 @@
+@@ -64,6 +70,7 @@
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FragmentDirective.h"
@@ -278,7 +278,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
#include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/dom/HTMLIFrameElement.h"
#include "mozilla/dom/Navigation.h"
-@@ -95,6 +102,7 @@
+@@ -96,6 +103,7 @@
#include "mozilla/dom/DocumentBinding.h"
#include "mozilla/glean/DocshellMetrics.h"
#include "mozilla/ipc/ProtocolUtils.h"
@@ -286,7 +286,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/ParentChannelWrapper.h"
-@@ -119,6 +127,7 @@
+@@ -120,6 +128,7 @@
#include "nsIDocumentViewer.h"
#include "mozilla/dom/Document.h"
#include "nsHTMLDocument.h"
@@ -294,7 +294,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
#include "nsIDocumentLoaderFactory.h"
#include "nsIDOMWindow.h"
#include "nsIEditingSession.h"
-@@ -214,6 +223,7 @@
+@@ -215,6 +224,7 @@
#include "nsGlobalWindowInner.h"
#include "nsGlobalWindowOuter.h"
#include "nsJSEnvironment.h"
@@ -302,7 +302,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsObjectLoadingContent.h"
-@@ -353,6 +363,14 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
+@@ -355,6 +365,14 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mAllowDNSPrefetch(true),
mAllowWindowControl(true),
mCSSErrorReportingEnabled(false),
@@ -317,7 +317,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
mAllowAuth(mItemType == typeContent),
mAllowKeywordFixup(false),
mDisableMetaRefreshWhenInactive(false),
-@@ -2957,6 +2975,174 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
+@@ -2976,6 +2994,174 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
return NS_OK;
}
@@ -492,7 +492,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
NS_IMETHODIMP
nsDocShell::GetIsNavigating(bool* aOut) {
*aOut = mIsNavigating;
-@@ -4624,7 +4810,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
+@@ -4655,7 +4841,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
}
void nsDocShell::ActivenessMaybeChanged() {
@@ -501,7 +501,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
if (RefPtr presShell = GetPresShell()) {
presShell->ActivenessMaybeChanged();
}
-@@ -7625,6 +7811,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
+@@ -7651,6 +7837,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
true, // aForceNoOpener
getter_AddRefs(newBC));
MOZ_ASSERT(!newBC);
@@ -514,7 +514,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
return rv;
}
-@@ -8854,6 +9046,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
+@@ -8883,6 +9075,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
nsCOMPtr req;
@@ -531,7 +531,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
if (NS_SUCCEEDED(rv)) {
-@@ -11985,6 +12187,9 @@ class OnLinkClickEvent : public Runnable {
+@@ -12019,6 +12221,9 @@ class OnLinkClickEvent : public Runnable {
mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
mTriggeringPrincipal);
}
@@ -541,9 +541,9 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
return NS_OK;
}
-@@ -12102,6 +12307,8 @@ nsresult nsDocShell::OnLinkClick(
+@@ -12136,6 +12341,8 @@ nsresult nsDocShell::OnLinkClick(
- nsCOMPtr ev = new OnLinkClickEvent(
+ RefPtr ev = MakeRefPtr(
this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal);
+ nsCOMPtr observerService = mozilla::services::GetObserverService();
+ observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr);
@@ -551,7 +551,7 @@ index 69d45a59b43350ca86a5a7740c6ebf5ed4e5926d..46cc80b5c8e4d8c866f2fdfab66508aa
}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
-index ac51e58b454bf97aa475416978f97b9dbca45290..52dd403b39d5e38c055873aff55001eb565f2df4 100644
+index aa834b35a1fb2ff6fe3cdd6de9582e48da510be5..ee2f86ddd28a9e18ee33cf509438b5f28cc8c952 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -15,6 +15,7 @@
@@ -586,7 +586,7 @@ index ac51e58b454bf97aa475416978f97b9dbca45290..52dd403b39d5e38c055873aff55001eb
// Creates a real network channel (not a DocumentChannel) using the specified
// parameters.
// Used by nsDocShell when not using DocumentChannel, by DocumentLoadListener
-@@ -987,6 +998,8 @@ class nsDocShell final : public nsDocLoader,
+@@ -988,6 +999,8 @@ class nsDocShell final : public nsDocLoader,
bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; }
@@ -595,7 +595,7 @@ index ac51e58b454bf97aa475416978f97b9dbca45290..52dd403b39d5e38c055873aff55001eb
// 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
-@@ -1298,6 +1311,16 @@ class nsDocShell final : public nsDocLoader,
+@@ -1299,6 +1312,16 @@ class nsDocShell final : public nsDocLoader,
bool mAllowDNSPrefetch : 1;
bool mAllowWindowControl : 1;
bool mCSSErrorReportingEnabled : 1;
@@ -667,10 +667,10 @@ index 4aa56b4ce16915d334f32c18953604765699e05a..ef80a46511d870c909dda6182e332c15
* 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 328b7524810322561700347fa7f26403897635a8..6b98c45166075950aea3e8f23044ca5975862c49 100644
+index 1081a150609c6068cde605228da6e95f84f06d6b..f51d5f53ea1d8985e7f75e4aba2a6996ac594d21 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
-@@ -3696,6 +3696,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) {
+@@ -3655,6 +3655,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) {
}
void Document::ApplySettingsFromCSP(bool aSpeculative) {
@@ -680,7 +680,7 @@ index 328b7524810322561700347fa7f26403897635a8..6b98c45166075950aea3e8f23044ca59
nsresult rv = NS_OK;
if (!aSpeculative) {
nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
-@@ -3784,6 +3787,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
+@@ -3752,6 +3755,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
MOZ_ASSERT(mPolicyContainer,
"Policy container must be initialized before CSP!");
@@ -692,7 +692,7 @@ index 328b7524810322561700347fa7f26403897635a8..6b98c45166075950aea3e8f23044ca59
// If this is a data document - no need to set CSP.
if (mLoadedAsData) {
return NS_OK;
-@@ -4739,6 +4747,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
+@@ -4722,6 +4730,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
return false;
}
@@ -704,10 +704,10 @@ index 328b7524810322561700347fa7f26403897635a8..6b98c45166075950aea3e8f23044ca59
return false;
}
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
-index 2f945e2f1a80524337c21cba7aee9b6cf4f70dbf..3ee7a92aff13c4f82bf792e709ae1c06456d4ccc 100644
+index 079746f77996ca03464c8f30b00cd34817b8430d..770a58853159c5d4972aab6002ab68254ee22360 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
-@@ -2370,7 +2370,8 @@ bool Navigator::Webdriver() {
+@@ -2381,7 +2381,8 @@ bool Navigator::Webdriver() {
}
#endif
@@ -718,10 +718,10 @@ index 2f945e2f1a80524337c21cba7aee9b6cf4f70dbf..3ee7a92aff13c4f82bf792e709ae1c06
AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) {
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
-index 8f53fc6dbe9058bad535ee704b9184932cdbaf4c..6a470df22ff8134ef2206c650413a0a4394aca38 100644
+index 55a786dd5bb66d2bb51568407bc942c8ac2e5fe4..c73e34868b03a11b44d195f419fb3fb2e7869965 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
-@@ -9790,6 +9790,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
+@@ -9873,6 +9873,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
EventMessage msg;
Maybe exitFrom;
bool contextMenuKey = false;
@@ -729,7 +729,7 @@ index 8f53fc6dbe9058bad535ee704b9184932cdbaf4c..6a470df22ff8134ef2206c650413a0a4
if (aType.EqualsLiteral("mousedown")) {
msg = eMouseDown;
} else if (aType.EqualsLiteral("mouseup")) {
-@@ -9816,13 +9817,26 @@ Result nsContentUtils::SynthesizeMouseEvent(
+@@ -9899,13 +9900,26 @@ Result nsContentUtils::SynthesizeMouseEvent(
msg = eMouseHitTest;
} else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
msg = eMouseExploreByTouch;
@@ -757,7 +757,7 @@ index 8f53fc6dbe9058bad535ee704b9184932cdbaf4c..6a470df22ff8134ef2206c650413a0a4
if (MOZ_UNLIKELY(aOptions.mIsWidgetEventSynthesized)) {
MOZ_ASSERT_UNREACHABLE(
"The event shouldn't be dispatched as a synthesized event");
-@@ -9850,6 +9864,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
+@@ -9933,6 +9947,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(callback);
WidgetMouseEvent& mouseOrPointerEvent =
@@ -765,7 +765,7 @@ index 8f53fc6dbe9058bad535ee704b9184932cdbaf4c..6a470df22ff8134ef2206c650413a0a4
pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref();
mouseOrPointerEvent.pointerId = aMouseEventData.mIdentifier;
mouseOrPointerEvent.mModifiers =
-@@ -9875,6 +9890,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
+@@ -9958,6 +9973,7 @@ Result nsContentUtils::SynthesizeMouseEvent(
aOptions.mIsDOMEventSynthesized;
mouseOrPointerEvent.mExitFrom = std::move(exitFrom);
mouseOrPointerEvent.mCallbackId = notifier.SaveCallback();
@@ -774,7 +774,7 @@ index 8f53fc6dbe9058bad535ee704b9184932cdbaf4c..6a470df22ff8134ef2206c650413a0a4
nsPresContext* presContext = aPresShell->GetPresContext();
if (!presContext) {
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
-index a2af624fec7930cfd78d7e59516007fc9ac2833b..365d29520195e907515a4971da3ef6fdb18a4168 100644
+index ed255ae7f0f3c3465f4d38a8af2a7899e0dbf3b9..aa60b5bdc059121ac7a9163a40f0d9d23afa5195 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1875,6 +1875,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent,
@@ -808,7 +808,7 @@ index a2af624fec7930cfd78d7e59516007fc9ac2833b..365d29520195e907515a4971da3ef6fd
// Keep a ref to presShell since dispatching the DOM event may cause
// the document to be destroyed.
RefPtr presShell = docShell->GetPresShell();
-@@ -3187,7 +3197,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
+@@ -3181,7 +3191,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
}
}
@@ -820,10 +820,10 @@ index a2af624fec7930cfd78d7e59516007fc9ac2833b..365d29520195e907515a4971da3ef6fd
// 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 f8428e5926fbd43aa66667dabe009c3b455883d8..39d0fae07570dfe72db7f4518c61185a315a3074 100644
+index 0fbb9d9e11c72f862d5777a0554a142336f8f17f..80ce0fc7a1b42722eb55a4a2e03af4022126a374 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
-@@ -2539,10 +2539,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
+@@ -2535,10 +2535,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
}();
if (!isAboutBlankInChromeDocshell) {
@@ -844,7 +844,7 @@ index f8428e5926fbd43aa66667dabe009c3b455883d8..39d0fae07570dfe72db7f4518c61185a
}
}
-@@ -2662,6 +2668,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
+@@ -2658,6 +2664,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
}
}
@@ -865,7 +865,7 @@ index f8428e5926fbd43aa66667dabe009c3b455883d8..39d0fae07570dfe72db7f4518c61185a
void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h
-index 6b29e4a8854729ed7919b1e6e83871d7a15d3f7d..5980d18660df8e7a731ec0ce034452d8d638d5a7 100644
+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,
@@ -877,10 +877,10 @@ index 6b29e4a8854729ed7919b1e6e83871d7a15d3f7d..5980d18660df8e7a731ec0ce034452d8
// Outer windows only.
virtual void EnsureSizeAndPositionUpToDate() override;
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
-index 361f78eb02c6e567c77738f0e0f65085fa147c04..e59d8a06bbc84cf7cd6360170816a34247a7fee4 100644
+index 5b21525de8dc0cc86bd14e2b36fcd9f0059a030a..65c6027e614bb8566a7a24bac6a7b75aa93d4032 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
-@@ -1622,6 +1622,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
+@@ -1803,6 +1803,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
}
@@ -943,10 +943,10 @@ index 361f78eb02c6e567c77738f0e0f65085fa147c04..e59d8a06bbc84cf7cd6360170816a342
DOMQuad& aQuad, const GeometryNode& aFrom,
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
-index bd8c0993aacd8f88ee2970541ac074a0f11969ef..b3ebfe9a8480e04284d2d12527480d03b1388c9b 100644
+index 4c882db32fb458c4e88dc35cdf6ea4c2ce6675f0..0b5ad5cb09b36bde91ddf5390e2e520c76bf6f49 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
-@@ -2509,6 +2509,10 @@ class nsINode : public mozilla::dom::EventTarget {
+@@ -2551,6 +2551,10 @@ class nsINode : public mozilla::dom::EventTarget {
nsTArray>& aResult,
ErrorResult& aRv);
@@ -990,10 +990,10 @@ index d0508a8c29e5bd008213f781beb8eb1fcb458fb8..518580aa7a2eb355f690d904d091e80c
* 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 4fe9337e868911439c92e62dce20dcf3e6cd4f77..dcda39bb4190af115b0d9edd311f9b299e93ef11 100644
+index f95ba91a36f5b4ac906a2d37a814d6eb42b31151..8e373d2e6eee50f60aa12c2651bf8c3fc7c35b8a 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
-@@ -2049,6 +2049,25 @@ static BrowserParent* GetBrowserParentAncestor(BrowserParent* aBrowserParent) {
+@@ -2104,6 +2104,25 @@ static BrowserParent* GetBrowserParentAncestor(BrowserParent* aBrowserParent) {
return bbp->Manager();
}
@@ -1019,7 +1019,7 @@ index 4fe9337e868911439c92e62dce20dcf3e6cd4f77..dcda39bb4190af115b0d9edd311f9b29
static void DispatchCrossProcessMouseExitEvents(WidgetMouseEvent* aMouseEvent,
BrowserParent* aRemoteTarget,
BrowserParent* aStopAncestor,
-@@ -2172,7 +2191,7 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
+@@ -2227,7 +2246,7 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
if (mouseEvent->mReason == WidgetMouseEvent::eReal &&
remote != oldRemote) {
MOZ_ASSERT(mouseEvent->mMessage != eMouseExitFromWidget);
@@ -1048,10 +1048,10 @@ index 180065668131acf3738117c84b6a11117fcb6979..a4a77ca8e006fb298769270bb3342ff4
auto& args = mArgs.as();
mFetchDriver->SetWorkerScript(args.mWorkerScript);
diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp
-index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0e35b5f2d 100644
+index 87b78f8d13a1a118eedb032d8feaf7d371e13844..f768f0549659263bc714b6339f9df859a1e33f9b 100644
--- a/dom/geolocation/Geolocation.cpp
+++ b/dom/geolocation/Geolocation.cpp
-@@ -119,8 +119,12 @@ class nsGeolocationRequest final : public ContentPermissionRequestBase,
+@@ -120,8 +120,12 @@ class nsGeolocationRequest final : public ContentPermissionRequestBase,
NS_IMETHOD GetIgnoreAllowSitePermission(
bool* aIgnoreAllowSitePermission) override {
@@ -1065,7 +1065,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
return NS_OK;
}
-@@ -409,7 +413,9 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) {
+@@ -410,7 +414,9 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) {
self->Cancel();
};
@@ -1076,7 +1076,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
// 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
-@@ -433,8 +439,6 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) {
+@@ -434,8 +440,6 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) {
return NS_OK;
}
@@ -1085,7 +1085,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
bool canUseCache = false;
CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
if (lastPosition.position) {
-@@ -721,11 +725,16 @@ NS_INTERFACE_MAP_END
+@@ -722,11 +726,16 @@ NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsGeolocationService)
NS_IMPL_RELEASE(nsGeolocationService)
@@ -1103,7 +1103,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
if (XRE_IsContentProcess()) {
return NS_OK;
}
-@@ -804,6 +813,10 @@ nsresult nsGeolocationService::Init() {
+@@ -805,6 +814,10 @@ nsresult nsGeolocationService::Init() {
return NS_OK;
}
@@ -1114,7 +1114,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
nsGeolocationService::~nsGeolocationService() = default;
NS_IMETHODIMP
-@@ -947,6 +960,10 @@ bool nsGeolocationService::HighAccuracyRequested() {
+@@ -948,6 +961,10 @@ bool nsGeolocationService::HighAccuracyRequested() {
}
void nsGeolocationService::UpdateAccuracy(bool aForceHigh) {
@@ -1126,7 +1126,7 @@ index 20a3eded3ad1af3bdfde461c26297b10d115cd9c..323900e22a195abd3262f89d70693ad0
if (XRE_IsContentProcess()) {
diff --git a/dom/geolocation/Geolocation.h b/dom/geolocation/Geolocation.h
-index 2194983e91bad4d6d54c1543de799aed09a336df..79c0e97ad80784dbcca3218356b7d15531771a15 100644
+index 0e1d90608d55db6a3d39c730d40efa2e708a4c4a..5d49528d5925b4f9ab55531efd694f355bf5a42d 100644
--- a/dom/geolocation/Geolocation.h
+++ b/dom/geolocation/Geolocation.h
@@ -63,7 +63,7 @@ class nsGeolocationService final : public nsIGeolocationUpdate,
@@ -1157,7 +1157,7 @@ index 2194983e91bad4d6d54c1543de799aed09a336df..79c0e97ad80784dbcca3218356b7d155
namespace mozilla::dom {
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
-index e3698a2cbcfd13ce6075e0a155c46191dfc15a57..65e7834f2df7e55c5958aa01d4cb1adeec85da88 100644
+index 45fa91a01ee24014c33396b4e057a797c72377b2..5e44eea99d06d2688e196ba1eb62eb1766b3cb70 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -60,6 +60,7 @@
@@ -1569,10 +1569,10 @@ index eaaf69687f669a4859a37f3f97b99e6ca519c420..6f965a110db3a1c86466d72f09ead23b
// No boxes to return
return;
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
-index 38891d8d9f00dbe49a6d4eac94b8e72b13baba96..0c649cec87cfabe2106949cb9cc3beb72f898de6 100644
+index 5b52c35994ce5167fc07b31ed9adb9b2ecc1df32..906f8c6e7a7736eed6ba149fd4a142b53522dbc9 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
-@@ -11791,7 +11791,9 @@ bool PresShell::ComputeActiveness() const {
+@@ -11835,7 +11835,9 @@ bool PresShell::ComputeActiveness() const {
if (!browserChild->IsVisible()) {
MOZ_LOG(gLog, LogLevel::Debug,
(" > BrowserChild %p is not visible", browserChild));
@@ -1584,7 +1584,7 @@ index 38891d8d9f00dbe49a6d4eac94b8e72b13baba96..0c649cec87cfabe2106949cb9cc3beb7
// 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 46967151f9d91dd99b9ae4d026098abe05619537..447e4e43ee46345cfda18c15540d6a2bc9651379 100644
+index aa6ef4435df02bad54e195607c5da02bd7ed315a..c7a3f02bf30704c53a3c6a619b2dfbee02edb0c2 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -702,6 +702,7 @@ bool nsLayoutUtils::AllowZoomingForDocument(
@@ -1633,10 +1633,10 @@ index 1f9613b8cd936fa9d884be010a8dd6167251faa6..0346bccc1c469446376c5bee3250e3dc
return StylePrefersContrast::NoPreference;
}
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
-index b96e9d02beda72c92c295a2799c851b94945ada6..55ab1a450f06225d0bec9e33e0b660e642ce42c3 100644
+index 3d33f9916a240af4ef5b0028b23e139dc50e9851..358b8365235dad23c08ad26d7d30cde3fd9b2bc8 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
-@@ -13109,18 +13109,20 @@
+@@ -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.
@@ -1660,10 +1660,10 @@ index b96e9d02beda72c92c295a2799c851b94945ada6..55ab1a450f06225d0bec9e33e0b660e6
# Use the libwebrtc ScreenCaptureKit desktop capture backend on Mac for screen
diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
-index a9abddc425e6570afbda8c27ed11052ab65bcac1..ee23f9ae179fdb3c2f9099b39888f569cc4f69b6 100644
+index a468108e7147e98f1037e751cb25bc5743a276c3..6b9fab4b6a07cdb9a54719bd7007e32368c485f6 100644
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
-@@ -751,7 +751,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
+@@ -752,7 +752,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mInterceptionInfo(rhs.mInterceptionInfo),
mSchemelessInput(rhs.mSchemelessInput),
mUserNavigationInvolvement(rhs.mUserNavigationInvolvement),
@@ -1673,7 +1673,7 @@ index a9abddc425e6570afbda8c27ed11052ab65bcac1..ee23f9ae179fdb3c2f9099b39888f569
}
LoadInfo::LoadInfo(
-@@ -2105,4 +2106,16 @@ void LoadInfo::UpdateParentAddressSpaceInfo() {
+@@ -2116,4 +2117,16 @@ void LoadInfo::UpdateParentAddressSpaceInfo() {
}
}
@@ -1756,10 +1756,10 @@ index 3654f3ed20f6b22d36c4238be40417e77e8f6867..f685e7668ad3310cac8bc8425124a6fe
* 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 29c5421f664406fc5be9134372d9959b4a3fa626..0ab0296f647e3339167f4c1d5693af24ad160c58 100644
+index 1df2115be38c8e4c71635581f486c0efb0a48d2d..6a8e39d7b3504b05b463ba218b038455ff52bf2a 100644
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
-@@ -195,6 +195,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
+@@ -196,6 +196,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
aLoadState->GetTextDirectiveUserActivation() ||
aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_FROM_EXTERNAL));
loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
@@ -1841,10 +1841,10 @@ index 430646881b927d2dddda1e0bcf5fd3427580224f..6b5bbb2a411794e9275d1ab83ea02d4c
* 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 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb365eb2ba3f 100644
+index c6d926448e399e0cb3032180efad3cddaf785fc0..7148ba816bf0dab3224323736001237a99d3b2ba 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
-@@ -941,11 +941,9 @@ nsresult nsHttpChannel::OnBeforeConnect() {
+@@ -942,11 +942,9 @@ nsresult nsHttpChannel::OnBeforeConnect() {
// SecurityInfo.sys.mjs
mLoadInfo->SetHstsStatus(isSecureURI);
@@ -1857,7 +1857,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
return NS_ERROR_OFFLINE;
}
-@@ -1058,9 +1056,7 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
+@@ -1066,9 +1064,7 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
return aStatus;
}
@@ -1868,7 +1868,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC() ||
forceOffline) {
-@@ -1523,15 +1519,14 @@ nsresult nsHttpChannel::ContinueConnect() {
+@@ -1531,15 +1527,14 @@ nsresult nsHttpChannel::ContinueConnect() {
"CORS preflight must have been finished by the time we "
"do the rest of ContinueConnect");
@@ -1886,7 +1886,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
return NS_ERROR_OFFLINE;
}
-@@ -1573,7 +1568,7 @@ nsresult nsHttpChannel::ContinueConnect() {
+@@ -1581,7 +1576,7 @@ nsresult nsHttpChannel::ContinueConnect() {
}
// We're about to hit the network. Don't if we're forced offline.
@@ -1895,7 +1895,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
return NS_ERROR_OFFLINE;
}
-@@ -1681,12 +1676,9 @@ void nsHttpChannel::SpeculativeConnect() {
+@@ -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).
@@ -1909,7 +1909,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
return;
}
-@@ -4962,7 +4954,7 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) {
+@@ -5007,7 +4999,7 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) {
return NS_OK;
}
@@ -1918,7 +1918,7 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
if (offline || (mLoadFlags & INHIBIT_CACHING) || forceOffline) {
if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()) &&
!offline && !forceOffline) {
-@@ -8196,6 +8188,20 @@ void nsHttpChannel::MaybeStartDNSPrefetch() {
+@@ -8306,6 +8298,20 @@ void nsHttpChannel::MaybeStartDNSPrefetch() {
}
}
@@ -1940,10 +1940,10 @@ index 42ee24d0b93641cae705d8709467141125a3f7ea..276a10796fd2f647fa0e469e2f1ceb36
nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) {
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
-index 4576bdef9b5667a54f88600bce04753bb6152b85..c023312fd12f3f390c0e7773ae4f5625e78642ac 100644
+index 602a3def6eab873986afc17ec92073eb476a43fb..4b267ff4859ebc3eecb169b7f793e60ac8ba1728 100644
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
-@@ -305,6 +305,10 @@ class nsHttpChannel final : public HttpBaseChannel,
+@@ -306,6 +306,10 @@ class nsHttpChannel final : public HttpBaseChannel,
void MaybeResolveProxyAndBeginConnect();
void MaybeStartDNSPrefetch();
@@ -1955,10 +1955,10 @@ index 4576bdef9b5667a54f88600bce04753bb6152b85..c023312fd12f3f390c0e7773ae4f5625
// end server host name.
nsIHttpChannelInternal::ProxyDNSStrategy ComputeProxyDNSStrategy();
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
-index 9d085e0a5f75ef4c87f4e443ffb2d271dba82610..928862ea7641b1cb23dcc120597992e228239d19 100644
+index ed63fe936c1c0fa38329c19c7adada56fd756f2d..b492aa20dd7ded36472b3816ea181d9a991b8d6f 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
-@@ -1448,6 +1448,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
+@@ -1449,6 +1449,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -1983,7 +1983,7 @@ index 068d9702fd015c974d698d2c8c741b5d2b0d8316..9ff49d6de9d712b7e5ab9711da7f2226
nsCOMPtr marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
if (marionette) {
diff --git a/services/settings/Utils.sys.mjs b/services/settings/Utils.sys.mjs
-index da12049e14b38adcbf074f87ecc5a5745cd9a2eb..24621137e2c8c8fac881ce4320266441b253ab3d 100644
+index 76f69ab74eba9f704b95a7e3c7b27c39a887bf5a..cf0b9223baed50bff6a08497fe571ca153efaeb0 100644
--- a/services/settings/Utils.sys.mjs
+++ b/services/settings/Utils.sys.mjs
@@ -99,7 +99,7 @@ const _cdnURLs = {};
@@ -1998,11 +1998,11 @@ index da12049e14b38adcbf074f87ecc5a5745cd9a2eb..24621137e2c8c8fac881ce4320266441
@@ -113,6 +113,9 @@ export var Utils = {
log,
- get shouldSkipRemoteActivityDueToTests() {
+ get shouldSkipRemoteActivity() {
+ // Playwright does not set Cu.isInAutomation, hence we just return true
+ // here in order to disable the remote activity.
+ return true;
- return (
+ 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
@@ -2020,7 +2020,7 @@ index a665bd039d49aeeb6896e224080a7cc00b0eacbc..099c3b08d7de697cc8edcd5bb1d8000f
// 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 0d081a03a92d4bcf8642aabeabaea3e87008665f..647db4d2f61ee7b6a45de30937932a98d9eae48d 100644
+index 217d51f4c765e0aac2107246f7142099d21d984e..1b623f956c178904945cddeceec9f737a01c053f 100644
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
@@ -115,7 +115,9 @@ EnterprisePoliciesManager.prototype = {
@@ -2034,7 +2034,7 @@ index 0d081a03a92d4bcf8642aabeabaea3e87008665f..647db4d2f61ee7b6a45de30937932a98
if (provider.failed) {
this.status = Ci.nsIEnterprisePolicies.FAILED;
-@@ -687,6 +689,19 @@ class JSONPoliciesProvider {
+@@ -736,6 +738,19 @@ class JSONPoliciesProvider {
}
}
@@ -2055,10 +2055,10 @@ index 0d081a03a92d4bcf8642aabeabaea3e87008665f..647db4d2f61ee7b6a45de30937932a98
constructor() {
this._policies = null;
diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp
-index 772232ea098016e010e7d9ff04754216f91666bf..75a959d89463957fd4da7bee2c0ae42e16ca7c90 100644
+index d0f9c88dcdaca07c6e10139b50b092bf80fc8c3e..f6d1a5ff8f241abdd931f47508d0fffaadce0391 100644
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
-@@ -359,7 +359,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
+@@ -377,7 +377,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
nsCOMPtr windowEnumerator;
nsCOMPtr mediator(
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
@@ -2083,12 +2083,12 @@ index efe8ff5541915c0fc632e75572d1e3968c60139d..e0157515ded811e47d21705f13f475d6
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
-index 387727f68ca95bd1da6788b909da69881aa949f3..9925947e126e3186616a0d00e6f118b612f48cb4 100644
+index d5ebdf4413568c89e93d89279510ae910bcdf9fd..a1dbe7916e39fd6bc0292d6262787d39a5c8015d 100644
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
-@@ -1922,7 +1922,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
-
- // Open a minimal popup.
+@@ -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;
@@ -2160,10 +2160,10 @@ index b7c376eadc1460b0f4e378ca3047eb82a079c19c..44385776349e13f7452d7c1b382cf738
// 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 059ef152a4c7146ed01ff8c3601584feed23cad4..772c7428afa6ebea5dae97a62daa0fb0f0152609 100644
+index 524451a83e03f8a9a83103b1f3d87850ad411515..a44b3c5da20f6a0bf9c892d4289a07ac698fe56d 100644
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
-@@ -884,6 +884,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout,
+@@ -886,6 +886,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout,
mIsLoadingJavascriptURI ? "javascript URI"
: "document.open"));
@@ -2177,10 +2177,10 @@ index 059ef152a4c7146ed01ff8c3601584feed23cad4..772c7428afa6ebea5dae97a62daa0fb0
// 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 bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e9eb3ed7a 100644
+index 1eab401455890aafc73f1da9080415fc035f1c30..d7ce1bccb117cac5d95181f6ab0ca7c3e7e62808 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
-@@ -110,6 +110,7 @@
+@@ -111,6 +111,7 @@
#include "mozilla/Components.h"
#include "mozilla/ClearOnShutdown.h"
@@ -2188,7 +2188,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
#include "mozilla/Preferences.h"
#include "mozilla/ipc/URIUtils.h"
-@@ -884,6 +885,12 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(
+@@ -879,6 +880,12 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(
return NS_OK;
}
@@ -2201,7 +2201,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
nsresult nsExternalHelperAppService::GetFileTokenForPath(
const char16_t* aPlatformAppPath, nsIFile** aFile) {
nsDependentString platformAppPath(aPlatformAppPath);
-@@ -1517,7 +1524,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) {
+@@ -1514,7 +1521,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) {
// Strip off the ".part" from mTempLeafName
mTempLeafName.Truncate(mTempLeafName.Length() - std::size(".part") + 1);
@@ -2214,7 +2214,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
mSaver =
do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
-@@ -1701,7 +1713,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
+@@ -1698,7 +1710,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
return NS_OK;
}
@@ -2252,7 +2252,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
if (NS_FAILED(rv)) {
nsresult transferError = rv;
-@@ -1763,6 +1804,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
+@@ -1760,6 +1801,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
bool alwaysAsk = true;
mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
@@ -2262,7 +2262,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
if (alwaysAsk) {
// But we *don't* ask if this mimeInfo didn't come from
// our user configuration datastore and the user has said
-@@ -2279,6 +2323,15 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
+@@ -2276,6 +2320,15 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
NotifyTransfer(aStatus);
}
@@ -2278,7 +2278,7 @@ index bad0ab78783560b0f3871ddd89c78f315fa13087..f126f30d3940f4b911e3c69ad466933e
return NS_OK;
}
-@@ -2764,6 +2817,14 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
+@@ -2761,6 +2814,14 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
}
}
@@ -2588,7 +2588,7 @@ index 34ef4bf32bc4c348bd226f1c3ea5a4f03ad4fac1..394e892cdf35c351b9d8536dcb82c1a1
default:
aResult = 0;
diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp
-index 867b4a9fd9926c5b560057656fd0641b1e5d198c..0d239412f9802f3ece3b5f85aedca3ce4b2a661b 100644
+index 0841e299ea23692310db27acfa78f7e2050bb002..3285e16397b14400127c81c0d1a13711e536b073 100644
--- a/widget/headless/HeadlessWidget.cpp
+++ b/widget/headless/HeadlessWidget.cpp
@@ -112,6 +112,8 @@ void HeadlessWidget::Destroy() {
@@ -2600,7 +2600,7 @@ index 867b4a9fd9926c5b560057656fd0641b1e5d198c..0d239412f9802f3ece3b5f85aedca3ce
nsIWidget::OnDestroy();
nsIWidget::Destroy();
-@@ -572,5 +574,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchpadPan(
+@@ -573,5 +575,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchpadPan(
return NS_OK;
}
@@ -2616,7 +2616,7 @@ index 867b4a9fd9926c5b560057656fd0641b1e5d198c..0d239412f9802f3ece3b5f85aedca3ce
} // namespace widget
} // namespace mozilla
diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h
-index ca31e584b77cee280dc61ce15ffaf13380ba17c8..1dff8fd40c2d7debe348c7c82309aeee394acb7c 100644
+index 9daa3334e7ec6aee433ee7a28e6b86a8548f6226..0aa21a1b0a3b07548d832bfb41f73fe37a174fdc 100644
--- a/widget/headless/HeadlessWidget.h
+++ b/widget/headless/HeadlessWidget.h
@@ -127,6 +127,9 @@ class HeadlessWidget final : public nsIWidget {
diff --git a/packages/isomorphic/deviceDescriptorsSource.json b/packages/isomorphic/deviceDescriptorsSource.json
index 6204363f4f1f5..4abd4446388b5 100644
--- a/packages/isomorphic/deviceDescriptorsSource.json
+++ b/packages/isomorphic/deviceDescriptorsSource.json
@@ -2662,7 +2662,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Firefox HiDPI": {
- "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0.4) Gecko/20100101 Firefox/152.0.4",
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:153.0) Gecko/20100101 Firefox/153.0",
"screen": {
"width": 1792,
"height": 1120
@@ -2722,7 +2722,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Firefox": {
- "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0.4) Gecko/20100101 Firefox/152.0.4",
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:153.0) Gecko/20100101 Firefox/153.0",
"screen": {
"width": 1920,
"height": 1080
diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json
index 35d97216c9873..25e42eae2b73a 100644
--- a/packages/playwright-core/browsers.json
+++ b/packages/playwright-core/browsers.json
@@ -31,9 +31,9 @@
},
{
"name": "firefox",
- "revision": "1536",
+ "revision": "1538",
"installByDefault": true,
- "browserVersion": "152.0.4",
+ "browserVersion": "153.0",
"title": "Firefox"
},
{
diff --git a/packages/playwright-core/src/server/firefox/ffBrowser.ts b/packages/playwright-core/src/server/firefox/ffBrowser.ts
index 5a11de9cba630..fa1e1b65768b7 100644
--- a/packages/playwright-core/src/server/firefox/ffBrowser.ts
+++ b/packages/playwright-core/src/server/firefox/ffBrowser.ts
@@ -278,18 +278,19 @@ export class FFBrowserContext extends BrowserContext {
}
async doGrantPermissions(origin: string, permissions: string[]) {
- const webPermissionToProtocol = new Map([
- ['geolocation', 'geo'],
- ['persistent-storage', 'persistent-storage'],
- ['push', 'push'],
- ['notifications', 'desktop-notification'],
- ['screen-wake-lock', 'screen-wake-lock'],
+ const webPermissionToProtocol = new Map([
+ ['geolocation', ['geo']],
+ ['persistent-storage', ['persistent-storage']],
+ ['push', ['push']],
+ ['notifications', ['desktop-notification']],
+ ['screen-wake-lock', ['screen-wake-lock']],
+ ['local-network-access', ['local-network', 'loopback-network']],
]);
- const filtered = permissions.map(permission => {
- const protocolPermission = webPermissionToProtocol.get(permission);
- if (!protocolPermission)
+ const filtered = permissions.flatMap(permission => {
+ const protocolPermissions = webPermissionToProtocol.get(permission);
+ if (!protocolPermissions)
throw new Error('Unknown permission: ' + permission);
- return protocolPermission;
+ return protocolPermissions;
});
await this._browser.session.send('Browser.grantPermissions', { origin: origin, browserContextId: this._browserContextId, permissions: filtered });
}
diff --git a/tests/library/browsercontext-locale.spec.ts b/tests/library/browsercontext-locale.spec.ts
index 74945bf7f1d6a..ef6b65ca7e8c2 100644
--- a/tests/library/browsercontext-locale.spec.ts
+++ b/tests/library/browsercontext-locale.spec.ts
@@ -165,8 +165,7 @@ it('should not change default locale in another context', async ({ browser }) =>
}
});
-it('should propagate locale to workers', async ({ browser, browserName, isBidi, server }) => {
- it.fail(browserName === 'firefox' && !isBidi, 'https://github.com/microsoft/playwright/issues/38919');
+it('should propagate locale to workers', async ({ browser, browserName, server }) => {
const context = await browser.newContext({ locale: 'ru-RU' });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
diff --git a/tests/library/browsercontext-timezone-id.spec.ts b/tests/library/browsercontext-timezone-id.spec.ts
index 2091dfc97e72c..6f3534357a24d 100644
--- a/tests/library/browsercontext-timezone-id.spec.ts
+++ b/tests/library/browsercontext-timezone-id.spec.ts
@@ -118,8 +118,7 @@ it('should affect Intl.DateTimeFormat().resolvedOptions().timeZone', async ({ br
await context.close();
});
-it('should propagate timezone to workers', async ({ browser, browserName, isBidi, server }) => {
- it.fail(browserName === 'firefox' && !isBidi, 'https://github.com/microsoft/playwright/issues/38919');
+it('should propagate timezone to workers', async ({ browser, server }) => {
const context = await browser.newContext({ timezoneId: 'America/Jamaica' });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
diff --git a/tests/library/permissions.spec.ts b/tests/library/permissions.spec.ts
index e880797b53452..d66340f46e85d 100644
--- a/tests/library/permissions.spec.ts
+++ b/tests/library/permissions.spec.ts
@@ -270,7 +270,7 @@ it('local network request is allowed from public origin', {
it.skip(browserName === 'webkit');
it.skip(browserName === 'chromium' && browserMajorVersion < 145, 'local-network-access permission support has changed between versions');
- if (browserName === 'chromium')
+ if (browserName === 'chromium' || browserName === 'firefox')
await context.grantPermissions(['local-network-access']);
const serverRequests = [];
server.setRoute('/cors', (req, res) => {
From 0edafe4baab7fd20122939d3cfe1acabd8ed84a9 Mon Sep 17 00:00:00 2001
From: Devin Rousso
Date: Wed, 22 Jul 2026 15:03:44 -0600
Subject: [PATCH 2/3] fix(mcp): launch the Chrome profile that has the
extension installed (#41939)
extension mode validated that the playwright extension was installed in any of Chrome's profiles under the user data directory, but then launched without `--profile-directory`, meaning it could open a different profile than the one that was checked
when the opened profile lacks the extension, `connect.html` can't load and the connection silently hangs for ~120s
resolve the profile that actually has the extension (preferring the last-used profile from Local State so we don't switch away from the user's session unnecessarily) and pass it as `--profile-directory`
fixes
---
.../playwright-core/src/tools/mcp/cdpRelay.ts | 20 ++++++---
.../src/tools/mcp/extensionContextFactory.ts | 5 ++-
.../src/tools/utils/extension.ts | 45 ++++++++++++++-----
tests/extension/extension.spec.ts | 26 +++++++++++
4 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/packages/playwright-core/src/tools/mcp/cdpRelay.ts b/packages/playwright-core/src/tools/mcp/cdpRelay.ts
index 60915e7e64b6f..b1a654131afe1 100644
--- a/packages/playwright-core/src/tools/mcp/cdpRelay.ts
+++ b/packages/playwright-core/src/tools/mcp/cdpRelay.ts
@@ -34,7 +34,7 @@ import ws, { WebSocketServer as wsServer } from 'ws';
import { ManualPromise } from '@isomorphic/manualPromise';
import { registry } from '../../server/registry/index';
-import { playwrightExtensionId } from '../utils/extension';
+import { findPlaywrightExtensionProfile, playwrightExtensionId } from '../utils/extension';
import { addressToString } from '../utils/mcp/http';
import { logUnhandledError } from './log';
import { ExtensionProtocolV2 } from './cdpRelayV2';
@@ -61,6 +61,7 @@ export class CDPRelayServer {
private _wsHost: string;
private _browserChannel: string;
private _executablePath?: string;
+ private _userDataDir?: string;
private _cdpPath: string;
private _extensionPath: string;
private _wss: WebSocketServer;
@@ -70,10 +71,11 @@ export class CDPRelayServer {
private _handler: ExtensionProtocolV2;
private _extensionConnectionPromise = new ManualPromise();
- constructor(server: http.Server, browserChannel: string, executablePath?: string) {
+ constructor(server: http.Server, browserChannel: string, executablePath?: string, userDataDir?: string) {
this._wsHost = addressToString(server.address(), { protocol: 'ws' });
this._browserChannel = browserChannel;
this._executablePath = executablePath;
+ this._userDataDir = userDataDir;
this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? protocol.VERSION.toString(), 10);
const sendCommand = (method: string, params: any): Promise => {
@@ -102,14 +104,14 @@ export class CDPRelayServer {
async establishExtensionConnection(clientName: string) {
debugLogger('Establishing extension connection');
- this._openConnectPageInBrowser(clientName);
+ await this._openConnectPageInBrowser(clientName);
debugLogger('Waiting for incoming extension connection');
await this._extensionConnectionPromise;
await this._handler.ready();
debugLogger('Extension connection established');
}
- private _openConnectPageInBrowser(clientName: string) {
+ private async _openConnectPageInBrowser(clientName: string) {
const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`;
const url = new URL(`chrome-extension://${playwrightExtensionId}/connect.html`);
url.searchParams.set('mcpRelayUrl', mcpRelayEndpoint);
@@ -137,9 +139,13 @@ export class CDPRelayServer {
}
const args: string[] = [];
- const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR;
- if (userDataDir)
- args.push(`--user-data-dir=${userDataDir}`);
+ const testUserDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR;
+ if (testUserDataDir)
+ args.push(`--user-data-dir=${testUserDataDir}`);
+ const userDataDir = testUserDataDir ?? this._userDataDir;
+ const profileDirectory = userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : undefined;
+ if (profileDirectory)
+ args.push(`--profile-directory=${profileDirectory}`);
if (os.platform() === 'linux' && channel === 'chromium')
args.push('--no-sandbox');
args.push(href);
diff --git a/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts b/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts
index ae2f2bedafe6d..16d8a6ae6c8b2 100644
--- a/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts
+++ b/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts
@@ -27,15 +27,16 @@ const debugLogger = debug('pw:mcp:relay');
export async function createExtensionBrowser(channel: string, executablePath: string | undefined, clientName: string): Promise {
// Custom executablePath may target a browser in a different filesystem (e.g. Windows chrome.exe from WSL2), so the local profile path is not meaningful.
+ let userDataDir: string | undefined;
if (!executablePath) {
- const userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel);
+ userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel);
if (userDataDir && !await isPlaywrightExtensionInstalled(userDataDir))
throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}`);
}
const httpServer = createHttpServer();
await startHttpServer(httpServer, {});
- const relay = new CDPRelayServer(httpServer, channel, executablePath);
+ const relay = new CDPRelayServer(httpServer, channel, executablePath, userDataDir);
debugLogger(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`);
try {
diff --git a/packages/playwright-core/src/tools/utils/extension.ts b/packages/playwright-core/src/tools/utils/extension.ts
index 1f8901ea65472..94a459766cffe 100644
--- a/packages/playwright-core/src/tools/utils/extension.ts
+++ b/packages/playwright-core/src/tools/utils/extension.ts
@@ -22,22 +22,47 @@ export const playwrightExtensionId = 'mmlmfjhmonkocbjadbfplnigmagldckm';
export const playwrightExtensionInstallUrl = `https://chromewebstore.google.com/detail/playwright-extension/${playwrightExtensionId}`;
-export async function isPlaywrightExtensionInstalled(userDataDir: string): Promise {
- // Chrome stores profiles as `Default` and `Profile ` subdirs of the user data dir;
- // the extension may be installed into any of them.
+export async function findPlaywrightExtensionProfile(userDataDir: string): Promise {
+ const profiles = await listProfileDirectories(userDataDir);
+ const lastUsed = await readLastUsedProfile(userDataDir);
+ const ordered = lastUsed && profiles.includes(lastUsed)
+ ? [lastUsed, ...profiles.filter(profile => profile !== lastUsed)]
+ : profiles;
+ for (const profile of ordered) {
+ if (await isExtensionInstalledInProfile(path.join(userDataDir, profile)))
+ return profile;
+ }
+ return undefined;
+}
+
+async function listProfileDirectories(userDataDir: string): Promise {
let entries: string[];
try {
entries = await fs.promises.readdir(userDataDir);
} catch {
- return false;
+ return [];
}
- for (const entry of entries) {
- if (entry !== 'Default' && !entry.startsWith('Profile '))
- continue;
- if (await isExtensionInstalledInProfile(path.join(userDataDir, entry)))
- return true;
+ const profiles = entries.filter(entry => entry === 'Default' || /^Profile \d+$/.test(entry));
+ profiles.sort((a, b) => profileRank(a) - profileRank(b));
+ return profiles;
+}
+
+function profileRank(profile: string): number {
+ return profile === 'Default' ? -1 : parseInt(profile.slice('Profile '.length), 10);
+}
+
+async function readLastUsedProfile(userDataDir: string): Promise {
+ try {
+ const localState = JSON.parse(await fs.promises.readFile(path.join(userDataDir, 'Local State'), 'utf-8'));
+ const lastUsed = localState?.profile?.last_used;
+ return typeof lastUsed === 'string' ? lastUsed : undefined;
+ } catch {
+ return undefined;
}
- return false;
+}
+
+export async function isPlaywrightExtensionInstalled(userDataDir: string): Promise {
+ return await findPlaywrightExtensionProfile(userDataDir) !== undefined;
}
async function isExtensionInstalledInProfile(profileDir: string): Promise {
diff --git a/tests/extension/extension.spec.ts b/tests/extension/extension.spec.ts
index 68c5c50b0af30..1f3c7b4b88f18 100644
--- a/tests/extension/extension.spec.ts
+++ b/tests/extension/extension.spec.ts
@@ -15,6 +15,7 @@
*/
import fs from 'fs/promises';
+import path from 'path';
import { test, testWithOldExtensionVersion, expect, extensionId, clickAllowAndSelect, connectAndNavigate, startWithExtensionFlag } from './extension-fixtures';
import { utils } from '../../packages/playwright-core/lib/coreBundle';
@@ -248,6 +249,31 @@ test(`custom executablePath skips local extension check`, {
}).toPass();
});
+test(`launches the profile that has the extension`, {
+ annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41916' },
+}, async ({ startClient, server }, testInfo) => {
+ // The extension lives in a non-default profile only; the launch must target that profile via
+ // `--profile-directory`, otherwise Chrome opens the default profile without the extension and the
+ // connection hangs. A fake executable records the launch arguments, so no real browser is needed.
+ const userDataDir = testInfo.outputPath('multi-profile');
+ await fs.mkdir(path.join(userDataDir, 'Default'), { recursive: true });
+ await fs.mkdir(path.join(userDataDir, 'Profile 1', 'Extensions', extensionId), { recursive: true });
+
+ const executablePath = testInfo.outputPath('echo.sh');
+ await fs.writeFile(executablePath, '#!/bin/bash\necho "Custom exec args: $@" > "$(dirname "$0")/output.txt"', { mode: 0o755 });
+
+ const { client } = await startClient({
+ args: [`--extension`, `--executable-path=${executablePath}`],
+ env: { PWTEST_EXTENSION_USER_DATA_DIR: userDataDir },
+ });
+
+ client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD } }).catch(() => {});
+ await expect(async () => {
+ const output = await fs.readFile(testInfo.outputPath('output.txt'), 'utf8');
+ expect(output).toContain(`--profile-directory=Profile 1`);
+ }).toPass();
+});
+
test(`fails when extension is missing in custom userDataDir`, async ({ startClient, server }) => {
const userDataDir = test.info().outputPath('empty-profile');
From f5fa967ff581f4263e2b1eec60d887b8c412fd52 Mon Sep 17 00:00:00 2001
From: tsushanth <78000697+tsushanth@users.noreply.github.com>
Date: Wed, 22 Jul 2026 14:51:01 -0700
Subject: [PATCH 3/3] fix(network): request.postData() returns null for empty
string body override (#41882)
---
packages/playwright-core/src/client/network.ts | 2 +-
tests/page/page-request-continue.spec.ts | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts
index 4d36f5dd0a8b1..82286526e8c9f 100644
--- a/packages/playwright-core/src/client/network.ts
+++ b/packages/playwright-core/src/client/network.ts
@@ -137,7 +137,7 @@ export class Request extends ChannelOwner implements ap
}
postData(): string | null {
- return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString('utf-8') || null;
+ return (this._fallbackOverrides.postDataBuffer ?? this._initializer.postData)?.toString('utf-8') ?? null;
}
postDataBuffer(): Buffer | null {
diff --git a/tests/page/page-request-continue.spec.ts b/tests/page/page-request-continue.spec.ts
index 48f9458012b46..a6b4137912db5 100644
--- a/tests/page/page-request-continue.spec.ts
+++ b/tests/page/page-request-continue.spec.ts
@@ -1010,3 +1010,15 @@ it('should not forward Host header on cross-origin redirect', {
expect(firstHost).toBe(new URL(server.PREFIX).host);
expect(redirectedHost).toBe(new URL(server.CROSS_PROCESS_PREFIX).host);
});
+
+it('postData should return empty string when overriding body with empty string', async ({ page, server }) => {
+ await page.goto(server.EMPTY_PAGE);
+ await page.route('**/*', route => {
+ void route.continue({ postData: '' });
+ });
+ const [request] = await Promise.all([
+ page.waitForRequest('**'),
+ page.evaluate(({ url }) => fetch(url, { method: 'POST', body: 'original' }), { url: server.PREFIX + '/sleep.zzz' }),
+ ]);
+ expect(request.postData()).toBe('');
+});