Compare commits

..

10 Commits

Author SHA1 Message Date
Michaela Laurencin
7f7a20e3c9 remove decorateURL from default_app 2026-04-09 11:25:21 -04:00
Shelley Vohr
7245c6a3f0 ci: re-check signed commits on every PR synchronize (#50811)
The needs-signed-commits label was previously added by the lightweight
synchronize workflow but only removed by a job in build.yml gated on
`gha-done`, which requires every macOS/Linux/Windows build to finish
green. That made label removal both slow (waits on the full pipeline)
and fragile (any unrelated build failure leaves the label pinned even
after commits are properly signed).

Drop the `if` guard on the synchronize job so it re-evaluates signing
on every push, and add a removal step that runs on success when the
label is present. Force-pushing signed commits now clears the label as
soon as the check completes, with no dependency on the build pipeline.
2026-04-09 11:02:01 -04:00
Charles Kerr
b484b0bde9 fix: fix inset and stop using gfx::ToFlooredRectDeprecated() (#50809)
fix: fix inset and stop using ToFlooredRectDeprecated()
2026-04-09 09:55:11 -05:00
Charles Kerr
6c8a910232 refactor: remove unnecessary raw_ptr SavePageHandler::web_contents_ (#50810)
refactor: remove unnecessary field raw_ptr<content::WebContents> SavePageHandler::web_contents_
2026-04-09 09:54:44 -05:00
Noah Gregory
cc3d4f5f58 fix: PDF support when site isolation trials disabled (#50689)
* fix: use proper OOPIF PDF check in `StreamsPrivateAPI`

* fix: add `ShouldEnableSubframeZoom` override to `ElectronBrowserClient` for upstream parity

* fix: add `MaybeOverrideLocalURLCrossOriginEmbedderPolicy` override to `ElectronBrowserClient` for upstream parity

* fix: add `DoesSiteRequireDedicatedProcess` override to `ElectronBrowserClient` for upstream parity

* style: move `DoesSiteRequireDedicatedProcess` to correct override section
2026-04-09 15:35:26 +02:00
Shelley Vohr
b711ce7b04 chore: remove window enlargement revert patch (#50612)
* chore: remove window enlargement revert patch

Chromium removed the `window_enlargement_` system from
DesktopWindowTreeHostWin (1771dbae), which was a workaround for an AMD
driver bug from 2013 (crbug.com/286609) where translucent HWNDs smaller
than 64x64 caused graphical glitches. Chromium confirmed this is no
longer needed and shipped the removal.

This removes the revert patch and all Electron-side code that depended
on the `kEnableTransparentHwndEnlargement` feature flag, including the
`GetExpandedWindowSize` helper and max size constraint expansion in
`NativeWindow::GetContentMaximumSize`.

* test: remove obsolete <64x64 transparent window test

The test was added in 2018 (#12904) to verify the AMD driver
workaround that artificially enlarged translucent HWNDs smaller than
64x64 (crbug.com/286609). The workaround set the real HWND to 64x64
and subtracted a stored window_enlargement_ from every client/window
bounds query, so getContentSize() reported the originally-requested
size even though the actual HWND was larger.

With both the Chromium window_enlargement_ system and Electron's
GetExpandedWindowSize gone, setContentSize on a transparent
thickFrame window calls SetWindowPos directly. WS_THICKFRAME windows
are subject to DefWindowProc's MINMAXINFO.ptMinTrackSize clamp on
programmatic resizes (Chromium's OnGetMinMaxInfo ends with
SetMsgHandled(FALSE), so DefWindowProc overwrites the zeroed
min-track with system defaults), which on Windows Server 2025
floors at 32x39 — hence the failing [32, 39] vs [30, 30].

The removed feature_list.cc comment explicitly flagged this test as
the blocker for retiring kEnableTransparentHwndEnlargement, so
delete it alongside the workaround it was validating.
2026-04-09 15:34:10 +02:00
Alexey
adf9a6e303 fix: restore std::deque for dynamic crash key storage (#50795)
#47171 migrated `std::deque` to `base::circular_deque` in
`shell/common/crash_keys.cc`. However, `CrashKeyString` wraps a
`crashpad::Annotation` that holds self-referential pointers and
registers itself in a process-global linked list. `circular_deque`
relocates elements on growth (via `VectorBuffer::MoveConstructRange`),
leaving those pointers dangling — causing missing crash keys or a hung
crashpad handler (especially on macOS). The `base/containers/README.md`
warns: "Since `base::deque` does not have stable iterators and it will
move the objects it contains, it may not be appropriate for all uses."

Reverts to `std::deque`, whose block-based layout never relocates
existing elements. Adds a regression test that registers 50 dynamic
crash keys and verifies they all survive a renderer crash.

Notes: Fixed crash keys being lost and the crash reporter hanging on
macOS when many dynamic crash keys were registered.

Made-with: Cursor
2026-04-09 10:50:32 +02:00
Calvin
6744293e96 fix: account for extraSize in aspect ratio min/max clamping on macOS (#50794)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 10:50:17 +02:00
dependabot[bot]
0d3342debf build(deps-dev): bump @xmldom/xmldom from 0.8.11 to 0.8.12 in the npm_and_yarn group across 1 directory (#50824)
build(deps-dev): bump @xmldom/xmldom

Bumps the npm_and_yarn group with 1 update in the / directory: [@xmldom/xmldom](https://github.com/xmldom/xmldom).


Updates `@xmldom/xmldom` from 0.8.11 to 0.8.12
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.8.11...0.8.12)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.8.12
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 09:54:13 +02:00
Calvin
157cdac4b9 test: use shared get_out_dir() in generate_node_headers.py (#50828)
The local get_out_dir() defaulted to 'Testing' instead of 'Default',
causing e test to fail when using a non-Testing build config. Replace
it with the canonical version from script/lib/util.py.
2026-04-09 09:52:14 +02:00
32 changed files with 200 additions and 930 deletions

View File

@@ -442,34 +442,7 @@ jobs:
contents: read
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
steps:
steps:
- name: GitHub Actions Jobs Done
run: |
echo "All GitHub Actions Jobs are done"
check-signed-commits:
name: Check signed commits in green PR
needs: gha-done
if: ${{ contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Check signed commits in PR
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
with:
comment: |
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
(`git push --force-with-lease`)
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
- name: Remove needs-signed-commits label
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --remove-label needs-signed-commits

View File

@@ -13,7 +13,6 @@ permissions: {}
jobs:
check-signed-commits:
name: Check signed commits in PR
if: ${{ !contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
runs-on: ubuntu-slim
permissions:
contents: read
@@ -23,9 +22,9 @@ jobs:
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
with:
comment: |
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
(`git push --force-with-lease`)
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
@@ -37,3 +36,11 @@ jobs:
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --add-label needs-signed-commits
- name: Remove needs-signed-commits label
if: ${{ success() && contains(github.event.pull_request.labels.*.name, 'needs-signed-commits') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --remove-label needs-signed-commits

View File

@@ -11,13 +11,6 @@ app.on('window-all-closed', () => {
app.quit();
});
function decorateURL (url: string) {
// safely add `?utm_source=default_app
const parsedUrl = new URL(url);
parsedUrl.searchParams.append('utm_source', 'default_app');
return parsedUrl.toString();
}
// Find the shortest path to the electron binary
const absoluteElectronPath = process.execPath;
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
@@ -69,7 +62,7 @@ async function createWindow (backgroundColor?: string) {
mainWindow.on('ready-to-show', () => mainWindow!.show());
mainWindow.webContents.setWindowOpenHandler(details => {
shell.openExternal(decorateURL(details.url));
shell.openExternal(details.url);
return { action: 'deny' };
});

View File

@@ -21,7 +21,7 @@
"@types/semver": "^7.5.8",
"@types/stream-json": "^1.7.8",
"@types/temp": "^0.9.4",
"@xmldom/xmldom": "^0.8.11",
"@xmldom/xmldom": "^0.8.12",
"buffer": "^6.0.3",
"chalk": "^4.1.0",
"check-for-leaks": "^1.2.1",

View File

@@ -125,7 +125,6 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
fix_win32_synchronous_spellcheck.patch
chore_grandfather_in_electron_views_and_delegates.patch
refactor_patch_electron_permissiontypes_into_blink.patch
revert_views_remove_desktopwindowtreehostwin_window_enlargement.patch
fix_add_macos_memory_query_fallback_to_avoid_crash.patch
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
feat_add_support_for_embedder_snapshot_validation.patch

View File

@@ -34,48 +34,6 @@ index 7ea6daec53a497bf867d799e041bf6ae7191ef7b..15940624940d5c629c40319f45c59282
agent_group_scheduler_compositor_task_runner =
execution_context->GetScheduler()
->ToFrameScheduler()
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
index 936f5ebe28caa993ed5de0f7de3613fa338e263f..961ac8091aa82128e1cfb8800a7efcb80d100a05 100644
--- a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
@@ -13,10 +13,12 @@
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/inspector/thread_debugger_common_impl.h"
#include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
@@ -135,6 +137,14 @@ void ThreadedWorkletMessagingProxy::Initialize(
DCHECK(csp);
LocalFrameClient* frame_client = window->GetFrame()->Client();
+ auto worklet_settings =
+ std::make_unique<WorkerSettings>(window->GetFrame()->GetSettings());
+ if (auto* web_local_frame = WebLocalFrameImpl::FromFrame(window->GetFrame())) {
+ if (auto* web_view = web_local_frame->ViewImpl()) {
+ worklet_settings->SetNodeIntegrationInWorker(
+ web_view->GetWebPreferences().node_integration_in_worker);
+ }
+ }
auto global_scope_creation_params =
std::make_unique<GlobalScopeCreationParams>(
window->Url(), mojom::blink::ScriptType::kModule, global_scope_name,
@@ -147,8 +157,7 @@ void ThreadedWorkletMessagingProxy::Initialize(
window->GetHttpsState(), worker_clients,
frame_client->CreateWorkerContentSettingsClient(),
OriginTrialContext::GetInheritedTrialFeatures(window).get(),
- base::UnguessableToken::Create(),
- std::make_unique<WorkerSettings>(window->GetFrame()->GetSettings()),
+ base::UnguessableToken::Create(), std::move(worklet_settings),
mojom::blink::V8CacheOptions::kDefault, module_responses_map,
mojo::NullRemote() /* browser_interface_broker */,
window->GetFrame()->Loader().CreateWorkerCodeCacheHost(),
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
index 45680c5f6ea0c7e89ccf43eb88f8a11e3318c02e..3fa3af62f4e7ba8186441c5e3184b1c04fe32d12 100644
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
@@ -113,56 +71,3 @@ index 45c60dd2c44b05fdd279f759069383479823c7f2..33a2a0337efb9a46293e11d0d09b3fc1
GenericFontFamilySettings generic_font_family_settings_;
};
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index b5300dea97f20d72a807543a6da0baf61d21955f..a7030c1ba6851b26c765c7b05cd26e1453866719 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -32,6 +32,7 @@
#include "third_party/blink/renderer/core/script/modulator.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
+#include "third_party/blink/renderer/core/workers/worker_settings.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
#include "third_party/blink/renderer/core/workers/worklet_module_tree_client.h"
@@ -110,6 +111,10 @@ WorkletGlobalScope::WorkletGlobalScope(
parent_cross_origin_isolated_capability_(
creation_params->cross_origin_isolated_capability),
parent_is_isolated_context_(creation_params->parent_is_isolated_context),
+ node_integration_in_worker_(
+ creation_params->worker_settings
+ ? creation_params->worker_settings->NodeIntegrationInWorker()
+ : false),
browser_interface_broker_proxy_(this) {
DCHECK((thread_type_ == ThreadType::kMainThread && frame_) ||
(thread_type_ == ThreadType::kOffMainThread && worker_thread_));
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
index c7dd62900f0de48ab992a7c99058f5b6d98212cf..47ceea11ec9db6b67cef6945d165f46c868f4ca5 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
@@ -140,6 +140,13 @@ class CORE_EXPORT WorkletGlobalScope : public WorkerOrWorkletGlobalScope {
// Returns the WorkletToken that uniquely identifies this worklet.
virtual WorkletToken GetWorkletToken() const = 0;
+ // Electron: returns whether the creator frame had the
+ // `nodeIntegrationInWorker` web preference enabled. Copied from
+ // GlobalScopeCreationParams::worker_settings at construction time so the
+ // value is readable on the worker thread without crossing back to the
+ // main thread.
+ bool NodeIntegrationInWorker() const { return node_integration_in_worker_; }
+
// Returns the ExecutionContextToken that uniquely identifies the parent
// context that created this worklet. Note that this will always be a
// LocalFrameToken.
@@ -207,6 +214,11 @@ class CORE_EXPORT WorkletGlobalScope : public WorkerOrWorkletGlobalScope {
// TODO(crbug.com/1206150): We need a spec for this capability.
const bool parent_is_isolated_context_;
+ // Electron: snapshot of the creator frame's nodeIntegrationInWorker
+ // WebPreference, copied out of GlobalScopeCreationParams::worker_settings
+ // at construction time.
+ const bool node_integration_in_worker_;
+
// This is the interface that handles generated code cache
// requests both to fetch code cache when loading resources
// and to store generated code cache to disk.

View File

@@ -8,10 +8,10 @@ such as the background turning black when maximizing the window and
dynamic background material settings not taking effect.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad8f5adaa5 100644
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..3a5833fcc018f32e86c0a95a42937fb9ac6c5a40 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -184,6 +184,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
@@ -167,6 +167,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
}
}
@@ -23,7 +23,7 @@ index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad
void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index a40bd9f25fa07a553c011cf19f155f8158f4ae5f..ae2baec731b5fcd8be97f2177d23b860d67ab8bc 100644
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..b8d1fa863fd05ebc3ab8ac5ef8c4d81361ce45fe 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -93,6 +93,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin

View File

@@ -1,285 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Kleinschmidt <jkleinsc@electronjs.org>
Date: Sat, 14 Jun 2025 16:21:07 -0400
Subject: Revert "[views] Remove DesktopWindowTreeHostWin::window_enlargement_"
This reverts commit 1771dbae6961e7bb7c22bbc6c77f84d90ef2be46.
Electron needs this patch to allow windows smaller than 64x64
on Windows. We should refactor our code so that this patch isn't
necessary.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 988866d79a5d1dbd366ebdbff0e8eb2c0c498168..5761ac48be0a64618be0a94606149dd944e46e27 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -22626,6 +22626,21 @@
]
}
],
+ "TransparentHwndEnlargement": [
+ {
+ "platforms": [
+ "windows"
+ ],
+ "experiments": [
+ {
+ "name": "DisableTransparentHwndEnlargement",
+ "disable_features": [
+ "EnableTransparentHwndEnlargement"
+ ]
+ }
+ ]
+ }
+ ],
"TransportSecurityFileWriterScheduleAndroid": [
{
"platforms": [
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc
index 47077e16870889ef8f8c8b2adf58015bd5aff7fa..ba59e6e1609e61579bf49aca095490b083d72051 100644
--- a/ui/views/views_features.cc
+++ b/ui/views/views_features.cc
@@ -30,6 +30,14 @@ BASE_FEATURE(kEnableInputProtection, base::FEATURE_DISABLED_BY_DEFAULT);
// crbug.com/370856871.
BASE_FEATURE(kEnableTouchDragCursorSync, base::FEATURE_ENABLED_BY_DEFAULT);
+// Enables enlargement of HWNDs to a minimum size of 64x64 to handle reported
+// graphical glitches on certain hardware.
+// TODO(crbug.com/401996981): Remove this once enlargement is confirmed to no
+// longer be needed.
+BASE_FEATURE(kEnableTransparentHwndEnlargement,
+ "EnableTransparentHwndEnlargement",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
// Used to enable keyboard-accessible tooltips in Views UI, as opposed
// to kKeyboardAccessibleTooltip in //ui/base/ui_base_features.cc.
BASE_FEATURE(kKeyboardAccessibleTooltipInViews,
diff --git a/ui/views/views_features.h b/ui/views/views_features.h
index 888a16fb6213eceb131ae636dc643d7f2d5bcad9..6077412165081cd4abeaf0b061feb2f795ee8131 100644
--- a/ui/views/views_features.h
+++ b/ui/views/views_features.h
@@ -15,6 +15,7 @@ namespace views::features {
VIEWS_EXPORT BASE_DECLARE_FEATURE(kApplyInitialUrlToWebContents);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableInputProtection);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTouchDragCursorSync);
+VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTransparentHwndEnlargement);
VIEWS_EXPORT BASE_DECLARE_FEATURE(kKeyboardAccessibleTooltipInViews);
} // namespace views::features
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..d1e06b675b19226cf3b78e1aada8d8f2d684fada 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -85,6 +85,23 @@ namespace {
// This constant controls how many pixels wide that border is.
const int kMouseCaptureRegionBorder = 5;
+gfx::Size GetExpandedWindowSize(bool is_translucent, gfx::Size size) {
+ if (!base::FeatureList::IsEnabled(
+ features::kEnableTransparentHwndEnlargement) ||
+ !is_translucent) {
+ return size;
+ }
+
+ // Some AMD drivers can't display windows that are less than 64x64 pixels,
+ // so expand them to be at least that size. http://crbug.com/286609
+ gfx::Size expanded(std::max(size.width(), 64), std::max(size.height(), 64));
+ return expanded;
+}
+
+void InsetBottomRight(gfx::Rect* rect, const gfx::Vector2d& vector) {
+ rect->Inset(gfx::Insets::TLBR(0, 0, vector.y(), vector.x()));
+}
+
// Updates the cursor clip region. Used for mouse locking.
void UpdateMouseLockRegion(aura::Window* window, bool locked) {
if (!locked) {
@@ -342,9 +359,14 @@ bool DesktopWindowTreeHostWin::IsVisible() const {
}
void DesktopWindowTreeHostWin::SetSize(const gfx::Size& size) {
- const gfx::Size size_in_pixels =
+ gfx::Size size_in_pixels =
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
- message_handler_->SetSize(size_in_pixels);
+ gfx::Size expanded =
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
+ window_enlargement_ =
+ gfx::Vector2d(expanded.width() - size_in_pixels.width(),
+ expanded.height() - size_in_pixels.height());
+ message_handler_->SetSize(expanded);
}
void DesktopWindowTreeHostWin::StackAbove(aura::Window* window) {
@@ -359,30 +381,40 @@ void DesktopWindowTreeHostWin::StackAtTop() {
}
void DesktopWindowTreeHostWin::CenterWindow(const gfx::Size& size) {
- const gfx::Size size_in_pixels =
+ gfx::Size size_in_pixels =
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
- message_handler_->CenterWindow(size_in_pixels);
+ gfx::Size expanded_size;
+ expanded_size =
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
+ window_enlargement_ =
+ gfx::Vector2d(expanded_size.width() - size_in_pixels.width(),
+ expanded_size.height() - size_in_pixels.height());
+ message_handler_->CenterWindow(expanded_size);
}
void DesktopWindowTreeHostWin::GetWindowPlacement(
gfx::Rect* bounds,
ui::mojom::WindowShowState* show_state) const {
message_handler_->GetWindowPlacement(bounds, show_state);
+ InsetBottomRight(bounds, window_enlargement_);
*bounds = display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), *bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetWindowBoundsInScreen() const {
gfx::Rect pixel_bounds = message_handler_->GetWindowBoundsInScreen();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() const {
gfx::Rect pixel_bounds = message_handler_->GetClientAreaBoundsInScreen();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
gfx::Rect DesktopWindowTreeHostWin::GetRestoredBounds() const {
gfx::Rect pixel_bounds = message_handler_->GetRestoredBounds();
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
}
@@ -701,37 +733,44 @@ void DesktopWindowTreeHostWin::HideImpl() {
// other get/set methods work in DIP.
gfx::Rect DesktopWindowTreeHostWin::GetBoundsInPixels() const {
- const gfx::Rect bounds_px(message_handler_->GetClientAreaBounds());
+ gfx::Rect bounds(message_handler_->GetClientAreaBounds());
// If the window bounds were expanded we need to return the original bounds
// To achieve this we do the reverse of the expansion, i.e. add the
// window_expansion_top_left_delta_ to the origin and subtract the
// window_expansion_bottom_right_delta_ from the width and height.
- const gfx::Rect without_expansion_bounds_px(
- bounds_px.x() + window_expansion_top_left_delta_.x(),
- bounds_px.y() + window_expansion_top_left_delta_.y(),
- bounds_px.width() - window_expansion_bottom_right_delta_.x(),
- bounds_px.height() - window_expansion_bottom_right_delta_.y());
- return without_expansion_bounds_px;
+ gfx::Rect without_expansion(
+ bounds.x() + window_expansion_top_left_delta_.x(),
+ bounds.y() + window_expansion_top_left_delta_.y(),
+ bounds.width() - window_expansion_bottom_right_delta_.x() -
+ window_enlargement_.x(),
+ bounds.height() - window_expansion_bottom_right_delta_.y() -
+ window_enlargement_.y());
+ return without_expansion;
}
-void DesktopWindowTreeHostWin::SetBoundsInPixels(
- const gfx::Rect& bounds_in_pixels) {
+void DesktopWindowTreeHostWin::SetBoundsInPixels(const gfx::Rect& bounds) {
// If the window bounds have to be expanded we need to subtract the
// window_expansion_top_left_delta_ from the origin and add the
// window_expansion_bottom_right_delta_ to the width and height
- const gfx::Size old_content_size_px = GetBoundsInPixels().size();
-
- const gfx::Rect expanded_bounds_px(
- bounds_in_pixels.x() - window_expansion_top_left_delta_.x(),
- bounds_in_pixels.y() - window_expansion_top_left_delta_.y(),
- bounds_in_pixels.width() + window_expansion_bottom_right_delta_.x(),
- bounds_in_pixels.height() + window_expansion_bottom_right_delta_.y());
-
- // When `expanded_bounds_px` causes the window to be moved to a display with a
+ gfx::Size old_content_size = GetBoundsInPixels().size();
+
+ gfx::Rect expanded(
+ bounds.x() - window_expansion_top_left_delta_.x(),
+ bounds.y() - window_expansion_top_left_delta_.y(),
+ bounds.width() + window_expansion_bottom_right_delta_.x(),
+ bounds.height() + window_expansion_bottom_right_delta_.y());
+
+ gfx::Rect new_expanded(
+ expanded.origin(),
+ GetExpandedWindowSize(message_handler_->is_translucent(),
+ expanded.size()));
+ window_enlargement_ =
+ gfx::Vector2d(new_expanded.width() - expanded.width(),
+ new_expanded.height() - expanded.height());
+ // When |new_expanded| causes the window to be moved to a display with a
// different DSF, HWNDMessageHandler::OnDpiChanged() will be called and the
// window size will be scaled automatically.
- message_handler_->SetBounds(expanded_bounds_px,
- old_content_size_px != bounds_in_pixels.size());
+ message_handler_->SetBounds(new_expanded, old_content_size != bounds.size());
}
gfx::Rect
@@ -943,18 +982,26 @@ int DesktopWindowTreeHostWin::GetNonClientComponent(
void DesktopWindowTreeHostWin::GetWindowMask(const gfx::Size& size_px,
SkPath* path) {
- Widget* widget = GetWidget();
- if (!widget || !widget->non_client_view()) {
- return;
- }
+ // Request the window mask for hwnd of `size_px`. The hwnd size must be
+ // adjusted by `window_enlargement` to return to the client-expected window
+ // size (see crbug.com/41047830).
+ const gfx::Size adjusted_size_in_px =
+ size_px - gfx::Size(window_enlargement_.x(), window_enlargement_.y());
- widget->non_client_view()->GetWindowMask(
- display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(), size_px), path);
- // Convert path in DIPs to pixels.
- if (!path->isEmpty()) {
- const float scale =
- display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
- *path = path->makeTransform(SkMatrix::Scale(scale, scale));
+ if (Widget* widget = GetWidget(); widget && widget->non_client_view()) {
+ widget->non_client_view()->GetWindowMask(
+ display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(),
+ adjusted_size_in_px),
+ path);
+ // Convert path in DIPs to pixels.
+ if (!path->isEmpty()) {
+ const float scale =
+ display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
+ *path = path->makeTransform(SkMatrix::Scale(scale, scale));
+ }
+ } else if (!window_enlargement_.IsZero()) {
+ *path = SkPath::Rect(SkRect::MakeXYWH(0, 0, adjusted_size_in_px.width(),
+ adjusted_size_in_px.height()));
}
}
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..a40bd9f25fa07a553c011cf19f155f8158f4ae5f 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -175,7 +175,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void ShowImpl() override;
void HideImpl() override;
gfx::Rect GetBoundsInPixels() const override;
- void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) override;
+ void SetBoundsInPixels(const gfx::Rect& bounds) override;
gfx::Rect GetBoundsInAcceleratedWidgetPixelCoordinates() override;
gfx::Point GetLocationOnScreenInPixels() const override;
void SetCapture() override;
@@ -330,6 +330,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
gfx::Vector2d window_expansion_top_left_delta_;
gfx::Vector2d window_expansion_bottom_right_delta_;
+ // Windows are enlarged to be at least 64x64 pixels, so keep track of the
+ // extra added here.
+ // TODO(crbug.com/401996981): This is likely no longer necessary and should be
+ // removed.
+ gfx::Vector2d window_enlargement_;
+
// Whether the window close should be converted to a hide, and then actually
// closed on the completion of the hide animation. This is cached because
// the property is set on the contained window which has a shorter lifetime.

View File

@@ -8,7 +8,9 @@ from pathlib import Path
SRC_DIR = Path(__file__).resolve().parents[3]
sys.path.append(os.path.join(SRC_DIR, 'third_party/electron_node/tools'))
sys.path.append(str(Path(__file__).resolve().parents[1])) # electron/script/
from lib.util import get_out_dir
import install
class LoadPythonDictionaryError(Exception):
@@ -31,13 +33,6 @@ def LoadPythonDictionary(path):
)
return file_data
def get_out_dir():
out_dir = 'Testing'
override = os.environ.get('ELECTRON_OUT_DIR')
if override is not None:
out_dir = override
return os.path.join(SRC_DIR, 'out', out_dir)
if __name__ == '__main__':
node_root_dir = os.path.join(SRC_DIR, 'third_party/electron_node')
out = {}

View File

@@ -2889,8 +2889,8 @@ v8::Local<v8::Promise> WebContents::SavePage(
return handle;
}
auto* handler = new SavePageHandler(web_contents(), std::move(promise));
handler->Handle(full_file_path, save_type);
auto* handler = new SavePageHandler{std::move(promise)};
handler->Handle(full_file_path, save_type, web_contents());
return handle;
}

View File

@@ -12,9 +12,8 @@
namespace electron::api {
SavePageHandler::SavePageHandler(content::WebContents* web_contents,
gin_helper::Promise<void> promise)
: web_contents_(web_contents), promise_(std::move(promise)) {}
SavePageHandler::SavePageHandler(gin_helper::Promise<void> promise)
: promise_{std::move(promise)} {}
SavePageHandler::~SavePageHandler() = default;
@@ -26,9 +25,10 @@ void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager,
}
bool SavePageHandler::Handle(const base::FilePath& full_path,
const content::SavePageType& save_type) {
const content::SavePageType& save_type,
content::WebContents* web_contents) {
auto* download_manager =
web_contents_->GetBrowserContext()->GetDownloadManager();
web_contents->GetBrowserContext()->GetDownloadManager();
download_manager->AddObserver(this);
// Chromium will create a 'foo_files' directory under the directory of saving
// page 'foo.html' for holding other resource files of 'foo.html'.
@@ -36,7 +36,7 @@ bool SavePageHandler::Handle(const base::FilePath& full_path,
full_path.RemoveExtension().BaseName().value() +
FILE_PATH_LITERAL("_files"));
bool result =
web_contents_->SavePage(full_path, saved_main_directory_path, save_type);
web_contents->SavePage(full_path, saved_main_directory_path, save_type);
download_manager->RemoveObserver(this);
// If initialization fails which means fail to create |DownloadItem|, we need
// to delete the |SavePageHandler| instance to avoid memory-leak.

View File

@@ -5,7 +5,6 @@
#ifndef ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
#define ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
#include "base/memory/raw_ptr.h"
#include "components/download/public/common/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/save_page_type.h"
@@ -26,12 +25,12 @@ namespace electron::api {
class SavePageHandler : private content::DownloadManager::Observer,
private download::DownloadItem::Observer {
public:
SavePageHandler(content::WebContents* web_contents,
gin_helper::Promise<void> promise);
explicit SavePageHandler(gin_helper::Promise<void> promise);
~SavePageHandler() override;
bool Handle(const base::FilePath& full_path,
const content::SavePageType& save_type);
const content::SavePageType& save_type,
content::WebContents* web_contents);
private:
void Destroy(download::DownloadItem* item);
@@ -43,7 +42,6 @@ class SavePageHandler : private content::DownloadManager::Observer,
// download::DownloadItem::Observer:
void OnDownloadUpdated(download::DownloadItem* item) override;
raw_ptr<content::WebContents> web_contents_; // weak
gin_helper::Promise<void> promise_;
};

View File

@@ -63,6 +63,7 @@
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_private_key.h"
#include "pdf/pdf_features.h"
#include "printing/buildflags/buildflags.h"
#include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
#include "services/device/public/cpp/geolocation/location_provider.h"
@@ -1591,6 +1592,46 @@ bool ElectronBrowserClient::ShouldEnableStrictSiteIsolation() {
return true;
}
bool ElectronBrowserClient::ShouldEnableSubframeZoom() {
#if BUILDFLAG(ENABLE_PDF_VIEWER)
return chrome_pdf::features::IsOopifPdfEnabled();
#else
return false;
#endif
}
#if BUILDFLAG(ENABLE_PDF_VIEWER)
std::optional<network::CrossOriginEmbedderPolicy>
ElectronBrowserClient::MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
content::NavigationHandle* navigation_handle) {
if (!chrome_pdf::features::IsOopifPdfEnabled() ||
!navigation_handle->IsPdf()) {
return std::nullopt;
}
content::RenderFrameHost* pdf_extension = navigation_handle->GetParentFrame();
if (!pdf_extension) {
return std::nullopt;
}
content::RenderFrameHost* pdf_embedder = pdf_extension->GetParent();
CHECK(pdf_embedder);
return pdf_embedder->GetCrossOriginEmbedderPolicy();
}
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
bool ElectronBrowserClient::DoesSiteRequireDedicatedProcess(
content::BrowserContext* browser_context,
const GURL& effective_site_url) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
return GetEnabledExtensionFromEffectiveURL(browser_context,
effective_site_url) != nullptr;
#else
return content::ContentBrowserClient::DoesSiteRequireDedicatedProcess(
browser_context, effective_site_url);
#endif
}
void ElectronBrowserClient::BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) {

View File

@@ -30,6 +30,7 @@ class FilePath;
namespace content {
class ClientCertificateDelegate;
class NavigationHandle;
class PlatformNotificationService;
class NavigationThrottleRegistry;
class QuotaPermissionContext;
@@ -82,6 +83,14 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
// content::ContentBrowserClient:
std::string GetApplicationLocale() override;
bool ShouldEnableStrictSiteIsolation() override;
bool ShouldEnableSubframeZoom() override;
#if BUILDFLAG(ENABLE_PDF_VIEWER)
std::optional<network::CrossOriginEmbedderPolicy>
MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
content::NavigationHandle* navigation_handle) override;
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context,
const GURL& effective_site_url) override;
void BindHostReceiverForRenderer(
content::RenderProcessHost* render_process_host,
mojo::GenericPendingReceiver receiver) override;

View File

@@ -71,7 +71,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
std::move(transferrable_loader), original_url);
#if BUILDFLAG(ENABLE_PDF_VIEWER)
if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif) &&
if (chrome_pdf::features::IsOopifPdfEnabled() &&
extension_id == extension_misc::kPdfExtensionId) {
pdf::PdfViewerStreamManager::Create(web_contents);
pdf::PdfViewerStreamManager::FromWebContents(web_contents)

View File

@@ -34,10 +34,6 @@
#include "printing/printing_features.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "ui/views/views_features.h"
#endif
namespace electron {
void InitializeFeatureList() {
@@ -71,13 +67,6 @@ void InitializeFeatureList() {
blink::features::kDropInputEventsWhilePaintHolding.name;
#if BUILDFLAG(IS_WIN)
// Refs https://issues.chromium.org/issues/401996981
// TODO(deepak1556): Remove this once test added in
// https://github.com/electron/electron/pull/12904
// can work without this feature.
enable_features += std::string(",") +
views::features::kEnableTransparentHwndEnlargement.name;
// See https://chromium-review.googlesource.com/c/chromium/src/+/7204292
// This feature causes the following sandbox failure on Windows:
// sandbox\policy\win\sandbox_win.cc:777 Sandbox cannot access executable

View File

@@ -31,11 +31,6 @@
#include "shell/browser/ui/views/frameless_view.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "ui/display/win/screen_win.h"
#include "ui/views/views_features.h"
#endif
#if defined(USE_OZONE)
#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
@@ -71,31 +66,6 @@ struct Converter<electron::NativeWindow::TitleBarStyle> {
namespace electron {
namespace {
#if BUILDFLAG(IS_WIN)
gfx::Size GetExpandedWindowSize(const NativeWindow* window,
bool transparent,
gfx::Size size) {
if (!base::FeatureList::IsEnabled(
views::features::kEnableTransparentHwndEnlargement) ||
!transparent) {
return size;
}
gfx::Size min_size = display::win::GetScreenWin()->ScreenToDIPSize(
window->GetAcceleratedWidget(), gfx::Size{64, 64});
// Some AMD drivers can't display windows that are less than 64x64 pixels,
// so expand them to be at least that size. http://crbug.com/286609
gfx::Size expanded(std::max(size.width(), min_size.width()),
std::max(size.height(), min_size.height()));
return expanded;
}
#endif
} // namespace
NativeWindow::NativeWindow(const int32_t base_window_id,
const gin_helper::Dictionary& options,
NativeWindow* parent)
@@ -398,15 +368,7 @@ gfx::Size NativeWindow::GetContentMinimumSize() const {
}
gfx::Size NativeWindow::GetContentMaximumSize() const {
const auto size_constraints = GetContentSizeConstraints();
gfx::Size maximum_size = size_constraints.GetMaximumSize();
#if BUILDFLAG(IS_WIN)
if (size_constraints.HasMaximumSize())
maximum_size = GetExpandedWindowSize(this, transparent(), maximum_size);
#endif
return maximum_size;
return GetContentSizeConstraints().GetMaximumSize();
}
void NativeWindow::SetSheetOffset(const double offsetX, const double offsetY) {

View File

@@ -158,45 +158,43 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
windowSize.width() - contentSize.width() + extraSize.width();
double extraHeightPlusFrame = titleBarHeight + extraSize.height();
newSize.width =
roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
extraWidthPlusFrame);
newSize.height =
roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
extraHeightPlusFrame);
auto widthForHeight = [&](double h) {
return (h - extraHeightPlusFrame) * aspectRatio + extraWidthPlusFrame;
};
auto heightForWidth = [&](double w) {
return (w - extraWidthPlusFrame) / aspectRatio + extraHeightPlusFrame;
};
newSize.width = roundf(widthForHeight(frameSize.height));
newSize.height = roundf(heightForWidth(newSize.width));
// Clamp to minimum width/height while ensuring aspect ratio remains.
NSSize minSize = [window minSize];
NSSize zeroSize =
shell_->has_frame() ? NSMakeSize(0, titleBarHeight) : NSZeroSize;
if (!NSEqualSizes(minSize, zeroSize)) {
double minWidthForAspectRatio =
(minSize.height - titleBarHeight) * aspectRatio;
bool atMinHeight =
minSize.height > zeroSize.height && newSize.height <= minSize.height;
newSize.width = atMinHeight ? minWidthForAspectRatio
newSize.width = atMinHeight ? widthForHeight(minSize.height)
: std::max(newSize.width, minSize.width);
double minHeightForAspectRatio = minSize.width / aspectRatio;
bool atMinWidth =
minSize.width > zeroSize.width && newSize.width <= minSize.width;
newSize.height = atMinWidth ? minHeightForAspectRatio
newSize.height = atMinWidth ? heightForWidth(minSize.width)
: std::max(newSize.height, minSize.height);
}
// Clamp to maximum width/height while ensuring aspect ratio remains.
NSSize maxSize = [window maxSize];
if (!NSEqualSizes(maxSize, NSMakeSize(FLT_MAX, FLT_MAX))) {
double maxWidthForAspectRatio = maxSize.height * aspectRatio;
bool atMaxHeight =
maxSize.height < FLT_MAX && newSize.height >= maxSize.height;
newSize.width = atMaxHeight ? maxWidthForAspectRatio
newSize.width = atMaxHeight ? widthForHeight(maxSize.height)
: std::min(newSize.width, maxSize.width);
double maxHeightForAspectRatio = maxSize.width / aspectRatio;
bool atMaxWidth =
maxSize.width < FLT_MAX && newSize.width >= maxSize.width;
newSize.height = atMaxWidth ? maxHeightForAspectRatio
newSize.height = atMaxWidth ? heightForWidth(maxSize.width)
: std::min(newSize.height, maxSize.height);
}
}

View File

@@ -106,29 +106,19 @@ int WinFrameView::NonClientHitTest(const gfx::Point& point) {
if (SUCCEEDED(DwmGetWindowAttribute(
views::HWNDForWidget(frame()), DWMWA_CAPTION_BUTTON_BOUNDS,
&button_bounds, sizeof(button_bounds)))) {
gfx::RectF button_bounds_in_dips = gfx::ConvertRectToDips(
gfx::Rect(button_bounds), display::win::GetDPIScale());
// TODO(crbug.com/1131681): GetMirroredRect() requires an integer rect,
// but the size in DIPs may not be an integer with a fractional device
// scale factor. If we want to keep using integers, the choice to use
// ToFlooredRectDeprecated() seems to be doing the wrong thing given the
// comment below about insetting 1 DIP instead of 1 physical pixel. We
// should probably use ToEnclosedRect() and then we could have inset 1
// physical pixel here.
gfx::Rect buttons =
GetMirroredRect(gfx::ToFlooredRectDeprecated(button_bounds_in_dips));
gfx::Rect button_bounds_px(button_bounds);
// There is a small one-pixel strip right above the caption buttons in
// which the resize border "peeks" through.
constexpr int kCaptionButtonTopInset = 1;
// The sizing region at the window edge above the caption buttons is
// 1 px regardless of scale factor. If we inset by 1 before converting
// to DIPs, the precision loss might eliminate this region entirely. The
// best we can do is to inset after conversion. This guarantees we'll
// show the resize cursor when resizing is possible. The cost of which
// is also maybe showing it over the portion of the DIP that isn't the
// outermost pixel.
buttons.Inset(gfx::Insets::TLBR(0, kCaptionButtonTopInset, 0, 0));
// which the resize border "peeks" through. Inset in physical pixels
// before converting to DIPs so the resize strip remains exposed at
// fractional scale factors.
button_bounds_px.Inset(gfx::Insets::TLBR(1, 0, 0, 0));
const gfx::RectF button_bounds_in_dips =
gfx::ConvertRectToDips(button_bounds_px, display::win::GetDPIScale());
// GetMirroredRect() requires an integer rect. Use ToEnclosedRect() so
// the top inset is preserved (rounded up) at fractional scale factors.
gfx::Rect buttons =
GetMirroredRect(gfx::ToEnclosedRect(button_bounds_in_dips));
if (buttons.Contains(point))
return HTNOWHERE;
}

View File

@@ -5,11 +5,11 @@
#include "shell/common/crash_keys.h"
#include <cstdint>
#include <deque>
#include <map>
#include <string>
#include "base/command_line.h"
#include "base/containers/circular_deque.h"
#include "base/environment.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
@@ -28,17 +28,22 @@ namespace electron::crash_keys {
namespace {
// Do NOT replace with base::circular_deque. CrashKeyString wraps a
// crashpad::Annotation that holds self-referential pointers and registers
// in a process-global linked list; relocating elements (as circular_deque
// does on growth) corrupts that list and hangs the crashpad handler.
// std::deque never relocates existing elements. See #50795.
auto& GetExtraCrashKeys() {
constexpr size_t kMaxCrashKeyValueSize = 20320;
static_assert(kMaxCrashKeyValueSize < crashpad::Annotation::kValueMaxSize,
"max crash key value length above what crashpad supports");
using CrashKeyString = crash_reporter::CrashKeyString<kMaxCrashKeyValueSize>;
static base::NoDestructor<base::circular_deque<CrashKeyString>> extra_keys;
static base::NoDestructor<std::deque<CrashKeyString>> extra_keys;
return *extra_keys;
}
auto& GetExtraCrashKeyNames() {
static base::NoDestructor<base::circular_deque<std::string>> crash_key_names;
static base::NoDestructor<std::deque<std::string>> crash_key_names;
return *crash_key_names;
}

View File

@@ -529,7 +529,14 @@ NodeBindings::NodeBindings(BrowserEnvironment browser_env)
uv_loop_{InitEventLoop(browser_env, &worker_loop_)} {}
NodeBindings::~NodeBindings() {
StopPolling();
// Quit the embed thread.
embed_closed_ = true;
uv_sem_post(&embed_sem_);
WakeupEmbedThread();
// Wait for everything to be done.
uv_thread_join(&embed_thread_);
// Clear uv.
uv_sem_destroy(&embed_sem_);
@@ -540,26 +547,6 @@ NodeBindings::~NodeBindings() {
stop_and_close_uv_loop(uv_loop_);
}
void NodeBindings::StopPolling() {
if (!initialized_)
return;
// Tell the embed thread to quit.
embed_closed_ = true;
// The embed thread alternates between uv_sem_wait (waiting for UvRunOnce
// to finish) and PollEvents (waiting for I/O). Wake it from both.
uv_sem_post(&embed_sem_);
WakeupEmbedThread();
// Wait for it to exit.
uv_thread_join(&embed_thread_);
// Allow PrepareEmbedThread + StartPolling to restart.
embed_closed_ = false;
initialized_ = false;
}
node::IsolateData* NodeBindings::isolate_data(
v8::Local<v8::Context> context) const {
if (context->GetNumberOfEmbedderDataFields() <=
@@ -946,21 +933,12 @@ void NodeBindings::PrepareEmbedThread() {
if (initialized_)
return;
// The async handle and semaphore live for the lifetime of this
// NodeBindings instance (destroyed in ~NodeBindings), but the embed
// thread itself may be stopped and restarted via StopPolling /
// PrepareEmbedThread for pooled worklet contexts. Only init the
// handles once.
if (!embed_thread_prepared_) {
// Add dummy handle for libuv, otherwise libuv would quit when there is
// nothing to do.
uv_async_init(uv_loop_, dummy_uv_handle_.get(), nullptr);
// Start worker that will interrupt main loop when having uv events.
uv_sem_init(&embed_sem_, 0);
embed_thread_prepared_ = true;
}
// Add dummy handle for libuv, otherwise libuv would quit when there is
// nothing to do.
uv_async_init(uv_loop_, dummy_uv_handle_.get(), nullptr);
// Start worker that will interrupt main loop when having uv events.
uv_sem_init(&embed_sem_, 0);
uv_thread_create(&embed_thread_, EmbedThreadRunner, this);
}

View File

@@ -157,12 +157,6 @@ class NodeBindings {
// Notify embed thread to start polling after environment is loaded.
void StartPolling();
// Stop the embed thread and polling without destroying handles or the loop.
// After this call, PrepareEmbedThread + StartPolling can restart them.
// Used by pooled worklets that need to pause the embed thread during
// environment teardown but reuse the same NodeBindings for the next context.
void StopPolling();
node::IsolateData* isolate_data(v8::Local<v8::Context> context) const;
// Gets/sets the environment to wrap uv loop.
@@ -231,11 +225,6 @@ class NodeBindings {
// Indicates whether polling thread has been created.
bool initialized_ = false;
// Whether PrepareEmbedThread has initialized the semaphore and async handle.
// Unlike |initialized_|, this is never reset — the handles live until the
// destructor.
bool embed_thread_prepared_ = false;
// Indicates whether the app code has finished loading
// for ESM this is async after the module is loaded
bool app_code_loaded_ = false;

View File

@@ -25,7 +25,6 @@
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
#include "third_party/blink/renderer/core/workers/worker_global_scope.h" // nogncheck
#include "third_party/blink/renderer/core/workers/worker_settings.h" // nogncheck
#include "third_party/blink/renderer/core/workers/worklet_global_scope.h" // nogncheck
namespace electron {
@@ -207,20 +206,11 @@ bool WorkerHasNodeIntegration(blink::ExecutionContext* ec) {
// owing to an inability to customize sandbox policies in these workers
// given that they're run out-of-process.
// Also avoid creating a Node.js environment for worklet global scope
// created on the main thread — those share the page's V8 context where
// Node is already wired up.
// created on the main thread.
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
ec->IsMainThreadWorkletGlobalScope())
return false;
// Off-main-thread worklets (AudioWorklet, PaintWorklet, AnimationWorklet,
// SharedStorageWorklet) have their own dedicated worker thread but do not
// derive from WorkerGlobalScope, so check for them separately and read the
// flag from WorkletGlobalScope, which copies it out of the same
// WorkerSettings as dedicated workers do.
if (auto* wlgs = blink::DynamicTo<blink::WorkletGlobalScope>(ec))
return wlgs->NodeIntegrationInWorker();
auto* wgs = blink::DynamicTo<blink::WorkerGlobalScope>(ec);
if (!wgs)
return false;
@@ -243,9 +233,9 @@ void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
return;
auto* current = WebWorkerObserver::GetCurrent();
if (!current)
current = WebWorkerObserver::Create();
current->WorkerScriptReadyForEvaluation(context);
if (current)
return;
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
}
void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(

View File

@@ -10,12 +10,11 @@
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/threading/thread_local.h"
#include "gin/converter.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/gin_helper/event_emitter_caller.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
#include "shell/common/node_util.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
namespace electron {
@@ -24,23 +23,6 @@ namespace {
static base::NoDestructor<base::ThreadLocalOwnedPointer<WebWorkerObserver>>
lazy_tls;
// Returns true if `context` belongs to a worklet that runs on a thread
// pooled by Blink's WorkletThreadHolder, where the worker thread can be
// reused for multiple worklet contexts. For these scopes the
// WebWorkerObserver and its NodeBindings must outlive the v8::Context so
// the next pooled context can reuse them — Node.js cannot be re-initialized
// on the same thread (the allocator shim only loads once). See callers of
// blink::WorkletThreadHolder in third_party/blink for the authoritative
// list.
bool IsPooledWorkletContext(v8::Local<v8::Context> context) {
auto* ec = blink::ExecutionContext::From(context);
if (!ec)
return false;
return ec->IsAudioWorkletGlobalScope() || ec->IsPaintWorkletGlobalScope() ||
ec->IsAnimationWorkletGlobalScope() ||
ec->IsSharedStorageWorkletGlobalScope();
}
} // namespace
// static
@@ -66,21 +48,6 @@ WebWorkerObserver::~WebWorkerObserver() = default;
void WebWorkerObserver::WorkerScriptReadyForEvaluation(
v8::Local<v8::Context> worker_context) {
active_context_count_++;
if (environments_.empty()) {
// First context on this thread - do full Node.js initialization.
InitializeNewEnvironment(worker_context);
} else {
// Thread is being reused (AudioWorklet thread pooling). Share the
// existing Node.js environment with the new context instead of
// reinitializing, which would break existing contexts on this thread.
ShareEnvironmentWithContext(worker_context);
}
}
void WebWorkerObserver::InitializeNewEnvironment(
v8::Local<v8::Context> worker_context) {
v8::Context::Scope context_scope(worker_context);
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
v8::MicrotasksScope microtasks_scope(
@@ -139,191 +106,26 @@ void WebWorkerObserver::InitializeNewEnvironment(
environments_.insert(std::move(env));
}
void WebWorkerObserver::ShareEnvironmentWithContext(
v8::Local<v8::Context> worker_context) {
v8::Context::Scope context_scope(worker_context);
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
v8::MicrotasksScope microtasks_scope(
worker_context, v8::MicrotasksScope::kDoNotRunMicrotasks);
// Get the existing environment from the first context on this thread.
DCHECK(!environments_.empty());
node::Environment* env = environments_.begin()->get();
// Initialize the V8 context for Node.js use.
v8::Maybe<bool> initialized = node::InitializeContext(worker_context);
CHECK(!initialized.IsNothing() && initialized.FromJust());
// Assign the existing Node.js environment to this new context so that
// node::Environment::GetCurrent(context) returns the shared environment.
env->AssignToContext(worker_context, env->principal_realm(),
node::ContextInfo("electron_worker"));
// Get process and require from the original context to make Node.js
// APIs available in the new context.
v8::Local<v8::Context> original_context = env->context();
v8::Local<v8::Object> original_global = original_context->Global();
v8::Local<v8::Object> new_global = worker_context->Global();
v8::Local<v8::Value> process_value;
CHECK(original_global
->Get(original_context, gin::StringToV8(isolate, "process"))
.ToLocal(&process_value));
v8::Local<v8::Value> require_value;
CHECK(original_global
->Get(original_context, gin::StringToV8(isolate, "require"))
.ToLocal(&require_value));
// Set up 'global' as an alias for globalThis. Node.js bootstrapping normally
// does this during LoadEnvironment, but we skip full bootstrap for shared
// contexts.
new_global
->Set(worker_context, gin::StringToV8(isolate, "global"), new_global)
.Check();
new_global
->Set(worker_context, gin::StringToV8(isolate, "process"), process_value)
.Check();
new_global
->Set(worker_context, gin::StringToV8(isolate, "require"), require_value)
.Check();
// Copy Buffer from the original context if it exists.
v8::Local<v8::Value> buffer_value;
if (original_global->Get(original_context, gin::StringToV8(isolate, "Buffer"))
.ToLocal(&buffer_value) &&
!buffer_value->IsUndefined()) {
new_global
->Set(worker_context, gin::StringToV8(isolate, "Buffer"), buffer_value)
.Check();
}
// Restore the Blink implementations of web APIs that Node.js may
// have deleted. For first-context init this is done by the node_init script
// but we can't run that for shared contexts (it calls internalBinding).
// Instead, copy the blink-prefixed values set during first init.
for (const std::string_view key :
{"fetch", "Response", "FormData", "Request", "Headers", "EventSource"}) {
// First, check if the new context has a working Blink version.
v8::MaybeLocal<v8::Value> blink_value =
new_global->Get(worker_context, gin::StringToV8(isolate, key));
if (!blink_value.IsEmpty() && !blink_value.ToLocalChecked()->IsUndefined())
continue;
// If not, copy from the original context.
std::string blink_key = base::StrCat({"blink", key});
v8::Local<v8::Value> orig_value;
if (original_global->Get(original_context, gin::StringToV8(isolate, key))
.ToLocal(&orig_value) &&
!orig_value->IsUndefined()) {
new_global->Set(worker_context, gin::StringToV8(isolate, key), orig_value)
.Check();
}
}
}
void WebWorkerObserver::ContextWillDestroy(v8::Local<v8::Context> context) {
node::Environment* env = node::Environment::GetCurrent(context);
if (!env)
return;
const bool is_pooled_worklet = IsPooledWorkletContext(context);
active_context_count_--;
if (active_context_count_ == 0) {
// Last context on this thread — full cleanup.
{
v8::Context::Scope context_scope(env->context());
// Emit the "exit" event on the process object. We avoid using
// gin_helper::EmitEvent here because it goes through
// CallMethodWithArgs, which creates a node::CallbackScope. During
// worker shutdown (PrepareForShutdownOnWorkerThread), the
// CallbackScope destructor's InternalCallbackScope::Close() tries to
// process ticks and microtask checkpoints, which can SEGV because the
// worker context is being torn down by Blink.
v8::Isolate* isolate = env->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> ctx = env->context();
v8::Local<v8::Value> emit_v;
if (env->process_object()
->Get(ctx, gin::StringToV8(isolate, "emit"))
.ToLocal(&emit_v) &&
emit_v->IsFunction()) {
v8::Local<v8::Value> args[] = {gin::StringToV8(isolate, "exit")};
v8::TryCatch try_catch(isolate);
emit_v.As<v8::Function>()
->Call(ctx, env->process_object(), 1, args)
.FromMaybe(v8::Local<v8::Value>());
// We are mid-teardown and about to destroy the worker's
// node::Environment, so we cannot let an exception thrown by an
// 'exit' listener propagate back into Blink (it would assert in
// V8::FromJustIsNothing on the next call into V8). Log it and
// explicitly reset the TryCatch so the destructor doesn't rethrow.
if (try_catch.HasCaught()) {
if (auto message = try_catch.Message(); !message.IsEmpty()) {
std::string str;
if (gin::ConvertFromV8(isolate, message->Get(), &str))
LOG(ERROR) << "Exception thrown from worker 'exit' handler: "
<< str;
}
try_catch.Reset();
}
}
}
// Prevent UvRunOnce from using the environment after it's destroyed.
node_bindings_->set_uv_env(nullptr);
// Stop the embed thread before destroying environments. The embed
// thread's PollEvents and FreeEnvironment's uv_run both compete for
// completions on the same libuv event loop; on Windows (IOCP) this
// race can deadlock. Joining the embed thread first eliminates the
// contention so FreeEnvironment's uv_run can drain handles cleanly.
// For pooled worklets the thread is restarted in
// InitializeNewEnvironment via PrepareEmbedThread + StartPolling.
node_bindings_->StopPolling();
// Destroying the node environment will also run the uv loop.
{
util::ExplicitMicrotasksScope microtasks_scope(
context->GetMicrotaskQueue());
environments_.clear();
}
// ElectronBindings is tracking node environments.
electron_bindings_->EnvironmentDestroyed(env);
// For non-pooled worker contexts (e.g., dedicated workers) Blink does
// not reuse the worker thread, so tear down the observer completely.
//
// For pooled worklet contexts (AudioWorklet, PaintWorklet,
// AnimationWorklet, SharedStorageWorklet — see
// blink::WorkletThreadHolder) the same NodeBindings must be reused
// for the next context on the thread because Node.js cannot be
// re-initialized on the same thread. Keep the observer alive and let
// the next WorkerScriptReadyForEvaluation call
// InitializeNewEnvironment, which restarts the embed thread via
// PrepareEmbedThread + StartPolling.
if (!is_pooled_worklet) {
lazy_tls->Set(nullptr); // destroys *this; do not access members below
return;
}
} else {
// Other contexts still use the shared environment. Just unassign
// this context from the environment if it's not the primary context
// (the primary context must stay assigned because env->context()
// references it, and UvRunOnce enters that context scope).
if (context != env->context()) {
env->UnassignFromContext(context);
}
// If the destroyed context IS the primary context, we leave the env
// assigned to it. The env's PrincipalRealm holds a Global<Context>
// reference that keeps the V8 context alive even though Blink has
// torn down its side. This is safe because UvRunOnce only needs
// the V8 context scope, not Blink-side objects.
if (env) {
v8::Context::Scope context_scope(env->context());
gin_helper::EmitEvent(env->isolate(), env->process_object(), "exit");
}
// Destroying the node environment will also run the uv loop.
{
util::ExplicitMicrotasksScope microtasks_scope(
context->GetMicrotaskQueue());
base::EraseIf(environments_,
[env](auto const& item) { return item.get() == env; });
}
// ElectronBindings is tracking node environments.
electron_bindings_->EnvironmentDestroyed(env);
if (lazy_tls->Get())
lazy_tls->Set(nullptr);
}
} // namespace electron

View File

@@ -40,17 +40,9 @@ class WebWorkerObserver {
void ContextWillDestroy(v8::Local<v8::Context> context);
private:
// Full initialization for the first context on a thread.
void InitializeNewEnvironment(v8::Local<v8::Context> context);
// Share existing environment with a new context on a reused thread.
void ShareEnvironmentWithContext(v8::Local<v8::Context> context);
std::unique_ptr<NodeBindings> node_bindings_;
std::unique_ptr<ElectronBindings> electron_bindings_;
base::flat_set<std::shared_ptr<node::Environment>> environments_;
// Number of active contexts using the environment on this thread.
size_t active_context_count_ = 0;
};
} // namespace electron

View File

@@ -5641,21 +5641,6 @@ describe('BrowserWindow module', () => {
expectBoundsEqual(w.getSize(), [400, 300]);
});
ifit(process.platform !== 'darwin')('works for a window smaller than 64x64', () => {
const w = new BrowserWindow({
show: false,
frame: false,
resizable: false,
transparent: true
});
w.setContentSize(60, 60);
expectBoundsEqual(w.getContentSize(), [60, 60]);
w.setContentSize(30, 30);
expectBoundsEqual(w.getContentSize(), [30, 30]);
w.setContentSize(10, 10);
expectBoundsEqual(w.getContentSize(), [10, 10]);
});
ifit(process.platform === 'win32')('do not change window with frame bounds when maximized', () => {
const w = new BrowserWindow({
show: true,

View File

@@ -250,6 +250,34 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
expect(crash.addedThenRemoved).to.be.undefined();
});
// Regression: base::circular_deque relocates elements on growth,
// corrupting crashpad::Annotation's self-referential pointers and
// causing missing crash keys or a hung handler. See crash_keys.cc.
it('does not corrupt the crashpad annotation list after deque reallocation', async function () {
// Tight timeout so a hanging handler fails fast instead of waiting
// for the mocha default of 120s.
this.timeout(45000);
const { port, waitForCrash } = await startServer();
runCrashApp('renderer-dynamic-keys', port);
const crash = await Promise.race([
waitForCrash(),
new Promise<never>((_resolve, reject) => {
global.setTimeout(
() => reject(new Error('crashpad handler hung walking corrupted annotation list; crash upload did not arrive within 30s')),
30000
);
})
]);
expect(crash.process_type).to.equal('renderer');
const missing: string[] = [];
for (let i = 0; i < 50; i++) {
if ((crash as any)[`dyn-key-${i}`] !== `val-${i}`) {
missing.push(`dyn-key-${i}`);
}
}
expect(missing, `missing dynamic crash keys: ${missing.join(', ')}`).to.be.empty();
});
it('contains v8 crash keys when a v8 crash occurs', async () => {
const { remotely } = await startRemoteControlApp();
const { port, waitForCrash } = await startServer();

View File

@@ -1625,27 +1625,6 @@ describe('chromium features', () => {
expect(data).to.equal('function function function function function');
});
it('AudioWorklet keeps node integration across pooled worker threads', async () => {
// Regression test for https://github.com/electron/electron/issues/41263.
// Blink pools the AudioWorklet backing thread (Chromium CL:5270028) so
// the Nth+ AudioWorklet on a page reuses the same thread; the page
// creates several AudioWorklet contexts in sequence and asserts node
// integration is wired up in every one of them.
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
contextIsolation: false
}
});
w.loadURL(`file://${fixturesPath}/pages/audio-worklet.html`);
const [, results] = await once(ipcMain, 'audio-worklet-result');
expect(results).to.be.an('array').with.length.greaterThan(0);
for (const r of results) expect(r).to.equal('ok');
});
describe('SharedWorker', () => {
it('can work', async () => {
const w = new BrowserWindow({ show: false });

View File

@@ -51,6 +51,19 @@ app.whenReady().then(() => {
});
w.loadURL(`about:blank?set_extra=${setExtraParameters ? 1 : 0}`);
w.webContents.on('render-process-gone', () => process.exit(0));
} else if (crashType === 'renderer-dynamic-keys') {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
w.webContents.on('render-process-gone', () => process.exit(0));
w.webContents.on('did-finish-load', () => {
w.webContents.executeJavaScript(`
const { crashReporter } = require('electron');
for (let i = 0; i < 50; i++) {
crashReporter.addExtraParameter('dyn-key-' + i, 'val-' + i);
}
process.crash();
`);
});
w.loadURL('about:blank');
} else if (crashType === 'node') {
const crashPath = path.join(__dirname, 'node-crash.js');
const child = childProcess.fork(crashPath, { silent: true });

View File

@@ -1,44 +0,0 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
const { ipcRenderer } = require('electron');
// Create a number of AudioContext + AudioWorklet pairs in sequence so
// that Blink's WorkletThreadHolder pools and reuses the underlying
// worker thread (Chromium CL:5270028). For each context we ask the
// worklet to report whether `require` is a function and post that back
// via its MessagePort. The bug being guarded is that the Nth+ pooled
// worklet would silently lose its Node.js environment, so the test
// must run enough iterations to exercise thread reuse.
const NUM_CONTEXTS = 6;
async function runOne(index) {
const audioCtx = new AudioContext();
try {
await audioCtx.audioWorklet.addModule('../workers/audio_worklet_node.js');
const node = new AudioWorkletNode(audioCtx, 'node-integration-probe');
const reply = new Promise((resolve) => {
node.port.onmessage = (e) => resolve(e.data);
});
node.port.postMessage('probe');
node.connect(audioCtx.destination);
return await reply;
} finally {
await audioCtx.close();
}
}
(async () => {
const results = [];
for (let i = 0; i < NUM_CONTEXTS; i++) {
try {
results.push(await runOne(i));
} catch (err) {
results.push(`error: ${err && err.message ? err.message : err}`);
}
}
ipcRenderer.send('audio-worklet-result', results);
})();
</script>
</body>
</html>

View File

@@ -1,28 +0,0 @@
// Reports whether the Node.js environment is wired up inside this
// AudioWorklet's global scope. Used by spec/fixtures/pages/audio-worklet.html
// to verify that nodeIntegrationInWorker keeps working when Blink reuses a
// pooled worker thread for multiple AudioWorklet contexts.
class NodeIntegrationProbeProcessor extends AudioWorkletProcessor {
constructor () {
super();
this.port.onmessage = () => {
let info;
try {
// require should be a function and `node:timers` should resolve.
const ok = typeof require === 'function' &&
typeof require('node:timers').setImmediate === 'function' &&
typeof process === 'object';
info = ok ? 'ok' : 'missing';
} catch (err) {
info = `throw: ${err && err.message ? err.message : err}`;
}
this.port.postMessage(info);
};
}
process () {
return true;
}
}
registerProcessor('node-integration-probe', NodeIntegrationProbeProcessor);

View File

@@ -590,7 +590,7 @@ __metadata:
"@types/semver": "npm:^7.5.8"
"@types/stream-json": "npm:^1.7.8"
"@types/temp": "npm:^0.9.4"
"@xmldom/xmldom": "npm:^0.8.11"
"@xmldom/xmldom": "npm:^0.8.12"
buffer: "npm:^6.0.3"
chalk: "npm:^4.1.0"
check-for-leaks: "npm:^1.2.1"
@@ -2503,7 +2503,14 @@ __metadata:
languageName: node
linkType: hard
"@xmldom/xmldom@npm:^0.8.11, @xmldom/xmldom@npm:^0.8.8":
"@xmldom/xmldom@npm:^0.8.12":
version: 0.8.12
resolution: "@xmldom/xmldom@npm:0.8.12"
checksum: 10c0/b733c84292d1bee32ef21a05aba8f9063456b51a54068d0b4a1abf5545156ee0b9894b7ae23775b5881b11c35a8a03871d1b514fb7e1b11654cdbee57e1c2707
languageName: node
linkType: hard
"@xmldom/xmldom@npm:^0.8.8":
version: 0.8.11
resolution: "@xmldom/xmldom@npm:0.8.11"
checksum: 10c0/e768623de72c95d3dae6b5da8e33dda0d81665047811b5498d23a328d45b13feb5536fe921d0308b96a4a8dd8addf80b1f6ef466508051c0b581e63e0dc74ed5