mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c37483f2c | ||
|
|
bb6a7d443b | ||
|
|
a3c0d0db3f | ||
|
|
0ffac056c3 | ||
|
|
ae70118ffb | ||
|
|
8c384c13b3 | ||
|
|
91fa68bd32 | ||
|
|
6b67839770 | ||
|
|
fc98a28d67 | ||
|
|
f1c1b22fd4 | ||
|
|
4439b13027 | ||
|
|
1bd2e574a7 | ||
|
|
468c4af661 | ||
|
|
83576caa40 | ||
|
|
36edecf606 | ||
|
|
3f3c0c45f2 | ||
|
|
805674fa8a | ||
|
|
e7ba28f8f9 | ||
|
|
8e0d1af44e | ||
|
|
dde1b500cc | ||
|
|
fefe45e897 | ||
|
|
bd43e652f6 | ||
|
|
bb01e52fb8 | ||
|
|
985b56a68d | ||
|
|
47a2df48b9 | ||
|
|
b5646778e9 | ||
|
|
772bbe775c | ||
|
|
be3b6c1e2b | ||
|
|
cf49565a31 |
@@ -641,6 +641,7 @@ step-nodejs-headers-build: &step-nodejs-headers-build
|
||||
step-electron-publish: &step-electron-publish
|
||||
run:
|
||||
name: Publish Electron Dist
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
rm -rf src/out/Default/obj
|
||||
@@ -735,8 +736,8 @@ step-show-goma-stats: &step-show-goma-stats
|
||||
command: |
|
||||
set +e
|
||||
set +o pipefail
|
||||
$GOMA_DIR/goma_ctl.py stat
|
||||
$GOMA_DIR/diagnose_goma_log.py
|
||||
python3 $GOMA_DIR/goma_ctl.py stat
|
||||
python3 $GOMA_DIR/diagnose_goma_log.py
|
||||
true
|
||||
when: always
|
||||
background: true
|
||||
|
||||
@@ -340,6 +340,34 @@ static_library("chrome") {
|
||||
"//components/pdf/renderer",
|
||||
]
|
||||
}
|
||||
} else {
|
||||
# These are required by the webRequest module.
|
||||
sources += [
|
||||
"//extensions/browser/api/declarative_net_request/request_action.cc",
|
||||
"//extensions/browser/api/declarative_net_request/request_action.h",
|
||||
"//extensions/browser/api/web_request/form_data_parser.cc",
|
||||
"//extensions/browser/api/web_request/form_data_parser.h",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.cc",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.h",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.cc",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.h",
|
||||
"//extensions/browser/api/web_request/web_request_info.cc",
|
||||
"//extensions/browser/api/web_request/web_request_info.h",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.cc",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.h",
|
||||
"//extensions/browser/extension_api_frame_id_map.cc",
|
||||
"//extensions/browser/extension_api_frame_id_map.h",
|
||||
"//extensions/browser/extension_navigation_ui_data.cc",
|
||||
"//extensions/browser/extension_navigation_ui_data.h",
|
||||
"//extensions/browser/extensions_browser_client.cc",
|
||||
"//extensions/browser/extensions_browser_client.h",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.cc",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.h",
|
||||
]
|
||||
|
||||
public_deps += [
|
||||
"//extensions/browser/api/declarative_net_request/flat:extension_ruleset",
|
||||
]
|
||||
}
|
||||
|
||||
if (!is_mas_build) {
|
||||
|
||||
@@ -1135,11 +1135,11 @@ indicates success while any other value indicates failure according to Chromium
|
||||
resolver will attempt to use the system's DNS settings to do DNS lookups
|
||||
itself. Enabled by default on macOS, disabled by default on Windows and
|
||||
Linux.
|
||||
* `secureDnsMode` string (optional) - Can be "off", "automatic" or "secure".
|
||||
Configures the DNS-over-HTTP mode. When "off", no DoH lookups will be
|
||||
performed. When "automatic", DoH lookups will be performed first if DoH is
|
||||
* `secureDnsMode` string (optional) - Can be 'off', 'automatic' or 'secure'.
|
||||
Configures the DNS-over-HTTP mode. When 'off', no DoH lookups will be
|
||||
performed. When 'automatic', DoH lookups will be performed first if DoH is
|
||||
available, and insecure DNS lookups will be performed as a fallback. When
|
||||
"secure", only DoH lookups will be performed. Defaults to "automatic".
|
||||
'secure', only DoH lookups will be performed. Defaults to 'automatic'.
|
||||
* `secureDnsServers` string[] (optional) - A list of DNS-over-HTTP
|
||||
server templates. See [RFC8484 § 3][] for details on the template format.
|
||||
Most servers support the POST method; the template for such servers is
|
||||
|
||||
@@ -44,14 +44,20 @@ provisional and may be removed.
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_inspectedWindow) for more information.
|
||||
|
||||
### `chrome.devtools.network`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_network) for more information.
|
||||
|
||||
### `chrome.devtools.panels`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/devtools_panels) for more information.
|
||||
|
||||
### `chrome.extension`
|
||||
|
||||
The following properties of `chrome.extension` are supported:
|
||||
@@ -63,6 +69,25 @@ The following methods of `chrome.extension` are supported:
|
||||
- `chrome.extension.getURL`
|
||||
- `chrome.extension.getBackgroundPage`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/extension) for more information.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
|
||||
The following events of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/management) for more information.
|
||||
|
||||
### `chrome.runtime`
|
||||
|
||||
The following properties of `chrome.runtime` are supported:
|
||||
@@ -89,12 +114,24 @@ The following events of `chrome.runtime` are supported:
|
||||
- `chrome.runtime.onConnect`
|
||||
- `chrome.runtime.onMessage`
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/runtime) for more information.
|
||||
|
||||
### `chrome.scripting`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/scripting) for more information.
|
||||
|
||||
### `chrome.storage`
|
||||
|
||||
The following methods of `chrome.storage` are supported:
|
||||
|
||||
- `chrome.storage.local`
|
||||
|
||||
`chrome.storage.sync` and `chrome.storage.managed` are **not** supported.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/storage) for more information.
|
||||
|
||||
### `chrome.tabs`
|
||||
|
||||
The following methods of `chrome.tabs` are supported:
|
||||
@@ -111,23 +148,12 @@ The following methods of `chrome.tabs` are supported:
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
|
||||
The following events of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/tabs) for more information.
|
||||
|
||||
### `chrome.webRequest`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers.
|
||||
|
||||
See [official documentation](https://developer.chrome.com/docs/extensions/reference/webRequest) for more information.
|
||||
|
||||
@@ -785,7 +785,7 @@ Returns `Promise<void>` - Resolves when all connections are closed.
|
||||
#### `ses.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) (optional)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Process: [Main](../glossary.md#main-process)<br />
|
||||
* `inherit`: equivalent to \['ignore', 'inherit', 'inherit']
|
||||
* `serviceName` string (optional) - Name of the process that will appear in `name` property of
|
||||
[`child-process-gone` event of `app`](app.md#event-child-process-gone).
|
||||
Default is `node.mojom.NodeService`.
|
||||
Default is `Node Utility Process`.
|
||||
* `allowLoadingUnsignedLibraries` boolean (optional) _macOS_ - With this flag, the utility process will be
|
||||
launched via the `Electron Helper (Plugin).app` helper executable on macOS, which can be
|
||||
codesigned with `com.apple.security.cs.disable-library-validation` and
|
||||
|
||||
@@ -1210,7 +1210,7 @@ Returns `string` - The user agent for this web page.
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `Promise<string>` - A promise that resolves with a key for the inserted CSS that can later be used to remove the CSS via `contents.removeInsertedCSS(key)`.
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ webFrame.setSpellCheckProvider('en-US', {
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `string` - A key for the inserted CSS that can later be used to remove
|
||||
the CSS via `webFrame.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -697,6 +697,8 @@ filenames = {
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/scripting/scripting_api.cc",
|
||||
"shell/browser/extensions/api/scripting/scripting_api.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
|
||||
@@ -114,5 +114,11 @@ for (const name of events) {
|
||||
}
|
||||
|
||||
// Deprecation.
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone');
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone');
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone', () => {
|
||||
// the old event is still emitted by App::OnGpuProcessCrashed()
|
||||
return undefined;
|
||||
});
|
||||
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone', (event: Electron.Event, webContents: Electron.WebContents, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, webContents, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
@@ -338,49 +338,53 @@ WebContents.prototype.printToPDF = async function (options) {
|
||||
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
||||
// print param logic into new file shared between printToPDF and print
|
||||
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions, callback) {
|
||||
if (typeof options === 'object') {
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
if (typeof options !== 'object') {
|
||||
throw new Error('webContents.print(): Invalid print settings specified.');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
const printSettings: Record<string, any> = { ...options };
|
||||
|
||||
options.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
|
||||
printSettings.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
printSettings.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
|
||||
if (this._print) {
|
||||
if (callback) {
|
||||
this._print(options, callback);
|
||||
this._print(printSettings, callback);
|
||||
} else {
|
||||
this._print(options);
|
||||
this._print(printSettings);
|
||||
}
|
||||
} else {
|
||||
console.error('Error: Printing feature is disabled.');
|
||||
@@ -665,8 +669,8 @@ WebContents.prototype._init = function () {
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
this.on('crashed', (event, ...args) => {
|
||||
app.emit('renderer-process-crashed', event, this, ...args);
|
||||
deprecate.event(this, 'crashed', 'render-process-gone', (event: Electron.Event, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
this.on('render-process-gone', (event, details) => {
|
||||
|
||||
@@ -66,14 +66,17 @@ export function renameFunction<T extends Function> (fn: T, newName: string): T {
|
||||
}
|
||||
|
||||
// change the name of an event
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string) {
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string, transformer: (...args: any[]) => any[] | undefined = (...args) => args) {
|
||||
const warn = newName.startsWith('-') /* internal event */
|
||||
? warnOnce(`${oldName} event`)
|
||||
: warnOnce(`${oldName} event`, `${newName} event`);
|
||||
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
|
||||
if (this.listenerCount(oldName) !== 0) {
|
||||
warn();
|
||||
this.emit(oldName, ...args);
|
||||
const transformedArgs = transformer(...args);
|
||||
if (transformedArgs) {
|
||||
this.emit(oldName, ...transformedArgs);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ make_gtk_getlibgtk_public.patch
|
||||
build_disable_print_content_analysis.patch
|
||||
custom_protocols_plzserviceworker.patch
|
||||
feat_filter_out_non-shareable_windows_in_the_current_application_in.patch
|
||||
fix_allow_guest_webcontents_to_enter_fullscreen.patch
|
||||
disable_freezing_flags_after_init_in_node.patch
|
||||
short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch
|
||||
chore_add_electron_deps_to_gitignores.patch
|
||||
@@ -141,3 +140,13 @@ revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||
cherry-pick-74a2eb9c8cb2.patch
|
||||
cherry-pick-26175b0903d8.patch
|
||||
fix_use_delegated_generic_capturer_when_available.patch
|
||||
cherry-pick-b11e7d07a6f4.patch
|
||||
cherry-pick-309b604c4e88.patch
|
||||
cherry-pick-f218b4f37018.patch
|
||||
cherry-pick-d756d71a652c.patch
|
||||
parameterize_axtreeserializer_by_vector_type.patch
|
||||
avoid_allocating_recordid_objects_in_elementtiming_and_lcp.patch
|
||||
cherry-pick-80106e31c7ea.patch
|
||||
gpu_use_load_program_shader_shm_count_on_drdc_thread.patch
|
||||
crash_gpu_process_and_clear_shader_cache_when_skia_reports.patch
|
||||
cherry-pick-3df423a5b8de.patch
|
||||
|
||||
@@ -0,0 +1,655 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yoav Weiss <yoavweiss@chromium.org>
|
||||
Date: Thu, 31 Aug 2023 10:11:57 +0000
|
||||
Subject: Avoid allocating RecordId objects in ElementTiming and LCP
|
||||
|
||||
RecordId objects in current code keep around references to LayoutObject
|
||||
and ImageResourceContent, both GCed objects.
|
||||
Turn out that most of these references are not needed and are only used
|
||||
as hashmap keys that would be better served with an actual hash. The
|
||||
ones that are needed don't need extensive lifetimes and can be stack
|
||||
allocated.
|
||||
|
||||
Bug=1472365,1472366
|
||||
|
||||
Change-Id: I3fd77bed9899932d5bfadc2a8e6403a8e434235f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4821128
|
||||
Commit-Queue: Yoav Weiss <yoavweiss@chromium.org>
|
||||
Reviewed-by: Omer Katz <omerkatz@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1190644}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/paint/DEPS b/third_party/blink/renderer/core/paint/DEPS
|
||||
index 7b2b644ed89b254e97c59116d0a41f6c137f3b17..38d0a0b6b9c1c3598d2ae5a782d405bf90bf3733 100644
|
||||
--- a/third_party/blink/renderer/core/paint/DEPS
|
||||
+++ b/third_party/blink/renderer/core/paint/DEPS
|
||||
@@ -5,6 +5,8 @@ include_rules = [
|
||||
# For DCHECK.
|
||||
"+base/logging.h",
|
||||
"+components/shared_highlighting/core/common",
|
||||
+ # Hash function access
|
||||
+ "+base/hash/hash.h",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
diff --git a/third_party/blink/renderer/core/paint/build.gni b/third_party/blink/renderer/core/paint/build.gni
|
||||
index 87df076817994e16e4a67331a9b3ce9dc1406c82..7dacd09f9a63eead72ad75d73039e76d1f7809c6 100644
|
||||
--- a/third_party/blink/renderer/core/paint/build.gni
|
||||
+++ b/third_party/blink/renderer/core/paint/build.gni
|
||||
@@ -196,6 +196,8 @@ blink_core_sources_paint = [
|
||||
"timing/image_paint_timing_detector.h",
|
||||
"timing/largest_contentful_paint_calculator.cc",
|
||||
"timing/largest_contentful_paint_calculator.h",
|
||||
+ "timing/media_record_id.cc",
|
||||
+ "timing/media_record_id.h",
|
||||
"timing/paint_timing.cc",
|
||||
"timing/paint_timing_detector.cc",
|
||||
"timing/paint_timing_detector.h",
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/image_element_timing.cc b/third_party/blink/renderer/core/paint/timing/image_element_timing.cc
|
||||
index a8501cfcc91e57b3348e3db8ff11256c4d7176b2..21f7a0ac38732e5381da6b82544044b8e6da3a11 100644
|
||||
--- a/third_party/blink/renderer/core/paint/timing/image_element_timing.cc
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/image_element_timing.cc
|
||||
@@ -68,7 +68,7 @@ void ImageElementTiming::NotifyImageFinished(
|
||||
return;
|
||||
|
||||
const auto& insertion_result = images_notified_.insert(
|
||||
- std::make_pair(&layout_object, cached_image), ImageInfo());
|
||||
+ MediaRecordId::GenerateHash(&layout_object, cached_image), ImageInfo());
|
||||
if (insertion_result.is_new_entry)
|
||||
insertion_result.stored_value->value.load_time_ = base::TimeTicks::Now();
|
||||
}
|
||||
@@ -97,8 +97,8 @@ void ImageElementTiming::NotifyImagePainted(
|
||||
if (!internal::IsExplicitlyRegisteredForTiming(layout_object))
|
||||
return;
|
||||
|
||||
- auto it =
|
||||
- images_notified_.find(std::make_pair(&layout_object, &cached_image));
|
||||
+ auto it = images_notified_.find(
|
||||
+ MediaRecordId::GenerateHash(&layout_object, &cached_image));
|
||||
// It is possible that the pair is not in |images_notified_|. See
|
||||
// https://crbug.com/1027948
|
||||
if (it != images_notified_.end() && !it->value.is_painted_) {
|
||||
@@ -218,7 +218,8 @@ void ImageElementTiming::NotifyBackgroundImagePainted(
|
||||
|
||||
ImageInfo& info =
|
||||
images_notified_
|
||||
- .insert(std::make_pair(layout_object, cached_image), ImageInfo())
|
||||
+ .insert(MediaRecordId::GenerateHash(layout_object, cached_image),
|
||||
+ ImageInfo())
|
||||
.stored_value->value;
|
||||
if (!info.is_painted_) {
|
||||
info.is_painted_ = true;
|
||||
@@ -246,7 +247,7 @@ void ImageElementTiming::ReportImagePaintPresentationTime(
|
||||
|
||||
void ImageElementTiming::NotifyImageRemoved(const LayoutObject* layout_object,
|
||||
const ImageResourceContent* image) {
|
||||
- images_notified_.erase(std::make_pair(layout_object, image));
|
||||
+ images_notified_.erase(MediaRecordId::GenerateHash(layout_object, image));
|
||||
}
|
||||
|
||||
void ImageElementTiming::Trace(Visitor* visitor) const {
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/image_element_timing.h b/third_party/blink/renderer/core/paint/timing/image_element_timing.h
|
||||
index 0a152210de91ab69c255be8caa951616afb3df06..7d3f5dd7c9137b277543e52d4d1e327d75b01e84 100644
|
||||
--- a/third_party/blink/renderer/core/paint/timing/image_element_timing.h
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/image_element_timing.h
|
||||
@@ -5,12 +5,11 @@
|
||||
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_IMAGE_ELEMENT_TIMING_H_
|
||||
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_IMAGE_ELEMENT_TIMING_H_
|
||||
|
||||
-#include <utility>
|
||||
-
|
||||
#include "base/time/time.h"
|
||||
#include "third_party/blink/renderer/core/core_export.h"
|
||||
#include "third_party/blink/renderer/core/dom/element.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
+#include "third_party/blink/renderer/core/paint/timing/media_record_id.h"
|
||||
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
|
||||
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
|
||||
#include "third_party/blink/renderer/platform/supplementable.h"
|
||||
@@ -123,13 +122,12 @@ class CORE_EXPORT ImageElementTiming final
|
||||
|
||||
DISALLOW_NEW();
|
||||
};
|
||||
- typedef std::pair<const LayoutObject*, const ImageResourceContent*> RecordId;
|
||||
// Hashmap of pairs of elements, LayoutObjects (for the elements) and
|
||||
// ImageResourceContent (for the src) which correspond to either images or
|
||||
// background images whose paint has been observed. For background images,
|
||||
// only the |is_painted_| bit is used, as the timestamp needs to be tracked by
|
||||
// |background_image_timestamps_|.
|
||||
- WTF::HashMap<RecordId, ImageInfo> images_notified_;
|
||||
+ WTF::HashMap<MediaRecordIdHash, ImageInfo> images_notified_;
|
||||
|
||||
// Hashmap of background images which contain information about the load time
|
||||
// of the background image.
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/image_element_timing_test.cc b/third_party/blink/renderer/core/paint/timing/image_element_timing_test.cc
|
||||
index f1aee5cdd179398a2552e8af2de273479522a623..3643ad57403d46d5bc341478ebb51db6ccc9f175 100644
|
||||
--- a/third_party/blink/renderer/core/paint/timing/image_element_timing_test.cc
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/image_element_timing_test.cc
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "third_party/blink/renderer/core/layout/layout_image.h"
|
||||
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
|
||||
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
|
||||
+#include "third_party/blink/renderer/core/paint/timing/media_record_id.h"
|
||||
#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
|
||||
#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
|
||||
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
|
||||
@@ -60,11 +61,9 @@ class ImageElementTimingTest : public testing::Test,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- bool ImagesNotifiedContains(
|
||||
- const std::pair<const LayoutObject*, const ImageResourceContent*>&
|
||||
- record_id) {
|
||||
+ bool ImagesNotifiedContains(MediaRecordIdHash record_id_hash) {
|
||||
return ImageElementTiming::From(*GetDoc()->domWindow())
|
||||
- .images_notified_.Contains(record_id);
|
||||
+ .images_notified_.Contains(record_id_hash);
|
||||
}
|
||||
|
||||
unsigned ImagesNotifiedSize() {
|
||||
@@ -159,7 +158,7 @@ TEST_P(ImageElementTimingTest, IgnoresUnmarkedElement) {
|
||||
ASSERT_TRUE(layout_image);
|
||||
UpdateAllLifecyclePhases();
|
||||
EXPECT_FALSE(ImagesNotifiedContains(
|
||||
- std::make_pair(layout_image, layout_image->CachedImage())));
|
||||
+ MediaRecordId::GenerateHash(layout_image, layout_image->CachedImage())));
|
||||
}
|
||||
|
||||
TEST_P(ImageElementTimingTest, ImageInsideSVG) {
|
||||
@@ -179,7 +178,7 @@ TEST_P(ImageElementTimingTest, ImageInsideSVG) {
|
||||
|
||||
// |layout_image| should have had its paint notified to ImageElementTiming.
|
||||
EXPECT_TRUE(ImagesNotifiedContains(
|
||||
- std::make_pair(layout_image, layout_image->CachedImage())));
|
||||
+ MediaRecordId::GenerateHash(layout_image, layout_image->CachedImage())));
|
||||
}
|
||||
|
||||
TEST_P(ImageElementTimingTest, ImageInsideNonRenderedSVG) {
|
||||
@@ -214,7 +213,7 @@ TEST_P(ImageElementTimingTest, ImageRemoved) {
|
||||
ASSERT_TRUE(layout_image);
|
||||
UpdateAllLifecyclePhases();
|
||||
EXPECT_TRUE(ImagesNotifiedContains(
|
||||
- std::make_pair(layout_image, layout_image->CachedImage())));
|
||||
+ MediaRecordId::GenerateHash(layout_image, layout_image->CachedImage())));
|
||||
|
||||
GetDoc()->getElementById("target")->remove();
|
||||
// |layout_image| should no longer be part of |images_notified| since it will
|
||||
@@ -234,7 +233,7 @@ TEST_P(ImageElementTimingTest, SVGImageRemoved) {
|
||||
LayoutSVGImage* layout_image = SetSVGImageResource("target", 5, 5);
|
||||
ASSERT_TRUE(layout_image);
|
||||
UpdateAllLifecyclePhases();
|
||||
- EXPECT_TRUE(ImagesNotifiedContains(std::make_pair(
|
||||
+ EXPECT_TRUE(ImagesNotifiedContains(MediaRecordId::GenerateHash(
|
||||
layout_image, layout_image->ImageResource()->CachedImage())));
|
||||
|
||||
GetDoc()->getElementById("target")->remove();
|
||||
@@ -261,7 +260,8 @@ TEST_P(ImageElementTimingTest, BackgroundImageRemoved) {
|
||||
object->Style()->BackgroundLayers().GetImage()->CachedImage();
|
||||
UpdateAllLifecyclePhases();
|
||||
EXPECT_EQ(ImagesNotifiedSize(), 1u);
|
||||
- EXPECT_TRUE(ImagesNotifiedContains(std::make_pair(object, content)));
|
||||
+ EXPECT_TRUE(
|
||||
+ ImagesNotifiedContains(MediaRecordId::GenerateHash(object, content)));
|
||||
|
||||
GetDoc()->getElementById("target")->remove();
|
||||
EXPECT_EQ(ImagesNotifiedSize(), 0u);
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
|
||||
index b4ed09ccd893b39d4497fc9a237c1ab00bda91fa..e516c2a15a362fa71fcb1d644e8eb61ea2934b1e 100644
|
||||
--- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
|
||||
@@ -231,8 +231,8 @@ void ImagePaintTimingDetector::OnPaintFinished() {
|
||||
void ImagePaintTimingDetector::NotifyImageRemoved(
|
||||
const LayoutObject& object,
|
||||
const MediaTiming* media_timing) {
|
||||
- RecordId record_id = std::make_pair(&object, media_timing);
|
||||
- records_manager_.RemoveRecord(record_id);
|
||||
+ records_manager_.RemoveRecord(
|
||||
+ MediaRecordId::GenerateHash(&object, media_timing));
|
||||
}
|
||||
|
||||
void ImagePaintTimingDetector::StopRecordEntries() {
|
||||
@@ -270,7 +270,7 @@ void ImageRecordsManager::AssignPaintTimeToRegisteredQueuedRecords(
|
||||
unsigned last_queued_frame_index) {
|
||||
while (!images_queued_for_paint_time_.empty()) {
|
||||
const base::WeakPtr<ImageRecord>& record =
|
||||
- images_queued_for_paint_time_.front().first;
|
||||
+ images_queued_for_paint_time_.front().image_record;
|
||||
if (!record) {
|
||||
images_queued_for_paint_time_.pop_front();
|
||||
continue;
|
||||
@@ -282,8 +282,8 @@ void ImageRecordsManager::AssignPaintTimeToRegisteredQueuedRecords(
|
||||
record->first_animated_frame_time = timestamp;
|
||||
record->queue_animated_paint = false;
|
||||
}
|
||||
- auto it =
|
||||
- pending_images_.find(images_queued_for_paint_time_.front().second);
|
||||
+ auto it = pending_images_.find(
|
||||
+ images_queued_for_paint_time_.front().record_id_hash);
|
||||
images_queued_for_paint_time_.pop_front();
|
||||
// A record may be in |images_queued_for_paint_time_| twice, for instance if
|
||||
// is already loaded by the time of its first paint.
|
||||
@@ -319,7 +319,8 @@ bool ImagePaintTimingDetector::RecordImage(
|
||||
if (image_border.IsEmpty())
|
||||
return false;
|
||||
|
||||
- RecordId record_id = std::make_pair(&object, &media_timing);
|
||||
+ MediaRecordId record_id(&object, &media_timing);
|
||||
+ MediaRecordIdHash record_id_hash = record_id.GetHash();
|
||||
|
||||
if (int depth = IgnorePaintTimingScope::IgnoreDepth()) {
|
||||
// Record the largest loaded image that is hidden due to documentElement
|
||||
@@ -340,17 +341,18 @@ bool ImagePaintTimingDetector::RecordImage(
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (records_manager_.IsRecordedImage(record_id)) {
|
||||
+ if (records_manager_.IsRecordedImage(record_id_hash)) {
|
||||
base::WeakPtr<ImageRecord> record =
|
||||
- records_manager_.GetPendingImage(record_id);
|
||||
+ records_manager_.GetPendingImage(record_id_hash);
|
||||
if (!record)
|
||||
return false;
|
||||
if (ShouldReportAnimatedImages() && media_timing.IsPaintedFirstFrame()) {
|
||||
added_entry_in_latest_frame_ |=
|
||||
- records_manager_.OnFirstAnimatedFramePainted(record_id, frame_index_);
|
||||
+ records_manager_.OnFirstAnimatedFramePainted(record_id_hash,
|
||||
+ frame_index_);
|
||||
}
|
||||
if (!record->loaded && media_timing.IsSufficientContentLoadedForPaint()) {
|
||||
- records_manager_.OnImageLoaded(record_id, frame_index_, style_image);
|
||||
+ records_manager_.OnImageLoaded(record_id_hash, frame_index_, style_image);
|
||||
added_entry_in_latest_frame_ = true;
|
||||
if (absl::optional<PaintTimingVisualizer>& visualizer =
|
||||
frame_view_->GetPaintTimingDetector().Visualizer()) {
|
||||
@@ -386,10 +388,11 @@ bool ImagePaintTimingDetector::RecordImage(
|
||||
|
||||
if (ShouldReportAnimatedImages() && media_timing.IsPaintedFirstFrame()) {
|
||||
added_entry_in_latest_frame_ |=
|
||||
- records_manager_.OnFirstAnimatedFramePainted(record_id, frame_index_);
|
||||
+ records_manager_.OnFirstAnimatedFramePainted(record_id_hash,
|
||||
+ frame_index_);
|
||||
}
|
||||
if (media_timing.IsSufficientContentLoadedForPaint()) {
|
||||
- records_manager_.OnImageLoaded(record_id, frame_index_, style_image);
|
||||
+ records_manager_.OnImageLoaded(record_id_hash, frame_index_, style_image);
|
||||
added_entry_in_latest_frame_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -445,8 +448,8 @@ uint64_t ImagePaintTimingDetector::ComputeImageRectSize(
|
||||
void ImagePaintTimingDetector::NotifyImageFinished(
|
||||
const LayoutObject& object,
|
||||
const MediaTiming* media_timing) {
|
||||
- RecordId record_id = std::make_pair(&object, media_timing);
|
||||
- records_manager_.NotifyImageFinished(record_id);
|
||||
+ records_manager_.NotifyImageFinished(
|
||||
+ MediaRecordId::GenerateHash(&object, media_timing));
|
||||
}
|
||||
|
||||
void ImagePaintTimingDetector::ReportLargestIgnoredImage() {
|
||||
@@ -458,9 +461,9 @@ ImageRecordsManager::ImageRecordsManager(LocalFrameView* frame_view)
|
||||
: size_ordered_set_(&LargeImageFirst), frame_view_(frame_view) {}
|
||||
|
||||
bool ImageRecordsManager::OnFirstAnimatedFramePainted(
|
||||
- const RecordId& record_id,
|
||||
+ MediaRecordIdHash record_id_hash,
|
||||
unsigned current_frame_index) {
|
||||
- base::WeakPtr<ImageRecord> record = GetPendingImage(record_id);
|
||||
+ base::WeakPtr<ImageRecord> record = GetPendingImage(record_id_hash);
|
||||
DCHECK(record);
|
||||
if (record->media_timing &&
|
||||
!record->media_timing->GetFirstVideoFrameTime().is_null()) {
|
||||
@@ -473,19 +476,19 @@ bool ImageRecordsManager::OnFirstAnimatedFramePainted(
|
||||
// Otherwise, this is an animated images, and so we should wait for the
|
||||
// presentation callback to fire to set the first frame presentation time.
|
||||
record->queue_animated_paint = true;
|
||||
- QueueToMeasurePaintTime(record_id, record, current_frame_index);
|
||||
+ QueueToMeasurePaintTime(record_id_hash, record, current_frame_index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
-void ImageRecordsManager::OnImageLoaded(const RecordId& record_id,
|
||||
+void ImageRecordsManager::OnImageLoaded(MediaRecordIdHash record_id_hash,
|
||||
unsigned current_frame_index,
|
||||
const StyleFetchedImage* style_image) {
|
||||
- base::WeakPtr<ImageRecord> record = GetPendingImage(record_id);
|
||||
+ base::WeakPtr<ImageRecord> record = GetPendingImage(record_id_hash);
|
||||
DCHECK(record);
|
||||
if (!style_image) {
|
||||
- auto it = image_finished_times_.find(record_id);
|
||||
+ auto it = image_finished_times_.find(record_id_hash);
|
||||
if (it != image_finished_times_.end()) {
|
||||
record->load_time = it->value;
|
||||
DCHECK(!record->load_time.is_null());
|
||||
@@ -498,7 +501,7 @@ void ImageRecordsManager::OnImageLoaded(const RecordId& record_id,
|
||||
record->origin_clean = style_image->IsOriginClean();
|
||||
}
|
||||
}
|
||||
- OnImageLoadedInternal(record_id, record, current_frame_index);
|
||||
+ OnImageLoadedInternal(record_id_hash, record, current_frame_index);
|
||||
}
|
||||
|
||||
void ImageRecordsManager::ReportLargestIgnoredImage(
|
||||
@@ -518,25 +521,25 @@ void ImageRecordsManager::ReportLargestIgnoredImage(
|
||||
DCHECK(document);
|
||||
PaintTiming::From(*document).MarkFirstContentfulPaint();
|
||||
|
||||
- RecordId record_id = std::make_pair(node->GetLayoutObject(),
|
||||
- largest_ignored_image_->media_timing);
|
||||
- recorded_images_.insert(record_id);
|
||||
+ MediaRecordIdHash record_id_hash = MediaRecordId::GenerateHash(
|
||||
+ node->GetLayoutObject(), largest_ignored_image_->media_timing);
|
||||
+ recorded_images_.insert(record_id_hash);
|
||||
base::WeakPtr<ImageRecord> record = largest_ignored_image_->AsWeakPtr();
|
||||
size_ordered_set_.insert(record);
|
||||
- pending_images_.insert(record_id, std::move(largest_ignored_image_));
|
||||
- OnImageLoadedInternal(record_id, record, current_frame_index);
|
||||
+ pending_images_.insert(record_id_hash, std::move(largest_ignored_image_));
|
||||
+ OnImageLoadedInternal(record_id_hash, record, current_frame_index);
|
||||
}
|
||||
|
||||
void ImageRecordsManager::OnImageLoadedInternal(
|
||||
- const RecordId& record_id,
|
||||
+ MediaRecordIdHash record_id_hash,
|
||||
base::WeakPtr<ImageRecord>& record,
|
||||
unsigned current_frame_index) {
|
||||
SetLoaded(record);
|
||||
- QueueToMeasurePaintTime(record_id, record, current_frame_index);
|
||||
+ QueueToMeasurePaintTime(record_id_hash, record, current_frame_index);
|
||||
}
|
||||
|
||||
void ImageRecordsManager::MaybeUpdateLargestIgnoredImage(
|
||||
- const RecordId& record_id,
|
||||
+ const MediaRecordId& record_id,
|
||||
const uint64_t& visual_size,
|
||||
const gfx::Rect& frame_visual_rect,
|
||||
const gfx::RectF& root_visual_rect,
|
||||
@@ -544,14 +547,14 @@ void ImageRecordsManager::MaybeUpdateLargestIgnoredImage(
|
||||
if (visual_size && (!largest_ignored_image_ ||
|
||||
visual_size > largest_ignored_image_->recorded_size)) {
|
||||
largest_ignored_image_ = CreateImageRecord(
|
||||
- *record_id.first, record_id.second, visual_size, frame_visual_rect,
|
||||
- root_visual_rect, is_loaded_after_mouseover);
|
||||
+ *record_id.GetLayoutObject(), record_id.GetMediaTiming(), visual_size,
|
||||
+ frame_visual_rect, root_visual_rect, is_loaded_after_mouseover);
|
||||
largest_ignored_image_->load_time = base::TimeTicks::Now();
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageRecordsManager::RecordFirstPaintAndReturnIsPending(
|
||||
- const RecordId& record_id,
|
||||
+ const MediaRecordId& record_id,
|
||||
const uint64_t& visual_size,
|
||||
const gfx::Rect& frame_visual_rect,
|
||||
const gfx::RectF& root_visual_rect,
|
||||
@@ -562,7 +565,7 @@ bool ImageRecordsManager::RecordFirstPaintAndReturnIsPending(
|
||||
if (visual_size == 0u) {
|
||||
return false;
|
||||
}
|
||||
- recorded_images_.insert(record_id);
|
||||
+ recorded_images_.insert(record_id.GetHash());
|
||||
// If this cannot become an LCP candidate, no need to do anything else.
|
||||
if (visual_size == 0u ||
|
||||
(largest_painted_image_ &&
|
||||
@@ -588,10 +591,10 @@ bool ImageRecordsManager::RecordFirstPaintAndReturnIsPending(
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageRecord> record = CreateImageRecord(
|
||||
- *record_id.first, record_id.second, visual_size, frame_visual_rect,
|
||||
- root_visual_rect, is_loaded_after_mouseover);
|
||||
+ *record_id.GetLayoutObject(), record_id.GetMediaTiming(), visual_size,
|
||||
+ frame_visual_rect, root_visual_rect, is_loaded_after_mouseover);
|
||||
size_ordered_set_.insert(record->AsWeakPtr());
|
||||
- pending_images_.insert(record_id, std::move(record));
|
||||
+ pending_images_.insert(record_id.GetHash(), std::move(record));
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h
|
||||
index 413176e992e110db2818d7ed2b06ebae232fea1f..5a5ee2a9033de3aa74c5296ce6b1e962524a8fd8 100644
|
||||
--- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "third_party/blink/renderer/core/core_export.h"
|
||||
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
|
||||
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
|
||||
+#include "third_party/blink/renderer/core/paint/timing/media_record_id.h"
|
||||
#include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h"
|
||||
#include "third_party/blink/renderer/platform/allow_discouraged_type.h"
|
||||
#include "third_party/blink/renderer/platform/loader/fetch/media_timing.h"
|
||||
@@ -90,8 +91,6 @@ class ImageRecord : public base::SupportsWeakPtr<ImageRecord> {
|
||||
bool is_loaded_after_mouseover = false;
|
||||
};
|
||||
|
||||
-typedef std::pair<const LayoutObject*, const MediaTiming*> RecordId;
|
||||
-
|
||||
// |ImageRecordsManager| is the manager of all of the images that Largest
|
||||
// Image Paint cares about. Note that an image does not necessarily correspond
|
||||
// to a node; it can also be one of the background images attached to a node.
|
||||
@@ -115,10 +114,10 @@ class CORE_EXPORT ImageRecordsManager {
|
||||
ImageRecordsManager& operator=(const ImageRecordsManager&) = delete;
|
||||
ImageRecord* LargestImage() const;
|
||||
|
||||
- inline void RemoveRecord(const RecordId& record_id) {
|
||||
- recorded_images_.erase(record_id);
|
||||
- image_finished_times_.erase(record_id);
|
||||
- auto it = pending_images_.find(record_id);
|
||||
+ inline void RemoveRecord(MediaRecordIdHash record_id_hash) {
|
||||
+ recorded_images_.erase(record_id_hash);
|
||||
+ image_finished_times_.erase(record_id_hash);
|
||||
+ auto it = pending_images_.find(record_id_hash);
|
||||
if (it != pending_images_.end()) {
|
||||
size_ordered_set_.erase(it->value->AsWeakPtr());
|
||||
pending_images_.erase(it);
|
||||
@@ -128,41 +127,42 @@ class CORE_EXPORT ImageRecordsManager {
|
||||
}
|
||||
}
|
||||
// Returns whether an image was added to |pending_images_|.
|
||||
- bool RecordFirstPaintAndReturnIsPending(const RecordId& record_id,
|
||||
+ bool RecordFirstPaintAndReturnIsPending(const MediaRecordId& record_id,
|
||||
const uint64_t& visual_size,
|
||||
const gfx::Rect& frame_visual_rect,
|
||||
const gfx::RectF& root_visual_rect,
|
||||
double bpp,
|
||||
bool is_loaded_after_mouseover);
|
||||
- bool IsRecordedImage(const RecordId& record_id) const {
|
||||
- return recorded_images_.Contains(record_id);
|
||||
+ bool IsRecordedImage(MediaRecordIdHash record_id_hash) const {
|
||||
+ return recorded_images_.Contains(record_id_hash);
|
||||
}
|
||||
|
||||
- void NotifyImageFinished(const RecordId& record_id) {
|
||||
+ void NotifyImageFinished(MediaRecordIdHash record_id_hash) {
|
||||
// TODO(npm): Ideally NotifyImageFinished() would only be called when the
|
||||
// record has not yet been inserted in |image_finished_times_| but that's
|
||||
// not currently the case. If we plumb some information from
|
||||
// MediaTiming we may be able to ensure that this call does not
|
||||
// require the Contains() check, which would save time.
|
||||
- if (!image_finished_times_.Contains(record_id)) {
|
||||
- image_finished_times_.insert(record_id, base::TimeTicks::Now());
|
||||
+ if (!image_finished_times_.Contains(record_id_hash)) {
|
||||
+ image_finished_times_.insert(record_id_hash, base::TimeTicks::Now());
|
||||
}
|
||||
}
|
||||
|
||||
- inline base::WeakPtr<ImageRecord> GetPendingImage(const RecordId& record_id) {
|
||||
- auto it = pending_images_.find(record_id);
|
||||
+ inline base::WeakPtr<ImageRecord> GetPendingImage(
|
||||
+ MediaRecordIdHash record_id_hash) {
|
||||
+ auto it = pending_images_.find(record_id_hash);
|
||||
return it == pending_images_.end() ? nullptr : it->value->AsWeakPtr();
|
||||
}
|
||||
- bool OnFirstAnimatedFramePainted(const RecordId&,
|
||||
+ bool OnFirstAnimatedFramePainted(MediaRecordIdHash,
|
||||
unsigned current_frame_index);
|
||||
- void OnImageLoaded(const RecordId&,
|
||||
+ void OnImageLoaded(MediaRecordIdHash,
|
||||
unsigned current_frame_index,
|
||||
const StyleFetchedImage*);
|
||||
|
||||
// Receives a candidate image painted under opacity 0 but without nested
|
||||
// opacity. May update |largest_ignored_image_| if the new candidate has a
|
||||
// larger size.
|
||||
- void MaybeUpdateLargestIgnoredImage(const RecordId&,
|
||||
+ void MaybeUpdateLargestIgnoredImage(const MediaRecordId&,
|
||||
const uint64_t& visual_size,
|
||||
const gfx::Rect& frame_visual_rect,
|
||||
const gfx::RectF& root_visual_rect,
|
||||
@@ -186,19 +186,31 @@ class CORE_EXPORT ImageRecordsManager {
|
||||
const gfx::Rect& frame_visual_rect,
|
||||
const gfx::RectF& root_visual_rect,
|
||||
bool is_loaded_after_mouseover);
|
||||
- inline void QueueToMeasurePaintTime(const RecordId& record_id,
|
||||
+ inline void QueueToMeasurePaintTime(MediaRecordIdHash record_id_hash,
|
||||
base::WeakPtr<ImageRecord>& record,
|
||||
unsigned current_frame_index) {
|
||||
record->frame_index = current_frame_index;
|
||||
- images_queued_for_paint_time_.push_back(std::make_pair(record, record_id));
|
||||
+ images_queued_for_paint_time_.push_back(
|
||||
+ ImageRecordAndHashPair(record, record_id_hash));
|
||||
}
|
||||
inline void SetLoaded(base::WeakPtr<ImageRecord>& record) {
|
||||
record->loaded = true;
|
||||
}
|
||||
- void OnImageLoadedInternal(const RecordId&,
|
||||
+ void OnImageLoadedInternal(MediaRecordIdHash,
|
||||
base::WeakPtr<ImageRecord>&,
|
||||
unsigned current_frame_index);
|
||||
|
||||
+ struct ImageRecordAndHashPair {
|
||||
+ ImageRecordAndHashPair(base::WeakPtr<ImageRecord>& record,
|
||||
+ MediaRecordIdHash id_hash) {
|
||||
+ image_record = record;
|
||||
+ record_id_hash = id_hash;
|
||||
+ }
|
||||
+
|
||||
+ base::WeakPtr<ImageRecord> image_record;
|
||||
+ MediaRecordIdHash record_id_hash;
|
||||
+ };
|
||||
+
|
||||
// The ImageRecord corresponding to the largest image that has been loaded and
|
||||
// painted.
|
||||
std::unique_ptr<ImageRecord> largest_painted_image_;
|
||||
@@ -207,24 +219,23 @@ class CORE_EXPORT ImageRecordsManager {
|
||||
// timestamp, ordered by size.
|
||||
ImageRecordSet size_ordered_set_;
|
||||
|
||||
- // RecordId for images for which we have seen a first paint. A RecordId is
|
||||
- // added to this set regardless of whether the image could be an LCP
|
||||
- // candidate.
|
||||
- HashSet<RecordId> recorded_images_;
|
||||
+ // MediaRecordId for images for which we have seen a first paint. A
|
||||
+ // MediaRecordId is added to this set regardless of whether the image could be
|
||||
+ // an LCP candidate.
|
||||
+ HashSet<MediaRecordIdHash> recorded_images_;
|
||||
|
||||
- // Map of RecordId to ImageRecord for images for which the first paint has
|
||||
- // been seen but which do not have the paint time set yet. This may contain
|
||||
- // only images which are potential LCP candidates.
|
||||
- HashMap<RecordId, std::unique_ptr<ImageRecord>> pending_images_;
|
||||
+ // Map of MediaRecordId to ImageRecord for images for which the first paint
|
||||
+ // has been seen but which do not have the paint time set yet. This may
|
||||
+ // contain only images which are potential LCP candidates.
|
||||
+ HashMap<MediaRecordIdHash, std::unique_ptr<ImageRecord>> pending_images_;
|
||||
|
||||
// |ImageRecord|s waiting for paint time are stored in this map
|
||||
// until they get a presentation time.
|
||||
- Deque<std::pair<base::WeakPtr<ImageRecord>, RecordId>>
|
||||
- images_queued_for_paint_time_;
|
||||
+ Deque<ImageRecordAndHashPair> images_queued_for_paint_time_;
|
||||
|
||||
// Map containing timestamps of when LayoutObject::ImageNotifyFinished is
|
||||
// first called.
|
||||
- HashMap<RecordId, base::TimeTicks> image_finished_times_;
|
||||
+ HashMap<MediaRecordIdHash, base::TimeTicks> image_finished_times_;
|
||||
|
||||
Member<LocalFrameView> frame_view_;
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/media_record_id.cc b/third_party/blink/renderer/core/paint/timing/media_record_id.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e49c898d1111015d80a71ddc11bd791bb2a0dca1
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/media_record_id.cc
|
||||
@@ -0,0 +1,27 @@
|
||||
+// Copyright 2023 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "third_party/blink/renderer/core/paint/timing/media_record_id.h"
|
||||
+
|
||||
+#include "base/hash/hash.h"
|
||||
+
|
||||
+namespace blink {
|
||||
+
|
||||
+MediaRecordId::MediaRecordId(const LayoutObject* layout,
|
||||
+ const MediaTiming* media)
|
||||
+ : layout_object_(layout),
|
||||
+ media_timing_(media),
|
||||
+ hash_(GenerateHash(layout, media)) {}
|
||||
+
|
||||
+// This hash is used as a key where previously MediaRecordId was used directly.
|
||||
+// That helps us avoid storing references to the GCed LayoutObject and
|
||||
+// MediaTiming, as that can be unsafe when using regular WTF containers. It also
|
||||
+// helps us avoid needlessly allocating MediaRecordId on the heap.
|
||||
+MediaRecordIdHash MediaRecordId::GenerateHash(const LayoutObject* layout,
|
||||
+ const MediaTiming* media) {
|
||||
+ return base::HashInts(reinterpret_cast<MediaRecordIdHash>(layout),
|
||||
+ reinterpret_cast<MediaRecordIdHash>(media));
|
||||
+}
|
||||
+
|
||||
+} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/core/paint/timing/media_record_id.h b/third_party/blink/renderer/core/paint/timing/media_record_id.h
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..32952101e8463e31617d1b5f67c36abf04303c4a
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/renderer/core/paint/timing/media_record_id.h
|
||||
@@ -0,0 +1,37 @@
|
||||
+// Copyright 2023 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_MEDIA_RECORD_ID_H_
|
||||
+#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TIMING_MEDIA_RECORD_ID_H_
|
||||
+
|
||||
+#include "third_party/blink/renderer/core/core_export.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
|
||||
+
|
||||
+namespace blink {
|
||||
+class LayoutObject;
|
||||
+class MediaTiming;
|
||||
+
|
||||
+using MediaRecordIdHash = size_t;
|
||||
+
|
||||
+class MediaRecordId {
|
||||
+ STACK_ALLOCATED();
|
||||
+
|
||||
+ public:
|
||||
+ static MediaRecordIdHash CORE_EXPORT GenerateHash(const LayoutObject* layout,
|
||||
+ const MediaTiming* media);
|
||||
+
|
||||
+ MediaRecordId(const LayoutObject* layout, const MediaTiming* media);
|
||||
+
|
||||
+ MediaRecordIdHash GetHash() const { return hash_; }
|
||||
+ const LayoutObject* GetLayoutObject() const { return layout_object_; }
|
||||
+ const MediaTiming* GetMediaTiming() const { return media_timing_; }
|
||||
+
|
||||
+ private:
|
||||
+ const LayoutObject* const layout_object_;
|
||||
+ const MediaTiming* const media_timing_;
|
||||
+ const MediaRecordIdHash hash_;
|
||||
+};
|
||||
+
|
||||
+} // namespace blink
|
||||
+#endif
|
||||
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
|
||||
index 569a2e9dcc37aaad1368d2daa742775ce12ecdd9..7197616a7be18333d51c61a256f1d712a52b8256 100755
|
||||
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
|
||||
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
|
||||
@@ -68,6 +68,7 @@ _CONFIG = [
|
||||
'base::DefaultTickClock',
|
||||
'base::ElapsedTimer',
|
||||
'base::EnumSet',
|
||||
+ 'base::HashInts',
|
||||
'base::JobDelegate',
|
||||
'base::JobHandle',
|
||||
'base::PostJob',
|
||||
151
patches/chromium/cherry-pick-309b604c4e88.patch
Normal file
151
patches/chromium/cherry-pick-309b604c4e88.patch
Normal file
@@ -0,0 +1,151 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tsuyoshi Horo <horo@chromium.org>
|
||||
Date: Fri, 29 Sep 2023 08:13:50 +0000
|
||||
Subject: Fix DataPipeDrainer usage in ExtensionLocalizationURLLoader
|
||||
|
||||
There is a bug that when ExtensionLocalizationURLLoader is destructed
|
||||
by canceling the CSS requests from extensions, DataPipeProducer may
|
||||
cause UAF.
|
||||
This is because DataPipeProducer is not correctly used in
|
||||
ExtensionLocalizationURLLoader. DataPipeProducer and the data must be
|
||||
kept alive until notified of completion.
|
||||
|
||||
This CL fix this by changing ExtensionLocalizationURLLoader to keep
|
||||
DataPipeProducer and the data even if ExtensionLocalizationURLLoader
|
||||
itself is destructed.
|
||||
|
||||
(cherry picked from commit b6e060e17ed9e46b3043a3c369fc10cbbe2245d8)
|
||||
|
||||
Bug: 1475798
|
||||
Change-Id: I013396f2c49f4712914b917c3330b99a1be791b8
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4821086
|
||||
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1191115}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4872577
|
||||
Commit-Queue: Zakhar Voit <voit@google.com>
|
||||
Owners-Override: Victor Gabriel Savu <vsavu@google.com>
|
||||
Reviewed-by: Victor Gabriel Savu <vsavu@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5735@{#1611}
|
||||
Cr-Branched-From: 2f562e4ddbaf79a3f3cb338b4d1bd4398d49eb67-refs/heads/main@{#1135570}
|
||||
|
||||
diff --git a/extensions/renderer/extension_localization_throttle.cc b/extensions/renderer/extension_localization_throttle.cc
|
||||
index 382283cea8a8a061f769dc234b67ef8b53b47517..65bbaf5a990fc86105048492e36813645a6a788c 100644
|
||||
--- a/extensions/renderer/extension_localization_throttle.cc
|
||||
+++ b/extensions/renderer/extension_localization_throttle.cc
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "extensions/renderer/extension_localization_throttle.h"
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
+#include "base/memory/weak_ptr.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
@@ -51,8 +52,7 @@ class ExtensionLocalizationURLLoader : public network::mojom::URLLoaderClient,
|
||||
|
||||
data_drainer_ =
|
||||
std::make_unique<mojo::DataPipeDrainer>(this, std::move(body));
|
||||
- data_producer_ =
|
||||
- std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle));
|
||||
+ producer_handle_ = std::move(producer_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -134,18 +134,31 @@ class ExtensionLocalizationURLLoader : public network::mojom::URLLoaderClient,
|
||||
ReplaceMessages();
|
||||
}
|
||||
|
||||
- // Safe to use Unretained(this) because `this` owns `data_producer_`.
|
||||
- data_producer_->Write(
|
||||
- std::make_unique<mojo::StringDataSource>(
|
||||
- base::StringPiece(data_), mojo::StringDataSource::AsyncWritingMode::
|
||||
- STRING_STAYS_VALID_UNTIL_COMPLETION),
|
||||
- base::BindOnce(&ExtensionLocalizationURLLoader::OnDataWritten,
|
||||
- base::Unretained(this)));
|
||||
+ auto data_producer =
|
||||
+ std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle_));
|
||||
+ auto data = std::make_unique<std::string>(std::move(data_));
|
||||
+ // To avoid unnecessary string copy, use STRING_STAYS_VALID_UNTIL_COMPLETION
|
||||
+ // here, and keep the original data hold in the closure below.
|
||||
+ auto source = std::make_unique<mojo::StringDataSource>(
|
||||
+ *data, mojo::StringDataSource::AsyncWritingMode::
|
||||
+ STRING_STAYS_VALID_UNTIL_COMPLETION);
|
||||
+ mojo::DataPipeProducer* data_producer_ptr = data_producer.get();
|
||||
+ data_producer_ptr->Write(
|
||||
+ std::move(source),
|
||||
+ base::BindOnce(
|
||||
+ [](std::unique_ptr<mojo::DataPipeProducer> data_producer,
|
||||
+ std::unique_ptr<std::string> data,
|
||||
+ base::OnceCallback<void(MojoResult)> on_data_written_callback,
|
||||
+ MojoResult result) {
|
||||
+ std::move(on_data_written_callback).Run(result);
|
||||
+ },
|
||||
+ std::move(data_producer), std::move(data),
|
||||
+ base::BindOnce(&ExtensionLocalizationURLLoader::OnDataWritten,
|
||||
+ weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
private:
|
||||
void OnDataWritten(MojoResult result) {
|
||||
- data_producer_.reset();
|
||||
data_write_result_ = result;
|
||||
MaybeSendOnComplete();
|
||||
}
|
||||
@@ -170,7 +183,7 @@ class ExtensionLocalizationURLLoader : public network::mojom::URLLoaderClient,
|
||||
|
||||
const std::string extension_id_;
|
||||
std::unique_ptr<mojo::DataPipeDrainer> data_drainer_;
|
||||
- std::unique_ptr<mojo::DataPipeProducer> data_producer_;
|
||||
+ mojo::ScopedDataPipeProducerHandle producer_handle_;
|
||||
std::string data_;
|
||||
|
||||
absl::optional<network::URLLoaderCompletionStatus> original_complete_status_;
|
||||
@@ -180,6 +193,7 @@ class ExtensionLocalizationURLLoader : public network::mojom::URLLoaderClient,
|
||||
this};
|
||||
mojo::Remote<network::mojom::URLLoader> source_url_loader_;
|
||||
mojo::Remote<network::mojom::URLLoaderClient> destination_url_loader_client_;
|
||||
+ base::WeakPtrFactory<ExtensionLocalizationURLLoader> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
diff --git a/extensions/renderer/extension_localization_throttle_unittest.cc b/extensions/renderer/extension_localization_throttle_unittest.cc
|
||||
index 732402366b697e3e982fe5feb3d25cb2e726abdd..221ee9062da9ea4262a4903cf815abf82cfb5d99 100644
|
||||
--- a/extensions/renderer/extension_localization_throttle_unittest.cc
|
||||
+++ b/extensions/renderer/extension_localization_throttle_unittest.cc
|
||||
@@ -306,6 +306,37 @@ TEST_F(ExtensionLocalizationThrottleTest, EmptyData) {
|
||||
delegate->destination_loader_client()->completion_status().error_code);
|
||||
}
|
||||
|
||||
+// Regression test for https://crbug.com/1475798
|
||||
+TEST_F(ExtensionLocalizationThrottleTest, Cancel) {
|
||||
+ const GURL url("chrome-extension://some_id/test.css");
|
||||
+ auto throttle =
|
||||
+ ExtensionLocalizationThrottle::MaybeCreate(blink::WebURL(url));
|
||||
+ ASSERT_TRUE(throttle);
|
||||
+
|
||||
+ auto delegate = std::make_unique<FakeDelegate>();
|
||||
+ throttle->set_delegate(delegate.get());
|
||||
+
|
||||
+ auto response_head = network::mojom::URLResponseHead::New();
|
||||
+ response_head->mime_type = "text/css";
|
||||
+ bool defer = false;
|
||||
+ throttle->WillProcessResponse(url, response_head.get(), &defer);
|
||||
+ EXPECT_FALSE(defer);
|
||||
+ EXPECT_TRUE(delegate->is_intercepted());
|
||||
+ delegate->LoadResponseBody("__MSG_hello__!");
|
||||
+ delegate->CompleteResponse();
|
||||
+ // Run all tasks in the main thread to make DataPipeProducer::SequenceState
|
||||
+ // call PostTask(&SequenceState::StartOnSequence) to a background thread.
|
||||
+ base::RunLoop().RunUntilIdle();
|
||||
+ // Resetting `destination_loader_remote` triggers
|
||||
+ // ExtensionLocalizationURLLoader destruction.
|
||||
+ delegate->destination_loader_remote().reset();
|
||||
+ // Run all tasks in the main thread to destroy the
|
||||
+ // ExtensionLocalizationURLLoader.
|
||||
+ base::RunLoop().RunUntilIdle();
|
||||
+ // Runs SequenceState::StartOnSequence in the background thread.
|
||||
+ task_environment_.RunUntilIdle();
|
||||
+}
|
||||
+
|
||||
TEST_F(ExtensionLocalizationThrottleTest, SourceSideError) {
|
||||
const GURL url("chrome-extension://some_id/test.css");
|
||||
auto throttle =
|
||||
57
patches/chromium/cherry-pick-3df423a5b8de.patch
Normal file
57
patches/chromium/cherry-pick-3df423a5b8de.patch
Normal file
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Hongchan Choi <hongchan@chromium.org>
|
||||
Date: Fri, 3 Nov 2023 16:39:55 +0000
|
||||
Subject: Check context status before recreating platform destination
|
||||
|
||||
Changing the channel count in the RealtimeAudioDestinationHandler will
|
||||
trigger the recreation of the platform destination. This in turn can
|
||||
activate the audio rendering thread.
|
||||
|
||||
This CL adds a check to prevent this from happening after the handler
|
||||
is garbage collected.
|
||||
|
||||
(cherry picked from commit 4997f2ba263ff7e1dbc7987dd3665459be14dffe)
|
||||
|
||||
Bug: 1497859
|
||||
Test: Locally confirmed with ASAN
|
||||
Change-Id: I5d2649f3fd3639779ae40b0ca4ef2fe305653421
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4995928
|
||||
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
|
||||
Reviewed-by: Michael Wilson <mjwilson@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1217868}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5004961
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5993@{#1520}
|
||||
Cr-Branched-From: 511350718e646be62331ae9d7213d10ec320d514-refs/heads/main@{#1192594}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
|
||||
index 6781dcff462db872d1f5a786aef0c89f43189100..2e4757d155800700b7c6a8b7cbf2e02250cfce65 100644
|
||||
--- a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
|
||||
+++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
|
||||
@@ -118,12 +118,21 @@ void RealtimeAudioDestinationHandler::SetChannelCount(
|
||||
uint32_t old_channel_count = ChannelCount();
|
||||
AudioHandler::SetChannelCount(channel_count, exception_state);
|
||||
|
||||
- // Stop, re-create and start the destination to apply the new channel count.
|
||||
- if (ChannelCount() != old_channel_count && !exception_state.HadException()) {
|
||||
- StopPlatformDestination();
|
||||
- CreatePlatformDestination();
|
||||
- StartPlatformDestination();
|
||||
+ // After the context is closed, changing channel count will be ignored
|
||||
+ // because it will trigger the recreation of the platform destination. This
|
||||
+ // in turn can activate the audio rendering thread.
|
||||
+ AudioContext* context = static_cast<AudioContext*>(Context());
|
||||
+ CHECK(context);
|
||||
+ if (context->ContextState() == AudioContext::kClosed ||
|
||||
+ ChannelCount() == old_channel_count ||
|
||||
+ exception_state.HadException()) {
|
||||
+ return;
|
||||
}
|
||||
+
|
||||
+ // Stop, re-create and start the destination to apply the new channel count.
|
||||
+ StopPlatformDestination();
|
||||
+ CreatePlatformDestination();
|
||||
+ StartPlatformDestination();
|
||||
}
|
||||
|
||||
void RealtimeAudioDestinationHandler::StartRendering() {
|
||||
428
patches/chromium/cherry-pick-80106e31c7ea.patch
Normal file
428
patches/chromium/cherry-pick-80106e31c7ea.patch
Normal file
@@ -0,0 +1,428 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Reynolds <mattreynolds@google.com>
|
||||
Date: Wed, 25 Oct 2023 00:56:26 +0000
|
||||
Subject: usb: Validate isochronous transfer packet lengths
|
||||
|
||||
USBDevice.isochronousTransferIn and
|
||||
USBDevice.isochronousTransferOut take a parameter containing
|
||||
a list of packet lengths. This CL adds validation that the
|
||||
total packet length does not exceed the maximum buffer size.
|
||||
For isochronousTransferOut, it also checks that the total
|
||||
length of all packets in bytes is equal to the size of the
|
||||
data buffer.
|
||||
|
||||
Passing invalid packet lengths causes the promise to be
|
||||
rejected with a DataError.
|
||||
|
||||
(cherry picked from commit bb36f739e7e0a3722beeb2744744195c22fd6143)
|
||||
|
||||
Bug: 1492381, 1492384
|
||||
Change-Id: Id9ae16c7e6f1c417e0fc4f21d53e9de11560b2b7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4944690
|
||||
Reviewed-by: Reilly Grant <reillyg@chromium.org>
|
||||
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1212916}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4974416
|
||||
Commit-Queue: Reilly Grant <reillyg@chromium.org>
|
||||
Auto-Submit: Matt Reynolds <mattreynolds@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5993@{#1425}
|
||||
Cr-Branched-From: 511350718e646be62331ae9d7213d10ec320d514-refs/heads/main@{#1192594}
|
||||
|
||||
diff --git a/services/device/usb/mojo/device_impl.cc b/services/device/usb/mojo/device_impl.cc
|
||||
index 34cc1f360a0340fa235ee0e086f5f5b2ee56309d..a44cb3b262203cc519012e60465cc01af9b4260c 100644
|
||||
--- a/services/device/usb/mojo/device_impl.cc
|
||||
+++ b/services/device/usb/mojo/device_impl.cc
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "base/ranges/algorithm.h"
|
||||
#include "services/device/public/cpp/usb/usb_utils.h"
|
||||
#include "services/device/usb/usb_device.h"
|
||||
+#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace device {
|
||||
|
||||
@@ -89,6 +90,20 @@ bool IsAndroidSecurityKeyRequest(
|
||||
memcmp(data.data(), magic, strlen(magic)) == 0;
|
||||
}
|
||||
|
||||
+// Returns the sum of `packet_lengths`, or nullopt if the sum would overflow.
|
||||
+absl::optional<uint32_t> TotalPacketLength(
|
||||
+ base::span<const uint32_t> packet_lengths) {
|
||||
+ uint32_t total_bytes = 0;
|
||||
+ for (const uint32_t packet_length : packet_lengths) {
|
||||
+ // Check for overflow.
|
||||
+ if (std::numeric_limits<uint32_t>::max() - total_bytes < packet_length) {
|
||||
+ return absl::nullopt;
|
||||
+ }
|
||||
+ total_bytes += packet_length;
|
||||
+ }
|
||||
+ return total_bytes;
|
||||
+}
|
||||
+
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@@ -397,6 +412,15 @@ void DeviceImpl::IsochronousTransferIn(
|
||||
return;
|
||||
}
|
||||
|
||||
+ absl::optional<uint32_t> total_bytes = TotalPacketLength(packet_lengths);
|
||||
+ if (!total_bytes.has_value()) {
|
||||
+ mojo::ReportBadMessage("Invalid isochronous packet lengths.");
|
||||
+ std::move(callback).Run(
|
||||
+ {}, BuildIsochronousPacketArray(
|
||||
+ packet_lengths, mojom::UsbTransferStatus::TRANSFER_ERROR));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
uint8_t endpoint_address = endpoint_number | 0x80;
|
||||
device_handle_->IsochronousTransferIn(
|
||||
endpoint_address, packet_lengths, timeout,
|
||||
@@ -415,6 +439,14 @@ void DeviceImpl::IsochronousTransferOut(
|
||||
return;
|
||||
}
|
||||
|
||||
+ absl::optional<uint32_t> total_bytes = TotalPacketLength(packet_lengths);
|
||||
+ if (!total_bytes.has_value() || total_bytes.value() != data.size()) {
|
||||
+ mojo::ReportBadMessage("Invalid isochronous packet lengths.");
|
||||
+ std::move(callback).Run(BuildIsochronousPacketArray(
|
||||
+ packet_lengths, mojom::UsbTransferStatus::TRANSFER_ERROR));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
uint8_t endpoint_address = endpoint_number;
|
||||
auto buffer = base::MakeRefCounted<base::RefCountedBytes>(data);
|
||||
device_handle_->IsochronousTransferOut(
|
||||
diff --git a/services/device/usb/mojo/device_impl_unittest.cc b/services/device/usb/mojo/device_impl_unittest.cc
|
||||
index b23918682064047f644344f96c7a13ccbbe7d95d..2d401f70b2b9d827153adb8b14a5acb6d4a9a8de 100644
|
||||
--- a/services/device/usb/mojo/device_impl_unittest.cc
|
||||
+++ b/services/device/usb/mojo/device_impl_unittest.cc
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
-#include <numeric>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -54,8 +53,11 @@ MATCHER_P(BufferSizeIs, size, "") {
|
||||
|
||||
class ConfigBuilder {
|
||||
public:
|
||||
- explicit ConfigBuilder(uint8_t value)
|
||||
- : config_(BuildUsbConfigurationInfoPtr(value, false, false, 0)) {}
|
||||
+ explicit ConfigBuilder(uint8_t configuration_value)
|
||||
+ : config_(BuildUsbConfigurationInfoPtr(configuration_value,
|
||||
+ /*self_powered=*/false,
|
||||
+ /*remote_wakeup=*/false,
|
||||
+ /*maximum_power=*/0)) {}
|
||||
|
||||
ConfigBuilder(const ConfigBuilder&) = delete;
|
||||
ConfigBuilder& operator=(const ConfigBuilder&) = delete;
|
||||
@@ -413,8 +415,10 @@ class USBDeviceImplTest : public testing::Test {
|
||||
|
||||
ASSERT_EQ(packets.size(), packet_lengths.size());
|
||||
for (size_t i = 0; i < packets.size(); ++i) {
|
||||
- EXPECT_EQ(packets[i]->length, packet_lengths[i])
|
||||
- << "Packet lengths differ at index: " << i;
|
||||
+ if (packets[i]->status == mojom::UsbTransferStatus::COMPLETED) {
|
||||
+ EXPECT_EQ(packets[i]->length, packet_lengths[i])
|
||||
+ << "Packet lengths differ at index: " << i;
|
||||
+ }
|
||||
}
|
||||
|
||||
std::move(callback).Run(buffer, std::move(packets));
|
||||
@@ -428,10 +432,8 @@ class USBDeviceImplTest : public testing::Test {
|
||||
UsbDeviceHandle::IsochronousTransferCallback& callback) {
|
||||
ASSERT_FALSE(mock_outbound_data_.empty());
|
||||
const std::vector<uint8_t>& bytes = mock_outbound_data_.front();
|
||||
- size_t length =
|
||||
- std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
|
||||
- ASSERT_EQ(bytes.size(), length);
|
||||
- for (size_t i = 0; i < length; ++i) {
|
||||
+ ASSERT_EQ(buffer->size(), bytes.size());
|
||||
+ for (size_t i = 0; i < bytes.size(); ++i) {
|
||||
EXPECT_EQ(bytes[i], buffer->front()[i])
|
||||
<< "Contents differ at index: " << i;
|
||||
}
|
||||
@@ -444,8 +446,10 @@ class USBDeviceImplTest : public testing::Test {
|
||||
|
||||
ASSERT_EQ(packets.size(), packet_lengths.size());
|
||||
for (size_t i = 0; i < packets.size(); ++i) {
|
||||
- EXPECT_EQ(packets[i]->length, packet_lengths[i])
|
||||
- << "Packet lengths differ at index: " << i;
|
||||
+ if (packets[i]->status == mojom::UsbTransferStatus::COMPLETED) {
|
||||
+ EXPECT_EQ(packets[i]->length, packet_lengths[i])
|
||||
+ << "Packet lengths differ at index: " << i;
|
||||
+ }
|
||||
}
|
||||
|
||||
std::move(callback).Run(buffer, std::move(packets));
|
||||
@@ -1084,6 +1088,122 @@ TEST_F(USBDeviceImplTest, IsochronousTransfer) {
|
||||
EXPECT_CALL(mock_handle(), Close());
|
||||
}
|
||||
|
||||
+TEST_F(USBDeviceImplTest, IsochronousTransferOutBufferSizeMismatch) {
|
||||
+ mojo::Remote<mojom::UsbDevice> device = GetMockDeviceProxy();
|
||||
+
|
||||
+ EXPECT_CALL(mock_device(), OpenInternal);
|
||||
+
|
||||
+ base::test::TestFuture<mojom::UsbOpenDeviceResultPtr> open_future;
|
||||
+ device->Open(open_future.GetCallback());
|
||||
+ EXPECT_TRUE(open_future.Get()->is_success());
|
||||
+
|
||||
+ constexpr size_t kPacketCount = 4;
|
||||
+ constexpr size_t kPacketLength = 8;
|
||||
+ std::vector<UsbIsochronousPacketPtr> fake_packets;
|
||||
+ for (size_t i = 0; i < kPacketCount; ++i) {
|
||||
+ fake_packets.push_back(mojom::UsbIsochronousPacket::New(
|
||||
+ kPacketLength, kPacketLength, UsbTransferStatus::TRANSFER_ERROR));
|
||||
+ }
|
||||
+
|
||||
+ std::string outbound_data = "aaaaaaaabbbbbbbbccccccccdddddddd";
|
||||
+ std::vector<uint8_t> fake_outbound_data(outbound_data.size());
|
||||
+ base::ranges::copy(outbound_data, fake_outbound_data.begin());
|
||||
+
|
||||
+ std::string inbound_data = "ddddddddccccccccbbbbbbbbaaaaaaaa";
|
||||
+ std::vector<uint8_t> fake_inbound_data(inbound_data.size());
|
||||
+ base::ranges::copy(inbound_data, fake_inbound_data.begin());
|
||||
+
|
||||
+ AddMockConfig(ConfigBuilder(/*configuration_value=*/1)
|
||||
+ .AddInterface(/*interface_number=*/7,
|
||||
+ /*alternate_setting=*/0, /*class_code=*/1,
|
||||
+ /*subclass_code=*/2, /*protocol_code=*/3)
|
||||
+ .Build());
|
||||
+ AddMockOutboundPackets(fake_outbound_data, mojo::Clone(fake_packets));
|
||||
+ AddMockInboundPackets(fake_inbound_data, mojo::Clone(fake_packets));
|
||||
+
|
||||
+ // The `packet_lengths` parameter for IsochronousTransferOut describes the
|
||||
+ // number of bytes in each packet. Set the size of the last packet one byte
|
||||
+ // shorter than the buffer size and check that the returned packets indicate
|
||||
+ // a transfer error.
|
||||
+ std::vector<uint32_t> short_packet_lengths(kPacketCount, kPacketLength);
|
||||
+ short_packet_lengths.back() = kPacketLength - 1;
|
||||
+
|
||||
+ base::test::TestFuture<std::vector<UsbIsochronousPacketPtr>>
|
||||
+ transfer_out_future;
|
||||
+ device->IsochronousTransferOut(
|
||||
+ /*endpoint_number=*/1, fake_outbound_data, short_packet_lengths,
|
||||
+ /*timeout=*/0, transfer_out_future.GetCallback());
|
||||
+ ASSERT_EQ(kPacketCount, transfer_out_future.Get().size());
|
||||
+ for (const auto& packet : transfer_out_future.Get()) {
|
||||
+ EXPECT_EQ(packet->status, UsbTransferStatus::TRANSFER_ERROR);
|
||||
+ }
|
||||
+
|
||||
+ EXPECT_CALL(mock_handle(), Close);
|
||||
+}
|
||||
+
|
||||
+TEST_F(USBDeviceImplTest, IsochronousTransferPacketLengthsOverflow) {
|
||||
+ mojo::Remote<mojom::UsbDevice> device = GetMockDeviceProxy();
|
||||
+
|
||||
+ EXPECT_CALL(mock_device(), OpenInternal);
|
||||
+
|
||||
+ base::test::TestFuture<mojom::UsbOpenDeviceResultPtr> open_future;
|
||||
+ device->Open(open_future.GetCallback());
|
||||
+ EXPECT_TRUE(open_future.Get()->is_success());
|
||||
+
|
||||
+ constexpr size_t kPacketCount = 2;
|
||||
+ constexpr size_t kPacketLength = 8;
|
||||
+ std::vector<UsbIsochronousPacketPtr> fake_packets;
|
||||
+ for (size_t i = 0; i < kPacketCount; ++i) {
|
||||
+ fake_packets.push_back(mojom::UsbIsochronousPacket::New(
|
||||
+ kPacketLength, kPacketLength, UsbTransferStatus::TRANSFER_ERROR));
|
||||
+ }
|
||||
+
|
||||
+ std::string outbound_data = "aaaaaaaabbbbbbbb";
|
||||
+ std::vector<uint8_t> fake_outbound_data(outbound_data.size());
|
||||
+ base::ranges::copy(outbound_data, fake_outbound_data.begin());
|
||||
+
|
||||
+ std::string inbound_data = "bbbbbbbbaaaaaaaa";
|
||||
+ std::vector<uint8_t> fake_inbound_data(inbound_data.size());
|
||||
+ base::ranges::copy(inbound_data, fake_inbound_data.begin());
|
||||
+
|
||||
+ AddMockConfig(ConfigBuilder(/*configuration_value=*/1)
|
||||
+ .AddInterface(/*interface_number=*/7,
|
||||
+ /*alternate_setting=*/0, /*class_code=*/1,
|
||||
+ /*subclass_code=*/2, /*protocol_code=*/3)
|
||||
+ .Build());
|
||||
+ AddMockOutboundPackets(fake_outbound_data, mojo::Clone(fake_packets));
|
||||
+ AddMockInboundPackets(fake_inbound_data, mojo::Clone(fake_packets));
|
||||
+
|
||||
+ // The `packet_lengths` parameter for IsochronousTransferOut and
|
||||
+ // IsochronousTransferIn describes the number of bytes in each packet. Set
|
||||
+ // the packet sizes so the total will exceed the maximum value for uint32_t
|
||||
+ // and check that the returned packets indicate a transfer error.
|
||||
+ std::vector<uint32_t> overflow_packet_lengths = {0xffffffff, 1};
|
||||
+
|
||||
+ base::test::TestFuture<std::vector<UsbIsochronousPacketPtr>>
|
||||
+ transfer_out_future;
|
||||
+ device->IsochronousTransferOut(
|
||||
+ /*endpoint_number=*/1, fake_outbound_data, overflow_packet_lengths,
|
||||
+ /*timeout=*/0, transfer_out_future.GetCallback());
|
||||
+ ASSERT_EQ(kPacketCount, transfer_out_future.Get().size());
|
||||
+ for (const auto& packet : transfer_out_future.Get()) {
|
||||
+ EXPECT_EQ(packet->status, UsbTransferStatus::TRANSFER_ERROR);
|
||||
+ }
|
||||
+
|
||||
+ base::test::TestFuture<base::span<const uint8_t>,
|
||||
+ std::vector<UsbIsochronousPacketPtr>>
|
||||
+ transfer_in_future;
|
||||
+ device->IsochronousTransferIn(
|
||||
+ /*endpoint_number=*/1, overflow_packet_lengths, /*timeout=*/0,
|
||||
+ transfer_in_future.GetCallback());
|
||||
+ ASSERT_EQ(kPacketCount, transfer_in_future.Get<1>().size());
|
||||
+ for (const auto& packet : transfer_in_future.Get<1>()) {
|
||||
+ EXPECT_EQ(packet->status, UsbTransferStatus::TRANSFER_ERROR);
|
||||
+ }
|
||||
+
|
||||
+ EXPECT_CALL(mock_handle(), Close);
|
||||
+}
|
||||
+
|
||||
class USBDeviceImplSecurityKeyTest : public USBDeviceImplTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
};
|
||||
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.cc b/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
index bffe45ca2fe4acbe8edfdbe19d889dd62c68cd10..25de0c1e96fb57f822de9dad6c6641c2b77ad48d 100644
|
||||
--- a/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
+++ b/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
#include "third_party/blink/renderer/modules/webusb/usb_device.h"
|
||||
|
||||
+#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
+#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "third_party/blink/public/platform/platform.h"
|
||||
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
|
||||
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
|
||||
@@ -43,6 +45,10 @@ namespace {
|
||||
|
||||
const char kAccessDeniedError[] = "Access denied.";
|
||||
const char kBufferTooBig[] = "The data buffer exceeded its maximum size.";
|
||||
+const char kPacketLengthsTooBig[] =
|
||||
+ "The total packet length exceeded the maximum size.";
|
||||
+const char kBufferSizeMismatch[] =
|
||||
+ "The data buffer size must match the total packet length.";
|
||||
const char kDetachedBuffer[] = "The data buffer has been detached.";
|
||||
const char kDeviceStateChangeInProgress[] =
|
||||
"An operation that changes the device state is in progress.";
|
||||
@@ -106,6 +112,20 @@ String ConvertTransferStatus(const UsbTransferStatus& status) {
|
||||
}
|
||||
}
|
||||
|
||||
+// Returns the sum of `packet_lengths`, or nullopt if the sum would overflow.
|
||||
+absl::optional<uint32_t> TotalPacketLength(
|
||||
+ const Vector<unsigned>& packet_lengths) {
|
||||
+ uint32_t total_bytes = 0;
|
||||
+ for (const auto packet_length : packet_lengths) {
|
||||
+ // Check for overflow.
|
||||
+ if (std::numeric_limits<uint32_t>::max() - total_bytes < packet_length) {
|
||||
+ return absl::nullopt;
|
||||
+ }
|
||||
+ total_bytes += packet_length;
|
||||
+ }
|
||||
+ return total_bytes;
|
||||
+}
|
||||
+
|
||||
} // namespace
|
||||
|
||||
USBDevice::USBDevice(USB* parent,
|
||||
@@ -578,6 +598,13 @@ ScriptPromise USBDevice::isochronousTransferIn(
|
||||
if (exception_state.HadException())
|
||||
return ScriptPromise();
|
||||
|
||||
+ absl::optional<uint32_t> total_bytes = TotalPacketLength(packet_lengths);
|
||||
+ if (!total_bytes.has_value()) {
|
||||
+ exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
|
||||
+ kPacketLengthsTooBig);
|
||||
+ return ScriptPromise();
|
||||
+ }
|
||||
+
|
||||
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
|
||||
script_state, exception_state.GetContext());
|
||||
ScriptPromise promise = resolver->Promise();
|
||||
@@ -615,6 +642,18 @@ ScriptPromise USBDevice::isochronousTransferOut(
|
||||
return ScriptPromise();
|
||||
}
|
||||
|
||||
+ absl::optional<uint32_t> total_bytes = TotalPacketLength(packet_lengths);
|
||||
+ if (!total_bytes.has_value()) {
|
||||
+ exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
|
||||
+ kPacketLengthsTooBig);
|
||||
+ return ScriptPromise();
|
||||
+ }
|
||||
+ if (total_bytes.value() != data.ByteLength()) {
|
||||
+ exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
|
||||
+ kBufferSizeMismatch);
|
||||
+ return ScriptPromise();
|
||||
+ }
|
||||
+
|
||||
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
|
||||
script_state, exception_state.GetContext());
|
||||
ScriptPromise promise = resolver->Promise();
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/webusb/usbDevice.https.any.js b/third_party/blink/web_tests/external/wpt/webusb/usbDevice.https.any.js
|
||||
index b1b0c133ce160a314ea392514ac5b38e4cac136d..804af2afb9db3a0d5fafbeb26aed64f89badb1b3 100644
|
||||
--- a/third_party/blink/web_tests/external/wpt/webusb/usbDevice.https.any.js
|
||||
+++ b/third_party/blink/web_tests/external/wpt/webusb/usbDevice.https.any.js
|
||||
@@ -1247,3 +1247,60 @@ usb_test((t) => {
|
||||
.then(() => promise_rejects_dom(t, 'NotFoundError', device.reset()));
|
||||
});
|
||||
}, 'resetDevice rejects when called on a disconnected device');
|
||||
+
|
||||
+usb_test(async (t) => {
|
||||
+ const PACKET_COUNT = 4;
|
||||
+ const PACKET_LENGTH = 8;
|
||||
+ const {device, fakeDevice} = await getFakeDevice();
|
||||
+ await device.open();
|
||||
+ await device.selectConfiguration(2);
|
||||
+ await device.claimInterface(0);
|
||||
+ await device.selectAlternateInterface(0, 1);
|
||||
+ const buffer = new Uint8Array(PACKET_COUNT * PACKET_LENGTH);
|
||||
+ const packetLengths = new Array(PACKET_COUNT).fill(PACKET_LENGTH);
|
||||
+ packetLengths[0] = PACKET_LENGTH - 1;
|
||||
+ await promise_rejects_dom(
|
||||
+ t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths));
|
||||
+}, 'isochronousTransferOut rejects when buffer size exceeds packet lengths');
|
||||
+
|
||||
+usb_test(async (t) => {
|
||||
+ const PACKET_COUNT = 4;
|
||||
+ const PACKET_LENGTH = 8;
|
||||
+ const {device, fakeDevice} = await getFakeDevice();
|
||||
+ await device.open();
|
||||
+ await device.selectConfiguration(2);
|
||||
+ await device.claimInterface(0);
|
||||
+ await device.selectAlternateInterface(0, 1);
|
||||
+ const buffer = new Uint8Array(PACKET_COUNT * PACKET_LENGTH);
|
||||
+ const packetLengths = new Array(PACKET_COUNT).fill(PACKET_LENGTH);
|
||||
+ packetLengths[0] = PACKET_LENGTH + 1;
|
||||
+ await promise_rejects_dom(
|
||||
+ t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths));
|
||||
+}, 'isochronousTransferOut rejects when packet lengths exceed buffer size');
|
||||
+
|
||||
+usb_test(async (t) => {
|
||||
+ const PACKET_COUNT = 2;
|
||||
+ const PACKET_LENGTH = 8;
|
||||
+ const {device, fakeDevice} = await getFakeDevice();
|
||||
+ await device.open();
|
||||
+ await device.selectConfiguration(2);
|
||||
+ await device.claimInterface(0);
|
||||
+ await device.selectAlternateInterface(0, 1);
|
||||
+ const packetLengths = [0xffffffff, 1];
|
||||
+ await promise_rejects_dom(
|
||||
+ t, 'DataError', device.isochronousTransferIn(1, packetLengths));
|
||||
+}, 'isochronousTransferIn rejects when packet lengths exceed maximum size');
|
||||
+
|
||||
+usb_test(async (t) => {
|
||||
+ const PACKET_COUNT = 2;
|
||||
+ const PACKET_LENGTH = 8;
|
||||
+ const {device, fakeDevice} = await getFakeDevice();
|
||||
+ await device.open();
|
||||
+ await device.selectConfiguration(2);
|
||||
+ await device.claimInterface(0);
|
||||
+ await device.selectAlternateInterface(0, 1);
|
||||
+ const buffer = new Uint8Array(PACKET_LENGTH * PACKET_COUNT);
|
||||
+ const packetLengths = [0xffffffff, 1];
|
||||
+ await promise_rejects_dom(
|
||||
+ t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths));
|
||||
+}, 'isochronousTransferOut rejects when packet lengths exceed maximum size');
|
||||
40
patches/chromium/cherry-pick-b11e7d07a6f4.patch
Normal file
40
patches/chromium/cherry-pick-b11e7d07a6f4.patch
Normal file
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lei Zhang <thestig@chromium.org>
|
||||
Date: Wed, 13 Sep 2023 23:32:40 +0000
|
||||
Subject: M117: Check for object destruction in PdfViewWebPlugin::UpdateFocus()
|
||||
|
||||
PdfViewWebPlugin::UpdateFocus() can potentially triggers its own
|
||||
destruction. Add a check for this and bail out.
|
||||
|
||||
(cherry picked from commit cacf485a202b342526374d444375b80a044add76)
|
||||
|
||||
Bug: 1480184
|
||||
Change-Id: I5e7760ed541a2bffb9dd1ebeb522f10650049033
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4852346
|
||||
Reviewed-by: Tom Sepez <tsepez@chromium.org>
|
||||
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
|
||||
Commit-Queue: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1194210}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4863395
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5938@{#1286}
|
||||
Cr-Branched-From: 2b50cb4bcc2318034581a816714d9535dc38966d-refs/heads/main@{#1181205}
|
||||
|
||||
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
|
||||
index 1abf1f5df13bb2f41862300adb166691e9625ab7..f9fbb3d718b885874c1def1c005d7cc710591fae 100644
|
||||
--- a/pdf/pdf_view_web_plugin.cc
|
||||
+++ b/pdf/pdf_view_web_plugin.cc
|
||||
@@ -515,7 +515,13 @@ void PdfViewWebPlugin::UpdateFocus(bool focused,
|
||||
if (has_focus_ != focused) {
|
||||
engine_->UpdateFocus(focused);
|
||||
client_->UpdateTextInputState();
|
||||
+
|
||||
+ // Make sure `this` is still alive after the UpdateSelectionBounds() call.
|
||||
+ auto weak_this = weak_factory_.GetWeakPtr();
|
||||
client_->UpdateSelectionBounds();
|
||||
+ if (!weak_this) {
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
has_focus_ = focused;
|
||||
|
||||
201
patches/chromium/cherry-pick-d756d71a652c.patch
Normal file
201
patches/chromium/cherry-pick-d756d71a652c.patch
Normal file
@@ -0,0 +1,201 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alex Moshchuk <alexmos@chromium.org>
|
||||
Date: Mon, 9 Oct 2023 17:14:13 +0000
|
||||
Subject: Fix RFHI::pending_navigate_ cleanup after crashes and early RFH
|
||||
swaps.
|
||||
|
||||
When resuming a navigation that had been saved into
|
||||
RenderFrameHostImpl::pending_navigate_, we need to account for the
|
||||
fact that OnBeginNavigation() calls GetFrameHostForNavigation() which
|
||||
may perform an early RenderFrameHost swap and synchronously destroy
|
||||
the old RFH.
|
||||
|
||||
There's also no need to keep a pending_navigate_ around after the
|
||||
corresponding renderer process crashes, so this CL also adds logic to
|
||||
clear it. Resuming such a navigation would require additional work,
|
||||
since the NavigationClient stashed in pending_navigate_ is no longer
|
||||
usable and would just immediately call the disconnect handler and
|
||||
cancel the navigation. But there isn't really any benefit to adding
|
||||
that complexity, and we already cancel the RFH's other ongoing
|
||||
navigations when its renderer process dies.
|
||||
|
||||
This CL also tweaks the logic in RenderWidgetHostImpl to allow the
|
||||
resuming logic (ResumeLoadingCreatedWebContents) to work without
|
||||
hitting DCHECKs, if it's called after a renderer process crash. This
|
||||
case never worked cleanly before, but is supported now (and allows the
|
||||
new test to work without crashing).
|
||||
|
||||
(cherry picked from commit 093daae65d50511c2027d01f9188681749b5a1be)
|
||||
|
||||
Bug: 1487110
|
||||
Change-Id: Icd6a55002e52729e6ee966210efba1a5ce23eb55
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4908270
|
||||
Commit-Queue: Alex Moshchuk <alexmos@chromium.org>
|
||||
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1205927}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4923011
|
||||
Owners-Override: Krishna Govind <govind@chromium.org>
|
||||
Reviewed-by: Krishna Govind <govind@chromium.org>
|
||||
Commit-Queue: Krishna Govind <govind@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5993@{#1208}
|
||||
Cr-Branched-From: 511350718e646be62331ae9d7213d10ec320d514-refs/heads/main@{#1192594}
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 820afdd8c73b6dbcf3aceba38af2cf0e202bd49a..17954b8b5796d02eeeb291d93aff4353c1ec1411 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -3133,6 +3133,13 @@ void RenderFrameHostImpl::RenderProcessGone(
|
||||
ResetOwnedNavigationRequests(NavigationDiscardReason::kRenderProcessGone);
|
||||
ResetLoadingState();
|
||||
|
||||
+ // Also, clear any pending navigations that have been blocked while the
|
||||
+ // embedder is processing window.open() requests. This is consistent
|
||||
+ // with clearing NavigationRequests and loading state above, and it also
|
||||
+ // makes sense because certain parts of `pending_navigate_`, like the
|
||||
+ // NavigationClient remote interface, can no longer be used.
|
||||
+ pending_navigate_.reset();
|
||||
+
|
||||
// Any future UpdateState or UpdateTitle messages from this or a recreated
|
||||
// process should be ignored until the next commit.
|
||||
set_nav_entry_id(0);
|
||||
@@ -3537,14 +3544,25 @@ void RenderFrameHostImpl::Init() {
|
||||
});
|
||||
|
||||
if (pending_navigate_) {
|
||||
+
|
||||
+ // Transfer `pending_navigate_` to a local variable, to avoid resetting it
|
||||
+ // after OnBeginNavigation since `this` might already be destroyed (see
|
||||
+ // below).
|
||||
+ //
|
||||
+ // This shouldn't matter for early RFH swaps out of crashed frames, since
|
||||
+ // `pending_navigate_` is cleared when the renderer process dies, but it
|
||||
+ // may matter for other current/future use cases of the early RFH swap.
|
||||
+ std::unique_ptr<PendingNavigation> pending_navigation =
|
||||
+ std::move(pending_navigate_);
|
||||
frame_tree_node()->navigator().OnBeginNavigation(
|
||||
- frame_tree_node(), std::move(pending_navigate_->common_params),
|
||||
- std::move(pending_navigate_->begin_navigation_params),
|
||||
- std::move(pending_navigate_->blob_url_loader_factory),
|
||||
- std::move(pending_navigate_->navigation_client),
|
||||
+ frame_tree_node(), std::move(pending_navigation->common_params),
|
||||
+ std::move(pending_navigation->begin_navigation_params),
|
||||
+ std::move(pending_navigation->blob_url_loader_factory),
|
||||
+ std::move(pending_navigation->navigation_client),
|
||||
EnsurePrefetchedSignedExchangeCache(),
|
||||
- std::move(pending_navigate_->renderer_cancellation_listener));
|
||||
- pending_navigate_.reset();
|
||||
+ std::move(pending_navigation->renderer_cancellation_listener));
|
||||
+ // DO NOT ADD CODE after this, as `this` might be deleted if an early
|
||||
+ // RenderFrameHost swap was performed when starting the navigation above.
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
index 07b6f899132ab4b94fb9474d441461c8fcf53705..4df864783b69bad7c578341ccf995c4a7ef62b62 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
@@ -756,9 +756,15 @@ void RenderWidgetHostImpl::RendererWidgetCreated(bool for_frame_widget) {
|
||||
}
|
||||
|
||||
void RenderWidgetHostImpl::Init() {
|
||||
- DCHECK(renderer_widget_created_);
|
||||
- DCHECK(waiting_for_init_);
|
||||
+ // Note that this may be called after a renderer crash. In this case, we can
|
||||
+ // just exit early, as there is nothing else to do. Note that
|
||||
+ // `waiting_for_init_` should've already been reset to false in that case.
|
||||
+ if (!renderer_widget_created_) {
|
||||
+ DCHECK(!waiting_for_init_);
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
+ DCHECK(waiting_for_init_);
|
||||
waiting_for_init_ = false;
|
||||
|
||||
// These two methods avoid running while we are `waiting_for_init_`, so we
|
||||
@@ -2215,6 +2221,10 @@ void RenderWidgetHostImpl::RendererExited() {
|
||||
|
||||
blink_widget_.reset();
|
||||
|
||||
+ // No need to perform a deferred show after the renderer crashes, and this
|
||||
+ // wouldn't work anyway as it requires a valid `blink_widget_`.
|
||||
+ pending_show_params_.reset();
|
||||
+
|
||||
// After the renderer crashes, the view is destroyed and so the
|
||||
// RenderWidgetHost cannot track its visibility anymore. We assume such
|
||||
// RenderWidgetHost to be invisible for the sake of internal accounting - be
|
||||
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
|
||||
index e008d0adb0e643b7796adf13946b19ab6cfaba56..b48b8fc0fc1e56e327663f9230ea599a86b1eb87 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "content/public/test/fenced_frame_test_util.h"
|
||||
#include "content/public/test/mock_client_hints_controller_delegate.h"
|
||||
#include "content/public/test/mock_web_contents_observer.h"
|
||||
+#include "content/public/test/navigation_handle_observer.h"
|
||||
#include "content/public/test/no_renderer_crashes_assertion.h"
|
||||
#include "content/public/test/prerender_test_util.h"
|
||||
#include "content/public/test/test_navigation_observer.h"
|
||||
@@ -5575,6 +5576,63 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
|
||||
EXPECT_FALSE(shell()->web_contents()->IsCrashed());
|
||||
}
|
||||
|
||||
+// Check that there's no crash if a new window is set to defer navigations (for
|
||||
+// example, this is done on Android Webview and for <webview> guests), then the
|
||||
+// renderer process crashes while there's a deferred new window navigation in
|
||||
+// place, and then navigations are resumed. Prior to fixing
|
||||
+// https://crbug.com/1487110, the deferred navigation was allowed to proceed,
|
||||
+// performing an early RenderFrameHost swap and hitting a bug while clearing
|
||||
+// the deferred navigation state. Now, the deferred navigation should be
|
||||
+// canceled when the renderer process dies.
|
||||
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
|
||||
+ DeferredWindowOpenNavigationIsResumedWithEarlySwap) {
|
||||
+ // Force WebContents in a new Shell to defer new navigations until the
|
||||
+ // delegate is set.
|
||||
+ shell()->set_delay_popup_contents_delegate_for_testing(true);
|
||||
+
|
||||
+ // Load an initial page.
|
||||
+ ASSERT_TRUE(embedded_test_server()->Start());
|
||||
+ GURL url(embedded_test_server()->GetURL("/title1.html"));
|
||||
+ EXPECT_TRUE(NavigateToURL(shell(), url));
|
||||
+
|
||||
+ // Open a popup to a same-site URL via window.open.
|
||||
+ ShellAddedObserver new_shell_observer;
|
||||
+ EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1);", url)));
|
||||
+ Shell* new_shell = new_shell_observer.GetShell();
|
||||
+ WebContents* new_contents = new_shell->web_contents();
|
||||
+
|
||||
+ // The navigation in the new popup should be deferred.
|
||||
+ EXPECT_TRUE(WaitForLoadStop(new_contents));
|
||||
+ EXPECT_TRUE(new_contents->GetController().IsInitialBlankNavigation());
|
||||
+ EXPECT_TRUE(new_contents->GetLastCommittedURL().is_empty());
|
||||
+
|
||||
+ // Set the new shell's delegate now. This doesn't resume the navigation just
|
||||
+ // yet.
|
||||
+ EXPECT_FALSE(new_contents->GetDelegate());
|
||||
+ new_contents->SetDelegate(new_shell);
|
||||
+
|
||||
+ // Crash the renderer process. This should clear the deferred navigation
|
||||
+ // state. If this wasn't done due to a bug, it would also force the resumed
|
||||
+ // navigation to use the early RenderFrameHost swap.
|
||||
+ {
|
||||
+ RenderProcessHost* popup_process =
|
||||
+ new_contents->GetPrimaryMainFrame()->GetProcess();
|
||||
+ RenderProcessHostWatcher crash_observer(
|
||||
+ popup_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
|
||||
+ EXPECT_TRUE(popup_process->Shutdown(0));
|
||||
+ crash_observer.Wait();
|
||||
+ }
|
||||
+
|
||||
+ // Resume the navigation and verify that it gets canceled. Ensure this
|
||||
+ // doesn't crash.
|
||||
+ NavigationHandleObserver handle_observer(new_contents, url);
|
||||
+ new_contents->ResumeLoadingCreatedWebContents();
|
||||
+ EXPECT_TRUE(WaitForLoadStop(new_contents));
|
||||
+ EXPECT_FALSE(handle_observer.has_committed());
|
||||
+ EXPECT_TRUE(new_contents->GetController().IsInitialBlankNavigation());
|
||||
+ EXPECT_TRUE(new_contents->GetLastCommittedURL().is_empty());
|
||||
+}
|
||||
+
|
||||
namespace {
|
||||
|
||||
class MediaWaiter : public WebContentsObserver {
|
||||
56
patches/chromium/cherry-pick-f218b4f37018.patch
Normal file
56
patches/chromium/cherry-pick-f218b4f37018.patch
Normal file
@@ -0,0 +1,56 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Joshua Pawlicki <waffles@chromium.org>
|
||||
Date: Fri, 29 Sep 2023 22:25:20 +0000
|
||||
Subject: update_client: Check string length before calling front().
|
||||
|
||||
(cherry picked from commit 6581c6b7c4b7b627b32b09cdbe9e84d2dd9ec018)
|
||||
|
||||
Fixed: 1486316
|
||||
Change-Id: I7be04ea0c8e040b5a67364925fc06d4ee9167242
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4892838
|
||||
Commit-Queue: Joshua Pawlicki <waffles@chromium.org>
|
||||
Auto-Submit: Joshua Pawlicki <waffles@chromium.org>
|
||||
Reviewed-by: Sorin Jianu <sorin@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1201617}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4902697
|
||||
Cr-Commit-Position: refs/branch-heads/5993@{#978}
|
||||
Cr-Branched-From: 511350718e646be62331ae9d7213d10ec320d514-refs/heads/main@{#1192594}
|
||||
|
||||
diff --git a/components/update_client/protocol_parser_json.cc b/components/update_client/protocol_parser_json.cc
|
||||
index 9858ba06290675ae39d2606fbe4aea34f2efe655..ba1aeba747d990bc97977bebb60f1141e3981517 100644
|
||||
--- a/components/update_client/protocol_parser_json.cc
|
||||
+++ b/components/update_client/protocol_parser_json.cc
|
||||
@@ -196,7 +196,7 @@ bool ParseUpdateCheck(const base::Value& updatecheck_node_val,
|
||||
const base::Value::Dict& updatecheck_node = updatecheck_node_val.GetDict();
|
||||
|
||||
for (auto kv : updatecheck_node) {
|
||||
- if (kv.first.front() == '_' && kv.second.is_string()) {
|
||||
+ if (!kv.first.empty() && kv.first.front() == '_' && kv.second.is_string()) {
|
||||
result->custom_attributes[kv.first] = kv.second.GetString();
|
||||
}
|
||||
}
|
||||
diff --git a/components/update_client/protocol_parser_json_unittest.cc b/components/update_client/protocol_parser_json_unittest.cc
|
||||
index 606c6ddc8f906e4bcd0722828ab34f5c77e73e5d..c59bf8ab7b1f7751c2e78396712f0de283c3b00d 100644
|
||||
--- a/components/update_client/protocol_parser_json_unittest.cc
|
||||
+++ b/components/update_client/protocol_parser_json_unittest.cc
|
||||
@@ -393,6 +393,10 @@ const char* kJSONCustomAttributes = R"()]}'
|
||||
]
|
||||
}})";
|
||||
|
||||
+const char* kBadJSONBadAppIdNoNewlinesBadUCKey =
|
||||
+ R"()]}'{"response":{"app":[{"appid":";","updatecheck":{"":1}}],)"
|
||||
+ R"("protocol":"3.1"}})";
|
||||
+
|
||||
TEST(UpdateClientProtocolParserJSONTest, Parse) {
|
||||
const auto parser = std::make_unique<ProtocolParserJSON>();
|
||||
|
||||
@@ -610,4 +614,9 @@ TEST(UpdateClientProtocolParserJSONTest, ParseAttrs) {
|
||||
}
|
||||
}
|
||||
|
||||
+TEST(UpdateClientProtocolParserJSONTest, ParseBadJSONNoCrash) {
|
||||
+ const auto parser = std::make_unique<ProtocolParserJSON>();
|
||||
+ EXPECT_TRUE(parser->Parse(kBadJSONBadAppIdNoNewlinesBadUCKey));
|
||||
+}
|
||||
+
|
||||
} // namespace update_client
|
||||
@@ -0,0 +1,54 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Sun, 5 Nov 2023 21:05:04 +0900
|
||||
Subject: Crash GPU process and clear shader cache when skia reports
|
||||
compileError.
|
||||
|
||||
Refs https://chromium-review.googlesource.com/c/chromium/src/+/4988290
|
||||
|
||||
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
|
||||
index 9960748b644a4f89919c6c19889d8cb3d875d3b8..71f9495306ad817a0d530f701d6ff35fc9966960 100644
|
||||
--- a/gpu/command_buffer/service/shared_context_state.cc
|
||||
+++ b/gpu/command_buffer/service/shared_context_state.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "gpu/command_buffer/service/shared_context_state.h"
|
||||
|
||||
+#include "base/immediate_crash.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/system/sys_info.h"
|
||||
@@ -82,6 +83,12 @@ void SharedContextState::compileError(const char* shader, const char* errors) {
|
||||
<< "------------------------\n"
|
||||
<< shader << "\nErrors:\n"
|
||||
<< errors;
|
||||
+ // Increase shader cache shm count and crash the GPU process so that the
|
||||
+ // browser process would clear the cache.
|
||||
+ GpuProcessActivityFlags::ScopedSetFlag set_flag(
|
||||
+ activity_flags_.get(), ActivityFlagsBase::FLAG_LOADING_PROGRAM_BINARY);
|
||||
+
|
||||
+ base::ImmediateCrash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +278,7 @@ bool SharedContextState::InitializeGanesh(
|
||||
gl::ProgressReporter* progress_reporter) {
|
||||
progress_reporter_ = progress_reporter;
|
||||
gr_shader_cache_ = cache;
|
||||
+ activity_flags_ = activity_flags;
|
||||
|
||||
size_t max_resource_cache_bytes;
|
||||
size_t glyph_cache_max_texture_bytes;
|
||||
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
|
||||
index c07796e90453c8ecbcfc4a8b3946deb1c46a3300..b795d0cf8e16218c01acdbe921bad067e0b63677 100644
|
||||
--- a/gpu/command_buffer/service/shared_context_state.h
|
||||
+++ b/gpu/command_buffer/service/shared_context_state.h
|
||||
@@ -379,6 +379,8 @@ class GPU_GLES2_EXPORT SharedContextState
|
||||
uint64_t skia_gr_cache_size_ = 0;
|
||||
std::vector<uint8_t> scratch_deserialization_buffer_;
|
||||
raw_ptr<gpu::raster::GrShaderCache> gr_shader_cache_ = nullptr;
|
||||
+ raw_ptr<GpuProcessActivityFlags, DanglingUntriaged> activity_flags_ =
|
||||
+ nullptr;
|
||||
|
||||
// |need_context_state_reset| is set whenever Skia may have altered the
|
||||
// driver's GL state.
|
||||
@@ -1,20 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Mon, 6 Jun 2022 14:25:15 -0700
|
||||
Subject: fix: allow guest webcontents to enter fullscreen
|
||||
|
||||
This can be upstreamed, a guest webcontents can't technically become the focused webContents. This DCHECK should allow all guest webContents to request fullscreen entrance.
|
||||
|
||||
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
|
||||
index 57a4cd485f53c0b1f373b3563635d8b529755511..36dc0614de108db66f7fe7760017cd1cdc4499c6 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl.cc
|
||||
@@ -3592,7 +3592,7 @@ void WebContentsImpl::EnterFullscreenMode(
|
||||
OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
|
||||
DCHECK(CanEnterFullscreenMode(requesting_frame, options));
|
||||
DCHECK(requesting_frame->IsActive());
|
||||
- DCHECK(ContainsOrIsFocusedWebContents());
|
||||
+ DCHECK(ContainsOrIsFocusedWebContents() || IsGuest());
|
||||
|
||||
// When WebView is the `delegate_` we can end up with VisualProperties changes
|
||||
// synchronously. Notify the view ahead so it can handle the transition.
|
||||
@@ -45,10 +45,10 @@ index 2ca4e42342ff6bf3f2ad104208944e36d572aa3c..7421cc779873b580d6f5a109d57ff744
|
||||
// RenderFrameMetadataProvider::Observer implementation.
|
||||
void OnRenderFrameMetadataChangedBeforeActivation(
|
||||
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
|
||||
index 36dc0614de108db66f7fe7760017cd1cdc4499c6..67094d6c1a557f6b0c812eedd80b369bd41afdc3 100644
|
||||
index 3a181a73da5657ed858179997a5d8928b63795d7..d3a9ec1f2c516e46ccf16b4bd00f91a08d1bc3d9 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl.cc
|
||||
@@ -8095,7 +8095,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame(
|
||||
@@ -8099,7 +8099,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame(
|
||||
"WebContentsImpl::OnFocusedElementChangedInFrame",
|
||||
"render_frame_host", frame);
|
||||
RenderWidgetHostViewBase* root_view =
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Sun, 5 Nov 2023 18:22:15 +0900
|
||||
Subject: gpu: Use load_program_shader_shm_count on DrDC thread
|
||||
|
||||
Refs https://chromium-review.googlesource.com/c/chromium/src/+/4766018
|
||||
|
||||
diff --git a/components/viz/service/display_embedder/compositor_gpu_thread.cc b/components/viz/service/display_embedder/compositor_gpu_thread.cc
|
||||
index a9ca58db18823c09457eb89eb9776ce51ec668b2..d8384bddd7ebd45c5fa87ddb7af10c967db9ac25 100644
|
||||
--- a/components/viz/service/display_embedder/compositor_gpu_thread.cc
|
||||
+++ b/components/viz/service/display_embedder/compositor_gpu_thread.cc
|
||||
@@ -207,7 +207,8 @@ CompositorGpuThread::GetSharedContextState() {
|
||||
// Initialize Skia.
|
||||
if (!shared_context_state->InitializeSkia(
|
||||
gpu_preferences, workarounds, gpu_channel_manager_->gr_shader_cache(),
|
||||
- /*activity_flags=*/nullptr, /*progress_reporter=*/nullptr)) {
|
||||
+ gpu_channel_manager_->activity_flags(),
|
||||
+ /*progress_reporter=*/nullptr)) {
|
||||
LOG(ERROR) << "Failed to Initialize Skia for DrDC SharedContextState";
|
||||
}
|
||||
shared_context_state_ = std::move(shared_context_state);
|
||||
@@ -0,0 +1,511 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Chris Harrelson <chrishtr@chromium.org>
|
||||
Date: Fri, 25 Aug 2023 17:55:16 +0000
|
||||
Subject: Parameterize AXTreeSerializer by vector type
|
||||
|
||||
This enables us to use HeapVector in Blink, which is necessary because
|
||||
AXObject is a garbage-collected type.
|
||||
|
||||
Bug: 1472368
|
||||
|
||||
Change-Id: Idf6fee18827f2914452be49f61de0b6a36b19678
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4811206
|
||||
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
|
||||
Commit-Queue: Chris Harrelson <chrishtr@chromium.org>
|
||||
Reviewed-by: Mark Schillaci <mschillaci@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1188419}
|
||||
|
||||
diff --git a/chrome/browser/ash/arc/accessibility/ax_tree_source_arc.h b/chrome/browser/ash/arc/accessibility/ax_tree_source_arc.h
|
||||
index b7e0e6dea25fff3deae082f30a765df43756d3df..02c15515f119c48e4d48ffca15ce0049a2b40c16 100644
|
||||
--- a/chrome/browser/ash/arc/accessibility/ax_tree_source_arc.h
|
||||
+++ b/chrome/browser/ash/arc/accessibility/ax_tree_source_arc.h
|
||||
@@ -31,7 +31,9 @@ class Window;
|
||||
namespace arc {
|
||||
class AXTreeSourceArcTest;
|
||||
|
||||
-using AXTreeArcSerializer = ui::AXTreeSerializer<AccessibilityInfoDataWrapper*>;
|
||||
+using AXTreeArcSerializer =
|
||||
+ ui::AXTreeSerializer<AccessibilityInfoDataWrapper*,
|
||||
+ std::vector<AccessibilityInfoDataWrapper*>>;
|
||||
|
||||
// This class represents the accessibility tree from the focused ARC window.
|
||||
class AXTreeSourceArc : public ui::AXTreeSource<AccessibilityInfoDataWrapper*>,
|
||||
diff --git a/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc b/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
|
||||
index f973c8c0c2c4b1f96a4ab7e22a924b3d85b44f35..e26827761686946669e21ba6459b24b571bdb4c0 100644
|
||||
--- a/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
|
||||
+++ b/chrome/browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc
|
||||
@@ -33,7 +33,9 @@ using views::Textfield;
|
||||
using views::View;
|
||||
using views::Widget;
|
||||
|
||||
-using AuraAXTreeSerializer = ui::AXTreeSerializer<views::AXAuraObjWrapper*>;
|
||||
+using AuraAXTreeSerializer =
|
||||
+ ui::AXTreeSerializer<views::AXAuraObjWrapper*,
|
||||
+ std::vector<views::AXAuraObjWrapper*>>;
|
||||
|
||||
// Helper to count the number of nodes in a tree.
|
||||
size_t GetSize(AXAuraObjWrapper* tree) {
|
||||
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
|
||||
index 498488f27e7fc273a0797063b6388576dfc5cd93..4d0ae2b291ce5bd607117288b0bfc1d3ce8e1c13 100644
|
||||
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
|
||||
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
|
||||
@@ -33,7 +33,9 @@ class AXAuraObjWrapper;
|
||||
class View;
|
||||
} // namespace views
|
||||
|
||||
-using AuraAXTreeSerializer = ui::AXTreeSerializer<views::AXAuraObjWrapper*>;
|
||||
+using AuraAXTreeSerializer =
|
||||
+ ui::AXTreeSerializer<views::AXAuraObjWrapper*,
|
||||
+ std::vector<views::AXAuraObjWrapper*>>;
|
||||
|
||||
// Manages a tree of automation nodes backed by aura constructs.
|
||||
class AutomationManagerAura : public ui::AXActionHandler,
|
||||
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
|
||||
index a30a4fb78d837d98a7c9532f2643ceac742ac5e5..89e45eae934f417c571f2eae8dfa76a977ead825 100644
|
||||
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
|
||||
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
|
||||
@@ -401,7 +401,8 @@ void ReadAnythingAppController::Distill() {
|
||||
model_.GetTreeFromId(model_.active_tree_id()).get();
|
||||
std::unique_ptr<ui::AXTreeSource<const ui::AXNode*>> tree_source(
|
||||
tree->CreateTreeSource());
|
||||
- ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get());
|
||||
+ ui::AXTreeSerializer<const ui::AXNode*, std::vector<const ui::AXNode*>>
|
||||
+ serializer(tree_source.get());
|
||||
ui::AXTreeUpdate snapshot;
|
||||
CHECK(serializer.SerializeChanges(tree->root(), &snapshot));
|
||||
model_.SetDistillationInProgress(true);
|
||||
diff --git a/components/services/screen_ai/screen_ai_ax_tree_serializer.cc b/components/services/screen_ai/screen_ai_ax_tree_serializer.cc
|
||||
index ecd818228f31dfefdc436302be9dcb47415b684c..6f921bd47e8dfb585da81cefdbdcf870f420b305 100644
|
||||
--- a/components/services/screen_ai/screen_ai_ax_tree_serializer.cc
|
||||
+++ b/components/services/screen_ai/screen_ai_ax_tree_serializer.cc
|
||||
@@ -43,7 +43,8 @@ ScreenAIAXTreeSerializer::ScreenAIAXTreeSerializer(
|
||||
tree_source_ = base::WrapUnique<ui::AXTreeSource<const ui::AXNode*>>(
|
||||
tree_->CreateTreeSource());
|
||||
DCHECK(tree_source_);
|
||||
- serializer_ = std::make_unique<ui::AXTreeSerializer<const ui::AXNode*>>(
|
||||
+ serializer_ = std::make_unique<
|
||||
+ ui::AXTreeSerializer<const ui::AXNode*, std::vector<const ui::AXNode*>>>(
|
||||
tree_source_.get(), /* crash_on_error */ true);
|
||||
}
|
||||
|
||||
diff --git a/components/services/screen_ai/screen_ai_ax_tree_serializer.h b/components/services/screen_ai/screen_ai_ax_tree_serializer.h
|
||||
index e522698819c4fe80bfd9da0b49934def0d2abb66..38fd5e2391a4a899f0f46069b42d6056d42f1e01 100644
|
||||
--- a/components/services/screen_ai/screen_ai_ax_tree_serializer.h
|
||||
+++ b/components/services/screen_ai/screen_ai_ax_tree_serializer.h
|
||||
@@ -41,7 +41,9 @@ class ScreenAIAXTreeSerializer final {
|
||||
private:
|
||||
const std::unique_ptr<ui::AXSerializableTree> tree_;
|
||||
std::unique_ptr<ui::AXTreeSource<const ui::AXNode*>> tree_source_;
|
||||
- mutable std::unique_ptr<ui::AXTreeSerializer<const ui::AXNode*>> serializer_;
|
||||
+ mutable std::unique_ptr<
|
||||
+ ui::AXTreeSerializer<const ui::AXNode*, std::vector<const ui::AXNode*>>>
|
||||
+ serializer_;
|
||||
};
|
||||
|
||||
} // namespace screen_ai
|
||||
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
index 482267f40346c0b0bb4cde27e8a581ec18bef0fd..727907e7d3b804c98625bbc90f9a4f15d3349638 100644
|
||||
--- a/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
@@ -1649,7 +1649,8 @@ bool BrowserAccessibilityManager::IsRootFrameManager() const {
|
||||
ui::AXTreeUpdate BrowserAccessibilityManager::SnapshotAXTreeForTesting() {
|
||||
std::unique_ptr<ui::AXTreeSource<const ui::AXNode*>> tree_source(
|
||||
ax_serializable_tree()->CreateTreeSource());
|
||||
- ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get());
|
||||
+ ui::AXTreeSerializer<const ui::AXNode*, std::vector<const ui::AXNode*>>
|
||||
+ serializer(tree_source.get());
|
||||
ui::AXTreeUpdate update;
|
||||
serializer.SerializeChanges(GetRoot(), &update);
|
||||
return update;
|
||||
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
|
||||
index ddd8273fb0384d326f5ee2c78de23e3e03afe520..088edf542ef06c17e0e149ba5927fae16ee38817 100644
|
||||
--- a/content/renderer/accessibility/render_accessibility_impl.h
|
||||
+++ b/content/renderer/accessibility/render_accessibility_impl.h
|
||||
@@ -278,7 +278,8 @@ class CONTENT_EXPORT RenderAccessibilityImpl : public RenderAccessibility,
|
||||
// Manages the automatic image annotations, if enabled.
|
||||
std::unique_ptr<AXImageAnnotator> ax_image_annotator_;
|
||||
|
||||
- using PluginAXTreeSerializer = ui::AXTreeSerializer<const ui::AXNode*>;
|
||||
+ using PluginAXTreeSerializer =
|
||||
+ ui::AXTreeSerializer<const ui::AXNode*, std::vector<const ui::AXNode*>>;
|
||||
std::unique_ptr<PluginAXTreeSerializer> plugin_serializer_;
|
||||
PluginAXTreeSource* plugin_tree_source_;
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
|
||||
index 0de34e2c2327120ce357fbbc9594bb6ffb9a6d6c..b0d37309e7dbd3b9e6061d2269e01e193fef5932 100644
|
||||
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
|
||||
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
|
||||
@@ -691,9 +691,11 @@ AXObjectCacheImpl::AXObjectCacheImpl(Document& document,
|
||||
permission_observer_receiver_(this, document.GetExecutionContext()),
|
||||
render_accessibility_host_(document.GetExecutionContext()),
|
||||
ax_tree_source_(BlinkAXTreeSource::Create(*this)),
|
||||
- ax_tree_serializer_(std::make_unique<ui::AXTreeSerializer<AXObject*>>(
|
||||
- ax_tree_source_,
|
||||
- /*crash_on_error*/ true)) {
|
||||
+ ax_tree_serializer_(
|
||||
+ std::make_unique<
|
||||
+ ui::AXTreeSerializer<AXObject*, HeapVector<AXObject*>>>(
|
||||
+ ax_tree_source_,
|
||||
+ /*crash_on_error*/ true)) {
|
||||
use_ax_menu_list_ = GetSettings()->GetUseAXMenuList();
|
||||
}
|
||||
|
||||
@@ -3987,7 +3989,8 @@ bool AXObjectCacheImpl::SerializeEntireTree(size_t max_node_count,
|
||||
// or a partial accessibility tree. AXTreeSerializer is stateful, but the
|
||||
// first time you serialize from a brand-new tree you're guaranteed to get a
|
||||
// complete tree.
|
||||
- ui::AXTreeSerializer<AXObject*> serializer(tree_source);
|
||||
+ ui::AXTreeSerializer<AXObject*, HeapVector<AXObject*>> serializer(
|
||||
+ tree_source);
|
||||
|
||||
if (max_node_count)
|
||||
serializer.set_max_node_count(max_node_count);
|
||||
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
|
||||
index eeea077f3bc086065b2dbed1fe38c3ee572e83a1..339eae38265b206e4a99447152503b39e12bf00d 100644
|
||||
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
|
||||
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
|
||||
@@ -930,7 +930,8 @@ class MODULES_EXPORT AXObjectCacheImpl
|
||||
render_accessibility_host_;
|
||||
|
||||
Member<BlinkAXTreeSource> ax_tree_source_;
|
||||
- std::unique_ptr<ui::AXTreeSerializer<AXObject*>> ax_tree_serializer_;
|
||||
+ std::unique_ptr<ui::AXTreeSerializer<AXObject*, HeapVector<AXObject*>>>
|
||||
+ ax_tree_serializer_;
|
||||
|
||||
HeapDeque<Member<AXDirtyObject>> dirty_objects_;
|
||||
|
||||
diff --git a/ui/accessibility/ax_generated_tree_unittest.cc b/ui/accessibility/ax_generated_tree_unittest.cc
|
||||
index 2e24152f026a1f6962af348c96744218c783561c..cea591f9a9315ec6476ddec5411a04bb04f49df3 100644
|
||||
--- a/ui/accessibility/ax_generated_tree_unittest.cc
|
||||
+++ b/ui/accessibility/ax_generated_tree_unittest.cc
|
||||
@@ -75,7 +75,8 @@ std::string TreeToString(const AXTree& tree) {
|
||||
AXTreeUpdate SerializeEntireTree(AXSerializableTree& tree) {
|
||||
std::unique_ptr<AXTreeSource<const AXNode*>> tree_source(
|
||||
tree.CreateTreeSource());
|
||||
- AXTreeSerializer<const AXNode*> serializer(tree_source.get());
|
||||
+ AXTreeSerializer<const AXNode*, std::vector<const AXNode*>> serializer(
|
||||
+ tree_source.get());
|
||||
AXTreeUpdate update;
|
||||
CHECK(serializer.SerializeChanges(tree.root(), &update));
|
||||
return update;
|
||||
@@ -270,7 +271,8 @@ TEST_P(SerializeGeneratedTreesTest, SerializeGeneratedTrees) {
|
||||
// empty tree |dst_tree|.
|
||||
std::unique_ptr<AXTreeSource<const AXNode*>> tree0_source(
|
||||
tree0.CreateTreeSource());
|
||||
- AXTreeSerializer<const AXNode*> serializer(tree0_source.get());
|
||||
+ AXTreeSerializer<const AXNode*, std::vector<const AXNode*>>
|
||||
+ serializer(tree0_source.get());
|
||||
AXTreeUpdate update0;
|
||||
ASSERT_TRUE(serializer.SerializeChanges(tree0.root(), &update0));
|
||||
|
||||
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
|
||||
index 0fb9303c39c979ab80c572769c2344701478f7e3..7874a5575823489aef2afa03b7f325e56ab689d1 100644
|
||||
--- a/ui/accessibility/ax_tree_serializer.h
|
||||
+++ b/ui/accessibility/ax_tree_serializer.h
|
||||
@@ -64,7 +64,7 @@ struct ClientTreeNode;
|
||||
// because AXTreeSerializer always keeps track of what updates it's sent,
|
||||
// it will never send an invalid update and the client tree will not break,
|
||||
// it just may not contain all of the changes.
|
||||
-template <typename AXSourceNode>
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
class AXTreeSerializer {
|
||||
public:
|
||||
explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree,
|
||||
@@ -260,14 +260,14 @@ struct AX_EXPORT ClientTreeNode {
|
||||
bool in_dirty_subtree;
|
||||
};
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::AXTreeSerializer(
|
||||
AXTreeSource<AXSourceNode>* tree,
|
||||
bool crash_on_error)
|
||||
: tree_(tree), crash_on_error_(crash_on_error) {}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::~AXTreeSerializer() {
|
||||
// Clear |tree_| to prevent any additional calls to the tree source
|
||||
// during teardown.
|
||||
// TODO(accessibility) How would that happen?
|
||||
@@ -276,14 +276,14 @@ AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::Reset() {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::Reset() {
|
||||
InternalReset();
|
||||
did_reset_ = true;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::InternalReset() {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::InternalReset() {
|
||||
client_tree_data_ = AXTreeData();
|
||||
|
||||
// Normally we use DeleteClientSubtree to remove nodes from the tree,
|
||||
@@ -296,26 +296,28 @@ void AXTreeSerializer<AXSourceNode>::InternalReset() {
|
||||
client_root_ = nullptr;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::ChangeTreeSourceForTesting(
|
||||
- AXTreeSource<AXSourceNode>* new_tree) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
|
||||
+ ChangeTreeSourceForTesting(AXTreeSource<AXSourceNode>* new_tree) {
|
||||
tree_ = new_tree;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-size_t AXTreeSerializer<AXSourceNode>::ClientTreeNodeCount() const {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+size_t AXTreeSerializer<AXSourceNode,
|
||||
+ AXSourceNodeVectorType>::ClientTreeNodeCount() const {
|
||||
return client_id_map_.size();
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+AXSourceNode
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::LeastCommonAncestor(
|
||||
AXSourceNode node,
|
||||
ClientTreeNode* client_node) {
|
||||
if (!node || client_node == nullptr) {
|
||||
return tree_->GetNull();
|
||||
}
|
||||
|
||||
- std::vector<AXSourceNode> ancestors;
|
||||
+ AXSourceNodeVectorType ancestors;
|
||||
while (node) {
|
||||
ancestors.push_back(node);
|
||||
node = tree_->GetParent(node);
|
||||
@@ -334,17 +336,18 @@ AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
|
||||
for (size_t source_index = ancestors.size(),
|
||||
client_index = client_ancestors.size();
|
||||
source_index > 0 && client_index > 0; --source_index, --client_index) {
|
||||
- if (tree_->GetId(ancestors[source_index - 1]) !=
|
||||
+ if (tree_->GetId(ancestors[(unsigned int)(source_index - 1)]) !=
|
||||
client_ancestors[client_index - 1]->id) {
|
||||
return lca;
|
||||
}
|
||||
- lca = ancestors[source_index - 1];
|
||||
+ lca = ancestors[(unsigned int)(source_index - 1)];
|
||||
}
|
||||
return lca;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+AXSourceNode
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::LeastCommonAncestor(
|
||||
AXSourceNode node) {
|
||||
// Walk up the tree until the source node's id also exists in the
|
||||
// client tree, whose parent is not invalid, then call LeastCommonAncestor
|
||||
@@ -371,10 +374,9 @@ AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
|
||||
return LeastCommonAncestor(node, client_node);
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-bool AXTreeSerializer<AXSourceNode>::AnyDescendantWasReparented(
|
||||
- AXSourceNode node,
|
||||
- AXSourceNode* out_lca) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
|
||||
+ AnyDescendantWasReparented(AXSourceNode node, AXSourceNode* out_lca) {
|
||||
bool result = false;
|
||||
int id = tree_->GetId(node);
|
||||
tree_->CacheChildrenIfNeeded(node);
|
||||
@@ -422,8 +424,9 @@ bool AXTreeSerializer<AXSourceNode>::AnyDescendantWasReparented(
|
||||
return result;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+ClientTreeNode*
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::ClientTreeNodeById(
|
||||
AXNodeID id) {
|
||||
std::map<AXNodeID, ClientTreeNode*>::iterator iter = client_id_map_.find(id);
|
||||
if (iter != client_id_map_.end())
|
||||
@@ -431,8 +434,9 @@ ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-ClientTreeNode* AXTreeSerializer<AXSourceNode>::GetClientTreeNodeParent(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+ClientTreeNode*
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::GetClientTreeNodeParent(
|
||||
ClientTreeNode* obj) {
|
||||
ClientTreeNode* parent = obj->parent;
|
||||
if (!parent)
|
||||
@@ -457,8 +461,8 @@ ClientTreeNode* AXTreeSerializer<AXSourceNode>::GetClientTreeNodeParent(
|
||||
return parent;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-bool AXTreeSerializer<AXSourceNode>::SerializeChanges(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::SerializeChanges(
|
||||
AXSourceNode node,
|
||||
AXTreeUpdate* out_update) {
|
||||
if (!timeout_.is_zero())
|
||||
@@ -540,33 +544,37 @@ bool AXTreeSerializer<AXSourceNode>::SerializeChanges(
|
||||
return true;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-std::vector<AXNodeID> AXTreeSerializer<AXSourceNode>::GetIncompleteNodeIds() {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+std::vector<AXNodeID>
|
||||
+AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::GetIncompleteNodeIds() {
|
||||
DCHECK(max_node_count_ > 0 || !timeout_.is_zero());
|
||||
return incomplete_node_ids_;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::MarkSubtreeDirty(AXSourceNode node) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::MarkSubtreeDirty(
|
||||
+ AXSourceNode node) {
|
||||
ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
|
||||
if (client_node)
|
||||
MarkClientSubtreeDirty(client_node);
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-bool AXTreeSerializer<AXSourceNode>::IsInClientTree(AXSourceNode node) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::IsInClientTree(
|
||||
+ AXSourceNode node) {
|
||||
return ClientTreeNodeById(tree_->GetId(node));
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-bool AXTreeSerializer<AXSourceNode>::IsDirty(AXSourceNode node) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::IsDirty(
|
||||
+ AXSourceNode node) {
|
||||
ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
|
||||
return client_node ? client_node->in_dirty_subtree : false;
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::MarkClientSubtreeDirty(
|
||||
- ClientTreeNode* client_node) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
|
||||
+ MarkClientSubtreeDirty(ClientTreeNode* client_node) {
|
||||
// Return early if already marked dirty, in order to avoid duplicate work in
|
||||
// subtree, as the only method that marks nodes dirty is this one.
|
||||
if (client_node->in_dirty_subtree) {
|
||||
@@ -578,9 +586,9 @@ void AXTreeSerializer<AXSourceNode>::MarkClientSubtreeDirty(
|
||||
}
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(
|
||||
- ClientTreeNode* client_node) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
|
||||
+ DeleteClientSubtree(ClientTreeNode* client_node) {
|
||||
if (client_node == client_root_) {
|
||||
Reset(); // Do not try to reuse a bad root later.
|
||||
// A heuristic for this condition rather than an explicit Reset() from a
|
||||
@@ -601,18 +609,17 @@ void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(
|
||||
}
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-void AXTreeSerializer<AXSourceNode>::DeleteDescendants(
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::DeleteDescendants(
|
||||
ClientTreeNode* client_node) {
|
||||
for (size_t i = 0; i < client_node->children.size(); ++i)
|
||||
DeleteClientSubtree(client_node->children[i]);
|
||||
client_node->children.clear();
|
||||
}
|
||||
|
||||
-template <typename AXSourceNode>
|
||||
-bool AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
|
||||
- AXSourceNode node,
|
||||
- AXTreeUpdate* out_update) {
|
||||
+template <typename AXSourceNode, typename AXSourceNodeVectorType>
|
||||
+bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
|
||||
+ SerializeChangedNodes(AXSourceNode node, AXTreeUpdate* out_update) {
|
||||
// This method has three responsibilities:
|
||||
// 1. Serialize |node| into an AXNodeData, and append it to
|
||||
// the AXTreeUpdate to be sent to the client.
|
||||
diff --git a/ui/accessibility/ax_tree_serializer_unittest.cc b/ui/accessibility/ax_tree_serializer_unittest.cc
|
||||
index 3955a06f31795d0941d76976d8ae0a5c3204eedd..126e43a2bdcb1f9780baac6657201f26b3bd3244 100644
|
||||
--- a/ui/accessibility/ax_tree_serializer_unittest.cc
|
||||
+++ b/ui/accessibility/ax_tree_serializer_unittest.cc
|
||||
@@ -21,7 +21,8 @@ using testing::UnorderedElementsAre;
|
||||
|
||||
namespace ui {
|
||||
|
||||
-using BasicAXTreeSerializer = AXTreeSerializer<const AXNode*>;
|
||||
+using BasicAXTreeSerializer =
|
||||
+ AXTreeSerializer<const AXNode*, std::vector<const AXNode*>>;
|
||||
|
||||
// The framework for these tests is that each test sets up |treedata0_|
|
||||
// and |treedata1_| and then calls GetTreeSerializer, which creates a
|
||||
@@ -535,7 +536,7 @@ TEST_F(AXTreeSerializerTest, TestPartialSerialization) {
|
||||
// The result should be indistinguishable from the source tree.
|
||||
std::unique_ptr<AXTreeSource<const AXNode*>> dst_tree_source(
|
||||
dst_tree.CreateTreeSource());
|
||||
- AXTreeSerializer<const AXNode*> serializer(dst_tree_source.get());
|
||||
+ BasicAXTreeSerializer serializer(dst_tree_source.get());
|
||||
AXTreeUpdate dst_update;
|
||||
CHECK(serializer.SerializeChanges(dst_tree.root(), &dst_update));
|
||||
ASSERT_EQ(treedata1_.ToString(), dst_update.ToString());
|
||||
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
|
||||
index 910c03370446e228692aed4e356d06db3d0ec574..bbd9fa9bafc2003204be9ac05c9044e7fc851b1d 100644
|
||||
--- a/ui/accessibility/ax_tree_unittest.cc
|
||||
+++ b/ui/accessibility/ax_tree_unittest.cc
|
||||
@@ -333,7 +333,8 @@ TEST(AXTreeTest, SerializeSimpleAXTree) {
|
||||
|
||||
std::unique_ptr<AXTreeSource<const AXNode*>> tree_source(
|
||||
src_tree.CreateTreeSource());
|
||||
- AXTreeSerializer<const AXNode*> serializer(tree_source.get());
|
||||
+ AXTreeSerializer<const AXNode*, std::vector<const AXNode*>> serializer(
|
||||
+ tree_source.get());
|
||||
AXTreeUpdate update;
|
||||
serializer.SerializeChanges(src_tree.root(), &update);
|
||||
|
||||
diff --git a/ui/views/accessibility/ax_aura_obj_cache_unittest.cc b/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
|
||||
index 42e505a548edab06ebd31c298791959fe1b55867..68f6b64b1e96a18549d78c88a7adff804c9b2da2 100644
|
||||
--- a/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
|
||||
+++ b/ui/views/accessibility/ax_aura_obj_cache_unittest.cc
|
||||
@@ -216,7 +216,8 @@ TEST_F(AXAuraObjCacheTest, ValidTree) {
|
||||
ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID();
|
||||
AXTreeSourceViews tree_source(
|
||||
cache.GetOrCreate(parent_widget->GetNativeWindow()), tree_id, &cache);
|
||||
- ui::AXTreeSerializer<AXAuraObjWrapper*> serializer(&tree_source);
|
||||
+ ui::AXTreeSerializer<AXAuraObjWrapper*, std::vector<AXAuraObjWrapper*>>
|
||||
+ serializer(&tree_source);
|
||||
ui::AXTreeUpdate serialized_tree;
|
||||
serializer.SerializeChanges(tree_source.GetRoot(), &serialized_tree);
|
||||
|
||||
diff --git a/ui/views/accessibility/views_ax_tree_manager.h b/ui/views/accessibility/views_ax_tree_manager.h
|
||||
index 0caff17e0568762d61c7f2fdc6b94c01bc3d9e30..00fe01d00e17b51d7d37d099a8f21549669e69a0 100644
|
||||
--- a/ui/views/accessibility/views_ax_tree_manager.h
|
||||
+++ b/ui/views/accessibility/views_ax_tree_manager.h
|
||||
@@ -93,7 +93,8 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
|
||||
void OnWidgetDestroyed(Widget* widget) override;
|
||||
|
||||
private:
|
||||
- using ViewsAXTreeSerializer = ui::AXTreeSerializer<AXAuraObjWrapper*>;
|
||||
+ using ViewsAXTreeSerializer =
|
||||
+ ui::AXTreeSerializer<AXAuraObjWrapper*, std::vector<AXAuraObjWrapper*>>;
|
||||
|
||||
void SerializeTreeUpdates();
|
||||
void UnserializeTreeUpdates(const std::vector<ui::AXTreeUpdate>& updates);
|
||||
@@ -8,7 +8,8 @@ the parent frame should also enter fullscreen mode too. Chromium handles
|
||||
this for iframes, but not for webviews as they are essentially main
|
||||
frames instead of child frames.
|
||||
|
||||
This patch makes webviews propagate the fullscreen state to embedder.
|
||||
This patch makes webviews propagate the fullscreen state to embedder.It also handles a
|
||||
DCHECK preventing guest webcontents from becoming the focused webContents.
|
||||
|
||||
Note that we also need to manually update embedder's
|
||||
`api::WebContents::IsFullscreenForTabOrPending` value.
|
||||
@@ -35,3 +36,67 @@ index 3ac38d7ed50be9ba1670bb2863475a192baacc3a..820afdd8c73b6dbcf3aceba38af2cf0e
|
||||
// Focus the window if another frame may have delegated the capability.
|
||||
if (had_fullscreen_token && !GetView()->HasFocus())
|
||||
GetView()->Focus();
|
||||
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
|
||||
index 57a4cd485f53c0b1f373b3563635d8b529755511..3a181a73da5657ed858179997a5d8928b63795d7 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl.cc
|
||||
@@ -3446,21 +3446,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(
|
||||
const NativeWebKeyboardEvent& event) {
|
||||
OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
|
||||
"WebContentsImpl::PreHandleKeyboardEvent");
|
||||
- auto* outermost_contents = GetOutermostWebContents();
|
||||
- // TODO(wjmaclean): Generalize this to forward all key events to the outermost
|
||||
- // delegate's handler.
|
||||
- if (outermost_contents != this && IsFullscreen() &&
|
||||
- event.windows_key_code == ui::VKEY_ESCAPE) {
|
||||
- // When an inner WebContents has focus and is fullscreen, redirect <esc>
|
||||
- // key events to the outermost WebContents so it can be handled by that
|
||||
- // WebContents' delegate.
|
||||
- if (outermost_contents->PreHandleKeyboardEvent(event) ==
|
||||
- KeyboardEventProcessingResult::HANDLED) {
|
||||
+
|
||||
+ auto handled = delegate_ ? delegate_->PreHandleKeyboardEvent(this, event)
|
||||
+ : KeyboardEventProcessingResult::NOT_HANDLED;
|
||||
+
|
||||
+ if (IsFullscreen() && event.windows_key_code == ui::VKEY_ESCAPE) {
|
||||
+ if (handled == KeyboardEventProcessingResult::HANDLED)
|
||||
return KeyboardEventProcessingResult::HANDLED;
|
||||
+
|
||||
+ // When an inner WebContents has focus and is fullscreen, traverse through
|
||||
+ // containing webcontents to any that may handle the escape key.
|
||||
+ while (auto* outer_web_contents = GetOuterWebContents()) {
|
||||
+ auto result = outer_web_contents->PreHandleKeyboardEvent(event);
|
||||
+ if (result == KeyboardEventProcessingResult::HANDLED) {
|
||||
+ return KeyboardEventProcessingResult::HANDLED;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- return delegate_ ? delegate_->PreHandleKeyboardEvent(this, event)
|
||||
- : KeyboardEventProcessingResult::NOT_HANDLED;
|
||||
+
|
||||
+ return handled;
|
||||
}
|
||||
|
||||
bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) {
|
||||
@@ -3592,7 +3596,7 @@ void WebContentsImpl::EnterFullscreenMode(
|
||||
OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
|
||||
DCHECK(CanEnterFullscreenMode(requesting_frame, options));
|
||||
DCHECK(requesting_frame->IsActive());
|
||||
- DCHECK(ContainsOrIsFocusedWebContents());
|
||||
+ DCHECK(ContainsOrIsFocusedWebContents() || IsGuest());
|
||||
|
||||
// When WebView is the `delegate_` we can end up with VisualProperties changes
|
||||
// synchronously. Notify the view ahead so it can handle the transition.
|
||||
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
|
||||
index 896e9079b5eba3b9c11cec2b1dc7fd539cdbb73a..eee1199785427ac905178115740129404ed49884 100644
|
||||
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
|
||||
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
|
||||
@@ -99,7 +99,7 @@ void FullscreenElementChanged(Document& document,
|
||||
// is the iframe element for the out-of-process frame that contains the
|
||||
// fullscreen element. Hence, it must match :-webkit-full-screen-ancestor.
|
||||
if (new_request_type & FullscreenRequestType::kForCrossProcessDescendant) {
|
||||
- DCHECK(IsA<HTMLIFrameElement>(new_element));
|
||||
+ // DCHECK(IsA<HTMLIFrameElement>(new_element));
|
||||
new_element->SetContainsFullScreenElement(true);
|
||||
}
|
||||
new_element->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
|
||||
|
||||
@@ -27,5 +27,7 @@
|
||||
|
||||
"src/electron/patches/libwebp": "src/third_party/libwebp/src",
|
||||
|
||||
"src/electron/patches/libvpx": "src/third_party/libvpx/source/libvpx"
|
||||
"src/electron/patches/libvpx": "src/third_party/libvpx/source/libvpx",
|
||||
|
||||
"src/electron/patches/dawn": "src/third_party/dawn"
|
||||
}
|
||||
|
||||
1
patches/dawn/.patches
Normal file
1
patches/dawn/.patches
Normal file
@@ -0,0 +1 @@
|
||||
cherry-pick-f666cceb92c2.patch
|
||||
195
patches/dawn/cherry-pick-f666cceb92c2.patch
Normal file
195
patches/dawn/cherry-pick-f666cceb92c2.patch
Normal file
@@ -0,0 +1,195 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Clayton <bclayton@google.com>
|
||||
Date: Tue, 17 Oct 2023 14:53:45 +0000
|
||||
Subject: Add support for large allocations
|
||||
|
||||
From the BumpAllocator.
|
||||
|
||||
Bug: chromium:1491912
|
||||
Change-Id: I632fea8de1451358aa24f8a8d05c07d7f6823d7d
|
||||
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/155840
|
||||
Kokoro: Ben Clayton <bclayton@google.com>
|
||||
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
|
||||
|
||||
diff --git a/src/tint/symbol_table.cc b/src/tint/symbol_table.cc
|
||||
index cdea0a488f2a57ec12628eff29d31eaea2889d7d..66461a9f427542752568edab386da026a3682ee1 100644
|
||||
--- a/src/tint/symbol_table.cc
|
||||
+++ b/src/tint/symbol_table.cc
|
||||
@@ -37,8 +37,10 @@ Symbol SymbolTable::Register(std::string_view name) {
|
||||
}
|
||||
|
||||
Symbol SymbolTable::RegisterInternal(std::string_view name) {
|
||||
- char* name_mem = name_allocator_.Allocate(name.length() + 1);
|
||||
+ char* name_mem = utils::Bitcast<char*>(name_allocator_.Allocate(name.length() + 1));
|
||||
if (name_mem == nullptr) {
|
||||
+ tint::diag::List diags;
|
||||
+ TINT_ICE(Utils, diags) << "failed to allocate memory for symbol's string";
|
||||
return Symbol();
|
||||
}
|
||||
|
||||
diff --git a/src/tint/utils/bump_allocator.h b/src/tint/utils/bump_allocator.h
|
||||
index 302f924bcb6695c01bc36d28c39d3c32482e82a5..6f41c18fb71d70ec8813f0ef781bac2824fef476 100644
|
||||
--- a/src/tint/utils/bump_allocator.h
|
||||
+++ b/src/tint/utils/bump_allocator.h
|
||||
@@ -15,11 +15,14 @@
|
||||
#ifndef SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
|
||||
#define SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
|
||||
|
||||
+#include <algorithm>
|
||||
#include <array>
|
||||
+#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/utils/bitcast.h"
|
||||
+#include "src/tint/utils/compiler_macros.h"
|
||||
#include "src/tint/utils/math.h"
|
||||
|
||||
namespace tint::utils {
|
||||
@@ -29,14 +32,17 @@ constexpr size_t kBlockSize = 64 * 1024;
|
||||
/// A allocator for chunks of memory. The memory is owned by the BumpAllocator. When the
|
||||
/// BumpAllocator is freed all of the allocated memory is freed.
|
||||
class BumpAllocator {
|
||||
- /// Block is linked list of memory blocks.
|
||||
+ /// BlockHeader is linked list of memory blocks.
|
||||
/// Blocks are allocated out of heap memory.
|
||||
- struct Block {
|
||||
- uint8_t data[kBlockSize];
|
||||
- Block* next;
|
||||
+ struct BlockHeader {
|
||||
+ BlockHeader* next;
|
||||
};
|
||||
|
||||
public:
|
||||
+ /// The default size for a block's data. Allocations can be greater than this, but smaller
|
||||
+ /// allocations will use this size.
|
||||
+ static constexpr size_t kDefaultBlockDataSize = 64 * 1024;
|
||||
+
|
||||
/// Constructor
|
||||
BumpAllocator() = default;
|
||||
|
||||
@@ -61,38 +67,43 @@ class BumpAllocator {
|
||||
/// Allocates @p size_in_bytes from the current block, or from a newly allocated block if the
|
||||
/// current block is full.
|
||||
/// @param size_in_bytes the number of bytes to allocate
|
||||
- /// @returns the pointer to the allocated memory or |nullptr| if the memory can not be allocated
|
||||
- char* Allocate(size_t size_in_bytes) {
|
||||
- auto& block = data.block;
|
||||
- if (block.current_offset + size_in_bytes > kBlockSize) {
|
||||
+ /// @returns the pointer to the allocated memory or `nullptr` if the memory can not be allocated
|
||||
+ std::byte* Allocate(size_t size_in_bytes) {
|
||||
+ if (TINT_UNLIKELY(data.current_offset + size_in_bytes < size_in_bytes)) {
|
||||
+ return nullptr; // integer overflow
|
||||
+ }
|
||||
+ if (data.current_offset + size_in_bytes > data.current_data_size) {
|
||||
// Allocate a new block from the heap
|
||||
- auto* prev_block = block.current;
|
||||
- block.current = new Block;
|
||||
- if (!block.current) {
|
||||
+ auto* prev_block = data.current;
|
||||
+ size_t data_size = std::max(size_in_bytes, kDefaultBlockDataSize);
|
||||
+ data.current = Bitcast<BlockHeader*>(new (std::nothrow)
|
||||
+ std::byte[sizeof(BlockHeader) + data_size]);
|
||||
+ if (TINT_UNLIKELY(!data.current)) {
|
||||
return nullptr; // out of memory
|
||||
}
|
||||
- block.current->next = nullptr;
|
||||
- block.current_offset = 0;
|
||||
+ data.current->next = nullptr;
|
||||
+ data.current_data_size = data_size;
|
||||
+ data.current_offset = 0;
|
||||
if (prev_block) {
|
||||
- prev_block->next = block.current;
|
||||
+ prev_block->next = data.current;
|
||||
} else {
|
||||
- block.root = block.current;
|
||||
+ data.root = data.current;
|
||||
}
|
||||
}
|
||||
|
||||
- auto* base = &block.current->data[0];
|
||||
- auto* ptr = reinterpret_cast<char*>(base + block.current_offset);
|
||||
- block.current_offset += size_in_bytes;
|
||||
+ auto* base = Bitcast<std::byte*>(data.current) + sizeof(BlockHeader);
|
||||
+ auto* ptr = base + data.current_offset;
|
||||
+ data.current_offset += size_in_bytes;
|
||||
data.count++;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Frees all allocations from the allocator.
|
||||
void Reset() {
|
||||
- auto* block = data.block.root;
|
||||
+ auto* block = data.root;
|
||||
while (block != nullptr) {
|
||||
auto* next = block->next;
|
||||
- delete block;
|
||||
+ delete[] Bitcast<std::byte*>(block);
|
||||
block = next;
|
||||
}
|
||||
data = {};
|
||||
@@ -106,18 +117,16 @@ class BumpAllocator {
|
||||
BumpAllocator& operator=(const BumpAllocator&) = delete;
|
||||
|
||||
struct {
|
||||
- struct {
|
||||
- /// The root block of the block linked list
|
||||
- Block* root = nullptr;
|
||||
- /// The current (end) block of the blocked linked list.
|
||||
- /// New allocations come from this block
|
||||
- Block* current = nullptr;
|
||||
- /// The byte offset in #current for the next allocation.
|
||||
- /// Initialized with kBlockSize so that the first allocation triggers a block
|
||||
- /// allocation.
|
||||
- size_t current_offset = kBlockSize;
|
||||
- } block;
|
||||
-
|
||||
+ /// The root block of the block linked list
|
||||
+ BlockHeader* root = nullptr;
|
||||
+ /// The current (end) block of the blocked linked list.
|
||||
+ /// New allocations come from this block
|
||||
+ BlockHeader* current = nullptr;
|
||||
+ /// The byte offset in #current for the next allocation.
|
||||
+ size_t current_offset = 0;
|
||||
+ /// The size of the #current, excluding the header size
|
||||
+ size_t current_data_size = 0;
|
||||
+ /// Total number of allocations
|
||||
size_t count = 0;
|
||||
} data;
|
||||
};
|
||||
diff --git a/src/tint/utils/bump_allocator_test.cc b/src/tint/utils/bump_allocator_test.cc
|
||||
index 4f224558fdd7baa943e8b7b09765d64d01a9f3e6..cd840fe5e023e26bc308904a881333de2b8e7c16 100644
|
||||
--- a/src/tint/utils/bump_allocator_test.cc
|
||||
+++ b/src/tint/utils/bump_allocator_test.cc
|
||||
@@ -21,6 +21,31 @@ namespace {
|
||||
|
||||
using BumpAllocatorTest = testing::Test;
|
||||
|
||||
+TEST_F(BumpAllocatorTest, AllocationSizes) {
|
||||
+ BumpAllocator allocator;
|
||||
+ for (size_t n : {1u, 0x10u, 0x100u, 0x1000u, 0x10000u, 0x100000u, //
|
||||
+ 2u, 0x34u, 0x567u, 0x8912u, 0x34567u, 0x891234u}) {
|
||||
+ auto ptr = allocator.Allocate(n);
|
||||
+ memset(ptr, 0x42, n);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+TEST_F(BumpAllocatorTest, AllocationSizesAroundBlockSize) {
|
||||
+ for (size_t n : {
|
||||
+ BumpAllocator::kDefaultBlockDataSize - sizeof(void*),
|
||||
+ BumpAllocator::kDefaultBlockDataSize - 4,
|
||||
+ BumpAllocator::kDefaultBlockDataSize - 1,
|
||||
+ BumpAllocator::kDefaultBlockDataSize,
|
||||
+ BumpAllocator::kDefaultBlockDataSize + 1,
|
||||
+ BumpAllocator::kDefaultBlockDataSize + 4,
|
||||
+ BumpAllocator::kDefaultBlockDataSize + sizeof(void*),
|
||||
+ }) {
|
||||
+ BumpAllocator allocator;
|
||||
+ auto* ptr = allocator.Allocate(n);
|
||||
+ memset(ptr, 0x42, n);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
TEST_F(BumpAllocatorTest, Count) {
|
||||
for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
|
||||
BumpAllocator allocator;
|
||||
@@ -43,3 +43,4 @@ tls_ensure_tls_sockets_are_closed_if_the_underlying_wrap_closes.patch
|
||||
test_deflake_test-tls-socket-close.patch
|
||||
net_fix_crash_due_to_simultaneous_close_shutdown_on_js_stream.patch
|
||||
net_use_asserts_in_js_socket_stream_to_catch_races_in_future.patch
|
||||
win_process_avoid_assert_after_spawning_store_app_4152.patch
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jameson Nash <vtjnash@gmail.com>
|
||||
Date: Mon, 2 Oct 2023 15:15:18 +0200
|
||||
Subject: win,process: avoid assert after spawning Store app (#4152)
|
||||
|
||||
Make sure this handle is functional. The Windows kernel seems to have a
|
||||
bug that if the first use of AssignProcessToJobObject is for a Windows
|
||||
Store program, subsequent attempts to use the handle with fail with
|
||||
INVALID_PARAMETER (87). This is possilby because all uses of the handle
|
||||
must be for the same Terminal Services session. We can ensure it is
|
||||
tied to our current session now by adding ourself to it. We could
|
||||
remove ourself afterwards, but there doesn't seem to be a reason to.
|
||||
|
||||
Secondly, we start the process suspended so that we can make sure we
|
||||
added it to the job control object before it does anything itself (such
|
||||
as launch more jobs or exit).
|
||||
|
||||
Fixes: https://github.com/JuliaLang/julia/issues/51461
|
||||
|
||||
diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c
|
||||
index 24c633393fd15dcf87726b174d6b027a969e0f0d..4ad9fec900fa66b0e8c6894701e94f420de903a8 100644
|
||||
--- a/deps/uv/src/win/process.c
|
||||
+++ b/deps/uv/src/win/process.c
|
||||
@@ -102,6 +102,21 @@ static void uv__init_global_job_handle(void) {
|
||||
&info,
|
||||
sizeof info))
|
||||
uv_fatal_error(GetLastError(), "SetInformationJobObject");
|
||||
+
|
||||
+
|
||||
+ if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) {
|
||||
+ /* Make sure this handle is functional. The Windows kernel has a bug that
|
||||
+ * if the first use of AssignProcessToJobObject is for a Windows Store
|
||||
+ * program, subsequent attempts to use the handle with fail with
|
||||
+ * INVALID_PARAMETER (87). This is possibly because all uses of the handle
|
||||
+ * must be for the same Terminal Services session. We can ensure it is tied
|
||||
+ * to our current session now by adding ourself to it. We could remove
|
||||
+ * ourself afterwards, but there doesn't seem to be a reason to.
|
||||
+ */
|
||||
+ DWORD err = GetLastError();
|
||||
+ if (err != ERROR_ACCESS_DENIED)
|
||||
+ uv_fatal_error(err, "AssignProcessToJobObject");
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -1098,6 +1113,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
* breakaway.
|
||||
*/
|
||||
process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
|
||||
+ process_flags |= CREATE_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!CreateProcessW(application_path,
|
||||
@@ -1115,11 +1131,6 @@ int uv_spawn(uv_loop_t* loop,
|
||||
goto done;
|
||||
}
|
||||
|
||||
- /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
|
||||
-
|
||||
- process->process_handle = info.hProcess;
|
||||
- process->pid = info.dwProcessId;
|
||||
-
|
||||
/* If the process isn't spawned as detached, assign to the global job object
|
||||
* so windows will kill it when the parent process dies. */
|
||||
if (!(options->flags & UV_PROCESS_DETACHED)) {
|
||||
@@ -1142,6 +1153,19 @@ int uv_spawn(uv_loop_t* loop,
|
||||
}
|
||||
}
|
||||
|
||||
+ if (process_flags & CREATE_SUSPENDED) {
|
||||
+ if (ResumeThread(info.hThread) == ((DWORD)-1)) {
|
||||
+ err = GetLastError();
|
||||
+ TerminateProcess(info.hProcess, 1);
|
||||
+ goto done;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
|
||||
+
|
||||
+ process->process_handle = info.hProcess;
|
||||
+ process->pid = info.dwProcessId;
|
||||
+
|
||||
/* Set IPC pid to all IPC pipes. */
|
||||
for (i = 0; i < options->stdio_count; i++) {
|
||||
const uv_stdio_container_t* fdopt = &options->stdio[i];
|
||||
@@ -13,3 +13,4 @@ cherry-pick-8ff63d378f2c.patch
|
||||
merged_squashed_multiple_commits.patch
|
||||
cherry-pick-038530c94a06.patch
|
||||
cherry-pick-cf1d4d3c0b6e.patch
|
||||
cherry-pick-b0ad701a609a.patch
|
||||
|
||||
56
patches/v8/cherry-pick-b0ad701a609a.patch
Normal file
56
patches/v8/cherry-pick-b0ad701a609a.patch
Normal file
@@ -0,0 +1,56 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Wed, 6 Sep 2023 17:36:38 -0700
|
||||
Subject: Merged: [builtins] Clear FixedArray slot in Promise builtins
|
||||
|
||||
(cherry picked from commit f1884222ad56734e56d80f9707e0e8279af9049e)
|
||||
|
||||
Bug: chromium:1479104
|
||||
Change-Id: Iddc16d8add4dc6bf6f55f537da44770bea6f4bc3
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4862980
|
||||
Auto-Submit: Shu-yu Guo <syg@chromium.org>
|
||||
Reviewed-by: Adam Klein <adamk@chromium.org>
|
||||
Commit-Queue: Adam Klein <adamk@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/11.6@{#36}
|
||||
Cr-Branched-From: e29c028f391389a7a60ee37097e3ca9e396d6fa4-refs/heads/11.6.189@{#3}
|
||||
Cr-Branched-From: 95cbef20e2aa556a1ea75431a48b36c4de6b9934-refs/heads/main@{#88340}
|
||||
|
||||
diff --git a/src/builtins/promise-any.tq b/src/builtins/promise-any.tq
|
||||
index 45bafac0e6b09143b69b21a7292f9ed6b9c46239..d531d57a375ba33bf11ccf698da5918f1e25f38c 100644
|
||||
--- a/src/builtins/promise-any.tq
|
||||
+++ b/src/builtins/promise-any.tq
|
||||
@@ -106,9 +106,10 @@ PromiseAnyRejectElementClosure(
|
||||
const index = identityHash - 1;
|
||||
|
||||
// 6. Let errors be F.[[Errors]].
|
||||
- let errors = *ContextSlot(
|
||||
+ let errorsRef:&FixedArray = ContextSlot(
|
||||
context,
|
||||
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementErrorsSlot);
|
||||
+ let errors = *errorsRef;
|
||||
|
||||
// 7. Let promiseCapability be F.[[Capability]].
|
||||
|
||||
@@ -134,10 +135,7 @@ PromiseAnyRejectElementClosure(
|
||||
IntPtrMax(SmiUntag(remainingElementsCount) - 1, index + 1);
|
||||
if (newCapacity > errors.length_intptr) deferred {
|
||||
errors = ExtractFixedArray(errors, 0, errors.length_intptr, newCapacity);
|
||||
- *ContextSlot(
|
||||
- context,
|
||||
- PromiseAnyRejectElementContextSlots::
|
||||
- kPromiseAnyRejectElementErrorsSlot) = errors;
|
||||
+ *errorsRef = errors;
|
||||
}
|
||||
errors.objects[index] = value;
|
||||
|
||||
@@ -155,6 +153,10 @@ PromiseAnyRejectElementClosure(
|
||||
|
||||
// b. Set error.[[AggregateErrors]] to errors.
|
||||
const error = ConstructAggregateError(errors);
|
||||
+
|
||||
+ // After this point, errors escapes to user code. Clear the slot.
|
||||
+ *errorsRef = kEmptyFixedArray;
|
||||
+
|
||||
// c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
|
||||
const capability = *ContextSlot(
|
||||
context,
|
||||
@@ -363,8 +363,10 @@ def upload_io_to_github(release, filename, filepath, version):
|
||||
(filename))
|
||||
script_path = os.path.join(
|
||||
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.ts')
|
||||
execute([TS_NODE, script_path, filepath, filename, str(release['id']),
|
||||
version])
|
||||
upload_gh_output = execute([TS_NODE, script_path, filepath, filename,
|
||||
str(release['id']), version])
|
||||
if is_verbose_mode():
|
||||
print(upload_gh_output)
|
||||
|
||||
|
||||
def upload_sha256_checksum(version, file_path, key_prefix=None):
|
||||
|
||||
@@ -352,7 +352,9 @@ void Tray::PopUpContextMenu(gin::Arguments* args) {
|
||||
}
|
||||
}
|
||||
}
|
||||
tray_icon_->PopUpContextMenu(pos, menu.IsEmpty() ? nullptr : menu->model());
|
||||
|
||||
tray_icon_->PopUpContextMenu(
|
||||
pos, menu.IsEmpty() ? nullptr : menu->model()->GetWeakPtr());
|
||||
}
|
||||
|
||||
void Tray::CloseContextMenu() {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "components/security_state/content/content_utils.h"
|
||||
#include "components/security_state/core/security_state.h"
|
||||
#include "content/browser/renderer_host/frame_tree_node.h" // nogncheck
|
||||
#include "content/browser/renderer_host/navigation_controller_impl.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
|
||||
@@ -508,9 +509,16 @@ base::IDMap<WebContents*>& GetAllWebContents() {
|
||||
void OnCapturePageDone(gin_helper::Promise<gfx::Image> promise,
|
||||
base::ScopedClosureRunner capture_handle,
|
||||
const SkBitmap& bitmap) {
|
||||
auto ui_task_runner = content::GetUIThreadTaskRunner({});
|
||||
if (!ui_task_runner->RunsTasksInCurrentSequence()) {
|
||||
ui_task_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(&OnCapturePageDone, std::move(promise),
|
||||
std::move(capture_handle), bitmap));
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack to enable transparency in captured image
|
||||
promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap));
|
||||
|
||||
capture_handle.RunAndReset();
|
||||
}
|
||||
|
||||
@@ -1778,13 +1786,6 @@ void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
||||
|
||||
void WebContents::PrimaryMainFrameRenderProcessGone(
|
||||
base::TerminationStatus status) {
|
||||
auto weak_this = GetWeakPtr();
|
||||
Emit("crashed", status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
|
||||
|
||||
// User might destroy WebContents in the crashed event.
|
||||
if (!weak_this || !web_contents())
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin_helper::Dictionary details = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
@@ -2460,8 +2461,20 @@ void WebContents::LoadURL(const GURL& url,
|
||||
params.transition_type = ui::PageTransitionFromInt(
|
||||
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
|
||||
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
|
||||
// Discard non-committed entries to ensure that we don't re-use a pending
|
||||
// entry
|
||||
|
||||
// It's not safe to start a new navigation or otherwise discard the current
|
||||
// one while the call that started it is still on the stack. See
|
||||
// http://crbug.com/347742.
|
||||
auto& ctrl_impl = static_cast<content::NavigationControllerImpl&>(
|
||||
web_contents()->GetController());
|
||||
if (ctrl_impl.in_navigate_to_pending_entry()) {
|
||||
Emit("did-fail-load", static_cast<int>(net::ERR_FAILED),
|
||||
net::ErrorToShortString(net::ERR_FAILED), url.possibly_invalid_spec(),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Discard non-committed entries to ensure we don't re-use a pending entry.
|
||||
web_contents()->GetController().DiscardNonCommittedEntries();
|
||||
web_contents()->GetController().LoadURLWithParams(params);
|
||||
|
||||
@@ -3467,22 +3480,16 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||
}
|
||||
|
||||
auto* const view = web_contents()->GetRenderWidgetHostView();
|
||||
if (!view) {
|
||||
if (!view || view->GetViewBounds().size().IsEmpty()) {
|
||||
promise.Resolve(gfx::Image());
|
||||
return handle;
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
// If the view's renderer is suspended this may fail on Windows/Linux -
|
||||
// bail if so. See CopyFromSurface in
|
||||
// content/public/browser/render_widget_host_view.h.
|
||||
auto* rfh = web_contents()->GetPrimaryMainFrame();
|
||||
if (rfh &&
|
||||
rfh->GetVisibilityState() == blink::mojom::PageVisibilityState::kHidden) {
|
||||
promise.Resolve(gfx::Image());
|
||||
if (!view->IsSurfaceAvailableForCopy()) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Current display surface not available for capture");
|
||||
return handle;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
auto capture_handle = web_contents()->IncrementCapturerCount(
|
||||
rect.size(), stay_hidden, stay_awake);
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include "crypto/crypto_buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "extensions/browser/api/messaging/messaging_api_message_filter.h"
|
||||
#include "extensions/browser/extension_navigation_ui_data.h"
|
||||
#include "mojo/public/cpp/bindings/binder_map.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
@@ -112,6 +112,7 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
|
||||
#include "third_party/blink/public/common/tokens/tokens.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
@@ -144,6 +145,7 @@
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/file_url_loader.h"
|
||||
#include "content/public/browser/web_ui_url_loader_factory.h"
|
||||
#include "extensions/browser/api/messaging/messaging_api_message_filter.h"
|
||||
#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
|
||||
#include "extensions/browser/api/web_request/web_request_api.h"
|
||||
#include "extensions/browser/browser_context_keyed_api_factory.h"
|
||||
@@ -151,7 +153,6 @@
|
||||
#include "extensions/browser/extension_host.h"
|
||||
#include "extensions/browser/extension_message_filter.h"
|
||||
#include "extensions/browser/extension_navigation_throttle.h"
|
||||
#include "extensions/browser/extension_navigation_ui_data.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/browser/extension_protocols.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
@@ -169,7 +170,6 @@
|
||||
#include "shell/browser/extensions/electron_extension_message_filter.h"
|
||||
#include "shell/browser/extensions/electron_extension_system.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
@@ -1211,12 +1211,12 @@ void ElectronBrowserClient::
|
||||
protocol_registry->RegisterURLLoaderFactories(factories,
|
||||
false /* allow_file_access */);
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
factories->emplace(
|
||||
extensions::kExtensionScheme,
|
||||
extensions::CreateExtensionServiceWorkerScriptURLLoaderFactory(
|
||||
browser_context));
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
|
||||
@@ -1400,9 +1400,10 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
|
||||
factory_params->disable_web_security = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
|
||||
browser_context, origin, is_for_isolated_world, factory_params);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::
|
||||
@@ -1460,7 +1461,7 @@ void ElectronBrowserClient::
|
||||
},
|
||||
&render_frame_host));
|
||||
#endif
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
associated_registry.AddInterface<extensions::mojom::LocalFrameHost>(
|
||||
base::BindRepeating(
|
||||
[](content::RenderFrameHost* render_frame_host,
|
||||
|
||||
@@ -13,6 +13,7 @@ function_registration("api_registration") {
|
||||
sources = [
|
||||
"//electron/shell/common/extensions/api/extension.json",
|
||||
"//electron/shell/common/extensions/api/resources_private.idl",
|
||||
"//electron/shell/common/extensions/api/scripting.idl",
|
||||
"//electron/shell/common/extensions/api/tabs.json",
|
||||
]
|
||||
|
||||
|
||||
1334
shell/browser/extensions/api/scripting/scripting_api.cc
Normal file
1334
shell/browser/extensions/api/scripting/scripting_api.cc
Normal file
File diff suppressed because it is too large
Load Diff
207
shell/browser/extensions/api/scripting/scripting_api.h
Normal file
207
shell/browser/extensions/api/scripting/scripting_api.h
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright 2023 Microsoft, GmbH
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_EXTENSIONS_API_SCRIPTING_SCRIPTING_API_H_
|
||||
#define ELECTRON_SHELL_BROWSER_EXTENSIONS_API_SCRIPTING_SCRIPTING_API_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "chrome/common/extensions/api/scripting.h"
|
||||
#include "extensions/browser/extension_function.h"
|
||||
#include "extensions/browser/script_executor.h"
|
||||
#include "extensions/common/mojom/code_injection.mojom.h"
|
||||
#include "extensions/common/user_script.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// A simple helper struct to represent a read file (either CSS or JS) to be
|
||||
// injected.
|
||||
struct InjectedFileSource {
|
||||
InjectedFileSource(std::string file_name, std::unique_ptr<std::string> data);
|
||||
InjectedFileSource(InjectedFileSource&&);
|
||||
~InjectedFileSource();
|
||||
|
||||
std::string file_name;
|
||||
std::unique_ptr<std::string> data;
|
||||
};
|
||||
|
||||
class ScriptingExecuteScriptFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.executeScript", SCRIPTING_EXECUTESCRIPT)
|
||||
|
||||
ScriptingExecuteScriptFunction();
|
||||
ScriptingExecuteScriptFunction(const ScriptingExecuteScriptFunction&) =
|
||||
delete;
|
||||
ScriptingExecuteScriptFunction& operator=(
|
||||
const ScriptingExecuteScriptFunction&) = delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingExecuteScriptFunction() override;
|
||||
|
||||
// Called when the resource files to be injected has been loaded.
|
||||
void DidLoadResources(std::vector<InjectedFileSource> file_sources,
|
||||
absl::optional<std::string> load_error);
|
||||
|
||||
// Triggers the execution of `sources` in the appropriate context.
|
||||
// Returns true on success; on failure, populates `error`.
|
||||
bool Execute(std::vector<mojom::JSSourcePtr> sources, std::string* error);
|
||||
|
||||
// Invoked when script execution is complete.
|
||||
void OnScriptExecuted(std::vector<ScriptExecutor::FrameResult> frame_results);
|
||||
|
||||
api::scripting::ScriptInjection injection_;
|
||||
};
|
||||
|
||||
class ScriptingInsertCSSFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.insertCSS", SCRIPTING_INSERTCSS)
|
||||
|
||||
ScriptingInsertCSSFunction();
|
||||
ScriptingInsertCSSFunction(const ScriptingInsertCSSFunction&) = delete;
|
||||
ScriptingInsertCSSFunction& operator=(const ScriptingInsertCSSFunction&) =
|
||||
delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingInsertCSSFunction() override;
|
||||
|
||||
// Called when the resource files to be injected has been loaded.
|
||||
void DidLoadResources(std::vector<InjectedFileSource> file_sources,
|
||||
absl::optional<std::string> load_error);
|
||||
|
||||
// Triggers the execution of `sources` in the appropriate context.
|
||||
// Returns true on success; on failure, populates `error`.
|
||||
bool Execute(std::vector<mojom::CSSSourcePtr> sources, std::string* error);
|
||||
|
||||
// Called when the CSS insertion is complete.
|
||||
void OnCSSInserted(std::vector<ScriptExecutor::FrameResult> results);
|
||||
|
||||
api::scripting::CSSInjection injection_;
|
||||
};
|
||||
|
||||
class ScriptingRemoveCSSFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.removeCSS", SCRIPTING_REMOVECSS)
|
||||
|
||||
ScriptingRemoveCSSFunction();
|
||||
ScriptingRemoveCSSFunction(const ScriptingRemoveCSSFunction&) = delete;
|
||||
ScriptingRemoveCSSFunction& operator=(const ScriptingRemoveCSSFunction&) =
|
||||
delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingRemoveCSSFunction() override;
|
||||
|
||||
// Called when the CSS removal is complete.
|
||||
void OnCSSRemoved(std::vector<ScriptExecutor::FrameResult> results);
|
||||
};
|
||||
|
||||
using ValidateContentScriptsResult =
|
||||
std::pair<std::unique_ptr<UserScriptList>, absl::optional<std::string>>;
|
||||
|
||||
class ScriptingRegisterContentScriptsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.registerContentScripts",
|
||||
SCRIPTING_REGISTERCONTENTSCRIPTS)
|
||||
|
||||
ScriptingRegisterContentScriptsFunction();
|
||||
ScriptingRegisterContentScriptsFunction(
|
||||
const ScriptingRegisterContentScriptsFunction&) = delete;
|
||||
ScriptingRegisterContentScriptsFunction& operator=(
|
||||
const ScriptingRegisterContentScriptsFunction&) = delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingRegisterContentScriptsFunction() override;
|
||||
|
||||
// Called when script files have been checked.
|
||||
void OnContentScriptFilesValidated(
|
||||
std::set<std::string> persistent_script_ids,
|
||||
ValidateContentScriptsResult result);
|
||||
|
||||
// Called when content scripts have been registered.
|
||||
void OnContentScriptsRegistered(const absl::optional<std::string>& error);
|
||||
};
|
||||
|
||||
class ScriptingGetRegisteredContentScriptsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.getRegisteredContentScripts",
|
||||
SCRIPTING_GETREGISTEREDCONTENTSCRIPTS)
|
||||
|
||||
ScriptingGetRegisteredContentScriptsFunction();
|
||||
ScriptingGetRegisteredContentScriptsFunction(
|
||||
const ScriptingGetRegisteredContentScriptsFunction&) = delete;
|
||||
ScriptingGetRegisteredContentScriptsFunction& operator=(
|
||||
const ScriptingGetRegisteredContentScriptsFunction&) = delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingGetRegisteredContentScriptsFunction() override;
|
||||
};
|
||||
|
||||
class ScriptingUnregisterContentScriptsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.unregisterContentScripts",
|
||||
SCRIPTING_UNREGISTERCONTENTSCRIPTS)
|
||||
|
||||
ScriptingUnregisterContentScriptsFunction();
|
||||
ScriptingUnregisterContentScriptsFunction(
|
||||
const ScriptingUnregisterContentScriptsFunction&) = delete;
|
||||
ScriptingUnregisterContentScriptsFunction& operator=(
|
||||
const ScriptingUnregisterContentScriptsFunction&) = delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingUnregisterContentScriptsFunction() override;
|
||||
|
||||
// Called when content scripts have been unregistered.
|
||||
void OnContentScriptsUnregistered(const absl::optional<std::string>& error);
|
||||
};
|
||||
|
||||
class ScriptingUpdateContentScriptsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("scripting.updateContentScripts",
|
||||
SCRIPTING_UPDATECONTENTSCRIPTS)
|
||||
|
||||
ScriptingUpdateContentScriptsFunction();
|
||||
ScriptingUpdateContentScriptsFunction(
|
||||
const ScriptingUpdateContentScriptsFunction&) = delete;
|
||||
ScriptingUpdateContentScriptsFunction& operator=(
|
||||
const ScriptingUpdateContentScriptsFunction&) = delete;
|
||||
|
||||
// ExtensionFunction:
|
||||
ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
~ScriptingUpdateContentScriptsFunction() override;
|
||||
|
||||
// Called when script files have been checked.
|
||||
void OnContentScriptFilesValidated(
|
||||
std::set<std::string> persistent_script_ids,
|
||||
ValidateContentScriptsResult result);
|
||||
|
||||
// Called when content scripts have been updated.
|
||||
void OnContentScriptsUpdated(const absl::optional<std::string>& error);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_EXTENSIONS_API_SCRIPTING_SCRIPTING_API_H_
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "extensions/browser/api/i18n/i18n_api.h"
|
||||
#include "extensions/browser/extension_function_registry.h"
|
||||
#include "shell/browser/extensions/api/generated_api_registration.h"
|
||||
#include "shell/browser/extensions/api/scripting/scripting_api.h"
|
||||
#include "shell/browser/extensions/api/tabs/tabs_api.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "base/containers/contains.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "services/device/public/cpp/hid/hid_switches.h"
|
||||
#include "shell/browser/electron_permission_manager.h"
|
||||
#include "shell/browser/hid/hid_chooser_context.h"
|
||||
@@ -19,9 +20,9 @@
|
||||
#include "shell/browser/web_contents_permission_helper.h"
|
||||
#include "third_party/blink/public/common/permissions/permission_utils.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/common/constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -128,14 +129,14 @@ bool ElectronHidDelegate::IsFidoAllowedForOrigin(
|
||||
|
||||
bool ElectronHidDelegate::IsServiceWorkerAllowedForOrigin(
|
||||
const url::Origin& origin) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// WebHID is only available on extension service workers with feature flag
|
||||
// enabled for now.
|
||||
if (base::FeatureList::IsEnabled(
|
||||
features::kEnableWebHidOnExtensionServiceWorker) &&
|
||||
origin.scheme() == extensions::kExtensionScheme)
|
||||
return true;
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ void NativeWindow::PreviewFile(const std::string& path,
|
||||
|
||||
void NativeWindow::CloseFilePreview() {}
|
||||
|
||||
gfx::Rect NativeWindow::GetWindowControlsOverlayRect() {
|
||||
absl::optional<gfx::Rect> NativeWindow::GetWindowControlsOverlayRect() {
|
||||
return overlay_rect_;
|
||||
}
|
||||
|
||||
@@ -623,6 +623,7 @@ void NativeWindow::NotifyWindowMoved() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowEnterFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowEnterFullScreen();
|
||||
}
|
||||
@@ -648,6 +649,7 @@ void NativeWindow::NotifyWindowSheetEnd() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowLeaveFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowLeaveFullScreen();
|
||||
}
|
||||
@@ -691,10 +693,10 @@ void NativeWindow::NotifyWindowSystemContextMenu(int x,
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyLayoutWindowControlsOverlay() {
|
||||
gfx::Rect bounding_rect = GetWindowControlsOverlayRect();
|
||||
if (!bounding_rect.IsEmpty()) {
|
||||
auto bounding_rect = GetWindowControlsOverlayRect();
|
||||
if (bounding_rect.has_value()) {
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.UpdateWindowControlsOverlay(bounding_rect);
|
||||
observer.UpdateWindowControlsOverlay(bounding_rect.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
virtual gfx::Rect GetWindowControlsOverlayRect();
|
||||
virtual absl::optional<gfx::Rect> GetWindowControlsOverlayRect();
|
||||
virtual void SetWindowControlsOverlayRect(const gfx::Rect& overlay_rect);
|
||||
|
||||
// Methods called by the WebContents.
|
||||
|
||||
@@ -150,7 +150,7 @@ class NativeWindowMac : public NativeWindow,
|
||||
void CloseFilePreview() override;
|
||||
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
|
||||
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
|
||||
gfx::Rect GetWindowControlsOverlayRect() override;
|
||||
absl::optional<gfx::Rect> GetWindowControlsOverlayRect() override;
|
||||
void NotifyWindowEnterFullScreen() override;
|
||||
void NotifyWindowLeaveFullScreen() override;
|
||||
void SetActive(bool is_key) override;
|
||||
@@ -186,6 +186,9 @@ class NativeWindowMac : public NativeWindow,
|
||||
has_deferred_window_close_ = defer_close;
|
||||
}
|
||||
|
||||
void set_wants_to_be_visible(bool visible) { wants_to_be_visible_ = visible; }
|
||||
bool wants_to_be_visible() const { return wants_to_be_visible_; }
|
||||
|
||||
enum class VisualEffectState {
|
||||
kFollowWindow,
|
||||
kActive,
|
||||
@@ -251,6 +254,10 @@ class NativeWindowMac : public NativeWindow,
|
||||
// transition is complete.
|
||||
bool has_deferred_window_close_ = false;
|
||||
|
||||
// If true, the window is either visible, or wants to be visible but is
|
||||
// currently hidden due to having a hidden parent.
|
||||
bool wants_to_be_visible_ = false;
|
||||
|
||||
NSInteger attention_request_id_ = 0; // identifier from requestUserAttention
|
||||
|
||||
// The presentation options before entering kiosk mode.
|
||||
|
||||
@@ -499,6 +499,8 @@ void NativeWindowMac::Show() {
|
||||
return;
|
||||
}
|
||||
|
||||
set_wants_to_be_visible(true);
|
||||
|
||||
// Reattach the window to the parent to actually show it.
|
||||
if (parent())
|
||||
InternalSetParentWindow(parent(), true);
|
||||
@@ -531,6 +533,10 @@ void NativeWindowMac::Hide() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the window wants to be visible and has a parent, then the parent may
|
||||
// order it back in (in the period between orderOut: and close).
|
||||
set_wants_to_be_visible(false);
|
||||
|
||||
DetachChildren();
|
||||
|
||||
// Detach the window from the parent before.
|
||||
@@ -666,6 +672,9 @@ void NativeWindowMac::RemoveChildFromParentWindow() {
|
||||
|
||||
void NativeWindowMac::AttachChildren() {
|
||||
for (auto* child : child_windows_) {
|
||||
if (!static_cast<NativeWindowMac*>(child)->wants_to_be_visible())
|
||||
continue;
|
||||
|
||||
auto* child_nswindow = child->GetNativeWindow().GetNativeNSWindow();
|
||||
if ([child_nswindow parentWindow] == window_)
|
||||
continue;
|
||||
@@ -679,8 +688,6 @@ void NativeWindowMac::AttachChildren() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::DetachChildren() {
|
||||
DCHECK(child_windows_.size() == [[window_ childWindows] count]);
|
||||
|
||||
// Hide all children before hiding/minimizing the window.
|
||||
// NativeWidgetNSWindowBridge::NotifyVisibilityChangeDown()
|
||||
// will DCHECK otherwise.
|
||||
@@ -1423,11 +1430,20 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
|
||||
if (vibrantView != nil && !vibrancy_type_.empty()) {
|
||||
const bool no_rounded_corner = !HasStyleMask(NSWindowStyleMaskTitled);
|
||||
if (!has_frame() && !is_modal() && !no_rounded_corner) {
|
||||
|
||||
// Modal window corners are rounded on macOS >= 11 or higher if the user
|
||||
// hasn't passed noRoundedCorners.
|
||||
bool should_round_modal =
|
||||
!no_rounded_corner && (base::mac::IsAtLeastOS11() ? true : !is_modal());
|
||||
// Nonmodal window corners are rounded if they're frameless and the user
|
||||
// hasn't passed noRoundedCorners.
|
||||
bool should_round_nonmodal = !no_rounded_corner && !has_frame();
|
||||
|
||||
if (should_round_nonmodal || should_round_modal) {
|
||||
CGFloat radius;
|
||||
if (fullscreen) {
|
||||
radius = 0.0f;
|
||||
} else if (@available(macOS 11.0, *)) {
|
||||
} else if (base::mac::IsAtLeastOS11()) {
|
||||
radius = 9.0f;
|
||||
} else {
|
||||
// Smaller corner radius on versions prior to Big Sur.
|
||||
@@ -1898,23 +1914,33 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) {
|
||||
[window_ setAcceptsMouseMovedEvents:forward];
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowMac::GetWindowControlsOverlayRect() {
|
||||
if (titlebar_overlay_ && buttons_proxy_ &&
|
||||
window_button_visibility_.value_or(true)) {
|
||||
absl::optional<gfx::Rect> NativeWindowMac::GetWindowControlsOverlayRect() {
|
||||
if (!titlebar_overlay_)
|
||||
return absl::nullopt;
|
||||
|
||||
// On macOS, when in fullscreen mode, window controls (the menu bar, title
|
||||
// bar, and toolbar) are attached to a separate NSView that slides down from
|
||||
// the top of the screen, independent of, and overlapping the WebContents.
|
||||
// Disable WCO when in fullscreen, because this space is inaccessible to
|
||||
// WebContents. https://crbug.com/915110.
|
||||
if (IsFullscreen())
|
||||
return gfx::Rect();
|
||||
|
||||
if (buttons_proxy_ && window_button_visibility_.value_or(true)) {
|
||||
NSRect buttons = [buttons_proxy_ getButtonsContainerBounds];
|
||||
gfx::Rect overlay;
|
||||
overlay.set_width(GetContentSize().width() - NSWidth(buttons));
|
||||
if ([buttons_proxy_ useCustomHeight]) {
|
||||
overlay.set_height(titlebar_overlay_height());
|
||||
} else {
|
||||
overlay.set_height(NSHeight(buttons));
|
||||
}
|
||||
overlay.set_height([buttons_proxy_ useCustomHeight]
|
||||
? titlebar_overlay_height()
|
||||
: NSHeight(buttons));
|
||||
|
||||
if (!base::i18n::IsRTL())
|
||||
overlay.set_x(NSMaxX(buttons));
|
||||
|
||||
return overlay;
|
||||
}
|
||||
return gfx::Rect();
|
||||
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_util.h"
|
||||
#include "extensions/common/constants.h"
|
||||
@@ -36,7 +36,7 @@ base::flat_map<std::string, std::string>
|
||||
PluginUtils::GetMimeTypeToExtensionIdMap(
|
||||
content::BrowserContext* browser_context) {
|
||||
base::flat_map<std::string, std::string> mime_type_to_extension_id_map;
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
std::vector<std::string> allowed_extension_ids =
|
||||
MimeTypesHandler::GetMIMETypeAllowlist();
|
||||
// Go through the white-listed extensions and try to use them to intercept
|
||||
|
||||
@@ -258,6 +258,7 @@ using FullScreenTransitionState =
|
||||
[super windowDidMiniaturize:notification];
|
||||
is_minimized_ = true;
|
||||
|
||||
shell_->set_wants_to_be_visible(false);
|
||||
shell_->NotifyWindowMinimize();
|
||||
}
|
||||
|
||||
@@ -265,6 +266,7 @@ using FullScreenTransitionState =
|
||||
[super windowDidDeminiaturize:notification];
|
||||
is_minimized_ = false;
|
||||
|
||||
shell_->set_wants_to_be_visible(true);
|
||||
shell_->AttachChildren();
|
||||
shell_->SetWindowLevel(level_);
|
||||
shell_->NotifyWindowRestore();
|
||||
|
||||
@@ -21,7 +21,7 @@ void TrayIcon::RemoveBalloon() {}
|
||||
void TrayIcon::Focus() {}
|
||||
|
||||
void TrayIcon::PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model) {}
|
||||
base::WeakPtr<ElectronMenuModel> menu_model) {}
|
||||
|
||||
void TrayIcon::CloseContextMenu() {}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class TrayIcon {
|
||||
|
||||
// Popups the menu.
|
||||
virtual void PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model);
|
||||
base::WeakPtr<ElectronMenuModel> menu_model);
|
||||
|
||||
virtual void CloseContextMenu();
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ class TrayIconCocoa : public TrayIcon {
|
||||
std::string GetTitle() override;
|
||||
void SetIgnoreDoubleClickEvents(bool ignore) override;
|
||||
bool GetIgnoreDoubleClickEvents() override;
|
||||
void PopUpOnUI(ElectronMenuModel* menu_model);
|
||||
void PopUpOnUI(base::WeakPtr<ElectronMenuModel> menu_model);
|
||||
void PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model) override;
|
||||
base::WeakPtr<ElectronMenuModel> menu_model) override;
|
||||
void CloseContextMenu() override;
|
||||
void SetContextMenu(ElectronMenuModel* menu_model) override;
|
||||
gfx::Rect GetBounds() override;
|
||||
|
||||
@@ -85,6 +85,11 @@
|
||||
[self removeTrackingArea:trackingArea_];
|
||||
trackingArea_.reset();
|
||||
}
|
||||
|
||||
// Ensure any open menu is closed.
|
||||
if ([statusItem_ menu])
|
||||
[[statusItem_ menu] cancelTracking];
|
||||
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
|
||||
[self removeFromSuperview];
|
||||
statusItem_.reset();
|
||||
@@ -260,7 +265,6 @@
|
||||
if (menuController_ && ![menuController_ isMenuOpen]) {
|
||||
// Ensure the UI can update while the menu is fading out.
|
||||
base::ScopedPumpMessagesInPrivateModes pump_private;
|
||||
|
||||
[[statusItem_ button] performClick:self];
|
||||
}
|
||||
}
|
||||
@@ -382,16 +386,16 @@ bool TrayIconCocoa::GetIgnoreDoubleClickEvents() {
|
||||
return [status_item_view_ getIgnoreDoubleClickEvents];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::PopUpOnUI(ElectronMenuModel* menu_model) {
|
||||
[status_item_view_ popUpContextMenu:menu_model];
|
||||
void TrayIconCocoa::PopUpOnUI(base::WeakPtr<ElectronMenuModel> menu_model) {
|
||||
[status_item_view_ popUpContextMenu:menu_model.get()];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model) {
|
||||
void TrayIconCocoa::PopUpContextMenu(
|
||||
const gfx::Point& pos,
|
||||
base::WeakPtr<ElectronMenuModel> menu_model) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&TrayIconCocoa::PopUpOnUI, weak_factory_.GetWeakPtr(),
|
||||
base::Unretained(menu_model)));
|
||||
FROM_HERE, base::BindOnce(&TrayIconCocoa::PopUpOnUI,
|
||||
weak_factory_.GetWeakPtr(), menu_model));
|
||||
}
|
||||
|
||||
void TrayIconCocoa::CloseContextMenu() {
|
||||
|
||||
@@ -143,8 +143,11 @@ void InspectableWebContentsViewViews::CloseDevTools() {
|
||||
|
||||
devtools_visible_ = false;
|
||||
if (devtools_window_) {
|
||||
inspectable_web_contents()->SaveDevToolsBounds(
|
||||
devtools_window_->GetWindowBoundsInScreen());
|
||||
auto save_bounds = devtools_window_->IsMinimized()
|
||||
? devtools_window_->GetRestoredBounds()
|
||||
: devtools_window_->GetWindowBoundsInScreen();
|
||||
inspectable_web_contents()->SaveDevToolsBounds(save_bounds);
|
||||
|
||||
devtools_window_.reset();
|
||||
devtools_window_web_view_ = nullptr;
|
||||
devtools_window_delegate_ = nullptr;
|
||||
@@ -213,6 +216,8 @@ void InspectableWebContentsViewViews::SetTitle(const std::u16string& title) {
|
||||
void InspectableWebContentsViewViews::Layout() {
|
||||
if (!devtools_web_view_->GetVisible()) {
|
||||
contents_web_view_->SetBoundsRect(GetContentsBounds());
|
||||
// Propagate layout call to all children, for example browser views.
|
||||
View::Layout();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -230,6 +235,9 @@ void InspectableWebContentsViewViews::Layout() {
|
||||
devtools_web_view_->SetBoundsRect(new_devtools_bounds);
|
||||
contents_web_view_->SetBoundsRect(new_contents_bounds);
|
||||
|
||||
// Propagate layout call to all children, for example browser views.
|
||||
View::Layout();
|
||||
|
||||
if (GetDelegate())
|
||||
GetDelegate()->DevToolsResized();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ void NotifyIcon::HandleClickEvent(int modifiers,
|
||||
return;
|
||||
} else if (!double_button_click) { // single right click
|
||||
if (menu_model_)
|
||||
PopUpContextMenu(gfx::Point(), menu_model_);
|
||||
PopUpContextMenu(gfx::Point(), menu_model_->GetWeakPtr());
|
||||
else
|
||||
NotifyRightClicked(bounds, modifiers);
|
||||
}
|
||||
@@ -191,7 +191,7 @@ void NotifyIcon::Focus() {
|
||||
}
|
||||
|
||||
void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model) {
|
||||
base::WeakPtr<ElectronMenuModel> menu_model) {
|
||||
// Returns if context menu isn't set.
|
||||
if (menu_model == nullptr && menu_model_ == nullptr)
|
||||
return;
|
||||
@@ -209,9 +209,13 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
|
||||
if (pos.IsOrigin())
|
||||
rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
|
||||
|
||||
menu_runner_ = std::make_unique<views::MenuRunner>(
|
||||
menu_model != nullptr ? menu_model : menu_model_,
|
||||
views::MenuRunner::HAS_MNEMONICS);
|
||||
if (menu_model) {
|
||||
menu_runner_ = std::make_unique<views::MenuRunner>(
|
||||
menu_model.get(), views::MenuRunner::HAS_MNEMONICS);
|
||||
} else {
|
||||
menu_runner_ = std::make_unique<views::MenuRunner>(
|
||||
menu_model_, views::MenuRunner::HAS_MNEMONICS);
|
||||
}
|
||||
menu_runner_->RunMenuAt(nullptr, nullptr, rect,
|
||||
views::MenuAnchorPosition::kTopLeft,
|
||||
ui::MENU_SOURCE_MOUSE);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "shell/browser/ui/tray_icon.h"
|
||||
#include "shell/browser/ui/win/notify_icon_host.h"
|
||||
@@ -65,7 +66,7 @@ class NotifyIcon : public TrayIcon {
|
||||
void RemoveBalloon() override;
|
||||
void Focus() override;
|
||||
void PopUpContextMenu(const gfx::Point& pos,
|
||||
ElectronMenuModel* menu_model) override;
|
||||
base::WeakPtr<ElectronMenuModel> menu_model) override;
|
||||
void CloseContextMenu() override;
|
||||
void SetContextMenu(ElectronMenuModel* menu_model) override;
|
||||
gfx::Rect GetBounds() override;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "base/scoped_observation.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "services/device/public/mojom/usb_enumeration_options.mojom.h"
|
||||
#include "shell/browser/electron_permission_manager.h"
|
||||
#include "shell/browser/usb/usb_chooser_context.h"
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "shell/browser/usb/usb_chooser_controller.h"
|
||||
#include "shell/browser/web_contents_permission_helper.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
@@ -40,7 +40,7 @@ electron::UsbChooserContext* GetChooserContext(
|
||||
browser_context);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// These extensions can claim the smart card USB class and automatically gain
|
||||
// permissions for devices that have an interface with this class.
|
||||
constexpr auto kSmartCardPrivilegedExtensionIds =
|
||||
@@ -64,12 +64,12 @@ bool DeviceHasInterfaceWithClass(
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
bool IsDevicePermissionAutoGranted(
|
||||
const url::Origin& origin,
|
||||
const device::mojom::UsbDeviceInfo& device_info) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// Note: The `DeviceHasInterfaceWithClass()` call is made after checking the
|
||||
// origin, since that method call is expensive.
|
||||
if (origin.scheme() == extensions::kExtensionScheme &&
|
||||
@@ -78,7 +78,7 @@ bool IsDevicePermissionAutoGranted(
|
||||
device::mojom::kUsbSmartCardClass)) {
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -250,14 +250,14 @@ ElectronUsbDelegate::GetContextObserver(
|
||||
|
||||
bool ElectronUsbDelegate::IsServiceWorkerAllowedForOrigin(
|
||||
const url::Origin& origin) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// WebUSB is only available on extension service workers for now.
|
||||
if (base::FeatureList::IsEnabled(
|
||||
features::kEnableWebUsbOnExtensionServiceWorker) &&
|
||||
origin.scheme() == extensions::kExtensionScheme) {
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ generated_json_strings("generated_api_json_strings") {
|
||||
sources = [
|
||||
"extension.json",
|
||||
"resources_private.idl",
|
||||
"scripting.idl",
|
||||
"tabs.json",
|
||||
]
|
||||
|
||||
@@ -59,6 +60,7 @@ generated_json_strings("generated_api_json_strings") {
|
||||
generated_types("generated_api_types") {
|
||||
sources = [
|
||||
"resources_private.idl",
|
||||
"scripting.idl",
|
||||
"tabs.json",
|
||||
]
|
||||
|
||||
|
||||
@@ -51,5 +51,9 @@
|
||||
"matches": [
|
||||
"chrome://print/*"
|
||||
]
|
||||
}]
|
||||
}],
|
||||
"scripting": {
|
||||
"dependencies": ["permission:scripting"],
|
||||
"contexts": ["blessed_extension"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,12 @@
|
||||
"extension_types": [
|
||||
"extension"
|
||||
]
|
||||
},
|
||||
"scripting": {
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"extension"
|
||||
],
|
||||
"min_manifest_version": 3
|
||||
}
|
||||
}
|
||||
262
shell/common/extensions/api/scripting.idl
Normal file
262
shell/common/extensions/api/scripting.idl
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright 2020 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Use the <code>chrome.scripting</code> API to execute script in different
|
||||
// contexts.
|
||||
namespace scripting {
|
||||
callback InjectedFunction = void();
|
||||
|
||||
// The origin for a style change.
|
||||
// See <a href="https://developer.mozilla.org/en-US/docs/Glossary/Style_origin">style origins</a>
|
||||
// for more info.
|
||||
enum StyleOrigin {
|
||||
AUTHOR,
|
||||
USER
|
||||
};
|
||||
|
||||
// The JavaScript world for a script to execute within.
|
||||
enum ExecutionWorld {
|
||||
// Specifies the isolated world, which is the execution environment unique
|
||||
// to this extension.
|
||||
ISOLATED,
|
||||
// Specifies the main world of the DOM, which is the execution environment
|
||||
// shared with the host page's JavaScript.
|
||||
MAIN
|
||||
};
|
||||
|
||||
dictionary InjectionTarget {
|
||||
// The ID of the tab into which to inject.
|
||||
long tabId;
|
||||
|
||||
// The <a href="https://developer.chrome.com/extensions/webNavigation#frame_ids">IDs</a>
|
||||
// of specific frames to inject into.
|
||||
long[]? frameIds;
|
||||
|
||||
// The <a href="https://developer.chrome.com/extensions/webNavigation#document_ids">IDs</a>
|
||||
// of specific documentIds to inject into. This must not be set if
|
||||
// <code>frameIds</code> is set.
|
||||
DOMString[]? documentIds;
|
||||
|
||||
// Whether the script should inject into all frames within the tab. Defaults
|
||||
// to false.
|
||||
// This must not be true if <code>frameIds</code> is specified.
|
||||
boolean? allFrames;
|
||||
};
|
||||
|
||||
dictionary ScriptInjection {
|
||||
// A JavaScript function to inject. This function will be serialized, and
|
||||
// then deserialized for injection. This means that any bound parameters
|
||||
// and execution context will be lost.
|
||||
// Exactly one of <code>files</code> and <code>func</code> must be
|
||||
// specified.
|
||||
[serializableFunction]InjectedFunction? func;
|
||||
|
||||
// The arguments to curry into a provided function. This is only valid if
|
||||
// the <code>func</code> parameter is specified. These arguments must be
|
||||
// JSON-serializable.
|
||||
any[]? args;
|
||||
|
||||
// We used to call the injected function `function`, but this is
|
||||
// incompatible with JavaScript's object declaration shorthand (see
|
||||
// https://crbug.com/1166438). We leave this silently in for backwards
|
||||
// compatibility.
|
||||
// TODO(devlin): Remove this in M95.
|
||||
[nodoc, serializableFunction]InjectedFunction? function;
|
||||
|
||||
// The path of the JS or CSS files to inject, relative to the extension's
|
||||
// root directory.
|
||||
// Exactly one of <code>files</code> and <code>func</code> must be
|
||||
// specified.
|
||||
DOMString[]? files;
|
||||
|
||||
// Details specifying the target into which to inject the script.
|
||||
InjectionTarget target;
|
||||
|
||||
// The JavaScript "world" to run the script in. Defaults to
|
||||
// <code>ISOLATED</code>.
|
||||
ExecutionWorld? world;
|
||||
|
||||
// Whether the injection should be triggered in the target as soon as
|
||||
// possible. Note that this is not a guarantee that injection will occur
|
||||
// prior to page load, as the page may have already loaded by the time the
|
||||
// script reaches the target.
|
||||
boolean? injectImmediately;
|
||||
};
|
||||
|
||||
dictionary CSSInjection {
|
||||
// Details specifying the target into which to insert the CSS.
|
||||
InjectionTarget target;
|
||||
|
||||
// A string containing the CSS to inject.
|
||||
// Exactly one of <code>files</code> and <code>css</code> must be
|
||||
// specified.
|
||||
DOMString? css;
|
||||
|
||||
// The path of the CSS files to inject, relative to the extension's root
|
||||
// directory.
|
||||
// Exactly one of <code>files</code> and <code>css</code> must be
|
||||
// specified.
|
||||
DOMString[]? files;
|
||||
|
||||
// The style origin for the injection. Defaults to <code>'AUTHOR'</code>.
|
||||
StyleOrigin? origin;
|
||||
};
|
||||
|
||||
dictionary InjectionResult {
|
||||
// The result of the script execution.
|
||||
any? result;
|
||||
|
||||
// The frame associated with the injection.
|
||||
long frameId;
|
||||
|
||||
// The document associated with the injection.
|
||||
DOMString documentId;
|
||||
};
|
||||
|
||||
// Describes a content script to be injected into a web page registered
|
||||
// through this API.
|
||||
dictionary RegisteredContentScript {
|
||||
// The id of the content script, specified in the API call. Must not start
|
||||
// with a '_' as it's reserved as a prefix for generated script IDs.
|
||||
DOMString id;
|
||||
// Specifies which pages this content script will be injected into. See
|
||||
// <a href="match_patterns">Match Patterns</a> for more details on the
|
||||
// syntax of these strings. Must be specified for
|
||||
// $(ref:registerContentScripts).
|
||||
DOMString[]? matches;
|
||||
// Excludes pages that this content script would otherwise be injected into.
|
||||
// See <a href="match_patterns">Match Patterns</a> for more details on the
|
||||
// syntax of these strings.
|
||||
DOMString[]? excludeMatches;
|
||||
// The list of CSS files to be injected into matching pages. These are
|
||||
// injected in the order they appear in this array, before any DOM is
|
||||
// constructed or displayed for the page.
|
||||
DOMString[]? css;
|
||||
// The list of JavaScript files to be injected into matching pages. These
|
||||
// are injected in the order they appear in this array.
|
||||
DOMString[]? js;
|
||||
// If specified true, it will inject into all frames, even if the frame is
|
||||
// not the top-most frame in the tab. Each frame is checked independently
|
||||
// for URL requirements; it will not inject into child frames if the URL
|
||||
// requirements are not met. Defaults to false, meaning that only the top
|
||||
// frame is matched.
|
||||
boolean? allFrames;
|
||||
// TODO(devlin): Add documentation once the implementation is complete. See
|
||||
// crbug.com/55084.
|
||||
[nodoc]
|
||||
boolean? matchOriginAsFallback;
|
||||
// Specifies when JavaScript files are injected into the web page. The
|
||||
// preferred and default value is <code>document_idle</code>.
|
||||
extensionTypes.RunAt? runAt;
|
||||
// Specifies if this content script will persist into future sessions. The
|
||||
// default is true.
|
||||
boolean? persistAcrossSessions;
|
||||
// The JavaScript "world" to run the script in. Defaults to
|
||||
// <code>ISOLATED</code>.
|
||||
ExecutionWorld? world;
|
||||
};
|
||||
|
||||
// An object used to filter content scripts for
|
||||
// ${ref:getRegisteredContentScripts}.
|
||||
dictionary ContentScriptFilter {
|
||||
// If specified, $(ref:getRegisteredContentScripts) will only return scripts
|
||||
// with an id specified in this list.
|
||||
DOMString[]? ids;
|
||||
};
|
||||
|
||||
callback ScriptInjectionCallback = void(InjectionResult[] results);
|
||||
|
||||
callback CSSInjectionCallback = void();
|
||||
|
||||
callback RegisterContentScriptsCallback = void();
|
||||
|
||||
callback GetRegisteredContentScriptsCallback = void(
|
||||
RegisteredContentScript[] scripts);
|
||||
|
||||
callback UnregisterContentScriptsCallback = void();
|
||||
|
||||
callback UpdateContentScriptsCallback = void();
|
||||
|
||||
interface Properties {
|
||||
// An object available for content scripts running in isolated worlds to use
|
||||
// and modify as a JS object. One instance exists per frame and is shared
|
||||
// between all content scripts for a given extension. This object is
|
||||
// initialized when the frame is created, before document_start.
|
||||
// TODO(crbug.com/1054624): Enable this once implementation is complete.
|
||||
[nodoc, nocompile] static long globalParams();
|
||||
};
|
||||
|
||||
interface Functions {
|
||||
// Injects a script into a target context. The script will be run at
|
||||
// <code>document_idle</code>. If the script evaluates to a promise,
|
||||
// the browser will wait for the promise to settle and return the
|
||||
// resulting value.
|
||||
// |injection|: The details of the script which to inject.
|
||||
// |callback|: Invoked upon completion of the injection. The resulting
|
||||
// array contains the result of execution for each frame where the
|
||||
// injection succeeded.
|
||||
[supportsPromises] static void executeScript(
|
||||
ScriptInjection injection,
|
||||
optional ScriptInjectionCallback callback);
|
||||
|
||||
// Inserts a CSS stylesheet into a target context.
|
||||
// If multiple frames are specified, unsuccessful injections are ignored.
|
||||
// |injection|: The details of the styles to insert.
|
||||
// |callback|: Invoked upon completion of the insertion.
|
||||
[supportsPromises] static void insertCSS(
|
||||
CSSInjection injection,
|
||||
optional CSSInjectionCallback callback);
|
||||
|
||||
// Removes a CSS stylesheet that was previously inserted by this extension
|
||||
// from a target context.
|
||||
// |injection|: The details of the styles to remove. Note that the
|
||||
// <code>css</code>, <code>files</code>, and <code>origin</code> properties
|
||||
// must exactly match the stylesheet inserted through $(ref:insertCSS).
|
||||
// Attempting to remove a non-existent stylesheet is a no-op.
|
||||
// |callback|: A callback to be invoked upon the completion of the removal.
|
||||
[supportsPromises] static void removeCSS(
|
||||
CSSInjection injection,
|
||||
optional CSSInjectionCallback callback);
|
||||
|
||||
// Registers one or more content scripts for this extension.
|
||||
// |scripts|: Contains a list of scripts to be registered. If there are
|
||||
// errors during script parsing/file validation, or if the IDs specified
|
||||
// already exist, then no scripts are registered.
|
||||
// |callback|: A callback to be invoked once scripts have been fully
|
||||
// registered or if an error has occurred.
|
||||
[supportsPromises] static void registerContentScripts(
|
||||
RegisteredContentScript[] scripts,
|
||||
optional RegisterContentScriptsCallback callback);
|
||||
|
||||
// Returns all dynamically registered content scripts for this extension
|
||||
// that match the given filter.
|
||||
// |filter|: An object to filter the extension's dynamically registered
|
||||
// scripts.
|
||||
[supportsPromises] static void getRegisteredContentScripts(
|
||||
optional ContentScriptFilter filter,
|
||||
GetRegisteredContentScriptsCallback callback);
|
||||
|
||||
// Unregisters content scripts for this extension.
|
||||
// |filter|: If specified, only unregisters dynamic content scripts which
|
||||
// match the filter. Otherwise, all of the extension's dynamic content
|
||||
// scripts are unregistered.
|
||||
// |callback|: A callback to be invoked once scripts have been unregistered
|
||||
// or if an error has occurred.
|
||||
[supportsPromises] static void unregisterContentScripts(
|
||||
optional ContentScriptFilter filter,
|
||||
optional UnregisterContentScriptsCallback callback);
|
||||
|
||||
// Updates one or more content scripts for this extension.
|
||||
// |scripts|: Contains a list of scripts to be updated. A property is only
|
||||
// updated for the existing script if it is specified in this object. If
|
||||
// there are errors during script parsing/file validation, or if the IDs
|
||||
// specified do not correspond to a fully registered script, then no scripts
|
||||
// are updated.
|
||||
// |callback|: A callback to be invoked once scripts have been updated or
|
||||
// if an error has occurred.
|
||||
[supportsPromises] static void updateContentScripts(
|
||||
RegisteredContentScript[] scripts,
|
||||
optional RegisterContentScriptsCallback callback);
|
||||
};
|
||||
};
|
||||
@@ -40,6 +40,8 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
|
||||
{mojom::APIPermissionID::kManagement, "management"},
|
||||
{mojom::APIPermissionID::kTab, "tabs",
|
||||
APIPermissionInfo::kFlagRequiresManagementUIWarning},
|
||||
{mojom::APIPermissionID::kScripting, "scripting",
|
||||
APIPermissionInfo::kFlagRequiresManagementUIWarning},
|
||||
};
|
||||
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
||||
return base::make_span(permissions_to_register);
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
#include <utility>
|
||||
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "third_party/blink/public/web/web_element.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_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)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -26,14 +26,14 @@ PrintRenderFrameHelperDelegate::~PrintRenderFrameHelperDelegate() = default;
|
||||
// Return the PDF object element if |frame| is the out of process PDF extension.
|
||||
blink::WebElement PrintRenderFrameHelperDelegate::GetPdfElement(
|
||||
blink::WebLocalFrame* frame) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
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)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return blink::WebElement();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ bool PrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() {
|
||||
|
||||
bool PrintRenderFrameHelperDelegate::OverridePrint(
|
||||
blink::WebLocalFrame* frame) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
auto* post_message_support =
|
||||
extensions::PostMessageSupport::FromWebLocalFrame(frame);
|
||||
if (post_message_support) {
|
||||
@@ -56,7 +56,7 @@ bool PrintRenderFrameHelperDelegate::OverridePrint(
|
||||
post_message_support->PostMessageFromValue(base::Value(std::move(message)));
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,14 +235,6 @@ void RendererClientBase::RenderThreadStarted() {
|
||||
extensions::ExtensionsRendererClient::Set(extensions_renderer_client_.get());
|
||||
|
||||
thread->AddObserver(extensions_renderer_client_->GetDispatcher());
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
spellcheck_ = std::make_unique<SpellCheck>(this);
|
||||
#endif
|
||||
|
||||
blink::WebCustomElement::AddEmbedderCustomElementName("webview");
|
||||
blink::WebCustomElement::AddEmbedderCustomElementName("browserplugin");
|
||||
|
||||
WTF::String extension_scheme(extensions::kExtensionScheme);
|
||||
// Extension resources are HTTP-like and safe to expose to the fetch API. The
|
||||
@@ -255,6 +247,14 @@ void RendererClientBase::RenderThreadStarted() {
|
||||
extension_scheme);
|
||||
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
|
||||
extension_scheme);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
spellcheck_ = std::make_unique<SpellCheck>(this);
|
||||
#endif
|
||||
|
||||
blink::WebCustomElement::AddEmbedderCustomElementName("webview");
|
||||
blink::WebCustomElement::AddEmbedderCustomElementName("browserplugin");
|
||||
|
||||
std::vector<std::string> fetch_enabled_schemes =
|
||||
ParseSchemesCLISwitch(command_line, switches::kFetchSchemes);
|
||||
|
||||
@@ -2215,7 +2215,22 @@ describe('BrowserWindow module', () => {
|
||||
expect(visible).to.equal('hidden');
|
||||
});
|
||||
|
||||
it('resolves after the window is hidden', async () => {
|
||||
it('resolves when the window is occluded', async () => {
|
||||
const w1 = new BrowserWindow({ show: false });
|
||||
w1.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w1, 'ready-to-show');
|
||||
w1.show();
|
||||
|
||||
const w2 = new BrowserWindow({ show: false });
|
||||
w2.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w2, 'ready-to-show');
|
||||
w2.show();
|
||||
|
||||
const visibleImage = await w1.capturePage();
|
||||
expect(visibleImage.isEmpty()).to.equal(false);
|
||||
});
|
||||
|
||||
it('resolves when the window is not visible', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w, 'ready-to-show');
|
||||
@@ -2224,21 +2239,10 @@ describe('BrowserWindow module', () => {
|
||||
const visibleImage = await w.capturePage();
|
||||
expect(visibleImage.isEmpty()).to.equal(false);
|
||||
|
||||
w.hide();
|
||||
w.minimize();
|
||||
|
||||
const hiddenImage = await w.capturePage();
|
||||
const isEmpty = process.platform !== 'darwin';
|
||||
expect(hiddenImage.isEmpty()).to.equal(isEmpty);
|
||||
});
|
||||
|
||||
it('resolves after the window is hidden and capturer count is non-zero', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.webContents.setBackgroundThrottling(false);
|
||||
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w, 'ready-to-show');
|
||||
|
||||
const image = await w.capturePage();
|
||||
expect(image.isEmpty()).to.equal(false);
|
||||
expect(hiddenImage.isEmpty()).to.equal(false);
|
||||
});
|
||||
|
||||
it('preserves transparency', async () => {
|
||||
@@ -4710,7 +4714,27 @@ describe('BrowserWindow module', () => {
|
||||
expect(w.getChildWindows().length).to.equal(0);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('child window matches visibility when visibility changes', async () => {
|
||||
ifit(process.platform === 'darwin')('only shows the intended window when a child with siblings is shown', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const childOne = new BrowserWindow({ show: false, parent: w });
|
||||
const childTwo = new BrowserWindow({ show: false, parent: w });
|
||||
|
||||
const parentShown = once(w, 'show');
|
||||
w.show();
|
||||
await parentShown;
|
||||
|
||||
expect(childOne.isVisible()).to.be.false('childOne is visible');
|
||||
expect(childTwo.isVisible()).to.be.false('childTwo is visible');
|
||||
|
||||
const childOneShown = once(childOne, 'show');
|
||||
childOne.show();
|
||||
await childOneShown;
|
||||
|
||||
expect(childOne.isVisible()).to.be.true('childOne is not visible');
|
||||
expect(childTwo.isVisible()).to.be.false('childTwo is visible');
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('child matches parent visibility when parent visibility changes', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const c = new BrowserWindow({ show: false, parent: w });
|
||||
|
||||
@@ -4737,7 +4761,7 @@ describe('BrowserWindow module', () => {
|
||||
expect(c.isVisible()).to.be.true('child is visible');
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('matches child window visibility when visibility changes', async () => {
|
||||
ifit(process.platform === 'darwin')('parent matches child visibility when child visibility changes', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const c = new BrowserWindow({ show: false, parent: w });
|
||||
|
||||
@@ -6357,7 +6381,7 @@ describe('BrowserWindow module', () => {
|
||||
foregroundWindow.loadFile(path.join(__dirname, 'fixtures', 'pages', 'css-transparent.html'));
|
||||
await once(ipcMain, 'set-transparent');
|
||||
|
||||
await setTimeout();
|
||||
await setTimeout(1000);
|
||||
const screenCapture = await captureScreen();
|
||||
const centerColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width / 2,
|
||||
@@ -6380,7 +6404,7 @@ describe('BrowserWindow module', () => {
|
||||
await once(window, 'show');
|
||||
await window.webContents.loadURL('data:text/html,<head><meta name="color-scheme" content="dark"></head>');
|
||||
|
||||
await setTimeout(500);
|
||||
await setTimeout(1000);
|
||||
const screenCapture = await captureScreen();
|
||||
const centerColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width / 2,
|
||||
@@ -6410,6 +6434,7 @@ describe('BrowserWindow module', () => {
|
||||
w.loadURL('about:blank');
|
||||
await once(w, 'ready-to-show');
|
||||
|
||||
await setTimeout(1000);
|
||||
const screenCapture = await captureScreen();
|
||||
const centerColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width / 2,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { expect } from 'chai';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as path from 'path';
|
||||
import { BrowserWindow, MessageChannelMain, utilityProcess } from 'electron/main';
|
||||
import { BrowserWindow, MessageChannelMain, utilityProcess, app } from 'electron/main';
|
||||
import { ifit } from './lib/spec-helpers';
|
||||
import { closeWindow } from './lib/window-helpers';
|
||||
import { once } from 'events';
|
||||
import { setImmediate } from 'timers/promises';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process');
|
||||
const isWindowsOnArm = process.platform === 'win32' && process.arch === 'arm64';
|
||||
@@ -94,6 +95,56 @@ describe('utilityProcess module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('app \'child-process-gone\' event', () => {
|
||||
it('with default serviceName', async () => {
|
||||
utilityProcess.fork(path.join(fixturesPath, 'crash.js'));
|
||||
const [, details] = await once(app, 'child-process-gone') as [any, Electron.Details];
|
||||
expect(details.type).to.equal('Utility');
|
||||
expect(details.serviceName).to.equal('node.mojom.NodeService');
|
||||
expect(details.name).to.equal('Node Utility Process');
|
||||
expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']);
|
||||
});
|
||||
|
||||
it('with custom serviceName', async () => {
|
||||
utilityProcess.fork(path.join(fixturesPath, 'crash.js'), [], { serviceName: 'Hello World!' });
|
||||
const [, details] = await once(app, 'child-process-gone') as [any, Electron.Details];
|
||||
expect(details.type).to.equal('Utility');
|
||||
expect(details.serviceName).to.equal('node.mojom.NodeService');
|
||||
expect(details.name).to.equal('Hello World!');
|
||||
expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.getAppMetrics()', () => {
|
||||
it('with default serviceName', async () => {
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'endless.js'));
|
||||
await once(child, 'spawn');
|
||||
expect(child.pid).to.not.be.null();
|
||||
|
||||
await setImmediate();
|
||||
|
||||
const details = app.getAppMetrics().find(item => item.pid === child.pid)!;
|
||||
expect(details).to.be.an('object');
|
||||
expect(details.type).to.equal('Utility');
|
||||
expect(details.serviceName).to.to.equal('node.mojom.NodeService');
|
||||
expect(details.name).to.equal('Node Utility Process');
|
||||
});
|
||||
|
||||
it('with custom serviceName', async () => {
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'endless.js'), [], { serviceName: 'Hello World!' });
|
||||
await once(child, 'spawn');
|
||||
expect(child.pid).to.not.be.null();
|
||||
|
||||
await setImmediate();
|
||||
|
||||
const details = app.getAppMetrics().find(item => item.pid === child.pid)!;
|
||||
expect(details).to.be.an('object');
|
||||
expect(details.type).to.equal('Utility');
|
||||
expect(details.serviceName).to.to.equal('node.mojom.NodeService');
|
||||
expect(details.name).to.equal('Hello World!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kill() API', () => {
|
||||
it('terminates the child process gracefully', async () => {
|
||||
const child = utilityProcess.fork(path.join(fixturesPath, 'endless.js'), [], {
|
||||
|
||||
@@ -431,6 +431,19 @@ describe('webContents module', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('fails if loadURL is called inside a non-reentrant critical section', (done) => {
|
||||
w.webContents.once('did-fail-load', (_event, _errorCode, _errorDescription, validatedURL) => {
|
||||
expect(validatedURL).to.contain('blank.html');
|
||||
done();
|
||||
});
|
||||
|
||||
w.webContents.once('did-start-loading', () => {
|
||||
w.loadURL(`file://${fixturesPath}/pages/blank.html`);
|
||||
});
|
||||
|
||||
w.loadURL('data:text/html,<h1>HELLO</h1>');
|
||||
});
|
||||
|
||||
it('sets appropriate error information on rejection', async () => {
|
||||
let err: any;
|
||||
try {
|
||||
@@ -2258,13 +2271,13 @@ describe('webContents module', () => {
|
||||
});
|
||||
|
||||
describe('crashed event', () => {
|
||||
it('does not crash main process when destroying WebContents in it', async () => {
|
||||
it('does not crash main process when destroying WebContents in it', (done) => {
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({ nodeIntegration: true });
|
||||
const crashEvent = once(contents, 'render-process-gone');
|
||||
await contents.loadURL('about:blank');
|
||||
contents.forcefullyCrashRenderer();
|
||||
await crashEvent;
|
||||
contents.destroy();
|
||||
contents.once('crashed', () => {
|
||||
contents.destroy();
|
||||
done();
|
||||
});
|
||||
contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1036,5 +1036,75 @@ describe('chrome extensions', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('chrome.scripting', () => {
|
||||
let customSession: Session;
|
||||
let w = null as unknown as BrowserWindow;
|
||||
|
||||
before(async () => {
|
||||
customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-scripting'));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
session: customSession,
|
||||
nodeIntegration: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('executeScript', async () => {
|
||||
await w.loadURL(url);
|
||||
|
||||
const message = { method: 'executeScript' };
|
||||
w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
|
||||
|
||||
const updated = await once(w.webContents, 'page-title-updated');
|
||||
expect(updated[1]).to.equal('HEY HEY HEY');
|
||||
});
|
||||
|
||||
it('registerContentScripts', async () => {
|
||||
await w.loadURL(url);
|
||||
|
||||
const message = { method: 'registerContentScripts' };
|
||||
w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
|
||||
|
||||
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||
const response = JSON.parse(responseString);
|
||||
expect(response).to.be.an('array').with.lengthOf(1);
|
||||
expect(response[0]).to.deep.equal({
|
||||
allFrames: false,
|
||||
id: 'session-script',
|
||||
js: ['content.js'],
|
||||
matchOriginAsFallback: false,
|
||||
matches: ['<all_urls>'],
|
||||
persistAcrossSessions: false,
|
||||
runAt: 'document_start',
|
||||
world: 'ISOLATED'
|
||||
});
|
||||
});
|
||||
|
||||
it('insertCSS', async () => {
|
||||
await w.loadURL(url);
|
||||
|
||||
const bgBefore = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).backgroundColor');
|
||||
expect(bgBefore).to.equal('rgba(0, 0, 0, 0)');
|
||||
|
||||
const message = { method: 'insertCSS' };
|
||||
w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
|
||||
|
||||
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||
const response = JSON.parse(responseString);
|
||||
expect(response.success).to.be.true();
|
||||
|
||||
const bgAfter = await w.webContents.executeJavaScript('window.getComputedStyle(document.body).backgroundColor');
|
||||
expect(bgAfter).to.equal('rgb(255, 0, 0)');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
51
spec/fixtures/extensions/chrome-scripting/background.js
vendored
Normal file
51
spec/fixtures/extensions/chrome-scripting/background.js
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/* global chrome */
|
||||
|
||||
const handleRequest = async (request, sender, sendResponse) => {
|
||||
const { method } = request;
|
||||
const tabId = sender.tab.id;
|
||||
|
||||
switch (method) {
|
||||
case 'executeScript': {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId },
|
||||
function: () => {
|
||||
document.title = 'HEY HEY HEY';
|
||||
return document.title;
|
||||
}
|
||||
}).then(() => {
|
||||
console.log('success');
|
||||
}).catch((err) => {
|
||||
console.log('error', err);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'registerContentScripts': {
|
||||
await chrome.scripting.registerContentScripts([{
|
||||
id: 'session-script',
|
||||
js: ['content.js'],
|
||||
persistAcrossSessions: false,
|
||||
matches: ['<all_urls>'],
|
||||
runAt: 'document_start'
|
||||
}]);
|
||||
|
||||
chrome.scripting.getRegisteredContentScripts().then(sendResponse);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'insertCSS': {
|
||||
chrome.scripting.insertCSS({
|
||||
target: { tabId },
|
||||
css: 'body { background-color: red; }'
|
||||
}).then(() => {
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
handleRequest(request, sender, sendResponse);
|
||||
return true;
|
||||
});
|
||||
0
spec/fixtures/extensions/chrome-scripting/content.js
vendored
Normal file
0
spec/fixtures/extensions/chrome-scripting/content.js
vendored
Normal file
30
spec/fixtures/extensions/chrome-scripting/main.js
vendored
Normal file
30
spec/fixtures/extensions/chrome-scripting/main.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/* global chrome */
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
sendResponse(request);
|
||||
});
|
||||
|
||||
const map = {
|
||||
executeScript () {
|
||||
chrome.runtime.sendMessage({ method: 'executeScript' }, response => {
|
||||
console.log(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
registerContentScripts () {
|
||||
chrome.runtime.sendMessage({ method: 'registerContentScripts' }, response => {
|
||||
console.log(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
insertCSS () {
|
||||
chrome.runtime.sendMessage({ method: 'insertCSS' }, response => {
|
||||
console.log(JSON.stringify(response));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const dispatchTest = (event) => {
|
||||
const { method, args = [] } = JSON.parse(event.data);
|
||||
map[method](...args);
|
||||
};
|
||||
|
||||
window.addEventListener('message', dispatchTest, false);
|
||||
17
spec/fixtures/extensions/chrome-scripting/manifest.json
vendored
Normal file
17
spec/fixtures/extensions/chrome-scripting/manifest.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "execute-script",
|
||||
"version": "1.0",
|
||||
"permissions": [
|
||||
"scripting"
|
||||
],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"content_scripts": [{
|
||||
"matches": [ "<all_urls>"],
|
||||
"js": ["main.js"],
|
||||
"run_at": "document_start"
|
||||
}],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"manifest_version": 3
|
||||
}
|
||||
@@ -418,6 +418,11 @@ win2.once('ready-to-show', () => {
|
||||
app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) });
|
||||
app.exit(0);
|
||||
|
||||
app.configureHostResolver({ secureDnsMode: 'off' });
|
||||
|
||||
// @ts-expect-error Invalid type value
|
||||
app.configureHostResolver({ secureDnsMode: 'foo' });
|
||||
|
||||
// auto-updater
|
||||
// https://github.com/electron/electron/blob/main/docs/api/auto-updater.md
|
||||
|
||||
@@ -1245,6 +1250,11 @@ win4.webContents.on('paint', (event, dirty, _image) => {
|
||||
console.log(dirty, _image.getBitmap());
|
||||
});
|
||||
|
||||
win4.webContents.insertCSS('body {}', { cssOrigin: 'user' });
|
||||
|
||||
// @ts-expect-error Invalid type value
|
||||
win4.webContents.insertCSS('body {}', { cssOrigin: 'foo' });
|
||||
|
||||
win4.loadURL('http://github.com');
|
||||
|
||||
// TouchBar
|
||||
|
||||
Reference in New Issue
Block a user