Compare commits

..

23 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
2ea9be3ade Bump v19.0.0-beta.7 2022-05-16 06:31:51 -07:00
trop[bot]
52a9566f28 fix: fetching PDF element from WebLocalFrame (#34215)
fix: fetching PDF element from WebLocalFrame

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-13 11:24:01 -05:00
trop[bot]
4ecaae9555 fix: call loadUrl when opening new windows from links (#34189)
* fix: call loadUrl when opening new windows from links

* spec: add regression test

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-12 21:49:25 -04:00
trop[bot]
21b8200170 fix: tray icon not highlighting on empty menu (#34208)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-12 16:20:00 -07:00
Sudowoodo Release Bot
2b6cd3458f Bump v19.0.0-beta.6 2022-05-12 06:31:14 -07:00
trop[bot]
00e747ac24 test: fixup done being called multiple times (#34188)
* test: fixup done being called multiple times

* chore: run ci

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-05-12 11:04:42 +02:00
Keeley Hammond
082b06cf4e build: re-enable 32-bit Windows symbol generation (#34179) 2022-05-11 13:16:49 -07:00
trop[bot]
b07e17a3bb fix: fix a crash in safeStorage on Linux (#34147)
On Linux, `isEncryptionAvailable()` was crashing instead of returning a
boolean before the 'ready' event was emitted by the app. The reason of
the crash is that [`CreateKeyStorage()`](https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=74;drc=35be6215ec8f09e50176f36753c68f26c63d1885;bpv=1;bpt=0)
expects the config to be set but the function responsible for setting the
config, [`SetConfig()`](https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=237;drc=35be6215ec8f09e50176f36753c68f26c63d1885;bpv=1;bpt=0),
is called only after the app is ready inside [`PostCreateMainMessageLoop()`](https://github.com/electron/electron/blob/main/shell/browser/electron_browser_main_parts.cc#L499).
So this changes `IsEncryptionAvailable()` to return `false` when the app
is not ready on Linux and uses that instead of the raw API in other
places like `EncryptString()` and `DecryptString()`.

Fixes: https://github.com/electron/electron/issues/32206
Signed-off-by: Darshan Sen <raisinten@gmail.com>

Co-authored-by: Darshan Sen <raisinten@gmail.com>
2022-05-11 10:20:30 -04:00
trop[bot]
49ee456797 test: run node specs with py3 (#34165)
Co-authored-by: Jeremy Rose <japthorp@slack-corp.com>
2022-05-11 10:02:00 +02:00
trop[bot]
85063322e9 fix: building node modules with Visual Studio 2017 (#34164)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-05-11 10:01:48 +02:00
trop[bot]
633d2961eb fix: create singleton pipename from user & executable (#34160)
* fix: create singleton pipename from user & executable

* fix: use process id & main thread id for pipe name

* fix: write rand to file using WIN method

* fix: remove file rand, add user_name to pipe

* chore: style fixes, shorten program_name & user_name

* fix: remove user_name

* chore: trigger CircleCI

Co-authored-by: VerteDinde <keeleymhammond@gmail.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-05-10 16:26:05 -07:00
trop[bot]
4f0592101b chore: stub gtk_native_dialog_get_type (#34151)
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-05-09 20:59:41 -07:00
trop[bot]
8797485564 build: change upload-to-s3 vars to upload-to-storage (#34143)
* build: change upload-to-s3 vars to upload-to-az

* build: change upload-to-az to upload-to-storage

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-05-09 12:57:14 -07:00
Sudowoodo Release Bot
c3746dc439 Bump v19.0.0-beta.5 2022-05-09 06:31:11 -07:00
Keeley Hammond
d5dadd0d4a fix: load FirstPartySets without Electron initialization (#34138) 2022-05-09 10:44:04 +02:00
trop[bot]
da62dd2721 build: stop uploading assets to S3 (#34116)
* build: remove S3 uploads

* build: remove ending slash upload-index-json (#34125)

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2022-05-07 03:25:57 -07:00
trop[bot]
c945629872 build: use azure function to hash assets instead of lambda (#34123)
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-05-07 03:25:29 -07:00
trop[bot]
536a17f5ed docs: add missing ackCallback parameter (#34128)
Co-authored-by: Raymond Zhao <raymondzhao@microsoft.com>
2022-05-06 23:34:54 -07:00
trop[bot]
433765cd73 refactor: prevent RemoveFromLoginItems() from mounting volumes from login items (#34108)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-05 22:29:41 -04:00
electron-roller[bot]
870110fd52 chore: bump chromium to 102.0.5005.40 (19-x-y) (#34089)
* chore: bump chromium in DEPS to 102.0.5005.40

* chore: update patches

* Trigger Build

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-05-05 22:06:43 -04:00
trop[bot]
8ce14231fb test: unflake some more tests (#34102)
* test: unflake webview fullscreen test

* test: unflake net throttle test

* Update spec-main/api-net-spec.ts

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Samuel Attard <sattard@salesforce.com>
Co-authored-by: Samuel Attard <sam@electronjs.org>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-05-05 20:19:46 -04:00
trop[bot]
de09ba2c51 fix: offscreen rendering crash on input select (#34093)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-05 11:49:28 -07:00
trop[bot]
290b548b15 test: unflake some focus tests (#34079)
* spec: unflake some focus tests

* test: disable flaky webFrame visibiilty spec

Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-05-05 09:40:11 -04:00
56 changed files with 1309 additions and 287 deletions

View File

@@ -14,7 +14,7 @@ parameters:
type: boolean
default: false
upload-to-s3:
upload-to-storage:
type: string
default: '1'

View File

@@ -5,7 +5,7 @@ parameters:
type: boolean
default: false
upload-to-s3:
upload-to-storage:
type: string
default: '1'
@@ -637,9 +637,9 @@ step-electron-publish: &step-electron-publish
fi
cd src/electron
if [ "$UPLOAD_TO_S3" == "1" ]; then
echo 'Uploading Electron release distribution to S3'
script/release/uploaders/upload.py --verbose --upload_to_s3
if [ "$UPLOAD_TO_STORAGE" == "1" ]; then
echo 'Uploading Electron release distribution to Azure'
script/release/uploaders/upload.py --verbose --UPLOAD_TO_STORAGE
else
echo 'Uploading Electron release distribution to Github releases'
script/release/uploaders/upload.py --verbose
@@ -1747,7 +1747,7 @@ jobs:
environment:
<<: *env-linux-2xlarge-release
<<: *env-release-build
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running
@@ -1791,7 +1791,7 @@ jobs:
<<: *env-release-build
<<: *env-32bit-release
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running
@@ -1844,7 +1844,7 @@ jobs:
<<: *env-arm64
<<: *env-release-build
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running
@@ -1892,7 +1892,7 @@ jobs:
environment:
<<: *env-mac-large-release
<<: *env-release-build
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running
@@ -1914,7 +1914,7 @@ jobs:
<<: *env-mac-large-release
<<: *env-release-build
<<: *env-apple-silicon
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running
@@ -1984,7 +1984,7 @@ jobs:
<<: *env-mac-large-release
<<: *env-mas
<<: *env-release-build
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
steps:
- run: echo running
- when:
@@ -2005,7 +2005,7 @@ jobs:
<<: *env-mac-large-release
<<: *env-mas-apple-silicon
<<: *env-release-build
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
<<: *env-ninja-status
steps:
- run: echo running

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'102.0.5005.27',
'102.0.5005.40',
'node_version':
'v16.14.2',
'nan_version':

View File

@@ -1 +1 @@
19.0.0-beta.4
19.0.0-beta.7

View File

@@ -11,7 +11,7 @@
# - "TARGET_ARCH" Choose from {'ia32', 'x64', 'arm', 'arm64', 'mips64el'}.
# Is used in some publishing scripts, but does NOT affect the Electron binary.
# Must match 'target_cpu' passed to "GN_EXTRA_ARGS" and "NPM_CONFIG_ARCH" value.
# - "UPLOAD_TO_S3" Set it to '1' upload a release to the S3 bucket.
# - "UPLOAD_TO_STORAGE" Set it to '1' upload a release to the Azure bucket.
# Otherwise the release will be uploaded to the Github Releases.
# (The value is only checked if "ELECTRON_RELEASE" is defined.)
#
@@ -193,19 +193,16 @@ build_script:
- appveyor PushArtifact out/Default/mksnapshot.zip
- appveyor PushArtifact out/Default/hunspell_dictionaries.zip
- appveyor PushArtifact out/Default/electron.lib
# Temporarily disable symbol generation on 32-bit Windows due to failures
- ps: >-
if ($env:GN_CONFIG -eq 'release' -And $env:TARGET_ARCH -ne 'ia32') {
if ($env:GN_CONFIG -eq 'release') {
# Needed for msdia140.dll on 64-bit windows
$env:Path += ";$pwd\third_party\llvm-build\Release+Asserts\bin"
ninja -C out/Default electron:electron_symbols
}
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
if ($env:TARGET_ARCH -ne 'ia32') {
python electron\script\zip-symbols.py
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
}
python electron\script\zip-symbols.py
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
} else {
# It's useful to have pdb files when debugging testing builds that are
# built on CI.
@@ -244,9 +241,9 @@ deploy_script:
- cd electron
- ps: >-
if (Test-Path Env:\ELECTRON_RELEASE) {
if (Test-Path Env:\UPLOAD_TO_S3) {
Write-Output "Uploading Electron release distribution to s3"
& python script\release\uploaders\upload.py --verbose --upload_to_s3
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
Write-Output "Uploading Electron release distribution to azure"
& python script\release\uploaders\upload.py --verbose --upload_to_storage
} else {
Write-Output "Uploading Electron release distribution to github releases"
& python script\release\uploaders\upload.py --verbose

View File

@@ -998,7 +998,7 @@ const gotTheLock = app.requestSingleInstanceLock(additionalData)
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
app.on('second-instance', (event, commandLine, workingDirectory, additionalData, ackCallback) => {
// We must call preventDefault if we're sending back data.
event.preventDefault()
// Print out data received from the second instance.

View File

@@ -18,8 +18,8 @@ The `safeStorage` module has the following methods:
Returns `boolean` - Whether encryption is available.
On Linux, returns true if the secret key is
available. On MacOS, returns true if Keychain is available.
On Linux, returns true if the app has emitted the `ready` event and the secret key is available.
On MacOS, returns true if Keychain is available.
On Windows, returns true once the app has emitted the `ready` event.
### `safeStorage.encryptString(plainText)`

View File

@@ -78,6 +78,19 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
...browserWindowOptions
});
if (!guest) {
// When we open a new window from a link (via OpenURLFromTab),
// the browser process is responsible for initiating navigation
// in the new window.
window.loadURL(url, {
httpReferrer: referrer,
...(postData && {
postData,
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
})
});
}
handleWindowLifecycleEvents({ embedder, frameName, guest: window, outlivesOpener });
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
@@ -243,6 +256,15 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
};
}
function formatPostDataHeaders (postData: PostData) {
if (!postData) return;
const { contentType, boundary } = parseContentTypeFormat(postData);
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
return `content-type: ${contentType}`;
}
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "19.0.0-beta.4",
"version": "19.0.0-beta.7",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -116,3 +116,4 @@ build_make_libcxx_abi_unstable_false_for_electron.patch
introduce_ozoneplatform_electron_can_call_x11_property.patch
make_gtk_getlibgtk_public.patch
build_disable_print_content_analysis.patch
feat_move_firstpartysets_to_content_browser_client.patch

View File

@@ -23,10 +23,10 @@ index eb6f4c87c4479d5f4fb8e3f85a231fb9cc744a63..11298b413021b4d438195482db253a93
int32_t world_id) {}
virtual void DidClearWindowObject() {}
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 10a15068a999692ad5acb08e3de7ca20d89e7de7..e2bdecaf0055900f5c5da65904c6a8aa6c0f0c40 100644
index f217de19126feeeafee01b2be31ced936696ce20..95966ce7f0eeb0a7f2822bef69b540ac6bfe298d 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4483,6 +4483,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
@@ -4489,6 +4489,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
observer.DidCreateScriptContext(context, world_id);
}

View File

@@ -46,7 +46,7 @@ index 1d80724ace438fb9d2c20ff78d87a93053cf7420..840548c9685c2e3bd584ea8f48d02d21
sources += [ "certificate_viewer_stub.cc" ]
}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 27f579a9e7acfd85957aa0eee22c17e62a84d5d3..425abe53ff954c0eea2680115a7229ccc0302ca8 100644
index 641f52eb7952858738a6623b480f767261e5d009..6d0c624ea62a9125ef7e5579da197de2327a12ff 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6002,7 +6002,6 @@ test("unit_tests") {

View File

@@ -9,7 +9,7 @@ potentially prevent a window from being created.
TODO(loc): this patch is currently broken.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 5da4442e7d6d2eb44e89110dc5f13d666dc76271..4fd4508ee8352f098e0b2a0119fc19744a50da76 100644
index ec886101b6e5347bdd832684a5c3b2cc3179c657..0287e414f4020147ddee297f7c1447c32a118905 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -6943,6 +6943,7 @@ void RenderFrameHostImpl::CreateNewWindow(

View File

@@ -24,10 +24,10 @@ This patch temporarily disables the metrics so we can have green CI, and we
should continue seeking for a real fix.
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index 7b85cd7af251e7b18e635a0b70a87dc958baaa6f..14991a8df16d8a5997ce0d23dfcd9aa4045bf6f7 100644
index c289c888bc9d74d3f696714650b65aa373be27b4..aad7ec5ed0b6f6ab5cdfe0d7902d952c7c9e36c6 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -1276,6 +1276,7 @@ void Navigator::RecordNavigationMetrics(
@@ -1286,6 +1286,7 @@ void Navigator::RecordNavigationMetrics(
.InMilliseconds());
}
@@ -35,7 +35,7 @@ index 7b85cd7af251e7b18e635a0b70a87dc958baaa6f..14991a8df16d8a5997ce0d23dfcd9aa4
// If this is a same-process navigation and we have timestamps for unload
// durations, fill those metrics out as well.
if (params.unload_start && params.unload_end &&
@@ -1322,6 +1323,7 @@ void Navigator::RecordNavigationMetrics(
@@ -1332,6 +1333,7 @@ void Navigator::RecordNavigationMetrics(
first_before_unload_start_time)
.InMilliseconds());
}

View File

@@ -11,10 +11,10 @@ This regressed in https://chromium-review.googlesource.com/c/chromium/src/+/2572
Upstream: https://chromium-review.googlesource.com/c/chromium/src/+/2598393
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index e2bdecaf0055900f5c5da65904c6a8aa6c0f0c40..b37bddc5b704adfa8283bdf3a38f99b5187d8433 100644
index 95966ce7f0eeb0a7f2822bef69b540ac6bfe298d..34c2bdc5d4dda2fb0bdf2a8c3e5f8f9a5e11b977 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2392,7 +2392,7 @@ const blink::WebView* RenderFrameImpl::GetWebView() const {
@@ -2398,7 +2398,7 @@ const blink::WebView* RenderFrameImpl::GetWebView() const {
}
const blink::web_pref::WebPreferences& RenderFrameImpl::GetBlinkPreferences() {

View File

@@ -282,10 +282,20 @@ index be2c417c07a4206fac4a9a6c03e516fd0493c942..78f74b0b21242553b6af98628dc48190
return PROCESS_NOTIFIED;
}
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d05018b627 100644
index ec725b44296266bea1a51aea889463a0bba8449c..6355e5c73ba9df9bdb9ff48205d909dca04470f1 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -21,6 +21,7 @@
@@ -13,14 +13,17 @@
#include "base/command_line.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
+#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -293,11 +303,10 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
#include "base/trace_event/base_tracing.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
@@ -45,6 +46,14 @@
@@ -45,6 +48,13 @@
namespace {
const char kLockfile[] = "lockfile";
+const LPCWSTR kPipeName = L"\\\\.\\pipe\\electronAckPipe";
+const DWORD kPipeTimeout = 10000;
+const DWORD kMaxMessageLength = 32 * 1024;
+
@@ -308,7 +317,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
// scope.
@@ -80,10 +89,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
@@ -80,10 +90,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine* parsed_command_line,
@@ -323,7 +332,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
@@ -133,11 +144,82 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
@@ -133,11 +145,82 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = base::CommandLine::FromString(cmd_line);
@@ -406,7 +415,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
bool ProcessLaunchNotification(
const ProcessSingleton::NotificationCallback& notification_callback,
UINT message,
@@ -151,16 +233,35 @@ bool ProcessLaunchNotification(
@@ -151,16 +234,35 @@ bool ProcessLaunchNotification(
// Handle the WM_COPYDATA message from another process.
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
@@ -446,7 +455,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
return true;
}
@@ -261,9 +362,13 @@ bool ProcessSingleton::EscapeVirtualization(
@@ -261,9 +363,13 @@ bool ProcessSingleton::EscapeVirtualization(
ProcessSingleton::ProcessSingleton(
const std::string& program_name,
const base::FilePath& user_data_dir,
@@ -461,16 +470,26 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
program_name_(program_name),
is_app_sandboxed_(is_app_sandboxed),
is_virtualized_(false),
@@ -278,6 +383,37 @@ ProcessSingleton::~ProcessSingleton() {
@@ -278,6 +384,47 @@ ProcessSingleton::~ProcessSingleton() {
::CloseHandle(lock_file_);
}
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback) {
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback,
+ const std::string program_name,
+ base::FilePath& user_data_dir) {
+ // We are reading the ack from the first instance.
+ // First, wait for the pipe.
+ ::WaitNamedPipe(kPipeName, NMPWAIT_USE_DEFAULT_WAIT);
+ HWND remote_window = chrome::FindRunningChromeWindow(user_data_dir);
+ DWORD process_id;
+ DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
+ std::string identifier = base::NumberToString(process_id) +
+ base::NumberToString(thread_id);
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
+ program_name);
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
+ ::WaitNamedPipe(w_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
+
+ HANDLE read_ack_pipe = ::CreateFile(kPipeName,
+ HANDLE read_ack_pipe = ::CreateFile(w_pipe_name,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
@@ -499,23 +518,31 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
@@ -290,8 +426,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
@@ -290,8 +437,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return PROCESS_NONE;
}
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
case chrome::NOTIFY_SUCCESS:
+ ReadAck(notification_ack_callback_);
+ ReadAck(notification_ack_callback_, program_name_, user_data_dir_);
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
@@ -429,6 +566,18 @@ bool ProcessSingleton::Create() {
@@ -429,6 +577,26 @@ bool ProcessSingleton::Create() {
<< "Lock file can not be created! Error code: " << error;
if (lock_file_ != INVALID_HANDLE_VALUE) {
+ // We are the first instance. Create a pipe to send out ack data.
+ ack_pipe_ = ::CreateNamedPipe(kPipeName,
+ // Create a per-process pipename using a combination of the
+ // username, process id, thread id, and program name. Pipe names max
+ // at 256 characters, can include any character other than a backslash
+ std::string identifier = base::NumberToString(::GetCurrentProcessId()) +
+ base::NumberToString(::GetCurrentThreadId());
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
+ program_name_);
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
+ ack_pipe_ = ::CreateNamedPipe(w_pipe_name,
+ PIPE_ACCESS_OUTBOUND,
+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
+ PIPE_UNLIMITED_INSTANCES,
@@ -529,7 +556,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d0
// Set the window's title to the path of our user data directory so
// other Chrome instances can decide if they should forward to us.
TRACE_EVENT0("startup", "ProcessSingleton::Create:CreateWindow");
@@ -456,6 +605,7 @@ bool ProcessSingleton::Create() {
@@ -456,6 +624,7 @@ bool ProcessSingleton::Create() {
}
void ProcessSingleton::Cleanup() {

View File

@@ -0,0 +1,874 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: VerteDinde <keeleymhammond@gmail.com>
Date: Sun, 8 May 2022 17:21:12 -0700
Subject: feat: replace ad-hoc SetPublicFirstPartySets calls with method in
ContentBrowserClient
Cherry-picked from upstream Chromium. This patch can be removed when the
fix is inherited from the next Chromium roll backport.
This essentially requires
embedders to indicate whether they will
(maybe asynchronously) call SetPublicFirstPartySets during startup, or
not. This makes it easier to initialize First-Party Sets properly, since
it is now done via a pull-based interface rather than a push-based
interface (which would require code in every embedder in order to
set up the First-Party Sets backend). Now, there is a single place (in
content) that handles every embedder that won't need to explicitly call
SetPublicFirstPartySets at some point (e.g. after initializing
Component Updater, in Chrome's case).
Bug: 1321908
Change-Id: I47eaaaf77e548079e1bd6360fd573e877aa79b32
Reviewed-on:
https://chromium-review.googlesource.com/c/chromium/src/+/3623985
Reviewed-by: Avi Drissman <avi@chromium.org>
Commit-Queue: Chris Fredrickson <cfredric@chromium.org>
Cr-Commit-Position: refs/heads/main@{#999047}
Patch-Filename:
feat_move_firstpartysets_to_content_browser_client.patch
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index e07874dc5a2fab83dff0a07d35aeb0cad7a1a67c..9f635870282f0f2a9b8bfaaa59e34f91675dcda3 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1601,10 +1601,6 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
// called inside PostProfileInit and depends on it.
if (!parsed_command_line().HasSwitch(switches::kDisableComponentUpdate)) {
component_updater::RegisterComponentsForUpdate();
- } else {
- // Initialize First-Party Sets even if component updater is disabled.
- content::FirstPartySetsHandler::GetInstance()->SetPublicFirstPartySets(
- base::File());
}
// TODO(stevenjb): Move WIN and MACOSX specific code to appropriate Parts.
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3943c32ab29e785f401c2a5b31f1f6ed832514f0..939c28a029418bc353795aa1a007508680f42e57 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -6462,6 +6462,12 @@ bool ChromeContentBrowserClient::IsFirstPartySetsEnabled() {
return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
}
+bool ChromeContentBrowserClient::WillProvidePublicFirstPartySets() {
+ return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableComponentUpdate) &&
+ base::FeatureList::IsEnabled(features::kFirstPartySets);
+}
+
base::Value::Dict ChromeContentBrowserClient::GetFirstPartySetsOverrides() {
if (!g_browser_process) {
// If browser process doesn't exist (e.g. in minimal mode on Android),
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index f0415a5099cdf181ab620fb6400db4f524c2c892..016b8276feeb6c35c88bc779ae84eea38d66f57d 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -772,6 +772,7 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
bool IsFindInPageDisabledForOrigin(const url::Origin& origin) override;
bool IsFirstPartySetsEnabled() override;
+ bool WillProvidePublicFirstPartySets() override;
base::Value::Dict GetFirstPartySetsOverrides() override;
bool ShouldPreconnectNavigation(
diff --git a/content/browser/first_party_sets/first_party_set_parser.cc b/content/browser/first_party_sets/first_party_set_parser.cc
index b03570f072f407a1d2c6a58646db0f48a845429d..3ac5c0d206c7910e5ae653004e96780c14c8315d 100644
--- a/content/browser/first_party_sets/first_party_set_parser.cc
+++ b/content/browser/first_party_sets/first_party_set_parser.cc
@@ -323,4 +323,4 @@ FirstPartySetParser::ParseSetsFromEnterprisePolicy(
return absl::nullopt;
}
-} // namespace content
+} // namespace content
\ No newline at end of file
diff --git a/content/browser/first_party_sets/first_party_set_parser.h b/content/browser/first_party_sets/first_party_set_parser.h
index 491a8b508ef0d093f37608d2fde615427b9319df..3f33b1604fecbff0d4db8739ad138d95223b579c 100644
--- a/content/browser/first_party_sets/first_party_set_parser.h
+++ b/content/browser/first_party_sets/first_party_set_parser.h
@@ -102,4 +102,4 @@ class CONTENT_EXPORT FirstPartySetParser {
} // namespace content
-#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SET_PARSER_H_
+#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SET_PARSER_H_
\ No newline at end of file
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.cc b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
index e5d277236b6393d35e8cf5a69c7f6d16f3394582..bb6840eecabfab9e9f0ae6b0fdc4e4436d1ea3b1 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
@@ -74,7 +74,8 @@ FirstPartySetsHandler* FirstPartySetsHandler::GetInstance() {
// static
FirstPartySetsHandlerImpl* FirstPartySetsHandlerImpl::GetInstance() {
static base::NoDestructor<FirstPartySetsHandlerImpl> instance(
- GetContentClient()->browser()->IsFirstPartySetsEnabled());
+ GetContentClient()->browser()->IsFirstPartySetsEnabled(),
+ GetContentClient()->browser()->WillProvidePublicFirstPartySets());
return instance.get();
}
@@ -89,8 +90,12 @@ FirstPartySetsHandler::ValidateEnterprisePolicy(
policy, /*out_sets=*/nullptr);
}
-FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(bool enabled)
- : enabled_(enabled) {
+FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(
+ bool enabled,
+ bool embedder_will_provide_public_sets)
+ : enabled_(enabled),
+ embedder_will_provide_public_sets_(enabled &&
+ embedder_will_provide_public_sets) {
sets_loader_ = std::make_unique<FirstPartySetsLoader>(
base::BindOnce(&FirstPartySetsHandlerImpl::SetCompleteSets,
// base::Unretained(this) is safe here because
@@ -112,12 +117,23 @@ void FirstPartySetsHandlerImpl::Init(const base::FilePath& user_data_dir,
const std::string& flag_value,
SetsReadyOnceCallback on_sets_ready) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!initialized_);
+ DCHECK(persisted_sets_path_.empty());
+ DCHECK(on_sets_ready_.is_null());
+
+ initialized_ = true;
on_sets_ready_ = std::move(on_sets_ready);
SetPersistedSets(user_data_dir);
- SetManuallySpecifiedSet(flag_value);
- if (!IsEnabled())
+ if (IsEnabled()) {
+ DCHECK(!on_sets_ready_.is_null());
+ sets_loader_->SetManuallySpecifiedSet(flag_value);
+ if (!embedder_will_provide_public_sets_) {
+ sets_loader_->SetComponentSets(base::File());
+ }
+ } else {
SetCompleteSets({});
+ }
}
bool FirstPartySetsHandlerImpl::IsEnabled() const {
@@ -127,16 +143,17 @@ bool FirstPartySetsHandlerImpl::IsEnabled() const {
void FirstPartySetsHandlerImpl::SetPublicFirstPartySets(base::File sets_file) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!IsEnabled()) {
- sets_loader_->DisposeFile(std::move(sets_file));
- return;
- }
+ DCHECK(enabled_);
+ DCHECK(embedder_will_provide_public_sets_);
sets_loader_->SetComponentSets(std::move(sets_file));
}
void FirstPartySetsHandlerImpl::ResetForTesting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ initialized_ = false;
enabled_ = GetContentClient()->browser()->IsFirstPartySetsEnabled();
+ embedder_will_provide_public_sets_ =
+ GetContentClient()->browser()->WillProvidePublicFirstPartySets();
// Initializes the `sets_loader_` member with a callback to SetCompleteSets
// and the result of content::GetFirstPartySetsOverrides.
@@ -153,19 +170,16 @@ void FirstPartySetsHandlerImpl::ResetForTesting() {
raw_persisted_sets_ = absl::nullopt;
}
-void FirstPartySetsHandlerImpl::SetManuallySpecifiedSet(
- const std::string& flag_value) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!IsEnabled())
- return;
- sets_loader_->SetManuallySpecifiedSet(flag_value);
-}
-
void FirstPartySetsHandlerImpl::SetPersistedSets(
const base::FilePath& user_data_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!raw_persisted_sets_.has_value());
+ DCHECK(persisted_sets_path_.empty());
if (user_data_dir.empty()) {
VLOG(1) << "Empty path. Failed loading serialized First-Party Sets file.";
+ // We have to continue, in case the embedder has enabled FPS but has not
+ // provided a directory to store persisted sets.
+ OnReadPersistedSetsFile("");
return;
}
persisted_sets_path_ = user_data_dir.Append(kPersistedFirstPartySetsFileName);
@@ -184,19 +198,36 @@ void FirstPartySetsHandlerImpl::SetPersistedSets(
void FirstPartySetsHandlerImpl::OnReadPersistedSetsFile(
const std::string& raw_persisted_sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!persisted_sets_path_.empty());
+ DCHECK(!raw_persisted_sets_.has_value());
raw_persisted_sets_ = raw_persisted_sets;
UmaHistogramTimes(
- "Cookie.FirstPartySets.InitializationDuration.ReadPersistedSets",
+ "Cookie.FirstPartySets.InitializationDuration.ReadPersistedSets2",
construction_timer_.Elapsed());
- ClearSiteDataOnChangedSetsIfReady();
+
+ if (sets_.has_value()) {
+ ClearSiteDataOnChangedSets();
+
+ if (IsEnabled()) {
+ DCHECK(!on_sets_ready_.is_null());
+ std::move(on_sets_ready_).Run(sets_.value());
+ }
+ }
}
void FirstPartySetsHandlerImpl::SetCompleteSets(
base::flat_map<net::SchemefulSite, net::SchemefulSite> sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!sets_.has_value());
sets_ = std::move(sets);
- ClearSiteDataOnChangedSetsIfReady();
+
+ if (raw_persisted_sets_.has_value()) {
+ ClearSiteDataOnChangedSets();
+
+ if (IsEnabled()) {
+ DCHECK(!on_sets_ready_.is_null());
+ std::move(on_sets_ready_).Run(sets_.value());
+ }
+ }
}
// static
@@ -229,10 +260,10 @@ base::flat_set<net::SchemefulSite> FirstPartySetsHandlerImpl::ComputeSetsDiff(
return result;
}
-void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSetsIfReady() {
+void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSets() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!raw_persisted_sets_.has_value() || !sets_.has_value())
- return;
+ DCHECK(sets_.has_value());
+ DCHECK(raw_persisted_sets_.has_value());
base::flat_set<net::SchemefulSite> diff =
ComputeSetsDiff(FirstPartySetParser::DeserializeFirstPartySets(
@@ -241,14 +272,13 @@ void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSetsIfReady() {
// TODO(shuuran@chromium.org): Implement site state clearing.
- if (!on_sets_ready_.is_null() && IsEnabledAndReady())
- std::move(on_sets_ready_).Run(sets_.value());
-
- base::ThreadPool::PostTask(
- FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- &MaybeWriteSetsToDisk, persisted_sets_path_,
- FirstPartySetParser::SerializeFirstPartySets(sets_.value())));
+ if (!persisted_sets_path_.empty()) {
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(
+ &MaybeWriteSetsToDisk, persisted_sets_path_,
+ FirstPartySetParser::SerializeFirstPartySets(sets_.value())));
+ }
}
bool FirstPartySetsHandlerImpl::IsEnabledAndReady() const {
@@ -256,4 +286,4 @@ bool FirstPartySetsHandlerImpl::IsEnabledAndReady() const {
return IsEnabled() && sets_.has_value();
}
-} // namespace content
+} // namespace content
\ No newline at end of file
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.h b/content/browser/first_party_sets/first_party_sets_handler_impl.h
index a60adcd16b5b8911965f9a22c2e70b5044150758..829cd96245cbe39eeddecf2e0403f2ba1ab88793 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.h
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.h
@@ -55,10 +55,9 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// persisted sets, since we may still need to clear data from a previous
// invocation of Chromium which had First-Party Sets enabled.
//
- // TODO(https://crbug.com/1309188): Init() should be called in the
- // BrowserMainLoop::PreMainMessageLoopRun(). But just in case it's
- // accidentally called from other places, make sure it's no-op for the
- // following calls.
+ // If First-Party Sets is enabled, `on_sets_ready` must not be null.
+ //
+ // Must be called exactly once.
void Init(const base::FilePath& user_data_dir,
const std::string& flag_value,
SetsReadyOnceCallback on_sets_ready);
@@ -78,6 +77,11 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
enabled_ = enabled;
}
+ void SetEmbedderWillProvidePublicSetsForTesting(bool will_provide) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ embedder_will_provide_public_sets_ = enabled_ && will_provide;
+ }
+
// Compares the map `old_sets` to `current_sets` and returns the set of sites
// that: 1) were in `old_sets` but are no longer in `current_sets`, i.e. leave
// the FPSs; or, 2) mapped to a different owner site.
@@ -92,41 +96,38 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
private:
friend class base::NoDestructor<FirstPartySetsHandlerImpl>;
- explicit FirstPartySetsHandlerImpl(bool enabled);
+ FirstPartySetsHandlerImpl(bool enabled,
+ bool embedder_will_provide_public_sets);
// This method reads the persisted First-Party Sets from the file under
- // `user_data_dir`.
+ // `user_data_dir`. Must be called exactly once.
void SetPersistedSets(const base::FilePath& user_data_dir);
- // Stores the read persisted sets in `raw_persisted_sets_`.
+ // Stores the read persisted sets in `raw_persisted_sets_`. Must be called
+ // exactly once.
void OnReadPersistedSetsFile(const std::string& raw_persisted_sets);
- // Parses and sets the First-Party Set that was provided via the
- // `kUseFirstPartySet` flag/switch.
- //
- // Has no effect if `kFirstPartySets` is disabled, or
- // `SetPublicFirstPartySets` is not called.
- void SetManuallySpecifiedSet(const std::string& flag_value);
-
- // Sets the current First-Party Sets data.
+ // Sets the current First-Party Sets data. Must be called exactly once.
void SetCompleteSets(FlattenedSets sets);
- // Checks the required inputs have been received, and if so:
+ // Does the following:
// 1) computes the diff between the `sets_` and the parsed
// `raw_persisted_sets_`;
// 2) clears the site data of the set of sites based on the diff;
- // 3) calls `on_sets_ready_` if conditions are met;
- // 4) writes the current First-Party Sets to the file in
+ // 3) writes the current First-Party Sets to the file in
// `persisted_sets_path_`.
//
// TODO(shuuran@chromium.org): Implement the code to clear site state.
- void ClearSiteDataOnChangedSetsIfReady();
+ void ClearSiteDataOnChangedSets() const;
// Returns true if:
// * First-Party Sets are enabled;
// * `sets_` is ready to be used.
bool IsEnabledAndReady() const;
+ // Whether Init has been called already or not.
+ bool initialized_ = false;
+
// Represents the mapping of site -> site, where keys are members of sets, and
// values are owners of the sets. Owners are explicitly represented as members
// of the set.
@@ -144,6 +145,7 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
base::FilePath persisted_sets_path_ GUARDED_BY_CONTEXT(sequence_checker_);
bool enabled_ GUARDED_BY_CONTEXT(sequence_checker_);
+ bool embedder_will_provide_public_sets_ GUARDED_BY_CONTEXT(sequence_checker_);
// We use a OnceCallback to ensure we only pass along the sets once
// during Chrome's lifetime (modulo reconfiguring the network service).
@@ -160,4 +162,4 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
} // namespace content
-#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_H_
+#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_H_
\ No newline at end of file
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
index 0c0eeda67a8a4bdc9876f5ad3fe78c06c4f18b5d..3e92801eb25e339e6b3a43b5fadab8f1b78b23f9 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
@@ -367,14 +367,6 @@ TEST_F(FirstPartySetsHandlerImplDisabledTest, IgnoresValid) {
FAIL(); // Should not be called.
}));
- // Set required inputs to be able to receive the merged sets from
- // FirstPartySetsLoader.
- const std::string input =
- "{\"owner\": \"https://example.test\",\"members\": "
- "[\"https://aaaa.test\"]}";
- ASSERT_TRUE(base::JSONReader::Read(input));
- SetPublicFirstPartySetsAndWait(input);
-
env().RunUntilIdle();
// TODO(shuuran@chromium.org): test site state is cleared.
@@ -397,9 +389,6 @@ TEST_F(FirstPartySetsHandlerImplDisabledTest,
FAIL(); // Should not be called.
}));
- SetPublicFirstPartySetsAndWait(R"({"owner": "https://example.test", )"
- R"("members": ["https://member.test"]})");
-
EXPECT_EQ(
FirstPartySetsHandlerImpl::GetInstance()->GetSetsIfEnabledAndReady(),
absl::nullopt);
@@ -413,11 +402,6 @@ class FirstPartySetsHandlerImplEnabledTest
};
TEST_F(FirstPartySetsHandlerImplEnabledTest, PersistedSetsNotReady) {
- const std::string input = R"({"owner": "https://foo.test", )"
- R"("members": ["https://member2.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- SetPublicFirstPartySetsAndWait(input);
-
// Empty `user_data_dir` will fail loading persisted sets.
FirstPartySetsHandlerImpl::GetInstance()->Init(
/*user_data_dir=*/{},
@@ -431,6 +415,8 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest, PersistedSetsNotReady) {
}
TEST_F(FirstPartySetsHandlerImplEnabledTest, PublicFirstPartySetsNotReady) {
+ FirstPartySetsHandlerImpl::GetInstance()
+ ->SetEmbedderWillProvidePublicSetsForTesting(true);
ASSERT_TRUE(base::WriteFile(persisted_sets_path_, "{}"));
// Persisted sets are expected to be loaded with the provided path.
@@ -447,6 +433,8 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest, PublicFirstPartySetsNotReady) {
TEST_F(FirstPartySetsHandlerImplEnabledTest,
Successful_PersistedSetsFileNotExist) {
+ FirstPartySetsHandlerImpl::GetInstance()
+ ->SetEmbedderWillProvidePublicSetsForTesting(true);
const std::string input = R"({"owner": "https://foo.test", )"
R"("members": ["https://member2.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
@@ -479,6 +467,8 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest,
}
TEST_F(FirstPartySetsHandlerImplEnabledTest, Successful_PersistedSetsEmpty) {
+ FirstPartySetsHandlerImpl::GetInstance()
+ ->SetEmbedderWillProvidePublicSetsForTesting(true);
ASSERT_TRUE(base::WriteFile(persisted_sets_path_, "{}"));
const std::string input = R"({"owner": "https://foo.test", )"
@@ -514,6 +504,8 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest, Successful_PersistedSetsEmpty) {
TEST_F(FirstPartySetsHandlerImplEnabledTest,
GetSetsIfEnabledAndReady_AfterSetsReady) {
+ FirstPartySetsHandlerImpl::GetInstance()
+ ->SetEmbedderWillProvidePublicSetsForTesting(true);
ASSERT_TRUE(base::WriteFile(persisted_sets_path_, "{}"));
const std::string input = R"({"owner": "https://example.test", )"
@@ -549,6 +541,8 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest,
TEST_F(FirstPartySetsHandlerImplEnabledTest,
GetSetsIfEnabledAndReady_BeforeSetsReady) {
+ FirstPartySetsHandlerImpl::GetInstance()
+ ->SetEmbedderWillProvidePublicSetsForTesting(true);
ASSERT_TRUE(base::WriteFile(persisted_sets_path_, "{}"));
// Call GetSetsIfEnabledAndReady before the sets are ready.
diff --git a/content/browser/first_party_sets/first_party_sets_loader.cc b/content/browser/first_party_sets/first_party_sets_loader.cc
index 52acedfde41b85acfc9246121ccfd6e63290270e..c229815ff14160c4f325fb51473e3b4baf5a9891 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.cc
+++ b/content/browser/first_party_sets/first_party_sets_loader.cc
@@ -20,6 +20,7 @@
#include "base/strings/string_split.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
+#include "content/browser/first_party_sets/addition_overlaps_union_find.h"
#include "content/browser/first_party_sets/first_party_set_parser.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -66,6 +67,37 @@ std::string ReadSetsFile(base::File sets_file) {
return base::ReadStreamToString(file.get(), &raw_sets) ? raw_sets : "";
}
+// Creates a set of SchemefulSites present with the given list of SingleSets.
+base::flat_set<net::SchemefulSite> FlattenSingleSetList(
+ const std::vector<content::FirstPartySetsLoader::SingleSet>& sets) {
+ std::vector<net::SchemefulSite> sites;
+ for (const content::FirstPartySetsLoader::SingleSet& set : sets) {
+ sites.push_back(set.first);
+ sites.insert(sites.end(), set.second.begin(), set.second.end());
+ }
+ return sites;
+}
+
+// Populates the `policy_set_overlaps` out-parameter by checking
+// `existing_sets`. If `site` is equal to an existing site e in `sets`, then
+// `policy_set_index` will be added to the list of set indices at
+// `policy_set_overlaps`[e].
+void AddIfPolicySetOverlaps(
+ const net::SchemefulSite& site,
+ size_t policy_set_index,
+ FirstPartySetsLoader::FlattenedSets existing_sets,
+ base::flat_map<net::SchemefulSite, base::flat_set<size_t>>&
+ policy_set_overlaps) {
+ // Check `site` for membership in `existing_sets`.
+ if (auto it = existing_sets.find(site); it != existing_sets.end()) {
+ // Add the index of `site`'s policy set to the list of policy set indices
+ // that also overlap with site_owner.
+ auto [site_and_sets, inserted] =
+ policy_set_overlaps.insert({it->second, {}});
+ site_and_sets->second.insert(policy_set_index);
+ }
+}
+
} // namespace
FirstPartySetsLoader::FirstPartySetsLoader(
@@ -89,7 +121,7 @@ void FirstPartySetsLoader::SetManuallySpecifiedSet(
manually_specified_set_ = {CanonicalizeSet(base::SplitString(
flag_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY))};
UmaHistogramTimes(
- "Cookie.FirstPartySets.InitializationDuration.ReadCommandLineSet",
+ "Cookie.FirstPartySets.InitializationDuration.ReadCommandLineSet2",
construction_timer_.Elapsed());
MaybeFinishLoading();
@@ -127,7 +159,7 @@ void FirstPartySetsLoader::OnReadSetsFile(const std::string& raw_sets) {
component_sets_parse_progress_ = Progress::kFinished;
UmaHistogramTimes(
- "Cookie.FirstPartySets.InitializationDuration.ReadComponentSets",
+ "Cookie.FirstPartySets.InitializationDuration.ReadComponentSets2",
construction_timer_.Elapsed());
MaybeFinishLoading();
}
@@ -144,33 +176,141 @@ void FirstPartySetsLoader::DisposeFile(base::File sets_file) {
}
}
+std::vector<FirstPartySetsLoader::SingleSet>
+FirstPartySetsLoader::NormalizeAdditionSets(
+ const FlattenedSets& existing_sets,
+ const std::vector<SingleSet>& addition_sets) {
+ // Create a mapping from an owner site in `existing_sets` to all policy sets
+ // that intersect with the set that it owns.
+ base::flat_map<net::SchemefulSite, base::flat_set<size_t>>
+ policy_set_overlaps;
+ for (size_t set_idx = 0; set_idx < addition_sets.size(); set_idx++) {
+ const net::SchemefulSite& owner = addition_sets[set_idx].first;
+ AddIfPolicySetOverlaps(owner, set_idx, existing_sets, policy_set_overlaps);
+ for (const net::SchemefulSite& member : addition_sets[set_idx].second) {
+ AddIfPolicySetOverlaps(member, set_idx, existing_sets,
+ policy_set_overlaps);
+ }
+ }
+
+ AdditionOverlapsUnionFind union_finder(addition_sets.size());
+ for (auto& [public_site, policy_set_indices] : policy_set_overlaps) {
+ // Union together all overlapping policy sets to determine which one will
+ // take ownership.
+ for (size_t representative : policy_set_indices) {
+ union_finder.Union(*policy_set_indices.begin(), representative);
+ }
+ }
+
+ // The union-find data structure now knows which policy set should be given
+ // the role of representative for each entry in policy_set_overlaps.
+ // AdditionOverlapsUnionFind::SetsMapping returns a map from representative
+ // index to list of its children.
+ std::vector<SingleSet> normalized_additions;
+ for (auto& [rep, children] : union_finder.SetsMapping()) {
+ SingleSet normalized = addition_sets[rep];
+ for (size_t child_set_idx : children) {
+ // Update normalized to absorb the child_set_idx-th addition set.
+ const SingleSet& child_set = addition_sets[child_set_idx];
+ normalized.second.insert(child_set.first);
+ normalized.second.insert(child_set.second.begin(),
+ child_set.second.end());
+ }
+ normalized_additions.push_back(normalized);
+ }
+ return normalized_additions;
+}
+
void FirstPartySetsLoader::ApplyManuallySpecifiedSet() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(component_sets_parse_progress_, Progress::kFinished);
- DCHECK(manually_specified_set_.has_value());
+ DCHECK(HasAllInputs());
if (!manually_specified_set_.value().has_value())
return;
+ ApplyReplacementOverrides({manually_specified_set_->value()});
+ RemoveAllSingletons();
+}
+
+void FirstPartySetsLoader::ApplyReplacementOverrides(
+ const std::vector<SingleSet>& override_sets) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(HasAllInputs());
+
+ base::flat_set<net::SchemefulSite> all_override_sites =
+ FlattenSingleSetList(override_sets);
+
+ // Erase the intersection between |sets_| and the list of |override_sets| and
+ // any members whose owner was in the intersection.
+ base::EraseIf(
+ sets_, [&all_override_sites](
+ const std::pair<net::SchemefulSite, net::SchemefulSite>& p) {
+ return all_override_sites.contains(p.first) ||
+ all_override_sites.contains(p.second);
+ });
+
+ // Next, we must add each site in the override_sets to |sets_|.
+ for (auto& [owner, members] : override_sets) {
+ sets_.emplace(owner, owner);
+ for (const net::SchemefulSite& member : members) {
+ sets_.emplace(member, owner);
+ }
+ }
+}
+
+void FirstPartySetsLoader::ApplyAdditionOverrides(
+ const std::vector<SingleSet>& new_sets) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(HasAllInputs());
+
+ if (new_sets.empty())
+ return;
- const net::SchemefulSite& manual_owner =
- manually_specified_set_.value()->first;
- const base::flat_set<net::SchemefulSite>& manual_members =
- manually_specified_set_.value()->second;
+ std::vector<SingleSet> normalized_additions =
+ NormalizeAdditionSets(sets_, new_sets);
- const auto was_manually_provided =
- [&manual_members, &manual_owner](const net::SchemefulSite& site) {
- return site == manual_owner || manual_members.contains(site);
- };
+ FlattenedSets flattened_additions;
+ for (const auto& [owner, members] : normalized_additions) {
+ for (const net::SchemefulSite& member : members)
+ flattened_additions.emplace(member, owner);
+ flattened_additions.emplace(owner, owner);
+ }
- // Erase the intersection between the manually-specified set and the
- // CU-supplied set, and any members whose owner was in the intersection.
- base::EraseIf(sets_, [&was_manually_provided](const auto& p) {
- return was_manually_provided(p.first) || was_manually_provided(p.second);
- });
+ // Identify intersections between addition sets and existing sets. This will
+ // be used to reparent existing sets if they intersect with an addition set.
+ //
+ // Since we reparent every member of an existing set (regardless of whether
+ // the intersection was via one of its members or its owner), we just keep
+ // track of the set itself, via its owner.
+ base::flat_map<net::SchemefulSite, net::SchemefulSite> owners_in_intersection;
+ for (const auto& [site, owner] : flattened_additions) {
+ // Found an overlap with an existing set. Add the existing owner to the
+ // map.
+ if (auto it = sets_.find(site); it != sets_.end()) {
+ owners_in_intersection[it->second] = owner;
+ }
+ }
+
+ // Update the (site, owner) mappings in sets_ such that if owner is in the
+ // intersection, then the site is mapped to owners_in_intersection[owner].
+ //
+ // This reparents existing sets to their owner given by the normalized
+ // addition sets.
+ for (auto& [site, owner] : sets_) {
+ if (auto owner_entry = owners_in_intersection.find(owner);
+ owner_entry != owners_in_intersection.end()) {
+ owner = owner_entry->second;
+ }
+ }
- // Now remove singleton sets. We already removed any sites that were part
- // of the intersection, or whose owner was part of the intersection. This
- // leaves sites that *are* owners, which no longer have any (other)
- // members.
+ // Since the intersection between sets_ and flattened_additions has already
+ // been updated above, we can insert flattened_additions into sets_ without
+ // affecting any existing mappings in sets_.
+ sets_.insert(flattened_additions.begin(), flattened_additions.end());
+}
+
+void FirstPartySetsLoader::RemoveAllSingletons() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Now remove singleton sets, which are sets that just contain sites that
+ // *are* owners, but no longer have any (other) members.
std::set<net::SchemefulSite> owners_with_members;
for (const auto& it : sets_) {
if (it.first != it.second)
@@ -179,21 +319,29 @@ void FirstPartySetsLoader::ApplyManuallySpecifiedSet() {
base::EraseIf(sets_, [&owners_with_members](const auto& p) {
return p.first == p.second && !base::Contains(owners_with_members, p.first);
});
+}
- // Next, we must add the manually-added set to the parsed value.
- for (const net::SchemefulSite& member : manual_members) {
- sets_.emplace(member, manual_owner);
- }
- sets_.emplace(manual_owner, manual_owner);
+void FirstPartySetsLoader::ApplyAllPolicyOverrides() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(HasAllInputs());
+ ApplyReplacementOverrides(policy_overrides_.replacements);
+ ApplyAdditionOverrides(policy_overrides_.additions);
+ RemoveAllSingletons();
}
void FirstPartySetsLoader::MaybeFinishLoading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (component_sets_parse_progress_ != Progress::kFinished ||
- !manually_specified_set_.has_value())
+ if (!HasAllInputs())
return;
ApplyManuallySpecifiedSet();
+ ApplyAllPolicyOverrides();
std::move(on_load_complete_).Run(std::move(sets_));
}
-} // namespace content
+bool FirstPartySetsLoader::HasAllInputs() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return component_sets_parse_progress_ == Progress::kFinished &&
+ manually_specified_set_.has_value();
+}
+
+} // namespace content
\ No newline at end of file
diff --git a/content/browser/first_party_sets/first_party_sets_loader.h b/content/browser/first_party_sets/first_party_sets_loader.h
index c27d2300c4c3533df91a8e814e89827bbd7d72ae..8588fe5f9922b36c9fe8e16ac16d02ccf00c8723 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.h
+++ b/content/browser/first_party_sets/first_party_sets_loader.h
@@ -57,6 +57,19 @@ class CONTENT_EXPORT FirstPartySetsLoader {
// Close the file on thread pool that allows blocking.
void DisposeFile(base::File sets_file);
+ // Handles addition sets which overlap by intersecting with the same existing
+ // set, known as a transitive-overlap.
+ //
+ // This uses a Union-Find algorithm to select the earliest-provided addition
+ // set as the representative of all other addition sets that
+ // transitively-overlap with it.
+ //
+ // The "earliest-provided" tie-breaker is determined using a set's index in
+ // `addition_sets`.
+ static std::vector<SingleSet> NormalizeAdditionSets(
+ const FlattenedSets& existing_sets,
+ const std::vector<SingleSet>& addition_sets);
+
private:
// Parses the contents of `raw_sets` as a collection of First-Party Set
// declarations, and assigns to `sets_`.
@@ -67,10 +80,42 @@ class CONTENT_EXPORT FirstPartySetsLoader {
// `SetManuallySpecifiedSet`, and the public sets via `SetComponentSets`.
void ApplyManuallySpecifiedSet();
+ // Removes the intersection between `sets_` and `override_sets` from the
+ // `sets_` member variable, and then adds the `override_sets` into `sets_`.
+ void ApplyReplacementOverrides(const std::vector<SingleSet>& override_sets);
+
+ // Updates the intersection between `sets_` and `override_sets` within the
+ // `sets_` member variable, and then adds the `override_sets` into
+ // `sets_`.
+ //
+ // The applied update ensures that invariants of First-Party Sets are
+ // maintained, and that all sets in sets_ are disjoint.
+ //
+ // This will add in the `override_sets` into `sets_` without removing
+ // any existing sites from the list of First-Party Sets.
+ void ApplyAdditionOverrides(const std::vector<SingleSet>& override_sets);
+
+ // Removes all singletons (owners that have no members) from sets_.
+ void RemoveAllSingletons();
+
+ // Applies the First-Party Sets overrides provided by policy.
+ //
+ // Must not be called until the loader has already received the public sets
+ // via `SetComponentSets` and the CLI-provided sets have been applied to
+ // `sets_`.
+ //
+ // Applies "Replacement" overrides before applying "Addition" overrides.
+ void ApplyAllPolicyOverrides();
+
// Checks the required inputs have been received, and if so, invokes the
// callback `on_load_complete_`, after merging sets appropriately.
void MaybeFinishLoading();
+ // Returns true if all sources are present (Component Updater sets, CLI set,
+ // and Policy sets). The Policy sets are provided at construction time, so
+ // this effectively checks that the other two sources are ready.
+ bool HasAllInputs() const;
+
// Represents the mapping of site -> site, where keys are members of sets,
// and values are owners of the sets (explicitly including an entry of owner
// -> owner).
@@ -117,4 +162,4 @@ class CONTENT_EXPORT FirstPartySetsLoader {
} // namespace content
-#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_LOADER_H_
+#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_LOADER_H_
\ No newline at end of file
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 16fb4946cb3ea2d097e8ed05bb340cc3f0782ed6..b751bf43d41a149579047faeec958646ecc3a5e0 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1327,6 +1327,10 @@ bool ContentBrowserClient::IsFirstPartySetsEnabled() {
return base::FeatureList::IsEnabled(features::kFirstPartySets);
}
+bool ContentBrowserClient::WillProvidePublicFirstPartySets() {
+ return false;
+}
+
base::Value::Dict ContentBrowserClient::GetFirstPartySetsOverrides() {
return base::Value::Dict();
}
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 4db1e6c2dbea27249ca15d5660b7fcd8c6736ad1..8aac2a95a3ac66f0b0c180f46ec29ca7341a1d4c 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -2227,6 +2227,16 @@ class CONTENT_EXPORT ContentBrowserClient {
// should not change in a single browser session.
virtual bool IsFirstPartySetsEnabled();
+ // Returns true iff the embedder will provide a list of First-Party Sets via
+ // content::FirstPartySetsHandler::SetPublicFirstPartySets during startup, at
+ // some point. If `IsFirstPartySetsEnabled()` returns false, this method will
+ // still be called, but its return value will be ignored.
+ //
+ // If this method returns false but `IsFirstPartySetsEnabled()` returns true
+ // (e.g. in tests), an empty list will be used instead of waiting for the
+ // embedder to call content::FirstPartySetsHandler::SetPublicFirstPartySets.
+ virtual bool WillProvidePublicFirstPartySets();
+
// Returns a base::Value::Dict containing the value of the First-Party Sets
// Overrides enterprise policy.
// If the policy was not present or it was invalid, this returns an empty
diff --git a/content/public/browser/first_party_sets_handler.h b/content/public/browser/first_party_sets_handler.h
index 8bd62717732a4afe1e8c02fd5ceb39bd1790e3e0..3ff5dea5612fe3329c7c612818f1436a980ea9e3 100644
--- a/content/public/browser/first_party_sets_handler.h
+++ b/content/public/browser/first_party_sets_handler.h
@@ -72,7 +72,9 @@ class CONTENT_EXPORT FirstPartySetsHandler {
//
// Embedder should call this method as early as possible during browser
// startup if First-Party Sets are enabled, since no First-Party Sets queries
- // are answered until initialization is complete.
+ // are answered until initialization is complete. Must not be called if
+ // `ContentBrowserClient::WillProvidePublicFirstPartySets` returns false or
+ // `ContentBrowserClient::IsFrstpartySetsEnabled` returns false.
virtual void SetPublicFirstPartySets(base::File sets_file) = 0;
// Resets the state on the instance for testing.
diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc
index 89040432c9f56d8285d8e9f59e1e0280abc25199..bd412789695f49253f67f237b2a92b8dee298e7a 100644
--- a/content/shell/browser/shell_browser_main_parts.cc
+++ b/content/shell/browser/shell_browser_main_parts.cc
@@ -189,8 +189,6 @@ int ShellBrowserMainParts::PreMainMessageLoopRun() {
net::NetModule::SetResourceProvider(PlatformResourceProvider);
ShellDevToolsManagerDelegate::StartHttpHandler(browser_context_.get());
InitializeMessageLoopContext();
- // The First-Party Sets feature always expects to be initialized
- FirstPartySetsHandler::GetInstance()->SetPublicFirstPartySets(base::File());
return 0;
}

View File

@@ -122,7 +122,7 @@ index c15f3a631292b538698625328fb429ee3c9964f5..37e038753ecf1b82ec92c06b2c0729b5
}
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 3f7dce0281f7b5a540d7b9377ef14a8a6aa9a2fa..11d8419791f3e45d5242081422d452d4fc703833 100644
index d342eb4e9fc94de4365d86c2d1af4b85a8bf63a3..012f9ce97d9ed6b00deb718a88f432e053cb3bd1 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -42,6 +42,7 @@

View File

@@ -14,7 +14,7 @@ Note that we also need to manually update embedder's
`api::WebContents::IsFullscreenForTabOrPending` value.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 4fd4508ee8352f098e0b2a0119fc19744a50da76..29e1c762b81025de1e3ff8b1a76fb2e71194456e 100644
index 0287e414f4020147ddee297f7c1447c32a118905..781793ae5cb461b7451b5c448465ba5b41aa805d 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -6326,6 +6326,15 @@ void RenderFrameHostImpl::EnterFullscreen(

View File

@@ -4,6 +4,6 @@ dcheck.patch
export_symbols_needed_for_windows_build.patch
workaround_an_undefined_symbol_error.patch
do_not_export_private_v8_symbols_on_windows.patch
fix_build_deprecated_attirbute_for_older_msvc_versions.patch
fix_build_deprecated_attribute_for_older_msvc_versions.patch
fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
revert_fix_cppgc_removed_deleted_cstors_in_cppheapcreateparams.patch

View File

@@ -1,15 +1,12 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Deepak Mohan <hop2deep@gmail.com>
Date: Tue, 28 Jan 2020 15:48:03 -0800
Subject: fix: usage of c++ [[deprecated]] attirbute for older msvc versions
Subject: fix: usage of c++ [[deprecated]] attribute for older msvc versions
VS 2015 update 3 has a bug where [[deprecated]] attribute cannot
be applied to constructor declarations, this is fixed in 2017 and
higher versions, but native module compiling with this version
will have an issue.
This attribute can only be used in all contexts in Visual Studio 2019
diff --git a/include/v8config.h b/include/v8config.h
index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503ef79e2f5 100644
index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..d203053d41c702733f5f3b950aa31cef74c2ab57 100644
--- a/include/v8config.h
+++ b/include/v8config.h
@@ -454,10 +454,13 @@ path. Add it with -I<path> to the command line
@@ -20,7 +17,7 @@ index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503
// A macro (V8_DEPRECATED) to mark classes or functions as deprecated.
#if defined(V8_DEPRECATION_WARNINGS)
-# define V8_DEPRECATED(message) [[deprecated(message)]]
+# if defined(_MSC_VER) && _MSC_VER <= 1900
+# if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1920
+# define V8_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# define V8_DEPRECATED(message) [[deprecated(message)]]
@@ -33,7 +30,7 @@ index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503
// A macro (V8_DEPRECATE_SOON) to make it easier to see what will be deprecated.
#if defined(V8_IMMINENT_DEPRECATION_WARNINGS)
-# define V8_DEPRECATE_SOON(message) [[deprecated(message)]]
+# if defined(_MSC_VER) && _MSC_VER <= 1900
+# if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1920
+# define V8_DEPRECATE_SOON(message) __declspec(deprecated(message))
+# else
+# define V8_DEPRECATE_SOON(message) [[deprecated(message)]]

View File

@@ -20,12 +20,7 @@ let anErrorOccurred = false;
function next (done) {
const file = files.shift();
if (!file) return done();
let key = filenameToKey(file);
// TODO: When we drop s3put, migrate the key to not include atom-shell in the callsites
key = key.replace('atom-shell/dist/', 'headers/dist/');
key = key.replace('atom-shell/symbols/', 'symbols/');
key = key.replace('atom-shell/tmp/', 'checksums-scratchpad/');
key = key.replace('electron-artifacts/', 'release-builds/');
const key = filenameToKey(file);
const [containerName, ...keyPath] = key.split('/');
const blobKey = keyPath.join('/');

View File

@@ -51,17 +51,6 @@ def get_env_var(name):
return value
def s3_config():
config = (get_env_var('S3_BUCKET'),
get_env_var('S3_ACCESS_KEY'),
get_env_var('S3_SECRET_KEY'))
message = ('Error: Please set the $ELECTRON_S3_BUCKET, '
'$ELECTRON_S3_ACCESS_KEY, and '
'$ELECTRON_S3_SECRET_KEY environment variables')
assert all(len(c) for c in config), message
return config
def enable_verbose_mode():
print('Running in verbose mode')
global verbose_mode

View File

@@ -1,40 +0,0 @@
/* eslint-disable camelcase */
const AWS = require('aws-sdk');
const fs = require('fs');
const path = require('path');
AWS.config.update({ region: 'us-west-2' });
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
const args = require('minimist')(process.argv.slice(2));
let { bucket, prefix = '/', key_prefix = '', grant, _: files } = args;
if (prefix && !prefix.endsWith(path.sep)) prefix = path.resolve(prefix) + path.sep;
function filenameToKey (file) {
file = path.resolve(file);
if (file.startsWith(prefix)) file = file.substr(prefix.length - 1);
return key_prefix + (path.sep === '\\' ? file.replace(/\\/g, '/') : file);
}
let anErrorOccurred = false;
function next (done) {
const file = files.shift();
if (!file) return done();
const key = filenameToKey(file);
console.log(`Uploading '${file}' to bucket '${bucket}' with key '${key}'...`);
s3.upload({
Bucket: bucket,
Key: key,
Body: fs.createReadStream(file),
ACL: grant
}, (err, data) => {
if (err) {
console.error(err);
anErrorOccurred = true;
}
next(done);
});
}
next(() => {
process.exit(anErrorOccurred ? 1 : 0);
});

View File

@@ -15,7 +15,7 @@ except ImportError:
from urllib2 import urlopen
import zipfile
from lib.config import is_verbose_mode, s3_config
from lib.config import is_verbose_mode
ELECTRON_DIR = os.path.abspath(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
@@ -156,26 +156,9 @@ def get_electron_version():
return 'v' + f.read().strip()
def store_artifact(prefix, key_prefix, files):
# Legacy S3 Bucket
s3put(prefix, key_prefix, files)
# New AZ Storage
# Azure Storage
azput(prefix, key_prefix, files)
def s3put(prefix, key_prefix, files):
bucket, access_key, secret_key = s3_config()
env = os.environ.copy()
env['AWS_ACCESS_KEY_ID'] = access_key
env['AWS_SECRET_ACCESS_KEY'] = secret_key
output = execute([
'node',
os.path.join(os.path.dirname(__file__), 's3put.js'),
'--bucket', bucket,
'--prefix', prefix,
'--key_prefix', key_prefix,
'--grant', 'public-read',
] + files, env)
print(output)
def azput(prefix, key_prefix, files):
env = os.environ.copy()
output = execute([

View File

@@ -55,7 +55,7 @@ const getCustomOptions = () => {
async function main () {
const options = args.default ? defaultOptions : getCustomOptions();
const testChild = cp.spawn('python', options, {
const testChild = cp.spawn('python3', options, {
env: {
...process.env,
ELECTRON_RUN_AS_NODE: 'true',

View File

@@ -62,9 +62,9 @@ async function circleCIcall (targetBranch, workflowName, options) {
parameters: {}
};
if (options.ghRelease) {
buildRequest.parameters['upload-to-s3'] = '0';
buildRequest.parameters['upload-to-storage'] = '0';
} else {
buildRequest.parameters['upload-to-s3'] = '1';
buildRequest.parameters['upload-to-storage'] = '1';
}
buildRequest.parameters[`run-${workflowName}`] = true;
if (options.arch) {
@@ -205,7 +205,7 @@ async function callAppVeyor (targetBranch, job, options) {
};
if (!options.ghRelease) {
environmentVariables.UPLOAD_TO_S3 = 1;
environmentVariables.UPLOAD_TO_STORAGE = 1;
}
const requestOpts = {

View File

@@ -1,34 +1,25 @@
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda({
credentials: {
accessKeyId: process.env.AWS_LAMBDA_EXECUTE_KEY,
secretAccessKey: process.env.AWS_LAMBDA_EXECUTE_SECRET
},
region: 'us-east-1'
});
const got = require('got');
const url = require('url');
module.exports = async function getUrlHash (targetUrl, algorithm = 'sha256', attempts = 3) {
const options = {
code: process.env.ELECTRON_ARTIFACT_HASHER_FUNCTION_KEY,
targetUrl,
algorithm
};
const search = new url.URLSearchParams(options);
const functionUrl = url.format({
protocol: 'https:',
hostname: 'electron-artifact-hasher.azurewebsites.net',
pathname: '/api/HashArtifact',
search: search.toString()
});
try {
return new Promise((resolve, reject) => {
lambda.invoke({
FunctionName: 'hasher',
Payload: JSON.stringify({
targetUrl,
algorithm
})
}, (err, data) => {
if (err) return reject(err);
try {
const response = JSON.parse(data.Payload);
if (response.statusCode !== 200) return reject(new Error('non-200 status code received from hasher function'));
if (!response.hash) return reject(new Error('Successful lambda call but failed to get valid hash'));
resolve(response.hash);
} catch (err) {
return reject(err);
}
});
});
const resp = await got(functionUrl);
if (resp.statusCode !== 200) throw new Error('non-200 status code received from hasher function');
if (!resp.body) throw new Error('Successful lambda call but failed to get valid hash');
return resp.body.trim();
} catch (err) {
if (attempts > 1) {
console.error('Failed to get URL hash for', targetUrl, 'we will retry', err);

View File

@@ -78,8 +78,6 @@ async function validateReleaseAssets (release, validatingRelease) {
console.log(`${fail} error verifyingShasums`, err);
});
}
const s3RemoteFiles = s3RemoteFilesForVersion(release.tag_name);
await verifyShasumsForRemoteFiles(s3RemoteFiles, true);
const azRemoteFiles = azRemoteFilesForVersion(release.tag_name);
await verifyShasumsForRemoteFiles(azRemoteFiles, true);
}
@@ -130,9 +128,8 @@ function assetsForVersion (version, validatingRelease) {
`electron-${version}-mas-arm64-dsym-snapshot.zip`,
`electron-${version}-mas-arm64-symbols.zip`,
`electron-${version}-mas-arm64.zip`,
// TODO(jkleinsc) Symbol generation on 32-bit Windows is temporarily disabled due to failures
// `electron-${version}-win32-ia32-pdb.zip`,
// `electron-${version}-win32-ia32-symbols.zip`,
`electron-${version}-win32-ia32-pdb.zip`,
`electron-${version}-win32-ia32-symbols.zip`,
`electron-${version}-win32-ia32.zip`,
`electron-${version}-win32-x64-pdb.zip`,
`electron-${version}-win32-x64-symbols.zip`,
@@ -195,15 +192,6 @@ const cloudStoreFilePaths = (version) => [
'SHASUMS256.txt'
];
function s3RemoteFilesForVersion (version) {
const bucket = 'https://gh-contractor-zcbenz.s3.amazonaws.com/';
const versionPrefix = `${bucket}atom-shell/dist/${version}/`;
return cloudStoreFilePaths(version).map((filePath) => ({
file: filePath,
url: `${versionPrefix}${filePath}`
}));
}
function azRemoteFilesForVersion (version) {
const azCDN = 'https://artifacts.electronjs.org/headers/';
const versionPrefix = `${azCDN}dist/${version}/`;

View File

@@ -59,7 +59,7 @@ def main():
with open(index_json, "wb") as f:
f.write(new_content)
store_artifact(OUT_DIR, 'atom-shell/dist', [index_json])
store_artifact(OUT_DIR, 'headers/dist', [index_json])
if __name__ == '__main__':

View File

@@ -29,7 +29,7 @@ def main():
]
if args.target_dir is None:
store_artifact(directory, 'atom-shell/dist/{0}'.format(args.version),
store_artifact(directory, 'headers/dist/{0}'.format(args.version),
checksums)
else:
copy_files(checksums, args.target_dir)

View File

@@ -45,9 +45,9 @@ def upload_node(version):
versioned_header_tar = header_tar.format(version)
shutil.copy2(generated_tar, os.path.join(GEN_DIR, versioned_header_tar))
store_artifact(GEN_DIR, 'atom-shell/dist/{0}'.format(version),
store_artifact(GEN_DIR, 'headers/dist/{0}'.format(version),
glob.glob('node-*.tar.gz'))
store_artifact(GEN_DIR, 'atom-shell/dist/{0}'.format(version),
store_artifact(GEN_DIR, 'headers/dist/{0}'.format(version),
glob.glob('iojs-*.tar.gz'))
if PLATFORM == 'win32':
@@ -73,13 +73,13 @@ def upload_node(version):
shutil.copy2(electron_lib, v4_node_lib)
# Upload the node.lib.
store_artifact(DIST_DIR, 'atom-shell/dist/{0}'.format(version), [node_lib])
store_artifact(DIST_DIR, 'headers/dist/{0}'.format(version), [node_lib])
# Upload the iojs.lib.
store_artifact(DIST_DIR, 'atom-shell/dist/{0}'.format(version), [iojs_lib])
store_artifact(DIST_DIR, 'headers/dist/{0}'.format(version), [iojs_lib])
# Upload the v4 node.lib.
store_artifact(DIST_DIR, 'atom-shell/dist/{0}'.format(version),
store_artifact(DIST_DIR, 'headers/dist/{0}'.format(version),
[v4_node_lib])

View File

@@ -56,7 +56,7 @@ def main():
files += glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')
# The file upload needs to be atom-shell/symbols/:symbol_name/:hash/:symbol
# The file upload needs to be symbols/:symbol_name/:hash/:symbol
os.chdir(SYMBOLS_DIR)
files = [os.path.relpath(f, os.getcwd()) for f in files]
@@ -84,7 +84,7 @@ def run_symstore(pdb, dest, product):
def upload_symbols(files):
store_artifact(SYMBOLS_DIR, 'atom-shell/symbols',
store_artifact(SYMBOLS_DIR, 'symbols',
files)

View File

@@ -47,7 +47,7 @@ def main():
args = parse_args()
if args.verbose:
enable_verbose_mode()
if args.upload_to_s3:
if args.upload_to_storage:
utcnow = datetime.datetime.utcnow()
args.upload_timestamp = utcnow.strftime('%Y%m%d')
@@ -64,7 +64,7 @@ def main():
if not release['draft']:
tag_exists = True
if not args.upload_to_s3:
if not args.upload_to_storage:
assert release['exists'], \
'Release does not exist; cannot upload to GitHub!'
assert tag_exists == args.overwrite, \
@@ -76,10 +76,9 @@ def main():
shutil.copy2(os.path.join(OUT_DIR, 'dist.zip'), electron_zip)
upload_electron(release, electron_zip, args)
if get_target_arch() != 'mips64el':
if get_target_arch() != 'ia32' or PLATFORM != 'win32':
symbols_zip = os.path.join(OUT_DIR, SYMBOLS_NAME)
shutil.copy2(os.path.join(OUT_DIR, 'symbols.zip'), symbols_zip)
upload_electron(release, symbols_zip, args)
symbols_zip = os.path.join(OUT_DIR, SYMBOLS_NAME)
shutil.copy2(os.path.join(OUT_DIR, 'symbols.zip'), symbols_zip)
upload_electron(release, symbols_zip, args)
if PLATFORM == 'darwin':
if get_platform_key() == 'darwin' and get_target_arch() == 'x64':
api_path = os.path.join(ELECTRON_DIR, 'electron-api.json')
@@ -96,10 +95,9 @@ def main():
shutil.copy2(os.path.join(OUT_DIR, 'dsym-snapshot.zip'), dsym_snaphot_zip)
upload_electron(release, dsym_snaphot_zip, args)
elif PLATFORM == 'win32':
if get_target_arch() != 'ia32':
pdb_zip = os.path.join(OUT_DIR, PDB_NAME)
shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip)
upload_electron(release, pdb_zip, args)
pdb_zip = os.path.join(OUT_DIR, PDB_NAME)
shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip)
upload_electron(release, pdb_zip, args)
elif PLATFORM == 'linux':
debug_zip = os.path.join(OUT_DIR, DEBUG_NAME)
shutil.copy2(os.path.join(OUT_DIR, 'debug.zip'), debug_zip)
@@ -148,7 +146,7 @@ def main():
OUT_DIR, 'hunspell_dictionaries.zip')
upload_electron(release, hunspell_dictionaries_zip, args)
if not tag_exists and not args.upload_to_s3:
if not tag_exists and not args.upload_to_storage:
# Upload symbols to symbol server.
run_python_upload_script('upload-symbols.py')
if PLATFORM == 'win32':
@@ -174,9 +172,9 @@ def parse_args():
parser.add_argument('-p', '--publish-release',
help='Publish the release',
action='store_true')
parser.add_argument('-s', '--upload_to_s3',
help='Upload assets to s3 bucket',
dest='upload_to_s3',
parser.add_argument('-s', '--upload_to_storage',
help='Upload assets to azure bucket',
dest='upload_to_storage',
action='store_true',
default=False,
required=False)
@@ -342,9 +340,10 @@ def upload_electron(release, file_path, args):
except NonZipFileError:
pass
# if upload_to_s3 is set, skip github upload.
if args.upload_to_s3:
key_prefix = 'electron-artifacts/{0}_{1}'.format(args.version,
# if upload_to_storage is set, skip github upload.
# todo (vertedinde): migrate this variable to upload_to_storage
if args.upload_to_storage:
key_prefix = 'release-builds/{0}_{1}'.format(args.version,
args.upload_timestamp)
store_artifact(os.path.dirname(file_path), key_prefix, [file_path])
upload_sha256_checksum(args.version, file_path, key_prefix)
@@ -369,7 +368,7 @@ def upload_io_to_github(release, filename, filepath, version):
def upload_sha256_checksum(version, file_path, key_prefix=None):
checksum_path = '{}.sha256sum'.format(file_path)
if key_prefix is None:
key_prefix = 'atom-shell/tmp/{0}'.format(version)
key_prefix = 'checksums-scratchpad/{0}'.format(version)
sha256 = hashlib.sha256()
with open(file_path, 'rb') as f:
sha256.update(f.read())

View File

@@ -31,12 +31,24 @@ void SetElectronCryptoReady(bool ready) {
#endif
bool IsEncryptionAvailable() {
#if BUILDFLAG(IS_LINUX)
// Calling IsEncryptionAvailable() before the app is ready results in a crash
// on Linux.
// Refs: https://github.com/electron/electron/issues/32206.
if (!Browser::Get()->is_ready())
return false;
#endif
return OSCrypt::IsEncryptionAvailable();
}
v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
const std::string& plaintext) {
if (!OSCrypt::IsEncryptionAvailable()) {
if (!IsEncryptionAvailable()) {
if (!Browser::Get()->is_ready()) {
gin_helper::ErrorThrower(isolate).ThrowError(
"safeStorage cannot be used before app is ready");
return v8::Local<v8::Value>();
}
gin_helper::ErrorThrower(isolate).ThrowError(
"Error while decrypting the ciphertext provided to "
"safeStorage.decryptString. "
@@ -59,7 +71,12 @@ v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
}
std::string DecryptString(v8::Isolate* isolate, v8::Local<v8::Value> buffer) {
if (!OSCrypt::IsEncryptionAvailable()) {
if (!IsEncryptionAvailable()) {
if (!Browser::Get()->is_ready()) {
gin_helper::ErrorThrower(isolate).ThrowError(
"safeStorage cannot be used before app is ready");
return "";
}
gin_helper::ErrorThrower(isolate).ThrowError(
"Error while decrypting the ciphertext provided to "
"safeStorage.decryptString. "

View File

@@ -314,26 +314,32 @@ Browser::LoginItemSettings Browser::GetLoginItemSettings(
return settings;
}
// Some logic here copied from GetLoginItemForApp in base/mac/mac_util.mm
void RemoveFromLoginItems() {
#pragma clang diagnostic push // https://crbug.com/1154377
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// logic to find the login item copied from GetLoginItemForApp in
// base/mac/mac_util.mm
base::ScopedCFTypeRef<LSSharedFileListRef> login_items(
LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
if (!login_items.get()) {
LOG(ERROR) << "Couldn't get a Login Items list.";
return;
}
base::scoped_nsobject<NSArray> login_items_array(
base::mac::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
for (id login_item in login_items_array.get()) {
LSSharedFileListItemRef item =
reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
reinterpret_cast<LSSharedFileListItemRef>(login_item);
// kLSSharedFileListDoNotMountVolumes is used so that we don't trigger
// mounting when it's not expected by a user. Just listing the login
// items should not cause any side-effects.
base::ScopedCFTypeRef<CFErrorRef> error;
CFURLRef item_url_ref =
LSSharedFileListItemCopyResolvedURL(item, 0, error.InitializeInto());
base::ScopedCFTypeRef<CFURLRef> item_url_ref(
LSSharedFileListItemCopyResolvedURL(
item, kLSSharedFileListDoNotMountVolumes, error.InitializeInto()));
if (!error && item_url_ref) {
base::ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
if (CFEqual(item_url, url)) {

View File

@@ -417,11 +417,6 @@ int ElectronBrowserMainParts::PreMainMessageLoopRun() {
// url::Add*Scheme are not threadsafe, this helps prevent data races.
url::LockSchemeRegistries();
// The First-Party Sets feature always expects to be initialized
// CL: https://chromium-review.googlesource.com/c/chromium/src/+/3448551
content::FirstPartySetsHandler::GetInstance()->SetPublicFirstPartySets(
base::File());
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
extensions_client_ = std::make_unique<ElectronExtensionsClient>();
extensions::ExtensionsClient::Set(extensions_client_.get());

View File

@@ -397,7 +397,7 @@ void OffScreenRenderWidgetHostView::ResetFallbackToFirstNavigationSurface() {
void OffScreenRenderWidgetHostView::InitAsPopup(
content::RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos,
const gfx::Rect& bounds,
const gfx::Rect& anchor_rect) {
DCHECK_EQ(parent_host_view_, parent_host_view);
DCHECK_EQ(widget_type_, content::WidgetType::kPopup);
@@ -411,13 +411,10 @@ void OffScreenRenderWidgetHostView::InitAsPopup(
base::BindRepeating(&OffScreenRenderWidgetHostView::OnPopupPaint,
parent_host_view_->weak_ptr_factory_.GetWeakPtr());
popup_position_ = pos;
popup_position_ = bounds;
ResizeRootLayer(false);
ResizeRootLayer(true);
SetPainting(parent_host_view_->IsPainting());
if (video_consumer_) {
video_consumer_->SizeChanged();
}
Show();
}
@@ -688,13 +685,8 @@ void OffScreenRenderWidgetHostView::OnPaint(const gfx::Rect& damage_rect,
gfx::Size OffScreenRenderWidgetHostView::SizeInPixels() {
float sf = GetDeviceScaleFactor();
if (IsPopupWidget()) {
return gfx::ToFlooredSize(
gfx::ConvertSizeToPixels(popup_position_.size(), sf));
} else {
return gfx::ToFlooredSize(
gfx::ConvertSizeToPixels(GetViewBounds().size(), sf));
}
return gfx::ToFlooredSize(
gfx::ConvertSizeToPixels(GetViewBounds().size(), sf));
}
void OffScreenRenderWidgetHostView::CompositeFrame(
@@ -995,7 +987,7 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) {
display::Screen::GetScreen()->GetDisplayNearestView(GetNativeView());
const float scaleFactor = display.device_scale_factor();
float sf = GetDeviceScaleFactor();
const bool scaleFactorDidChange = scaleFactor != sf;
const bool sf_did_change = scaleFactor != sf;
// Initialize a screen_infos_ struct as needed, to cache the scale factor.
if (screen_infos_.screen_infos.empty()) {
@@ -1003,14 +995,9 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) {
}
screen_infos_.mutable_current().device_scale_factor = scaleFactor;
gfx::Size size;
if (!IsPopupWidget())
size = GetViewBounds().size();
else
size = popup_position_.size();
gfx::Size size = GetViewBounds().size();
if (!force && !scaleFactorDidChange &&
size == GetRootLayer()->bounds().size())
if (!force && !sf_did_change && size == GetRootLayer()->bounds().size())
return;
GetRootLayer()->SetBounds(gfx::Rect(size));

View File

@@ -121,7 +121,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void ResetFallbackToFirstNavigationSurface() override;
void InitAsPopup(content::RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos,
const gfx::Rect& bounds,
const gfx::Rect& anchor_rect) override;
void UpdateCursor(const content::WebCursor&) override;
void SetIsLoading(bool is_loading) override;

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "media/base/limits.h"
#include "media/base/video_frame_metadata.h"
#include "media/capture/mojom/video_capture_buffer.mojom.h"
#include "media/capture/mojom/video_capture_types.mojom.h"
@@ -13,6 +14,21 @@
#include "shell/browser/osr/osr_render_widget_host_view.h"
#include "ui/gfx/skbitmap_operations.h"
namespace {
bool IsValidMinAndMaxFrameSize(gfx::Size min_frame_size,
gfx::Size max_frame_size) {
// Returns true if
// 0 < |min_frame_size| <= |max_frame_size| <= media::limits::kMaxDimension.
return 0 < min_frame_size.width() && 0 < min_frame_size.height() &&
min_frame_size.width() <= max_frame_size.width() &&
min_frame_size.height() <= max_frame_size.height() &&
max_frame_size.width() <= media::limits::kMaxDimension &&
max_frame_size.height() <= media::limits::kMaxDimension;
}
} // namespace
namespace electron {
OffScreenVideoConsumer::OffScreenVideoConsumer(
@@ -21,11 +37,11 @@ OffScreenVideoConsumer::OffScreenVideoConsumer(
: callback_(callback),
view_(view),
video_capturer_(view->CreateVideoCapturer()) {
video_capturer_->SetResolutionConstraints(view_->SizeInPixels(),
view_->SizeInPixels(), true);
video_capturer_->SetAutoThrottlingEnabled(false);
video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB);
SizeChanged(view_->SizeInPixels());
SetFrameRate(view_->GetFrameRate());
}
@@ -43,9 +59,10 @@ void OffScreenVideoConsumer::SetFrameRate(int frame_rate) {
video_capturer_->SetMinCapturePeriod(base::Seconds(1) / frame_rate);
}
void OffScreenVideoConsumer::SizeChanged() {
video_capturer_->SetResolutionConstraints(view_->SizeInPixels(),
view_->SizeInPixels(), true);
void OffScreenVideoConsumer::SizeChanged(const gfx::Size& size_in_pixels) {
DCHECK(IsValidMinAndMaxFrameSize(size_in_pixels, size_in_pixels));
video_capturer_->SetResolutionConstraints(size_in_pixels, size_in_pixels,
true);
video_capturer_->RequestRefreshFrame();
}
@@ -58,9 +75,7 @@ void OffScreenVideoConsumer::OnFrameCaptured(
auto& data_region = data->get_read_only_shmem_region();
if (!CheckContentRect(content_rect)) {
gfx::Size view_size = view_->SizeInPixels();
video_capturer_->SetResolutionConstraints(view_size, view_size, true);
video_capturer_->RequestRefreshFrame();
SizeChanged(view_->SizeInPixels());
return;
}

View File

@@ -33,7 +33,7 @@ class OffScreenVideoConsumer : public viz::mojom::FrameSinkVideoConsumer {
void SetActive(bool active);
void SetFrameRate(int frame_rate);
void SizeChanged();
void SizeChanged(const gfx::Size& size_in_pixels);
private:
// viz::mojom::FrameSinkVideoConsumer implementation.

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 19,0,0,4
PRODUCTVERSION 19,0,0,4
FILEVERSION 19,0,0,7
PRODUCTVERSION 19,0,0,7
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -3,4 +3,5 @@ void gtk_native_dialog_set_modal(GtkNativeDialog* self, gboolean modal);
void gtk_native_dialog_show(GtkNativeDialog* self);
void gtk_native_dialog_hide(GtkNativeDialog* self);
gint gtk_native_dialog_run(GtkNativeDialog* self);
void gtk_native_dialog_destroy(GtkNativeDialog* self);
void gtk_native_dialog_destroy(GtkNativeDialog* self);
GType gtk_native_dialog_get_type(void);

View File

@@ -190,9 +190,9 @@
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
// Pass click to superclass to show menu. Custom mouseUp handler won't be
// invoked.
if (menuController_) {
// Pass click to superclass to show menu if one exists and has a non-zero
// number of items. Custom mouseUp handler won't be invoked in this case.
if (menuController_ && [[menuController_ menu] numberOfItems] > 0) {
[self handleClickNotifications:event];
[super mouseDown:event];
} else {

View File

@@ -10,6 +10,7 @@
#include "third_party/blink/public/web/web_local_frame.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/common/pdf_util.h"
#include "extensions/common/constants.h"
#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
@@ -24,22 +25,11 @@ PrintRenderFrameHelperDelegate::~PrintRenderFrameHelperDelegate() = default;
blink::WebElement PrintRenderFrameHelperDelegate::GetPdfElement(
blink::WebLocalFrame* frame) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
GURL url = frame->GetDocument().Url();
bool inside_pdf_extension =
url.SchemeIs(extensions::kExtensionScheme) &&
url.host_piece() == extension_misc::kPdfExtensionId;
if (inside_pdf_extension) {
// <object> with id="plugin" is created in
// chrome/browser/resources/pdf/pdf_viewer_base.js.
auto viewer_element = frame->GetDocument().GetElementById("viewer");
if (!viewer_element.IsNull() && !viewer_element.ShadowRoot().IsNull()) {
auto plugin_element =
viewer_element.ShadowRoot().QuerySelector("#plugin");
if (!plugin_element.IsNull()) {
return plugin_element;
}
}
NOTREACHED();
if (frame->Parent() &&
IsPdfInternalPluginAllowedOrigin(frame->Parent()->GetSecurityOrigin())) {
auto plugin_element = frame->GetDocument().QuerySelector("embed");
DCHECK(!plugin_element.IsNull());
return plugin_element;
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
return blink::WebElement();

View File

@@ -694,12 +694,12 @@ describe('BrowserWindow module', () => {
});
describe('BrowserWindow.show()', () => {
it('should focus on window', () => {
w.show();
it('should focus on window', async () => {
await emittedOnce(w, 'focus', () => w.show());
expect(w.isFocused()).to.equal(true);
});
it('should make the window visible', () => {
w.show();
it('should make the window visible', async () => {
await emittedOnce(w, 'focus', () => w.show());
expect(w.isVisible()).to.equal(true);
});
it('emits when window is shown', async () => {
@@ -895,7 +895,7 @@ describe('BrowserWindow module', () => {
describe('BrowserWindow.getFocusedWindow()', () => {
it('returns the opener window when dev tools window is focused', async () => {
w.show();
await emittedOnce(w, 'focus', () => w.show());
w.webContents.openDevTools({ mode: 'undocked' });
await emittedOnce(w.webContents, 'devtools-focused');
expect(BrowserWindow.getFocusedWindow()).to.equal(w);
@@ -1937,6 +1937,26 @@ describe('BrowserWindow module', () => {
});
});
describe('Opening a BrowserWindow from a link', () => {
let appProcess: childProcess.ChildProcessWithoutNullStreams | undefined;
afterEach(() => {
if (appProcess && !appProcess.killed) {
appProcess.kill();
appProcess = undefined;
}
});
it('can properly open and load a new window from a link', async () => {
const appPath = path.join(__dirname, 'fixtures', 'apps', 'open-new-window-from-link');
appProcess = childProcess.spawn(process.execPath, [appPath]);
const [code] = await emittedOnce(appProcess, 'exit');
expect(code).to.equal(0);
});
});
describe('BrowserWindow.fromWebContents(webContents)', () => {
afterEach(closeAllWindows);
@@ -3516,9 +3536,14 @@ describe('BrowserWindow module', () => {
describe('beginFrameSubscription method', () => {
it('does not crash when callback returns nothing', (done) => {
const w = new BrowserWindow({ show: false });
let called = false;
w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
w.webContents.on('dom-ready', () => {
w.webContents.beginFrameSubscription(function () {
// This callback might be called twice.
if (called) return;
called = true;
// Pending endFrameSubscription to next tick can reliably reproduce
// a crash which happens when nothing is returned in the callback.
setTimeout(() => {

View File

@@ -1811,7 +1811,12 @@ describe('net module', () => {
urlRequest.on('response', () => {});
urlRequest.end();
await delay(2000);
expect(numChunksSent).to.be.at.most(20);
// TODO(nornagon): I think this ought to max out at 20, but in practice
// it seems to exceed that sometimes. This is at 25 to avoid test flakes,
// but we should investigate if there's actually something broken here and
// if so fix it and reset this to max at 20, and if not then delete this
// comment.
expect(numChunksSent).to.be.at.most(25);
});
});

View File

@@ -12,8 +12,27 @@ import * as fs from 'fs';
*
* Because all encryption methods are gated by isEncryptionAvailable, the methods will never return the correct values
* when run on CI and linux.
* Refs: https://github.com/electron/electron/issues/30424.
*/
describe('safeStorage module', () => {
it('safeStorage before and after app is ready', async () => {
const appPath = path.join(__dirname, 'fixtures', 'crash-cases', 'safe-storage');
const appProcess = cp.spawn(process.execPath, [appPath]);
let output = '';
appProcess.stdout.on('data', data => { output += data; });
appProcess.stderr.on('data', data => { output += data; });
const code = (await emittedOnce(appProcess, 'exit'))[0] ?? 1;
if (code !== 0 && output) {
console.log(output);
}
expect(code).to.equal(0);
});
});
ifdescribe(process.platform !== 'linux')('safeStorage module', () => {
after(async () => {
const pathToEncryptedString = path.resolve(__dirname, 'fixtures', 'api', 'safe-storage', 'encrypted.txt');

View File

@@ -137,7 +137,9 @@ describe('webFrameMain module', () => {
});
describe('WebFrame.visibilityState', () => {
it('should match window state', async () => {
// TODO(MarshallOfSound): Fix flaky test
// @flaky-test
it.skip('should match window state', async () => {
const w = new BrowserWindow({ show: true });
await w.loadURL('about:blank');
const webFrame = w.webContents.mainFrame;

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<title>Hello World!</title>
</head>
<body>
<a href="./new-window-page.html">Open New Window</a>
</body>
</html>

View File

@@ -0,0 +1,64 @@
const { app, BrowserWindow } = require('electron');
const path = require('path');
async function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
x: 100,
y: 100,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: false,
nodeIntegration: true
}
});
await mainWindow.loadFile('index.html');
const rect = await mainWindow.webContents.executeJavaScript('JSON.parse(JSON.stringify(document.querySelector("a").getBoundingClientRect()))');
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
function click (x, y, options) {
x = Math.floor(x);
y = Math.floor(y);
mainWindow.webContents.sendInputEvent({
type: 'mouseDown',
button: 'left',
x,
y,
clickCount: 1,
...options
});
mainWindow.webContents.sendInputEvent({
type: 'mouseUp',
button: 'left',
x,
y,
clickCount: 1,
...options
});
}
click(x, y, { modifiers: ['shift'] });
}
app.whenReady().then(() => {
app.on('web-contents-created', (e, wc) => {
wc.on('render-process-gone', (e, details) => {
console.error(details);
process.exit(1);
});
wc.on('did-finish-load', () => {
const title = wc.getTitle();
if (title === 'Window From Link') {
process.exit(0);
}
});
});
createWindow();
});

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<title>Window From Link</title>
</head>
<body>
I'm a window opened from a link!
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"name": "electron-test-open-new-window-from-link",
"main": "main.js"
}

View File

@@ -0,0 +1,3 @@
window.addEventListener('click', e => {
console.log('click', e);
});

View File

@@ -0,0 +1,39 @@
const { app, safeStorage } = require('electron');
const { expect } = require('chai');
(async () => {
if (!app.isReady()) {
// isEncryptionAvailable() returns false before the app is ready on
// Linux: https://github.com/electron/electron/issues/32206
// and
// Windows: https://github.com/electron/electron/issues/33640.
expect(safeStorage.isEncryptionAvailable()).to.equal(process.platform === 'darwin');
if (safeStorage.isEncryptionAvailable()) {
const plaintext = 'plaintext';
const ciphertext = safeStorage.encryptString(plaintext);
expect(Buffer.isBuffer(ciphertext)).to.equal(true);
expect(safeStorage.decryptString(ciphertext)).to.equal(plaintext);
} else {
expect(() => safeStorage.encryptString('plaintext')).to.throw(/safeStorage cannot be used before app is ready/);
expect(() => safeStorage.decryptString(Buffer.from(''))).to.throw(/safeStorage cannot be used before app is ready/);
}
}
await app.whenReady();
// isEncryptionAvailable() will always return false on CI due to a mocked
// dbus as mentioned above.
expect(safeStorage.isEncryptionAvailable()).to.equal(process.platform !== 'linux');
if (safeStorage.isEncryptionAvailable()) {
const plaintext = 'plaintext';
const ciphertext = safeStorage.encryptString(plaintext);
expect(Buffer.isBuffer(ciphertext)).to.equal(true);
expect(safeStorage.decryptString(ciphertext)).to.equal(plaintext);
} else {
expect(() => safeStorage.encryptString('plaintext')).to.throw(/Encryption is not available/);
expect(() => safeStorage.decryptString(Buffer.from(''))).to.throw(/Decryption is not available/);
}
})()
.then(app.quit)
.catch((err) => {
console.error(err);
app.exit(1);
});

View File

@@ -435,6 +435,11 @@ describe('<webview> tag', function () {
};
afterEach(closeAllWindows);
afterEach(async () => {
// The leaving animation is un-observable but can interfere with future tests
// Specifically this is async on macOS but can be on other platforms too
await delay(1000);
});
// TODO(jkleinsc) fix this test on arm64 macOS. It causes the tests following it to fail/be flaky
ifit(process.platform !== 'darwin' || process.arch !== 'arm64')('should make parent frame element fullscreen too', async () => {