Compare commits

...

29 Commits

Author SHA1 Message Date
Pedro Pontes
6c37483f2c chore: cherry-pick 1 changes from Release-1-M119 (#40519)
* chore: [25-x-y] cherry-pick 1 changes from Release-1-M119

* 3df423a5b8de from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-11-14 12:54:36 -08:00
trop[bot]
bb6a7d443b fix: broken shader cache due to compilation error (#40473)
* fix: broken shader cache due to compilation error

Backports:
- https://chromium-review.googlesource.com/c/chromium/src/+/4988290
- https://chromium-review.googlesource.com/c/chromium/src/+/4766018

Co-authored-by: deepak1556 <hop2deep@gmail.com>

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2023-11-08 19:14:25 +09:00
Pedro Pontes
a3c0d0db3f chore: cherry-pick 1 changes from Release-0-M119 (#40437)
* chore: [25-x-y] cherry-pick 1 changes from Release-0-M119

* 80106e31c7ea from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-11-03 10:30:53 -04:00
trop[bot]
0ffac056c3 docs: add bypassCustomProtocolHandlers to ses.fetch (#40421)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Brandon Fowler <brandfowl@gmail.com>
2023-11-02 10:10:25 -04:00
Shelley Vohr
ae70118ffb fix: webview exiting fullscreen presentation mode (#40411)
* fix: webview exiting fullscreen presentation mode

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-11-01 18:58:16 -04:00
trop[bot]
8c384c13b3 build: actually show github upload output if verbose is true. (#40396)
* build: actually show github upload output if verbose is true.

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

* chore: fixup lint

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2023-11-01 10:20:17 -04:00
trop[bot]
91fa68bd32 build: upload slow, more time good (#40334)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2023-10-26 00:21:23 -07:00
Milan Burda
6b67839770 test: add spec for app.getAppMetrics() for utility process (#40320)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
2023-10-24 13:32:51 -07:00
Milan Burda
fc98a28d67 test: add spec for child-process-gone event for utility process (#40310)
test: add spec for `child-process-gone` event for utility process (#40281)
2023-10-24 11:15:08 -04:00
Pedro Pontes
f1c1b22fd4 chore: cherry-pick f666cceb92c2 from dawn (#40266)
* chore: cherry-pick f666cceb92c2 from dawn

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-10-18 19:19:00 -04:00
Shelley Vohr
4439b13027 fix: failing build with enable_electron_extensions=false (#40270) 2023-10-18 19:15:42 -04:00
Shelley Vohr
1bd2e574a7 fix: potential crash calling tray.popUpContextMenu() (#40271) 2023-10-18 19:15:00 -04:00
trop[bot]
468c4af661 fix: incorrect wco bounds in macOS fullscreen (#40220)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-16 14:52:00 +02:00
Pedro Pontes
83576caa40 chore: cherry-pick 4 changes from Release-0-M118 (#40210)
* chore: [25-x-y] cherry-pick 4 changes from Release-0-M118

* f218b4f37018 from chromium
* d756d71a652c from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-10-16 12:31:11 +02:00
David Sanders
36edecf606 docs: fix some string union typings (#40204) 2023-10-16 09:17:55 +09:00
trop[bot]
3f3c0c45f2 fix: webContents.capturePage() for hidden windows on Windows/Linux (#40186)
fix: capturePage for hidden windows on Windows/Linux

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-13 11:31:25 +02:00
trop[bot]
805674fa8a test: make capturePage color matching timeouts consistent (#40165)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-10 15:38:56 -04:00
trop[bot]
e7ba28f8f9 fix: crash when calling non-reentrant function in loadURL (#40161)
fix: crash when calling non-reentrant function in loadURL

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-10 21:10:37 +02:00
trop[bot]
8e0d1af44e test: fix "crashed event does not crash main process when destroying WebContents in it" (#40148)
test: fix "crashed event does not crash main process when destroying WebContents in it" (#40135)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2023-10-10 12:45:40 +02:00
trop[bot]
dde1b500cc ci: fixup diagnose_goma_log.py call (#40150)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2023-10-10 10:36:23 +02:00
trop[bot]
fefe45e897 chore: cherry-pick c03569f from libuv (#40125)
* chore: cherry-pick c03569f from libuv

Refs c03569f0df

Co-authored-by: deepak1556 <hop2deep@gmail.com>

* chore: update .patches

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-10-09 12:33:56 +09:00
Pedro Pontes
bd43e652f6 chore: cherry-pick 3 changes from Release-1-M117 (#40078)
* chore: [25-x-y] cherry-pick 3 changes from Release-1-M117

* b0ad701a609a from v8
* b11e7d07a6f4 from chromium
* 309b604c4e88 from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-10-06 09:54:50 -04:00
trop[bot]
bb01e52fb8 fix: toggling DevTools while minimized on Windows (#40117)
fix: toggling devtools while minimized on Windows

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-06 09:51:11 -04:00
trop[bot]
985b56a68d fix: crashed events deprecation (#40113)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2023-10-06 09:50:48 -04:00
trop[bot]
47a2df48b9 fix: all children showing when showing child window (#40106)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-10-05 19:56:21 -04:00
trop[bot]
b5646778e9 fix: propagate layout call to all children of InspectableWebContentsViewViews (#40035)
Propagate layout call to all children of InspectableWebContentsViewViews.

When BrowserView bounds are set from js, those might not trigger layout
immediately, sometimes propagating InvalidateLayout call to parent.
View is marked as needing layout, expecting to receive it from parent on
next layout call. The problem is that BrowserView's view is added as child
of InspectableWebContentsViews which does not call setBounds (which
would trigger layout) on all of it's children when doing it's layout,
so it skips propagating Layout call to its children BrowserViews views,
even though those were marked as needing layout.
Call base class View::Layout which will iterate over views' children
and call Layout on those that were marked as needing them.

Fixes #39993.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Marek Haranczyk <marek@openfin.co>
2023-09-29 21:42:58 +02:00
trop[bot]
772bbe775c fix: rounded corners on vibrant macOS modals (#39997)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-09-28 12:50:20 -04:00
trop[bot]
be3b6c1e2b fix: failure on immutable webContents.print(options) (#40028)
fix: failure on immutable webContents.print(options)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-09-28 12:47:58 -04:00
trop[bot]
cf49565a31 feat: support chrome.scripting extension APIs (#39677)
feat: support chrome.scripting extension APIs

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2023-09-28 11:29:46 -04:00
72 changed files with 5008 additions and 215 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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[]&#32;(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

View File

@@ -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.

View File

@@ -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).

View File

@@ -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

View File

@@ -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)`.

View File

@@ -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)`.

View File

@@ -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",

View File

@@ -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'];
});

View File

@@ -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) => {

View File

@@ -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);
}
}
});
}

View File

@@ -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

View File

@@ -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',

View 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 =

View 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() {

View 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');

View 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;

View 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 {

View 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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 =

View File

@@ -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);

View File

@@ -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);

View File

@@ -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(

View File

@@ -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
View File

@@ -0,0 +1 @@
cherry-pick-f666cceb92c2.patch

View 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;

View File

@@ -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

View File

@@ -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];

View File

@@ -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

View 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,

View File

@@ -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):

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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,

View File

@@ -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",
]

File diff suppressed because it is too large Load Diff

View 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_

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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() {}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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",
]

View File

@@ -51,5 +51,9 @@
"matches": [
"chrome://print/*"
]
}]
}],
"scripting": {
"dependencies": ["permission:scripting"],
"contexts": ["blessed_extension"]
}
}

View File

@@ -26,5 +26,12 @@
"extension_types": [
"extension"
]
},
"scripting": {
"channel": "stable",
"extension_types": [
"extension"
],
"min_manifest_version": 3
}
}

View 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);
};
};

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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'), [], {

View File

@@ -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());
});
});

View File

@@ -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)');
});
});
});
});

View 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;
});

View File

View 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);

View 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
}

View File

@@ -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