mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
23 Commits
v19.0.0-be
...
v19.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ea9be3ade | ||
|
|
52a9566f28 | ||
|
|
4ecaae9555 | ||
|
|
21b8200170 | ||
|
|
2b6cd3458f | ||
|
|
00e747ac24 | ||
|
|
082b06cf4e | ||
|
|
b07e17a3bb | ||
|
|
49ee456797 | ||
|
|
85063322e9 | ||
|
|
633d2961eb | ||
|
|
4f0592101b | ||
|
|
8797485564 | ||
|
|
c3746dc439 | ||
|
|
d5dadd0d4a | ||
|
|
da62dd2721 | ||
|
|
c945629872 | ||
|
|
536a17f5ed | ||
|
|
433765cd73 | ||
|
|
870110fd52 | ||
|
|
8ce14231fb | ||
|
|
de09ba2c51 | ||
|
|
290b548b15 |
@@ -14,7 +14,7 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
upload-to-s3:
|
||||
upload-to-storage:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
|
||||
@@ -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
2
DEPS
@@ -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':
|
||||
|
||||
@@ -1 +1 @@
|
||||
19.0.0-beta.4
|
||||
19.0.0-beta.7
|
||||
17
appveyor.yml
17
appveyor.yml
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)`
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]]
|
||||
@@ -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('/');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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([
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}/`;
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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. "
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
11
spec-main/fixtures/apps/open-new-window-from-link/index.html
Normal file
11
spec-main/fixtures/apps/open-new-window-from-link/index.html
Normal 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>
|
||||
64
spec-main/fixtures/apps/open-new-window-from-link/main.js
Normal file
64
spec-main/fixtures/apps/open-new-window-from-link/main.js
Normal 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();
|
||||
});
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "electron-test-open-new-window-from-link",
|
||||
"main": "main.js"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
window.addEventListener('click', e => {
|
||||
console.log('click', e);
|
||||
});
|
||||
39
spec-main/fixtures/crash-cases/safe-storage/index.js
Normal file
39
spec-main/fixtures/crash-cases/safe-storage/index.js
Normal 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);
|
||||
});
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user