mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
25 Commits
fix-util-p
...
printing-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96c81bd6a7 | ||
|
|
920f909e3e | ||
|
|
76f7bbb0a8 | ||
|
|
22c149812c | ||
|
|
42164d7081 | ||
|
|
3eb94b72ba | ||
|
|
752f2eb124 | ||
|
|
beafbfd511 | ||
|
|
d54645e554 | ||
|
|
0bf53a3876 | ||
|
|
62d4b21819 | ||
|
|
61457c9498 | ||
|
|
c6102b9278 | ||
|
|
72c2b9e862 | ||
|
|
08241669bc | ||
|
|
6e36153799 | ||
|
|
4f76fff978 | ||
|
|
c82ec0c72b | ||
|
|
c57ce31e84 | ||
|
|
32b44aa5c8 | ||
|
|
7032c0d03c | ||
|
|
b9c4b27781 | ||
|
|
8c427253b3 | ||
|
|
ed28ead8ac | ||
|
|
90a7e5acae |
4
.github/workflows/issue-opened.yml
vendored
4
.github/workflows/issue-opened.yml
vendored
@@ -38,6 +38,8 @@ jobs:
|
||||
- run: npm install mdast-util-from-markdown@2.0.0 unist-util-select@5.1.0
|
||||
- name: Add labels
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
env:
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
@@ -47,7 +49,7 @@ jobs:
|
||||
const [ owner, repo ] = '${{ github.repository }}'.split('/');
|
||||
const issue_number = ${{ github.event.issue.number }};
|
||||
|
||||
const tree = fromMarkdown(`${{ github.event.issue.body }}`);
|
||||
const tree = fromMarkdown(process.env.ISSUE_BODY);
|
||||
|
||||
const labels = [];
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
{
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json"
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json",
|
||||
"no-angle-brackets": true,
|
||||
"no-inline-html": {
|
||||
"allowed_elements": [
|
||||
"br",
|
||||
"details",
|
||||
"img",
|
||||
"li",
|
||||
"summary",
|
||||
"ul",
|
||||
"unknown",
|
||||
"Tabs",
|
||||
"TabItem",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
BUILD.gn
3
BUILD.gn
@@ -475,6 +475,7 @@ source_set("electron_lib") {
|
||||
"//net:extras",
|
||||
"//net:net_resources",
|
||||
"//printing/buildflags",
|
||||
"//services/device/public/cpp/bluetooth:bluetooth",
|
||||
"//services/device/public/cpp/geolocation",
|
||||
"//services/device/public/cpp/hid",
|
||||
"//services/device/public/mojom",
|
||||
@@ -699,6 +700,8 @@ source_set("electron_lib") {
|
||||
sources += [
|
||||
"shell/browser/printing/print_view_manager_electron.cc",
|
||||
"shell/browser/printing/print_view_manager_electron.h",
|
||||
"shell/browser/printing/printing_utils.cc",
|
||||
"shell/browser/printing/printing_utils.h",
|
||||
"shell/renderer/printing/print_render_frame_helper_delegate.cc",
|
||||
"shell/renderer/printing/print_render_frame_helper_delegate.h",
|
||||
]
|
||||
|
||||
@@ -32,7 +32,7 @@ In most cases, you should do everything in the `ready` event handler.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `launchInfo` Record<string, any> | [NotificationResponse](structures/notification-response.md) _macOS_
|
||||
* `launchInfo` Record\<string, any\> | [NotificationResponse](structures/notification-response.md) _macOS_
|
||||
|
||||
Emitted once, when Electron has finished initializing. On macOS, `launchInfo`
|
||||
holds the `userInfo` of the [`NSUserNotification`](https://developer.apple.com/documentation/foundation/nsusernotification)
|
||||
@@ -970,7 +970,7 @@ app.setJumpList([
|
||||
|
||||
### `app.requestSingleInstanceLock([additionalData])`
|
||||
|
||||
* `additionalData` Record<any, any> (optional) - A JSON object containing additional data to send to the first instance.
|
||||
* `additionalData` Record\<any, any\> (optional) - A JSON object containing additional data to send to the first instance.
|
||||
|
||||
Returns `boolean`
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ The `autoUpdater` object has the following methods:
|
||||
|
||||
* `options` Object
|
||||
* `url` string
|
||||
* `headers` Record<string, string> (optional) _macOS_ - HTTP request headers.
|
||||
* `headers` Record\<string, string\> (optional) _macOS_ - HTTP request headers.
|
||||
* `serverType` string (optional) _macOS_ - Can be `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
|
||||
README for more information.
|
||||
|
||||
|
||||
@@ -656,7 +656,7 @@ Closes the currently open [Quick Look][quick-look] panel.
|
||||
|
||||
#### `win.setBounds(bounds[, animate])`
|
||||
|
||||
* `bounds` Partial<[Rectangle](structures/rectangle.md)>
|
||||
* `bounds` Partial\<[Rectangle](structures/rectangle.md)\>
|
||||
* `animate` boolean (optional) _macOS_
|
||||
|
||||
Resizes and moves the window to the supplied bounds. Any properties that are not supplied will default to their current values.
|
||||
|
||||
@@ -779,7 +779,7 @@ Closes the currently open [Quick Look][quick-look] panel.
|
||||
|
||||
#### `win.setBounds(bounds[, animate])`
|
||||
|
||||
* `bounds` Partial<[Rectangle](structures/rectangle.md)>
|
||||
* `bounds` Partial\<[Rectangle](structures/rectangle.md)\>
|
||||
* `animate` boolean (optional) _macOS_
|
||||
|
||||
Resizes and moves the window to the supplied bounds. Any properties that are not supplied will default to their current values.
|
||||
@@ -1215,7 +1215,7 @@ win.loadURL('http://localhost:8000/post', {
|
||||
|
||||
* `filePath` string
|
||||
* `options` Object (optional)
|
||||
* `query` Record<string, string> (optional) - Passed to `url.format()`.
|
||||
* `query` Record\<string, string\> (optional) - Passed to `url.format()`.
|
||||
* `search` string (optional) - Passed to `url.format()`.
|
||||
* `hash` string (optional) - Passed to `url.format()`.
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ following properties:
|
||||
method.
|
||||
* `url` string (optional) - The request URL. Must be provided in the absolute
|
||||
form with the protocol scheme specified as http or https.
|
||||
* `headers` Record\<string, string | string[]\> (optional) - Headers to be sent
|
||||
with the request.
|
||||
* `session` Session (optional) - The [`Session`](session.md) instance with
|
||||
which the request is associated.
|
||||
* `partition` string (optional) - The name of the [`partition`](session.md)
|
||||
@@ -158,7 +160,7 @@ Returns:
|
||||
* `statusCode` Integer
|
||||
* `method` string
|
||||
* `redirectUrl` string
|
||||
* `responseHeaders` Record<string, string[]>
|
||||
* `responseHeaders` Record\<string, string[]\>
|
||||
|
||||
Emitted when the server returns a redirect response (e.g. 301 Moved
|
||||
Permanently). Calling [`request.followRedirect`](#requestfollowredirect) will
|
||||
|
||||
@@ -59,14 +59,14 @@ The `crashReporter` module has the following methods:
|
||||
number of crashes uploaded to 1/hour. Default is `false`.
|
||||
* `compress` boolean (optional) - If true, crash reports will be compressed
|
||||
and uploaded with `Content-Encoding: gzip`. Default is `true`.
|
||||
* `extra` Record<string, string> (optional) - Extra string key/value
|
||||
* `extra` Record\<string, string\> (optional) - Extra string key/value
|
||||
annotations that will be sent along with crash reports that are generated
|
||||
in the main process. Only string values are supported. Crashes generated in
|
||||
child processes will not contain these extra
|
||||
parameters to crash reports generated from child processes, call
|
||||
[`addExtraParameter`](#crashreporteraddextraparameterkey-value) from the
|
||||
child process.
|
||||
* `globalExtra` Record<string, string> (optional) - Extra string key/value
|
||||
* `globalExtra` Record\<string, string\> (optional) - Extra string key/value
|
||||
annotations that will be sent along with any crash reports generated in any
|
||||
process. These annotations cannot be changed once the crash reporter has
|
||||
been started. If a key is present in both the global extra parameters and
|
||||
|
||||
@@ -72,7 +72,7 @@ Removes listeners of the specified `channel`.
|
||||
### `ipcMain.handle(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<any> | any>
|
||||
* `listener` Function\<Promise\<any\> | any\>
|
||||
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
@@ -109,7 +109,7 @@ provided to the renderer process. Please refer to
|
||||
### `ipcMain.handleOnce(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function<Promise\<any> | any>
|
||||
* `listener` Function\<Promise\<any\> | any\>
|
||||
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ expect streaming responses.
|
||||
|
||||
* `scheme` string - scheme to handle, for example `https` or `my-app`. This is
|
||||
the bit before the `:` in a URL.
|
||||
* `handler` Function<[GlobalResponse](https://nodejs.org/api/globals.html#response) | Promise<GlobalResponse>>
|
||||
* `handler` Function\<[GlobalResponse](https://nodejs.org/api/globals.html#response) | Promise\<GlobalResponse\>\>
|
||||
* `request` [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
|
||||
Register a protocol handler for `scheme`. Requests made to URLs with this
|
||||
|
||||
@@ -27,7 +27,7 @@ The `pushNotification` module emits the following events:
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `userInfo` Record<String, any>
|
||||
* `userInfo` Record\<String, any\>
|
||||
|
||||
Emitted when the app receives a remote notification while running.
|
||||
See: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428430-application?language=objc
|
||||
|
||||
@@ -814,6 +814,8 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `keyboardLock` - Request capture of keypresses for any or all of the keys on the physical keyboard via the [Keyboard Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/lock). These requests always appear to originate from the main frame.
|
||||
* `openExternal` - Request to open links in external applications.
|
||||
* `speaker-selection` - Request to enumerate and select audio output devices via the [speaker-selection permissions policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/speaker-selection).
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `window-management` - Request access to enumerate screens using the [`getScreenDetails`](https://developer.chrome.com/en/articles/multi-screen-window-placement/) API.
|
||||
* `unknown` - An unrecognized permission request.
|
||||
* `callback` Function
|
||||
@@ -862,6 +864,8 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||
* `openExternal` - Open links in external applications.
|
||||
* `pointerLock` - Directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `serial` - Read from and write to serial devices with the [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
||||
* `requestingOrigin` string - The origin URL of the permission check
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
@@ -1216,7 +1220,7 @@ Returns `Promise<Buffer>` - resolves with blob data.
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
* `headers` Record\<string, string\> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url`.
|
||||
The API will generate a [DownloadItem](download-item.md) that can be accessed
|
||||
@@ -1424,23 +1428,34 @@ is emitted.
|
||||
Returns `string | null` - The absolute file system path where data for this
|
||||
session is persisted on disk. For in memory sessions this returns `null`.
|
||||
|
||||
#### `ses.clearData()`
|
||||
#### `ses.clearData([options])`
|
||||
|
||||
* `options` Object (optional)
|
||||
* `dataTypes` String[] (optional) - The types of data to clear. By default, this will clear all types of data.
|
||||
* `backgroundFetch` - Background Fetch
|
||||
* `cache` - Cache
|
||||
* `cookies` - Cookies
|
||||
* `downloads` - Downloads
|
||||
* `fileSystems` - File Systems
|
||||
* `indexedDB` - IndexedDB
|
||||
* `localStorage` - Local Storage
|
||||
* `serviceWorkers` - Service Workers
|
||||
* `webSQL` - WebSQL
|
||||
* `origins` String[] (optional) - Clear data for only these origins. Cannot be used with `excludeOrigins`.
|
||||
* `excludeOrigins` String[] (optional) - Clear data for all origins except these ones. Cannot be used with `origins`.
|
||||
* `avoidClosingConnections` boolean (optional) - Skips deleting cookies that would close current network connections. (Default: `false`)
|
||||
* `originMatchingMode` String (optional) - The behavior for matching data to origins.
|
||||
* `third-parties-included` (default) - Storage is matched on origin in first-party contexts and top-level-site in third-party contexts.
|
||||
* `origin-in-all-contexts` - Storage is matched on origin only in all contexts.
|
||||
|
||||
Returns `Promise<void>` - resolves when all data has been cleared.
|
||||
|
||||
This method clears many different types of data, inlcuding:
|
||||
|
||||
* Cache
|
||||
* Cookies
|
||||
* Downloads
|
||||
* IndexedDB
|
||||
* Local Storage
|
||||
* Service Workers
|
||||
* And more...
|
||||
Clears various different types of data.
|
||||
|
||||
This method clears more types of data and is more thourough than the
|
||||
`clearStorageData` method, however it is currently less configurable than that
|
||||
method.
|
||||
`clearStorageData` method.
|
||||
|
||||
**Note:** Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`.
|
||||
|
||||
For more information, refer to Chromium's [`BrowsingDataRemover` interface](https://source.chromium.org/chromium/chromium/src/+/main:content/public/browser/browsing_data_remover.h).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# FilePathWithHeaders Object
|
||||
|
||||
* `path` string - The path to the file to send.
|
||||
* `headers` Record<string, string> (optional) - Additional headers to be sent.
|
||||
* `headers` Record\<string, string\> (optional) - Additional headers to be sent.
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
* `actionIdentifier` string - The identifier string of the action that the user selected.
|
||||
* `date` number - The delivery date of the notification.
|
||||
* `identifier` string - The unique identifier for this notification request.
|
||||
* `userInfo` Record<string, any> - A dictionary of custom information associated with the notification.
|
||||
* `userInfo` Record\<string, any\> - A dictionary of custom information associated with the notification.
|
||||
* `userText` string (optional) - The text entered or chosen by the user.
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* `referrer` string
|
||||
* `method` string
|
||||
* `uploadData` [UploadData[]](upload-data.md) (optional)
|
||||
* `headers` Record<string, string>
|
||||
* `headers` Record\<string, string\>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
`"text/html"`. Setting `mimeType` would implicitly set the `content-type`
|
||||
header in response, but if `content-type` is already set in `headers`, the
|
||||
`mimeType` would be ignored.
|
||||
* `headers` Record<string, string | string[]> (optional) - An object containing the response headers. The
|
||||
* `headers` Record\<string, string | string[]\> (optional) - An object containing the response headers. The
|
||||
keys must be string, and values must be either string or Array of string.
|
||||
* `data` (Buffer | string | ReadableStream) (optional) - The response body. When
|
||||
returning stream as response, this is a Node.js readable stream representing
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
include in the trace. If not specified, trace all processes.
|
||||
* `histogram_names` string[] (optional) - a list of [histogram][] names to report
|
||||
with the trace.
|
||||
* `memory_dump_config` Record<string, any> (optional) - if the
|
||||
* `memory_dump_config` Record\<string, any\> (optional) - if the
|
||||
`disabled-by-default-memory-infra` category is enabled, this contains
|
||||
optional additional configuration for data collection. See the [Chromium
|
||||
memory-infra docs][memory-infra docs] for more information.
|
||||
|
||||
@@ -36,7 +36,7 @@ Returns `boolean` - Whether the Swipe between pages setting is on.
|
||||
### `systemPreferences.postNotification(event, userInfo[, deliverImmediately])` _macOS_
|
||||
|
||||
* `event` string
|
||||
* `userInfo` Record<string, any>
|
||||
* `userInfo` Record\<string, any\>
|
||||
* `deliverImmediately` boolean (optional) - `true` to post notifications immediately even when the subscribing app is inactive.
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
@@ -45,7 +45,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
### `systemPreferences.postLocalNotification(event, userInfo)` _macOS_
|
||||
|
||||
* `event` string
|
||||
* `userInfo` Record<string, any>
|
||||
* `userInfo` Record\<string, any\>
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
that contains the user information dictionary sent along with the notification.
|
||||
@@ -53,7 +53,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
### `systemPreferences.postWorkspaceNotification(event, userInfo)` _macOS_
|
||||
|
||||
* `event` string
|
||||
* `userInfo` Record<string, any>
|
||||
* `userInfo` Record\<string, any\>
|
||||
|
||||
Posts `event` as native notifications of macOS. The `userInfo` is an Object
|
||||
that contains the user information dictionary sent along with the notification.
|
||||
@@ -63,7 +63,7 @@ that contains the user information dictionary sent along with the notification.
|
||||
* `event` string | null
|
||||
* `callback` Function
|
||||
* `event` string
|
||||
* `userInfo` Record<string, unknown>
|
||||
* `userInfo` Record\<string, unknown\>
|
||||
* `object` string
|
||||
|
||||
Returns `number` - The ID of this subscription
|
||||
@@ -92,7 +92,7 @@ If `event` is null, the `NSDistributedNotificationCenter` doesn’t use it as cr
|
||||
* `event` string | null
|
||||
* `callback` Function
|
||||
* `event` string
|
||||
* `userInfo` Record<string, unknown>
|
||||
* `userInfo` Record\<string, unknown\>
|
||||
* `object` string
|
||||
|
||||
Returns `number` - The ID of this subscription
|
||||
@@ -107,7 +107,7 @@ If `event` is null, the `NSNotificationCenter` doesn’t use it as criteria for
|
||||
* `event` string | null
|
||||
* `callback` Function
|
||||
* `event` string
|
||||
* `userInfo` Record<string, unknown>
|
||||
* `userInfo` Record\<string, unknown\>
|
||||
* `object` string
|
||||
|
||||
Returns `number` - The ID of this subscription
|
||||
@@ -137,7 +137,7 @@ Same as `unsubscribeNotification`, but removes the subscriber from `NSWorkspace.
|
||||
|
||||
### `systemPreferences.registerDefaults(defaults)` _macOS_
|
||||
|
||||
* `defaults` Record<string, string | boolean | number> - a dictionary of (`key: value`) user defaults
|
||||
* `defaults` Record\<string, string | boolean | number\> - a dictionary of (`key: value`) user defaults
|
||||
|
||||
Add the specified defaults to your application's `NSUserDefaults`.
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ See [`window.open()`](window-open.md) for more details and how to use this in co
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `details` Event\<\>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - This event does not fire for same document navigations using window.history api and reference fragment navigations.
|
||||
This property is always set to `false` for this event.
|
||||
@@ -270,7 +270,7 @@ Calling `event.preventDefault()` will prevent the navigation.
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `details` Event\<\>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - This event does not fire for same document navigations using window.history api and reference fragment navigations.
|
||||
This property is always set to `false` for this event.
|
||||
@@ -300,7 +300,7 @@ Calling `event.preventDefault()` will prevent the navigation.
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `details` Event\<\>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
@@ -324,7 +324,7 @@ Emitted when any frame (including main) starts navigating.
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `details` Event\<\>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
@@ -355,7 +355,7 @@ redirect).
|
||||
|
||||
Returns:
|
||||
|
||||
* `details` Event<>
|
||||
* `details` Event\<\>
|
||||
* `url` string - The URL the frame is navigating to.
|
||||
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||
document. Examples of same document navigations are reference fragment
|
||||
@@ -683,7 +683,7 @@ Emitted when media is paused or done playing.
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event<>
|
||||
* `event` Event\<\>
|
||||
* `audible` boolean - True if one or more frames or child `webContents` are emitting audio.
|
||||
|
||||
Emitted when media becomes audible or inaudible.
|
||||
@@ -900,7 +900,7 @@ Returns:
|
||||
* `webPreferences` [WebPreferences](structures/web-preferences.md) - The web preferences that will be used by the guest
|
||||
page. This object can be modified to adjust the preferences for the guest
|
||||
page.
|
||||
* `params` Record<string, string> - The other `<webview>` parameters such as the `src` URL.
|
||||
* `params` Record\<string, string\> - The other `<webview>` parameters such as the `src` URL.
|
||||
This object can be modified to adjust the parameters of the guest page.
|
||||
|
||||
Emitted when a `<webview>`'s web contents is being attached to this web
|
||||
@@ -1020,7 +1020,7 @@ win.webContents.loadURL('https://github.com', options)
|
||||
|
||||
* `filePath` string
|
||||
* `options` Object (optional)
|
||||
* `query` Record<string, string> (optional) - Passed to `url.format()`.
|
||||
* `query` Record\<string, string\> (optional) - Passed to `url.format()`.
|
||||
* `search` string (optional) - Passed to `url.format()`.
|
||||
* `hash` string (optional) - Passed to `url.format()`.
|
||||
|
||||
@@ -1051,7 +1051,7 @@ win.loadFile('src/index.html')
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
* `headers` Record\<string, string\> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url` without navigating. The
|
||||
`will-download` event of `session` will be triggered.
|
||||
@@ -1288,7 +1288,7 @@ Ignore application menu shortcuts while this web contents is focused.
|
||||
|
||||
#### `contents.setWindowOpenHandler(handler)`
|
||||
|
||||
* `handler` Function<[WindowOpenHandlerResponse](structures/window-open-handler-response.md)>
|
||||
* `handler` Function\<[WindowOpenHandlerResponse](structures/window-open-handler-response.md)\>
|
||||
* `details` Object
|
||||
* `url` string - The _resolved_ version of the URL passed to `window.open()`. e.g. opening a window with `window.open('foo')` will yield something like `https://the-origin/the/current/path/foo`.
|
||||
* `frameName` string - Name of the window provided in `window.open()`
|
||||
@@ -1583,7 +1583,7 @@ Returns `Promise<PrinterInfo[]>` - Resolves with a [`PrinterInfo[]`](structures/
|
||||
* `from` number - Index of the first page to print (0-based).
|
||||
* `to` number - Index of the last page to print (inclusive) (0-based).
|
||||
* `duplexMode` string (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
|
||||
* `dpi` Record<string, number> (optional)
|
||||
* `dpi` Record\<string, number\> (optional)
|
||||
* `horizontal` number (optional) - The horizontal dpi.
|
||||
* `vertical` number (optional) - The vertical dpi.
|
||||
* `header` string (optional) - string to be printed as page header.
|
||||
|
||||
@@ -99,11 +99,11 @@ Some examples of valid `urls`:
|
||||
* `referrer` string
|
||||
* `timestamp` Double
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md) (optional)
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `requestHeaders` Record\<string, string\>
|
||||
* `callback` Function
|
||||
* `beforeSendResponse` Object
|
||||
* `cancel` boolean (optional)
|
||||
* `requestHeaders` Record<string, string | string[]> (optional) - When provided, request will be made
|
||||
* `requestHeaders` Record\<string, string | string[]\> (optional) - When provided, request will be made
|
||||
with these headers.
|
||||
|
||||
The `listener` will be called with `listener(details, callback)` before sending
|
||||
@@ -126,7 +126,7 @@ The `callback` has to be called with a `response` object.
|
||||
* `resourceType` string - Can be `mainFrame`, `subFrame`, `stylesheet`, `script`, `image`, `font`, `object`, `xhr`, `ping`, `cspReport`, `media`, `webSocket` or `other`.
|
||||
* `referrer` string
|
||||
* `timestamp` Double
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `requestHeaders` Record\<string, string\>
|
||||
|
||||
The `listener` will be called with `listener(details)` just before a request is
|
||||
going to be sent to the server, modifications of previous `onBeforeSendHeaders`
|
||||
@@ -148,11 +148,11 @@ response are visible by the time this listener is fired.
|
||||
* `timestamp` Double
|
||||
* `statusLine` string
|
||||
* `statusCode` Integer
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record\<string, string[]\> (optional)
|
||||
* `callback` Function
|
||||
* `headersReceivedResponse` Object
|
||||
* `cancel` boolean (optional)
|
||||
* `responseHeaders` Record<string, string | string[]> (optional) - When provided, the server is assumed
|
||||
* `responseHeaders` Record\<string, string | string[]\> (optional) - When provided, the server is assumed
|
||||
to have responded with these headers.
|
||||
* `statusLine` string (optional) - Should be provided when overriding
|
||||
`responseHeaders` to change header status otherwise original response
|
||||
@@ -177,7 +177,7 @@ The `callback` has to be called with a `response` object.
|
||||
* `resourceType` string - Can be `mainFrame`, `subFrame`, `stylesheet`, `script`, `image`, `font`, `object`, `xhr`, `ping`, `cspReport`, `media`, `webSocket` or `other`.
|
||||
* `referrer` string
|
||||
* `timestamp` Double
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record\<string, string[]\> (optional)
|
||||
* `fromCache` boolean - Indicates whether the response was fetched from disk
|
||||
cache.
|
||||
* `statusCode` Integer
|
||||
@@ -207,7 +207,7 @@ and response headers are available.
|
||||
* `ip` string (optional) - The server IP address that the request was
|
||||
actually sent to.
|
||||
* `fromCache` boolean
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record\<string, string[]\> (optional)
|
||||
|
||||
The `listener` will be called with `listener(details)` when a server initiated
|
||||
redirect is about to occur.
|
||||
@@ -226,7 +226,7 @@ redirect is about to occur.
|
||||
* `resourceType` string - Can be `mainFrame`, `subFrame`, `stylesheet`, `script`, `image`, `font`, `object`, `xhr`, `ping`, `cspReport`, `media`, `webSocket` or `other`.
|
||||
* `referrer` string
|
||||
* `timestamp` Double
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `responseHeaders` Record\<string, string[]\> (optional)
|
||||
* `fromCache` boolean
|
||||
* `statusCode` Integer
|
||||
* `statusLine` string
|
||||
|
||||
@@ -287,7 +287,7 @@ e.g. the `http://` or `file://`.
|
||||
|
||||
* `url` string
|
||||
* `options` Object (optional)
|
||||
* `headers` Record<string, string> (optional) - HTTP request headers.
|
||||
* `headers` Record\<string, string\> (optional) - HTTP request headers.
|
||||
|
||||
Initiates a download of the resource at `url` without navigating.
|
||||
|
||||
@@ -580,7 +580,7 @@ Stops any `findInPage` request for the `webview` with the provided `action`.
|
||||
* `from` number - Index of the first page to print (0-based).
|
||||
* `to` number - Index of the last page to print (inclusive) (0-based).
|
||||
* `duplexMode` string (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
|
||||
* `dpi` Record<string, number> (optional)
|
||||
* `dpi` Record\<string, number\> (optional)
|
||||
* `horizontal` number (optional) - The horizontal dpi.
|
||||
* `vertical` number (optional) - The vertical dpi.
|
||||
* `header` string (optional) - string to be printed as page header.
|
||||
|
||||
@@ -1686,7 +1686,7 @@ folder
|
||||
└── file3
|
||||
```
|
||||
|
||||
In Electron <=6, this would return a `FileList` with a `File` object for:
|
||||
In Electron <=6, this would return a `FileList` with a `File` object for:
|
||||
|
||||
```console
|
||||
path/to/folder
|
||||
|
||||
@@ -234,7 +234,7 @@ Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRende
|
||||
<details><summary>Typed import aliases</summary>
|
||||
|
||||
For better type checking when writing TypeScript code, you can choose to import
|
||||
main process modules from <code>electron/main</code>.
|
||||
main process modules from `electron/main`.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron/main')
|
||||
|
||||
@@ -209,6 +209,22 @@ type ExtraURLLoaderOptions = {
|
||||
headers: Record<string, { name: string, value: string | string[] }>;
|
||||
allowNonHttpProtocols: boolean;
|
||||
}
|
||||
|
||||
function validateHeader (name: any, value: any): void {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & ExtraURLLoaderOptions {
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
|
||||
@@ -275,12 +291,7 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
};
|
||||
const headers: Record<string, string | string[]> = options.headers || {};
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
validateHeader(name, value);
|
||||
const key = name.toLowerCase();
|
||||
urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
@@ -351,21 +362,10 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
}
|
||||
|
||||
setHeader (name: string, value: string) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
validateHeader(name, value);
|
||||
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.headers[key] = { name, value };
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import { app } from 'electron/main';
|
||||
import { IncomingMessage } from 'electron/utility';
|
||||
import type { ClientRequestConstructorOptions } from 'electron/utility';
|
||||
import { ClientRequest } from '@electron/internal/common/api/net-client-request';
|
||||
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
|
||||
|
||||
const { isOnline, resolveHost } = process._linkedBinding('electron_common_net');
|
||||
const { _ensureNetworkServiceInitialized } = process._linkedBinding('electron_browser_utility_process');
|
||||
|
||||
if (app.isReady()) {
|
||||
_ensureNetworkServiceInitialized();
|
||||
}
|
||||
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@electron/docs-parser": "^1.2.0",
|
||||
"@electron/fiddle-core": "^1.0.4",
|
||||
"@electron/github-app-auth": "^2.0.0",
|
||||
"@electron/lint-roller": "^1.9.0",
|
||||
"@electron/lint-roller": "^1.12.1",
|
||||
"@electron/typescript-definitions": "^8.15.2",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
@@ -49,7 +49,7 @@
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-unicorn": "^46.0.1",
|
||||
"events": "^3.2.0",
|
||||
"express": "^4.16.4",
|
||||
"express": "^4.19.2",
|
||||
"folder-hash": "^2.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"got": "^11.8.5",
|
||||
|
||||
@@ -34,64 +34,22 @@ index 69e759c3b34692beac06ceeddf4b3f1637fb7788..42d0da7f936626aa8bb90057723deeaa
|
||||
}
|
||||
|
||||
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
|
||||
index 0d8a53178e8d509a573ffdaadf2c4cec670522ac..8e7f9b92beb317888a5a3acb3bc2fa29f283ff03 100644
|
||||
index 0d8a53178e8d509a573ffdaadf2c4cec670522ac..b6fa337b2f5f445f38ef521613702af320df82cb 100644
|
||||
--- a/chrome/browser/printing/print_job.cc
|
||||
+++ b/chrome/browser/printing/print_job.cc
|
||||
@@ -96,6 +96,7 @@ bool PrintWithReducedRasterization(PrefService* prefs) {
|
||||
return base::FeatureList::IsEnabled(features::kPrintWithReducedRasterization);
|
||||
}
|
||||
|
||||
+#if 0
|
||||
PrefService* GetPrefsForWebContents(content::WebContents* web_contents) {
|
||||
@@ -100,9 +100,7 @@ PrefService* GetPrefsForWebContents(content::WebContents* web_contents) {
|
||||
// TODO(thestig): Figure out why crbug.com/1083911 occurred, which is likely
|
||||
// because `web_contents` was null. As a result, this section has many more
|
||||
@@ -110,6 +111,7 @@ content::WebContents* GetWebContents(content::GlobalRenderFrameHostId rfh_id) {
|
||||
auto* rfh = content::RenderFrameHost::FromID(rfh_id);
|
||||
return rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
|
||||
// pointer checks to avoid crashing.
|
||||
- content::BrowserContext* context =
|
||||
- web_contents ? web_contents->GetBrowserContext() : nullptr;
|
||||
- return context ? Profile::FromBrowserContext(context)->GetPrefs() : nullptr;
|
||||
+ return nullptr;
|
||||
}
|
||||
+#endif
|
||||
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
@@ -150,10 +152,8 @@ void PrintJob::Initialize(std::unique_ptr<PrinterQuery> query,
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
pdf_page_mapping_ = PageNumber::GetPages(settings->ranges(), page_count);
|
||||
- PrefService* prefs = GetPrefsForWebContents(GetWebContents(rfh_id_));
|
||||
- if (prefs && prefs->IsManagedPreference(prefs::kPdfUseSkiaRendererEnabled)) {
|
||||
- use_skia_ = prefs->GetBoolean(prefs::kPdfUseSkiaRendererEnabled);
|
||||
- }
|
||||
+ // TODO(codebytere): should we enable this later?
|
||||
+ use_skia_ = false;
|
||||
#endif
|
||||
|
||||
auto new_doc = base::MakeRefCounted<PrintedDocument>(std::move(settings),
|
||||
@@ -404,8 +404,10 @@ void PrintJob::StartPdfToEmfConversion(
|
||||
|
||||
const PrintSettings& settings = document()->settings();
|
||||
|
||||
+#if 0
|
||||
PrefService* prefs = GetPrefsForWebContents(GetWebContents(rfh_id_));
|
||||
- bool print_with_reduced_rasterization = PrintWithReducedRasterization(prefs);
|
||||
+#endif
|
||||
+ bool print_with_reduced_rasterization = PrintWithReducedRasterization(nullptr);
|
||||
|
||||
using RenderMode = PdfRenderSettings::Mode;
|
||||
RenderMode mode = print_with_reduced_rasterization
|
||||
@@ -497,8 +499,10 @@ void PrintJob::StartPdfToPostScriptConversion(
|
||||
if (ps_level2) {
|
||||
mode = PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2;
|
||||
} else {
|
||||
+#if 0
|
||||
PrefService* prefs = GetPrefsForWebContents(GetWebContents(rfh_id_));
|
||||
- mode = PrintWithPostScriptType42Fonts(prefs)
|
||||
+#endif
|
||||
+ mode = PrintWithPostScriptType42Fonts(nullptr)
|
||||
? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3_WITH_TYPE42_FONTS
|
||||
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3;
|
||||
}
|
||||
content::WebContents* GetWebContents(content::GlobalRenderFrameHostId rfh_id) {
|
||||
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
|
||||
index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09b1e78450 100644
|
||||
index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..168b7b773996a621fbbebea08933317b895705a2 100644
|
||||
--- a/chrome/browser/printing/print_view_manager_base.cc
|
||||
+++ b/chrome/browser/printing/print_view_manager_base.cc
|
||||
@@ -23,7 +23,9 @@
|
||||
@@ -179,13 +137,12 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
}
|
||||
|
||||
PrintViewManagerBase::~PrintViewManagerBase() {
|
||||
@@ -202,12 +228,17 @@ void PrintViewManagerBase::DisableThirdPartyBlocking() {
|
||||
@@ -202,12 +228,16 @@ void PrintViewManagerBase::DisableThirdPartyBlocking() {
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
|
||||
|
||||
-bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh) {
|
||||
+bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh,
|
||||
+ bool silent,
|
||||
+ base::Value::Dict settings,
|
||||
+ CompletionCallback callback) {
|
||||
if (!StartPrintCommon(rfh)) {
|
||||
@@ -195,11 +152,11 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
- GetPrintRenderFrame(rfh)->PrintRequestedPages();
|
||||
+ callback_ = std::move(callback);
|
||||
+
|
||||
+ GetPrintRenderFrame(rfh)->PrintRequestedPages(silent, std::move(settings));
|
||||
+ GetPrintRenderFrame(rfh)->PrintRequestedPages(std::move(settings));
|
||||
|
||||
for (auto& observer : GetTestObservers()) {
|
||||
observer.OnPrintNow(rfh);
|
||||
@@ -336,12 +367,13 @@ void PrintViewManagerBase::OnDidUpdatePrintableArea(
|
||||
@@ -336,12 +366,13 @@ void PrintViewManagerBase::OnDidUpdatePrintableArea(
|
||||
}
|
||||
PRINTER_LOG(EVENT) << "Paper printable area updated for vendor id "
|
||||
<< print_settings->requested_media().vendor_id;
|
||||
@@ -214,7 +171,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
base::Value::Dict job_settings,
|
||||
std::unique_ptr<PrintSettings> print_settings,
|
||||
UpdatePrintSettingsCallback callback) {
|
||||
@@ -349,7 +381,8 @@ void PrintViewManagerBase::CompleteUpdatePrintSettings(
|
||||
@@ -349,7 +380,8 @@ void PrintViewManagerBase::CompleteUpdatePrintSettings(
|
||||
settings->pages = GetPageRangesFromJobSettings(job_settings);
|
||||
settings->params = mojom::PrintParams::New();
|
||||
RenderParamsFromPrintSettings(*print_settings, settings->params.get());
|
||||
@@ -224,7 +181,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
if (!PrintMsgPrintParamsIsValid(*settings->params)) {
|
||||
mojom::PrinterType printer_type = static_cast<mojom::PrinterType>(
|
||||
*job_settings.FindInt(kSettingPrinterType));
|
||||
@@ -361,6 +394,10 @@ void PrintViewManagerBase::CompleteUpdatePrintSettings(
|
||||
@@ -361,6 +393,10 @@ void PrintViewManagerBase::CompleteUpdatePrintSettings(
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,7 +192,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
set_cookie(settings->params->document_cookie);
|
||||
std::move(callback).Run(std::move(settings));
|
||||
}
|
||||
@@ -647,11 +684,12 @@ void PrintViewManagerBase::DidPrintDocument(
|
||||
@@ -647,11 +683,12 @@ void PrintViewManagerBase::DidPrintDocument(
|
||||
void PrintViewManagerBase::GetDefaultPrintSettings(
|
||||
GetDefaultPrintSettingsCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
@@ -249,7 +206,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
content::RenderFrameHost* render_frame_host = GetCurrentTargetFrame();
|
||||
if (base::FeatureList::IsEnabled(kCheckPrintRfhIsActive) &&
|
||||
!render_frame_host->IsActive()) {
|
||||
@@ -710,10 +748,12 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
@@ -710,10 +747,12 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
base::Value::Dict job_settings,
|
||||
UpdatePrintSettingsCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
@@ -262,7 +219,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
|
||||
std::optional<int> printer_type_value =
|
||||
job_settings.FindInt(kSettingPrinterType);
|
||||
@@ -724,6 +764,7 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
@@ -724,6 +763,7 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
|
||||
mojom::PrinterType printer_type =
|
||||
static_cast<mojom::PrinterType>(*printer_type_value);
|
||||
@@ -270,7 +227,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
if (printer_type != mojom::PrinterType::kExtension &&
|
||||
printer_type != mojom::PrinterType::kPdf &&
|
||||
printer_type != mojom::PrinterType::kLocal) {
|
||||
@@ -743,6 +784,7 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
@@ -743,6 +783,7 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
if (value > 0)
|
||||
job_settings.Set(kSettingRasterizePdfDpi, value);
|
||||
}
|
||||
@@ -278,7 +235,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
|
||||
std::unique_ptr<PrintSettings> print_settings =
|
||||
PrintSettingsFromJobSettings(job_settings);
|
||||
@@ -762,7 +804,21 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
@@ -762,7 +803,21 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +258,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
// TODO(crbug.com/1424368): Remove this if the printable areas can be made
|
||||
// fully available from `PrintBackend::GetPrinterSemanticCapsAndDefaults()`
|
||||
// for in-browser queries.
|
||||
@@ -784,8 +840,6 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
@@ -784,8 +839,6 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -310,7 +267,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::SetAccessibilityTree(
|
||||
@@ -801,7 +855,7 @@ void PrintViewManagerBase::SetAccessibilityTree(
|
||||
@@ -801,7 +854,7 @@ void PrintViewManagerBase::SetAccessibilityTree(
|
||||
void PrintViewManagerBase::IsPrintingEnabled(
|
||||
IsPrintingEnabledCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
@@ -319,7 +276,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
|
||||
@@ -852,6 +906,7 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
|
||||
@@ -852,6 +905,7 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
|
||||
|
||||
PrintManager::PrintingFailed(cookie, reason);
|
||||
|
||||
@@ -327,7 +284,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
// `PrintingFailed()` can occur because asynchronous compositing results
|
||||
// don't complete until after a print job has already failed and been
|
||||
// destroyed. In such cases the error notification to the user will
|
||||
@@ -861,7 +916,7 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
|
||||
@@ -861,7 +915,7 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
|
||||
print_job_->document()->cookie() == cookie) {
|
||||
ShowPrintErrorDialogForGenericError();
|
||||
}
|
||||
@@ -336,7 +293,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
ReleasePrinterQuery();
|
||||
}
|
||||
|
||||
@@ -873,15 +928,24 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
|
||||
@@ -873,15 +927,24 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
|
||||
test_observers_.RemoveObserver(&observer);
|
||||
}
|
||||
|
||||
@@ -361,7 +318,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::RenderFrameDeleted(
|
||||
@@ -933,7 +997,12 @@ void PrintViewManagerBase::OnJobDone() {
|
||||
@@ -933,7 +996,12 @@ void PrintViewManagerBase::OnJobDone() {
|
||||
// Printing is done, we don't need it anymore.
|
||||
// print_job_->is_job_pending() may still be true, depending on the order
|
||||
// of object registration.
|
||||
@@ -375,7 +332,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
ReleasePrintJob();
|
||||
}
|
||||
|
||||
@@ -942,9 +1011,10 @@ void PrintViewManagerBase::OnCanceling() {
|
||||
@@ -942,9 +1010,10 @@ void PrintViewManagerBase::OnCanceling() {
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnFailed() {
|
||||
@@ -387,7 +344,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
TerminatePrintJob(true);
|
||||
}
|
||||
|
||||
@@ -954,7 +1024,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
|
||||
@@ -954,7 +1023,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
|
||||
|
||||
// Is the document already complete?
|
||||
if (print_job_->document() && print_job_->document()->IsComplete()) {
|
||||
@@ -396,7 +353,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1007,7 +1077,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
|
||||
@@ -1007,7 +1076,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
|
||||
|
||||
// Disconnect the current `print_job_`.
|
||||
auto weak_this = weak_ptr_factory_.GetWeakPtr();
|
||||
@@ -408,7 +365,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
if (!weak_this)
|
||||
return false;
|
||||
|
||||
@@ -1027,7 +1100,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
|
||||
@@ -1027,7 +1099,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
|
||||
#endif
|
||||
print_job_->AddObserver(*this);
|
||||
|
||||
@@ -417,7 +374,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1095,6 +1168,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
|
||||
@@ -1095,6 +1167,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -429,7 +386,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
if (!print_job_)
|
||||
return;
|
||||
|
||||
@@ -1102,7 +1180,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
|
||||
@@ -1102,7 +1179,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
|
||||
// printing_rfh_ should only ever point to a RenderFrameHost with a live
|
||||
// RenderFrame.
|
||||
DCHECK(rfh->IsRenderFrameLive());
|
||||
@@ -438,7 +395,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
}
|
||||
|
||||
print_job_->RemoveObserver(*this);
|
||||
@@ -1144,7 +1222,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
|
||||
@@ -1144,7 +1221,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
|
||||
@@ -447,7 +404,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
return true;
|
||||
|
||||
if (!cookie) {
|
||||
@@ -1298,6 +1376,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
|
||||
@@ -1298,6 +1375,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
|
||||
auto callback_wrapper = base::BindOnce(
|
||||
&PrintViewManagerBase::ScriptedPrintReply, weak_ptr_factory_.GetWeakPtr(),
|
||||
std::move(callback), render_process_host->GetID());
|
||||
@@ -456,7 +413,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
|
||||
DisableThirdPartyBlocking();
|
||||
#endif
|
||||
@@ -1312,7 +1392,7 @@ void PrintViewManagerBase::CompleteScriptedPrint(
|
||||
@@ -1312,7 +1391,7 @@ void PrintViewManagerBase::CompleteScriptedPrint(
|
||||
params->expected_pages_count, params->has_selection, params->margin_type,
|
||||
params->is_scripted, !render_process_host->IsPdf(),
|
||||
base::BindOnce(&OnDidScriptedPrint, queue_, std::move(printer_query),
|
||||
@@ -466,7 +423,7 @@ index 301a4a47fb4dfb6007c2214b04ffdff744cc3043..427a045c310109475b83a229637f4d09
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
|
||||
diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h
|
||||
index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..402aab3b14e6987bde0e22e6c82ac438d465330a 100644
|
||||
index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..0399e0bafa51d7b02d4dd05e6df8b48fc51d7457 100644
|
||||
--- a/chrome/browser/printing/print_view_manager_base.h
|
||||
+++ b/chrome/browser/printing/print_view_manager_base.h
|
||||
@@ -49,6 +49,8 @@ class PrinterQuery;
|
||||
@@ -478,19 +435,18 @@ index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..402aab3b14e6987bde0e22e6c82ac438
|
||||
// Base class for managing the print commands for a WebContents.
|
||||
class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
public:
|
||||
@@ -82,7 +84,10 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
@@ -82,7 +84,9 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
// Prints the current document immediately. Since the rendering is
|
||||
// asynchronous, the actual printing will not be completed on the return of
|
||||
// this function. Returns false if printing is impossible at the moment.
|
||||
- virtual bool PrintNow(content::RenderFrameHost* rfh);
|
||||
+ virtual bool PrintNow(content::RenderFrameHost* rfh,
|
||||
+ bool silent = true,
|
||||
+ base::Value::Dict settings = {},
|
||||
+ CompletionCallback callback = {});
|
||||
|
||||
// Like PrintNow(), but for the node under the context menu, instead of the
|
||||
// entire frame.
|
||||
@@ -136,8 +141,10 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
@@ -136,8 +140,10 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
void IsPrintingEnabled(IsPrintingEnabledCallback callback) override;
|
||||
void ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
|
||||
ScriptedPrintCallback callback) override;
|
||||
@@ -501,7 +457,7 @@ index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..402aab3b14e6987bde0e22e6c82ac438
|
||||
|
||||
// Adds and removes observers for `PrintViewManagerBase` events. The order in
|
||||
// which notifications are sent to observers is undefined. Observers must be
|
||||
@@ -145,6 +152,14 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
@@ -145,6 +151,14 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
void AddTestObserver(TestObserver& observer);
|
||||
void RemoveTestObserver(TestObserver& observer);
|
||||
|
||||
@@ -516,7 +472,7 @@ index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..402aab3b14e6987bde0e22e6c82ac438
|
||||
protected:
|
||||
explicit PrintViewManagerBase(content::WebContents* web_contents);
|
||||
|
||||
@@ -268,6 +283,7 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
@@ -268,6 +282,7 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
bool success);
|
||||
#endif
|
||||
void CompleteUpdatePrintSettings(
|
||||
@@ -524,7 +480,7 @@ index 24a2bc8b26462e6cafbcd5eb89e838b73266ec70..402aab3b14e6987bde0e22e6c82ac438
|
||||
base::Value::Dict job_settings,
|
||||
std::unique_ptr<PrintSettings> print_settings,
|
||||
UpdatePrintSettingsCallback callback);
|
||||
@@ -362,8 +378,11 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
@@ -362,8 +377,11 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
|
||||
// The current RFH that is printing with a system printing dialog.
|
||||
raw_ptr<content::RenderFrameHost> printing_rfh_ = nullptr;
|
||||
|
||||
@@ -565,32 +521,6 @@ index 418f53b0db3a2b2624298fca5628aacdd2a99c94..4a110fda692e16ec887cb90c6a2fa611
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
diff --git a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
index ac54519f611f719470c4b7a139a58f489884ede2..89055b6df74389560b6c5884a511897a36425b7b 100644
|
||||
--- a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
+++ b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
@@ -21,7 +21,7 @@ FakePrintRenderFrame::FakePrintRenderFrame(
|
||||
|
||||
FakePrintRenderFrame::~FakePrintRenderFrame() = default;
|
||||
|
||||
-void FakePrintRenderFrame::PrintRequestedPages() {}
|
||||
+void FakePrintRenderFrame::PrintRequestedPages(bool /*silent*/, ::base::Value::Dict /*settings*/) {}
|
||||
|
||||
void FakePrintRenderFrame::PrintWithParams(mojom::PrintPagesParamsPtr params,
|
||||
PrintWithParamsCallback callback) {
|
||||
diff --git a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
index 1e8f7bedaf679ee19788bf181b33e5d574d1f863..8be77b57580621bf659a6a2a63a2be94a2301c70 100644
|
||||
--- a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
+++ b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
@@ -25,7 +25,7 @@ class FakePrintRenderFrame : public mojom::PrintRenderFrame {
|
||||
|
||||
private:
|
||||
// printing::mojom::PrintRenderFrame:
|
||||
- void PrintRequestedPages() override;
|
||||
+ void PrintRequestedPages(bool silent, ::base::Value::Dict settings) override;
|
||||
void PrintWithParams(mojom::PrintPagesParamsPtr params,
|
||||
PrintWithParamsCallback callback) override;
|
||||
void PrintForSystemDialog() override;
|
||||
diff --git a/components/printing/browser/print_manager.cc b/components/printing/browser/print_manager.cc
|
||||
index 21c81377d32ae8d4185598a7eba88ed1d2063ef0..0767f4e9369e926b1cea99178c1a1975941f1765 100644
|
||||
--- a/components/printing/browser/print_manager.cc
|
||||
@@ -617,7 +547,7 @@ index ca71560874a0189068dd11fbc039f5673bf6bd96..a8551d95e64da2afbc1685b2df8f1fc3
|
||||
mojom::PrintFailureReason reason) override;
|
||||
|
||||
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
|
||||
index 4ecdb28904fac480cf102fffdff24ae008ac88cf..e8150c95fafd83d7e2fe1f472a35acec3ca6e391 100644
|
||||
index 4ecdb28904fac480cf102fffdff24ae008ac88cf..0d7d20c1a7920b1961585b345d30d9969411ca9e 100644
|
||||
--- a/components/printing/common/print.mojom
|
||||
+++ b/components/printing/common/print.mojom
|
||||
@@ -302,7 +302,7 @@ union PrintWithParamsResult {
|
||||
@@ -625,7 +555,7 @@ index 4ecdb28904fac480cf102fffdff24ae008ac88cf..e8150c95fafd83d7e2fe1f472a35acec
|
||||
// Tells the RenderFrame to switch the CSS to print media type, render every
|
||||
// requested page, and then switch back the CSS to display media type.
|
||||
- PrintRequestedPages();
|
||||
+ PrintRequestedPages(bool silent, mojo_base.mojom.DictionaryValue settings);
|
||||
+ PrintRequestedPages(mojo_base.mojom.DictionaryValue settings);
|
||||
|
||||
// Requests the frame to be printed with specified parameters. This is used
|
||||
// to programmatically produce PDF by request from the browser (e.g. over
|
||||
@@ -640,7 +570,7 @@ index 4ecdb28904fac480cf102fffdff24ae008ac88cf..e8150c95fafd83d7e2fe1f472a35acec
|
||||
PrintingFailed(int32 cookie, PrintFailureReason reason);
|
||||
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
|
||||
index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a95306c72e2d 100644
|
||||
index a0ec654b13f82d9c5ff4eb838585485a59c45924..01381687ee4e4c4096c0407ffc211c3e0bc57369 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.cc
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.cc
|
||||
@@ -47,6 +47,7 @@
|
||||
@@ -664,7 +594,7 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
|
||||
- Print(web_frame, blink::WebNode(), PrintRequestType::kScripted);
|
||||
+ Print(web_frame, blink::WebNode(), PrintRequestType::kScripted,
|
||||
+ false /* silent */, base::Value::Dict() /* new_settings */);
|
||||
+ base::Value::Dict() /* new_settings */);
|
||||
if (!weak_this) {
|
||||
return;
|
||||
}
|
||||
@@ -673,7 +603,7 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
}
|
||||
|
||||
-void PrintRenderFrameHelper::PrintRequestedPages() {
|
||||
+void PrintRenderFrameHelper::PrintRequestedPages(bool silent, base::Value::Dict settings) {
|
||||
+void PrintRenderFrameHelper::PrintRequestedPages(base::Value::Dict settings) {
|
||||
ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
|
||||
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
|
||||
return;
|
||||
@@ -682,21 +612,20 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
auto plugin = delegate_->GetPdfElement(frame);
|
||||
|
||||
- Print(frame, plugin, PrintRequestType::kRegular);
|
||||
+ Print(frame, plugin, PrintRequestType::kRegular, silent, std::move(settings));
|
||||
+ Print(frame, plugin, PrintRequestType::kRegular, std::move(settings));
|
||||
|
||||
if (render_frame_gone_) {
|
||||
return;
|
||||
@@ -1371,7 +1372,8 @@ void PrintRenderFrameHelper::PrintForSystemDialog() {
|
||||
@@ -1371,7 +1372,7 @@ void PrintRenderFrameHelper::PrintForSystemDialog() {
|
||||
}
|
||||
|
||||
Print(frame, print_preview_context_.source_node(),
|
||||
- PrintRequestType::kRegular);
|
||||
+ PrintRequestType::kRegular, false,
|
||||
+ base::Value::Dict());
|
||||
+ PrintRequestType::kRegular, base::Value::Dict());
|
||||
if (render_frame_gone_) {
|
||||
return;
|
||||
}
|
||||
@@ -1434,6 +1436,8 @@ void PrintRenderFrameHelper::PrintPreview(base::Value::Dict settings) {
|
||||
@@ -1434,6 +1435,8 @@ void PrintRenderFrameHelper::PrintPreview(base::Value::Dict settings) {
|
||||
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
|
||||
return;
|
||||
|
||||
@@ -705,28 +634,27 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
print_preview_context_.OnPrintPreview();
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
@@ -2011,7 +2015,8 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
@@ -2011,7 +2014,7 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
}
|
||||
|
||||
Print(duplicate_node.GetDocument().GetFrame(), duplicate_node,
|
||||
- PrintRequestType::kRegular);
|
||||
+ PrintRequestType::kRegular, false /* silent */,
|
||||
+ base::Value::Dict() /* new_settings */);
|
||||
+ PrintRequestType::kRegular, base::Value::Dict());
|
||||
// Check if `this` is still valid.
|
||||
if (!weak_this) {
|
||||
return;
|
||||
@@ -2027,17 +2032,19 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
@@ -2027,17 +2030,19 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
|
||||
void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
const blink::WebNode& node,
|
||||
- PrintRequestType print_request_type) {
|
||||
+ PrintRequestType print_request_type,
|
||||
+ bool silent,
|
||||
+ base::Value::Dict settings) {
|
||||
// If still not finished with earlier print request simply ignore.
|
||||
if (prep_frame_view_)
|
||||
return;
|
||||
|
||||
+ bool silent = settings.FindBool("silent").value_or(false);
|
||||
FrameReference frame_ref(frame);
|
||||
|
||||
- if (!InitPrintSettings(frame, node)) {
|
||||
@@ -738,7 +666,7 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
DidFinishPrinting(PrintingResult::kFailPrintInit);
|
||||
return;
|
||||
}
|
||||
@@ -2058,8 +2065,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
@@ -2058,8 +2063,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
print_pages_params_->params->print_scaling_option;
|
||||
|
||||
auto self = weak_ptr_factory_.GetWeakPtr();
|
||||
@@ -755,7 +683,7 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
// Check if `this` is still valid.
|
||||
if (!self)
|
||||
return;
|
||||
@@ -2317,25 +2331,33 @@ void PrintRenderFrameHelper::IPCProcessed() {
|
||||
@@ -2317,25 +2329,33 @@ void PrintRenderFrameHelper::IPCProcessed() {
|
||||
}
|
||||
|
||||
bool PrintRenderFrameHelper::InitPrintSettings(blink::WebLocalFrame* frame,
|
||||
@@ -796,7 +724,7 @@ index a0ec654b13f82d9c5ff4eb838585485a59c45924..e24f2e209e547685daf18570c0a8a953
|
||||
}
|
||||
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
|
||||
index b0ac94751454bd16b4e9bfdc071e2623813133ec..271bd9949a802a370b3619340f3364df14c7fe4a 100644
|
||||
index b0ac94751454bd16b4e9bfdc071e2623813133ec..74809543b8c1030494638a58c6d27ee69e2867b2 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.h
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.h
|
||||
@@ -245,7 +245,7 @@ class PrintRenderFrameHelper
|
||||
@@ -804,22 +732,21 @@ index b0ac94751454bd16b4e9bfdc071e2623813133ec..271bd9949a802a370b3619340f3364df
|
||||
|
||||
// printing::mojom::PrintRenderFrame:
|
||||
- void PrintRequestedPages() override;
|
||||
+ void PrintRequestedPages(bool silent, base::Value::Dict settings) override;
|
||||
+ void PrintRequestedPages(base::Value::Dict settings) override;
|
||||
void PrintWithParams(mojom::PrintPagesParamsPtr params,
|
||||
PrintWithParamsCallback callback) override;
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
@@ -312,7 +312,9 @@ class PrintRenderFrameHelper
|
||||
@@ -312,7 +312,8 @@ class PrintRenderFrameHelper
|
||||
// WARNING: |this| may be gone after this method returns.
|
||||
void Print(blink::WebLocalFrame* frame,
|
||||
const blink::WebNode& node,
|
||||
- PrintRequestType print_request_type);
|
||||
+ PrintRequestType print_request_type,
|
||||
+ bool silent,
|
||||
+ base::Value::Dict settings);
|
||||
|
||||
// Notification when printing is done - signal tear-down/free resources.
|
||||
void DidFinishPrinting(PrintingResult result);
|
||||
@@ -322,7 +324,8 @@ class PrintRenderFrameHelper
|
||||
@@ -322,7 +323,8 @@ class PrintRenderFrameHelper
|
||||
// Initialize print page settings with default settings.
|
||||
// Used only for native printing workflow.
|
||||
bool InitPrintSettings(blink::WebLocalFrame* frame,
|
||||
|
||||
@@ -33,7 +33,6 @@ fix_assert_module_in_the_renderer_process.patch
|
||||
fix_add_trusted_space_and_trusted_lo_space_to_the_v8_heap.patch
|
||||
win_process_avoid_assert_after_spawning_store_app_4152.patch
|
||||
chore_remove_use_of_deprecated_kmaxlength.patch
|
||||
fix_missing_include_for_node_extern.patch
|
||||
feat_optionally_prevent_calling_v8_enablewebassemblytraphandler.patch
|
||||
build_only_create_cppgc_heap_on_non-32_bit_platforms.patch
|
||||
fix_-wshadow_error_in_uvwasi_c.patch
|
||||
@@ -47,3 +46,4 @@ build_ensure_v8_pointer_compression_sandbox_is_enabled_on_64bit.patch
|
||||
fix_revert_src_lb_reducing_c_calls_of_esm_legacy_main_resolve.patch
|
||||
src_preload_function_for_environment.patch
|
||||
deprecate_vector_v8_local_in_v8.patch
|
||||
fs_fix_wtf-8_decoding_issue.patch
|
||||
|
||||
@@ -87,10 +87,18 @@ index 895ff3a5948add3513700ecc2f32fce4c2fbe4eb..3182a5e4aad2ba0be2b6769edb696b81
|
||||
|
||||
MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
|
||||
diff --git a/src/module_wrap.h b/src/module_wrap.h
|
||||
index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f96356322 100644
|
||||
index e17048357feca2419087621ed280de30882a90bc..63682be31ce00a3bf7b9be909cac4b7f9ec02a8e 100644
|
||||
--- a/src/module_wrap.h
|
||||
+++ b/src/module_wrap.h
|
||||
@@ -31,7 +31,14 @@ enum HostDefinedOptions : int {
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "base_object.h"
|
||||
+#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
@@ -31,7 +32,14 @@ enum HostDefinedOptions : int {
|
||||
kLength = 9,
|
||||
};
|
||||
|
||||
@@ -106,7 +114,7 @@ index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f
|
||||
public:
|
||||
enum InternalFields {
|
||||
kModuleSlot = BaseObject::kInternalFieldCount,
|
||||
@@ -68,6 +75,8 @@ class ModuleWrap : public BaseObject {
|
||||
@@ -68,6 +76,8 @@ class ModuleWrap : public BaseObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f
|
||||
private:
|
||||
ModuleWrap(Realm* realm,
|
||||
v8::Local<v8::Object> object,
|
||||
@@ -102,7 +111,6 @@ class ModuleWrap : public BaseObject {
|
||||
@@ -102,7 +112,6 @@ class ModuleWrap : public BaseObject {
|
||||
v8::Local<v8::String> specifier,
|
||||
v8::Local<v8::FixedArray> import_attributes,
|
||||
v8::Local<v8::Module> referrer);
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Wed, 15 Nov 2023 12:25:39 +0100
|
||||
Subject: fix: missing include for NODE_EXTERN
|
||||
|
||||
At some point it seems that node.h was removed from the include chain,
|
||||
causing the following error:
|
||||
|
||||
../../third_party/electron_node/src/module_wrap.h:33:1: error: unknown type name 'NODE_EXTERN'
|
||||
33 | NODE_EXTERN v8::MaybeLocal<v8::Promise> ImportModuleDynamically(
|
||||
| ^
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/src/module_wrap.h b/src/module_wrap.h
|
||||
index 061117dc3182d63e35d7e99ffd95801f96356322..63682be31ce00a3bf7b9be909cac4b7f9ec02a8e 100644
|
||||
--- a/src/module_wrap.h
|
||||
+++ b/src/module_wrap.h
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "base_object.h"
|
||||
+#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
30
patches/node/fs_fix_wtf-8_decoding_issue.patch
Normal file
30
patches/node/fs_fix_wtf-8_decoding_issue.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Richard Lau <rlau@redhat.com>
|
||||
Date: Fri, 1 Mar 2024 19:15:40 +0000
|
||||
Subject: fs: fix WTF-8 decoding issue
|
||||
|
||||
Cherry-pick of libuv/libuv@d09441c
|
||||
|
||||
Refs: https://github.com/libuv/libuv/pull/2970
|
||||
Fixes: https://github.com/nodejs/node/issues/48673
|
||||
|
||||
We forgot to mask off the high bits from the first byte, so we ended up
|
||||
always failing the subsequent range check.
|
||||
|
||||
diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c
|
||||
index fc209c54f470edaa031009979061cff071cbf66d..4fc13b04bdae5bd9e2627027a10c534c2d9675dc 100644
|
||||
--- a/deps/uv/src/win/fs.c
|
||||
+++ b/deps/uv/src/win/fs.c
|
||||
@@ -176,9 +176,11 @@ static int32_t fs__decode_wtf8_char(const char** input) {
|
||||
if ((b4 & 0xC0) != 0x80)
|
||||
return -1; /* invalid: not a continuation byte */
|
||||
code_point = (code_point << 6) | (b4 & 0x3F);
|
||||
- if (b1 <= 0xF4)
|
||||
+ if (b1 <= 0xF4) {
|
||||
+ code_point &= 0x1FFFFF;
|
||||
if (code_point <= 0x10FFFF)
|
||||
return code_point; /* four-byte character */
|
||||
+ }
|
||||
|
||||
/* code point too large */
|
||||
return -1;
|
||||
@@ -3,9 +3,18 @@ if (!process.env.CI) require('dotenv-safe').load();
|
||||
const assert = require('node:assert');
|
||||
const got = require('got');
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.ELECTRON_GITHUB_TOKEN
|
||||
});
|
||||
|
||||
const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds';
|
||||
const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline';
|
||||
const GH_ACTIONS_PIPELINE_URL = 'https://github.com/electron/electron/actions';
|
||||
const GH_ACTIONS_API_URL = '/repos/electron/electron/actions';
|
||||
|
||||
const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000;
|
||||
const GH_ACTIONS_WAIT_TIME = process.env.GH_ACTIONS_WAIT_TIME || 30000;
|
||||
|
||||
const appVeyorJobs = {
|
||||
'electron-x64': 'electron-x64-release',
|
||||
@@ -23,6 +32,14 @@ const circleCIPublishIndividualArches = {
|
||||
'linux-publish': ['arm', 'arm64', 'x64']
|
||||
};
|
||||
|
||||
const ghActionsPublishWorkflows = [
|
||||
'macos-publish'
|
||||
];
|
||||
|
||||
const ghActionsPublishIndividualArches = {
|
||||
'macos-publish': ['osx-x64', 'mas-x64', 'osx-arm64', 'mas-arm64']
|
||||
};
|
||||
|
||||
let jobRequestedCount = 0;
|
||||
|
||||
async function makeRequest ({ auth, username, password, url, headers, body, method }) {
|
||||
@@ -53,6 +70,65 @@ async function makeRequest ({ auth, username, password, url, headers, body, meth
|
||||
return JSON.parse(response.body);
|
||||
}
|
||||
|
||||
async function githubActionsCall (targetBranch, workflowName, options) {
|
||||
console.log(`Triggering GitHub Actions to run build job: ${workflowName} on branch: ${targetBranch} with release flag.`);
|
||||
const buildRequest = {
|
||||
branch: targetBranch,
|
||||
parameters: {}
|
||||
};
|
||||
if (options.ghRelease) {
|
||||
buildRequest.parameters['upload-to-storage'] = '0';
|
||||
} else {
|
||||
buildRequest.parameters['upload-to-storage'] = '1';
|
||||
}
|
||||
buildRequest.parameters[`run-${workflowName}`] = true;
|
||||
if (options.arch) {
|
||||
const validArches = ghActionsPublishIndividualArches[workflowName];
|
||||
assert(validArches.includes(options.arch), `Unknown GitHub Actions architecture "${options.arch}". Valid values are ${JSON.stringify(validArches)}`);
|
||||
buildRequest.parameters['macos-publish-arch-limit'] = options.arch;
|
||||
}
|
||||
|
||||
jobRequestedCount++;
|
||||
try {
|
||||
const commits = await octokit.repos.listCommits({
|
||||
owner: 'electron',
|
||||
repo: 'electron',
|
||||
sha: targetBranch,
|
||||
per_page: 5
|
||||
});
|
||||
if (!commits.data.length) {
|
||||
console.error('Could not fetch most recent commits for GitHub Actions, returning early');
|
||||
}
|
||||
|
||||
await octokit.request(`POST ${GH_ACTIONS_API_URL}/workflows/${workflowName}.yml/dispatches`, {
|
||||
ref: buildRequest.branch,
|
||||
inputs: {
|
||||
...buildRequest.parameters
|
||||
},
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
|
||||
const runNumber = await getGitHubActionsRun(workflowName, commits.data[0].sha);
|
||||
if (runNumber === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`GitHub Actions release build pipeline ${runNumber} for ${workflowName} triggered.`);
|
||||
const runUrl = `${GH_ACTIONS_PIPELINE_URL}/runs/${runNumber}`;
|
||||
|
||||
if (options.runningPublishWorkflows) {
|
||||
console.log(`GitHub Actions release workflow request for ${workflowName} successful. Check ${runUrl} for status.`);
|
||||
} else {
|
||||
console.log(`GitHub Actions release build workflow running at ${GH_ACTIONS_PIPELINE_URL}/runs/${runNumber} for ${workflowName}.`);
|
||||
console.log(`GitHub Actions release build request for ${workflowName} successful. Check ${runUrl} for status.`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error calling GitHub Actions: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function circleCIcall (targetBranch, workflowName, options) {
|
||||
console.log(`Triggering CircleCI to run build job: ${workflowName} on branch: ${targetBranch} with release flag.`);
|
||||
const buildRequest = {
|
||||
@@ -167,6 +243,59 @@ async function getCircleCIJobNumber (workflowId) {
|
||||
return jobNumber;
|
||||
}
|
||||
|
||||
async function getGitHubActionsRun (workflowId, headCommit) {
|
||||
let runNumber = 0;
|
||||
let actionRun;
|
||||
while (runNumber === 0) {
|
||||
const actionsRuns = await octokit.request(`GET ${GH_ACTIONS_API_URL}/workflows/${workflowId}.yml/runs`, {
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
if (!actionsRuns.data.workflow_runs.length) {
|
||||
console.log(`No current workflow_runs found for ${workflowId}, response was: ${actionsRuns.data.workflow_runs}`);
|
||||
runNumber = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const run of actionsRuns.data.workflow_runs) {
|
||||
if (run.head_sha === headCommit) {
|
||||
console.log(`GitHub Actions run ${run.html_url} found for ${headCommit}, waiting on status.`);
|
||||
actionRun = run;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (actionRun) {
|
||||
switch (actionRun.status) {
|
||||
case 'in_progress':
|
||||
case 'pending':
|
||||
case 'queued':
|
||||
case 'requested':
|
||||
case 'waiting': {
|
||||
if (actionRun.id && !isNaN(actionRun.id)) {
|
||||
console.log(`GitHub Actions run ${actionRun.status} for ${actionRun.html_url}.`);
|
||||
runNumber = actionRun.id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'action_required':
|
||||
case 'cancelled':
|
||||
case 'failure':
|
||||
case 'skipped':
|
||||
case 'timed_out':
|
||||
case 'failed': {
|
||||
console.log(`Error workflow run returned a status of ${actionRun.status} for ${actionRun.html_url}`);
|
||||
runNumber = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, GH_ACTIONS_WAIT_TIME));
|
||||
}
|
||||
}
|
||||
return runNumber;
|
||||
}
|
||||
|
||||
async function circleCIRequest (url, method, requestBody) {
|
||||
const requestOpts = {
|
||||
username: process.env.CIRCLE_TOKEN,
|
||||
@@ -194,18 +323,6 @@ async function circleCIRequest (url, method, requestBody) {
|
||||
});
|
||||
}
|
||||
|
||||
function buildAppVeyor (targetBranch, options) {
|
||||
const validJobs = Object.keys(appVeyorJobs);
|
||||
if (options.job) {
|
||||
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`);
|
||||
callAppVeyor(targetBranch, options.job, options);
|
||||
} else {
|
||||
for (const job of validJobs) {
|
||||
callAppVeyor(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function callAppVeyor (targetBranch, job, options) {
|
||||
console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`);
|
||||
const environmentVariables = {
|
||||
@@ -252,6 +369,18 @@ async function callAppVeyor (targetBranch, job, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildAppVeyor (targetBranch, options) {
|
||||
const validJobs = Object.keys(appVeyorJobs);
|
||||
if (options.job) {
|
||||
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`);
|
||||
callAppVeyor(targetBranch, options.job, options);
|
||||
} else {
|
||||
for (const job of validJobs) {
|
||||
callAppVeyor(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildCircleCI (targetBranch, options) {
|
||||
if (options.job) {
|
||||
assert(circleCIPublishWorkflows.includes(options.job), `Unknown CircleCI workflow name: ${options.job}. Valid values are: ${circleCIPublishWorkflows}.`);
|
||||
@@ -265,6 +394,19 @@ function buildCircleCI (targetBranch, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildGHActions (targetBranch, options) {
|
||||
if (options.job) {
|
||||
assert(ghActionsPublishWorkflows.includes(options.job), `Unknown GitHub Actions workflow name: ${options.job}. Valid values are: ${ghActionsPublishWorkflows}.`);
|
||||
githubActionsCall(targetBranch, options.job, options);
|
||||
} else {
|
||||
assert(!options.arch, 'Cannot provide a single architecture while building all workflows, please specify a single workflow via --workflow');
|
||||
options.runningPublishWorkflows = true;
|
||||
for (const job of ghActionsPublishWorkflows) {
|
||||
githubActionsCall(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runRelease (targetBranch, options) {
|
||||
if (options.ci) {
|
||||
switch (options.ci) {
|
||||
@@ -272,6 +414,10 @@ function runRelease (targetBranch, options) {
|
||||
buildCircleCI(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
case 'GitHubActions': {
|
||||
buildGHActions(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
case 'AppVeyor': {
|
||||
buildAppVeyor(targetBranch, options);
|
||||
break;
|
||||
@@ -284,6 +430,8 @@ function runRelease (targetBranch, options) {
|
||||
} else {
|
||||
buildCircleCI(targetBranch, options);
|
||||
buildAppVeyor(targetBranch, options);
|
||||
// TODO(vertedinde): Enable GH Actions in defaults when ready
|
||||
// buildGHActions(targetBranch, options);
|
||||
}
|
||||
console.log(`${jobRequestedCount} jobs were requested.`);
|
||||
}
|
||||
@@ -297,7 +445,7 @@ if (require.main === module) {
|
||||
const targetBranch = args._[0];
|
||||
if (args._.length < 1) {
|
||||
console.log(`Trigger CI to build release builds of electron.
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor]
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor|GitHubActions]
|
||||
[--ghRelease] [--circleBuildNum=xxx] [--appveyorJobId=xxx] [--commit=sha] TARGET_BRANCH
|
||||
`);
|
||||
process.exit(0);
|
||||
|
||||
@@ -77,6 +77,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
WebContentsView::Create(isolate, web_preferences);
|
||||
DCHECK(web_contents_view.get());
|
||||
window_->AddDraggableRegionProvider(web_contents_view.get());
|
||||
web_contents_view_.Reset(isolate, web_contents_view.ToV8());
|
||||
|
||||
// Save a reference of the WebContents.
|
||||
gin::Handle<WebContents> web_contents =
|
||||
@@ -92,7 +93,14 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
InitWithArgs(args);
|
||||
|
||||
// Install the content view after BaseWindow's JS code is initialized.
|
||||
SetContentView(gin::CreateHandle<View>(isolate, web_contents_view.get()));
|
||||
// The WebContentsView is added a sibling of BaseWindow's contentView (before
|
||||
// it in the paint order) so that any views added to BrowserWindow's
|
||||
// contentView will be painted on top of the BrowserWindow's WebContentsView.
|
||||
// See https://github.com/electron/electron/pull/41256.
|
||||
// Note that |GetContentsView|, confusingly, does not refer to the same thing
|
||||
// as |BaseWindow::GetContentView|.
|
||||
window()->GetContentsView()->AddChildViewAt(web_contents_view->view(), 0);
|
||||
window()->GetContentsView()->DeprecatedLayoutImmediately();
|
||||
|
||||
// Init window after everything has been setup.
|
||||
window()->InitFromOptions(options);
|
||||
|
||||
@@ -95,6 +95,7 @@ class BrowserWindow : public BaseWindow,
|
||||
base::CancelableRepeatingClosure window_unresponsive_closure_;
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
v8::Global<v8::Value> web_contents_view_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
base::WeakPtrFactory<BrowserWindow> weak_factory_{this};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
@@ -33,12 +34,14 @@
|
||||
#include "content/browser/code_cache/generated_code_cache_context.h" // nogncheck
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/browsing_data_filter_builder.h"
|
||||
#include "content/public/browser/browsing_data_remover.h"
|
||||
#include "content/public/browser/download_item_utils.h"
|
||||
#include "content/public/browser/download_manager_delegate.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "gin/converter.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
||||
#include "net/base/completion_repeating_callback.h"
|
||||
@@ -86,6 +89,7 @@
|
||||
#include "third_party/blink/public/common/storage_key/storage_key.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
@@ -106,6 +110,8 @@
|
||||
#endif
|
||||
|
||||
using content::BrowserThread;
|
||||
using content::BrowsingDataFilterBuilder;
|
||||
using content::BrowsingDataRemover;
|
||||
using content::StoragePartition;
|
||||
|
||||
namespace {
|
||||
@@ -117,25 +123,23 @@ struct ClearStorageDataOptions {
|
||||
};
|
||||
|
||||
uint32_t GetStorageMask(const std::vector<std::string>& storage_types) {
|
||||
static constexpr auto Lookup =
|
||||
base::MakeFixedFlatMap<std::string_view, uint32_t>(
|
||||
{{"cookies", StoragePartition::REMOVE_DATA_MASK_COOKIES},
|
||||
{"filesystem", StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS},
|
||||
{"indexdb", StoragePartition::REMOVE_DATA_MASK_INDEXEDDB},
|
||||
{"localstorage", StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE},
|
||||
{"shadercache", StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE},
|
||||
{"websql", StoragePartition::REMOVE_DATA_MASK_WEBSQL},
|
||||
{"serviceworkers",
|
||||
StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS},
|
||||
{"cachestorage", StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE}});
|
||||
|
||||
uint32_t storage_mask = 0;
|
||||
for (const auto& it : storage_types) {
|
||||
auto type = base::ToLowerASCII(it);
|
||||
if (type == "cookies")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
|
||||
else if (type == "filesystem")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
|
||||
else if (type == "indexdb")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
|
||||
else if (type == "localstorage")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
|
||||
else if (type == "shadercache")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
|
||||
else if (type == "websql")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
|
||||
else if (type == "serviceworkers")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
|
||||
else if (type == "cachestorage")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
|
||||
if (Lookup.contains(type))
|
||||
storage_mask |= Lookup.at(type);
|
||||
}
|
||||
return storage_mask;
|
||||
}
|
||||
@@ -152,38 +156,202 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
|
||||
return quota_mask;
|
||||
}
|
||||
|
||||
constexpr content::BrowsingDataRemover::DataType kClearDataTypeAll = ~0ULL;
|
||||
constexpr content::BrowsingDataRemover::OriginType kClearOriginTypeAll =
|
||||
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
||||
content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
||||
constexpr BrowsingDataRemover::DataType kClearDataTypeAll = ~0ULL;
|
||||
constexpr BrowsingDataRemover::OriginType kClearOriginTypeAll =
|
||||
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
||||
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
||||
|
||||
// Observes the BrowsingDataRemover that backs the `clearData` method and
|
||||
// fulfills that API's promise once it's done. This type manages its own
|
||||
// lifetime, deleting itself once it's done.
|
||||
class ClearDataObserver : public content::BrowsingDataRemover::Observer {
|
||||
public:
|
||||
ClearDataObserver(gin_helper::Promise<void> promise,
|
||||
content::BrowsingDataRemover* remover)
|
||||
: promise_(std::move(promise)) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
constexpr auto kDataTypeLookup =
|
||||
base::MakeFixedFlatMap<std::string_view, BrowsingDataRemover::DataType>({
|
||||
{"backgroundFetch", BrowsingDataRemover::DATA_TYPE_BACKGROUND_FETCH},
|
||||
{"cache", BrowsingDataRemover::DATA_TYPE_CACHE |
|
||||
BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE},
|
||||
{"cookies", BrowsingDataRemover::DATA_TYPE_COOKIES},
|
||||
{"downloads", BrowsingDataRemover::DATA_TYPE_DOWNLOADS},
|
||||
{"fileSystems", BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS},
|
||||
{"indexedDB", BrowsingDataRemover::DATA_TYPE_INDEXED_DB},
|
||||
{"localStorage", BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE},
|
||||
{"serviceWorkers", BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS},
|
||||
{"webSQL", BrowsingDataRemover::DATA_TYPE_WEB_SQL},
|
||||
});
|
||||
|
||||
void OnBrowsingDataRemoverDone(
|
||||
content::BrowsingDataRemover::DataType failed_data_types) override {
|
||||
if (failed_data_types == 0ULL) {
|
||||
promise_.Resolve();
|
||||
} else {
|
||||
promise_.RejectWithErrorMessage(base::StringPrintf(
|
||||
"Failed to clear browsing data (%" PRIu64 ")", failed_data_types));
|
||||
BrowsingDataRemover::DataType GetDataTypeMask(
|
||||
const std::vector<std::string>& data_types) {
|
||||
BrowsingDataRemover::DataType mask = 0u;
|
||||
for (const auto& type : data_types) {
|
||||
if (kDataTypeLookup.contains(type)) {
|
||||
mask |= kDataTypeLookup.at(type);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetDataTypesFromMask(
|
||||
BrowsingDataRemover::DataType mask) {
|
||||
std::vector<std::string> results;
|
||||
for (const auto [type, flag] : kDataTypeLookup) {
|
||||
if (mask & flag) {
|
||||
results.emplace_back(type);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// Represents a task to clear browsing data for the `clearData` API method.
|
||||
//
|
||||
// This type manages its own lifetime, deleting itself once the task finishes
|
||||
// completely.
|
||||
class ClearDataTask {
|
||||
public:
|
||||
// Starts running a task. This function will return before the task is
|
||||
// finished, but will resolve or reject the |promise| when it finishes.
|
||||
static void Run(
|
||||
BrowsingDataRemover* remover,
|
||||
gin_helper::Promise<void> promise,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::vector<url::Origin> origins,
|
||||
BrowsingDataFilterBuilder::Mode filter_mode,
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode) {
|
||||
std::shared_ptr<ClearDataTask> task(new ClearDataTask(std::move(promise)));
|
||||
|
||||
// This method counts as an operation. This is important so we can call
|
||||
// `OnOperationFinished` at the end of this method as a fallback if all the
|
||||
// other operations finished while this method was still executing
|
||||
task->operations_running_ = 1;
|
||||
|
||||
// Cookies are scoped more broadly than other types of data, so if we are
|
||||
// filtering then we need to do it at the registrable domain level
|
||||
if (!origins.empty() &&
|
||||
data_type_mask & BrowsingDataRemover::DATA_TYPE_COOKIES) {
|
||||
data_type_mask &= ~BrowsingDataRemover::DATA_TYPE_COOKIES;
|
||||
|
||||
auto cookies_filter_builder =
|
||||
BrowsingDataFilterBuilder::Create(filter_mode);
|
||||
|
||||
for (const url::Origin& origin : origins) {
|
||||
std::string domain = GetDomainAndRegistry(
|
||||
origin,
|
||||
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
|
||||
if (domain.empty()) {
|
||||
domain = origin.host();
|
||||
}
|
||||
cookies_filter_builder->AddRegisterableDomain(domain);
|
||||
}
|
||||
|
||||
StartOperation(task, remover, BrowsingDataRemover::DATA_TYPE_COOKIES,
|
||||
std::move(cookies_filter_builder));
|
||||
}
|
||||
|
||||
// If cookies aren't the only data type and weren't handled above, then we
|
||||
// can start an operation that is scoped to origins
|
||||
if (data_type_mask) {
|
||||
auto filter_builder =
|
||||
BrowsingDataFilterBuilder::Create(filter_mode, origin_matching_mode);
|
||||
|
||||
for (auto const& origin : origins) {
|
||||
filter_builder->AddOrigin(origin);
|
||||
}
|
||||
|
||||
StartOperation(task, remover, data_type_mask, std::move(filter_builder));
|
||||
}
|
||||
|
||||
// This static method counts as an operation.
|
||||
task->OnOperationFinished(std::nullopt);
|
||||
}
|
||||
|
||||
private:
|
||||
// An individiual |content::BrowsingDataRemover::Remove...| operation as part
|
||||
// of a full |ClearDataTask|. This class manages its own lifetime, cleaning
|
||||
// itself up after the operation completes and notifies the task of the
|
||||
// result.
|
||||
class ClearDataOperation : public BrowsingDataRemover::Observer {
|
||||
public:
|
||||
static void Run(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
auto* operation = new ClearDataOperation(task, remover);
|
||||
|
||||
remover->RemoveWithFilterAndReply(base::Time::Min(), base::Time::Max(),
|
||||
data_type_mask, kClearOriginTypeAll,
|
||||
std::move(filter_builder), operation);
|
||||
}
|
||||
|
||||
// BrowsingDataRemover::Observer:
|
||||
void OnBrowsingDataRemoverDone(
|
||||
BrowsingDataRemover::DataType failed_data_types) override {
|
||||
task_->OnOperationFinished(failed_data_types);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
ClearDataOperation(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover)
|
||||
: task_(task) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
|
||||
std::shared_ptr<ClearDataTask> task_;
|
||||
base::ScopedObservation<BrowsingDataRemover, BrowsingDataRemover::Observer>
|
||||
observation_{this};
|
||||
};
|
||||
|
||||
explicit ClearDataTask(gin_helper::Promise<void> promise)
|
||||
: promise_(std::move(promise)) {}
|
||||
|
||||
static void StartOperation(
|
||||
std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
// Track this operation
|
||||
task->operations_running_ += 1;
|
||||
|
||||
ClearDataOperation::Run(task, remover, data_type_mask,
|
||||
std::move(filter_builder));
|
||||
}
|
||||
|
||||
void OnOperationFinished(
|
||||
std::optional<BrowsingDataRemover::DataType> failed_data_types) {
|
||||
DCHECK_GT(operations_running_, 0);
|
||||
operations_running_ -= 1;
|
||||
|
||||
if (failed_data_types.has_value()) {
|
||||
failed_data_types_ |= failed_data_types.value();
|
||||
}
|
||||
|
||||
// If this is the last operation, then the task is finished
|
||||
if (operations_running_ == 0) {
|
||||
OnTaskFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void OnTaskFinished() {
|
||||
if (failed_data_types_ == 0ULL) {
|
||||
promise_.Resolve();
|
||||
} else {
|
||||
v8::Isolate* isolate = promise_.isolate();
|
||||
|
||||
v8::Local<v8::Value> failed_data_types_array =
|
||||
gin::ConvertToV8(isolate, GetDataTypesFromMask(failed_data_types_));
|
||||
|
||||
// Create a rich error object with extra detail about what data types
|
||||
// failed
|
||||
auto error = v8::Exception::Error(
|
||||
gin::StringToV8(isolate, "Failed to clear data"));
|
||||
error.As<v8::Object>()
|
||||
->Set(promise_.GetContext(),
|
||||
gin::StringToV8(isolate, "failedDataTypes"),
|
||||
failed_data_types_array)
|
||||
.Check();
|
||||
|
||||
promise_.Reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
int operations_running_ = 0;
|
||||
BrowsingDataRemover::DataType failed_data_types_ = 0ULL;
|
||||
gin_helper::Promise<void> promise_;
|
||||
base::ScopedObservation<content::BrowsingDataRemover,
|
||||
content::BrowsingDataRemover::Observer>
|
||||
observation_{this};
|
||||
};
|
||||
|
||||
base::Value::Dict createProxyConfig(ProxyPrefs::ProxyMode proxy_mode,
|
||||
@@ -1138,17 +1306,85 @@ v8::Local<v8::Promise> Session::ClearCodeCaches(
|
||||
return handle;
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Session::ClearData(gin::Arguments* args) {
|
||||
v8::Local<v8::Value> Session::ClearData(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args) {
|
||||
auto* isolate = JavascriptEnvironment::GetIsolate();
|
||||
|
||||
BrowsingDataRemover::DataType data_type_mask = kClearDataTypeAll;
|
||||
std::vector<url::Origin> origins;
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode =
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode::kThirdPartiesIncluded;
|
||||
BrowsingDataFilterBuilder::Mode filter_mode =
|
||||
BrowsingDataFilterBuilder::Mode::kPreserve;
|
||||
|
||||
if (gin_helper::Dictionary options; args->GetNext(&options)) {
|
||||
if (std::vector<std::string> data_types;
|
||||
options.Get("dataTypes", &data_types)) {
|
||||
data_type_mask = GetDataTypeMask(data_types);
|
||||
}
|
||||
|
||||
if (bool avoid_closing_connections;
|
||||
options.Get("avoidClosingConnections", &avoid_closing_connections) &&
|
||||
avoid_closing_connections) {
|
||||
data_type_mask |=
|
||||
BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
|
||||
}
|
||||
|
||||
std::vector<GURL> origin_urls;
|
||||
{
|
||||
bool has_origins_key = options.Get("origins", &origin_urls);
|
||||
std::vector<GURL> exclude_origin_urls;
|
||||
bool has_exclude_origins_key =
|
||||
options.Get("excludeOrigins", &exclude_origin_urls);
|
||||
|
||||
if (has_origins_key && has_exclude_origins_key) {
|
||||
thrower.ThrowError(
|
||||
"Cannot provide both 'origins' and 'excludeOrigins'");
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
if (has_origins_key) {
|
||||
filter_mode = BrowsingDataFilterBuilder::Mode::kDelete;
|
||||
} else if (has_exclude_origins_key) {
|
||||
origin_urls = std::move(exclude_origin_urls);
|
||||
}
|
||||
}
|
||||
|
||||
if (!origin_urls.empty()) {
|
||||
origins.reserve(origin_urls.size());
|
||||
for (const GURL& origin_url : origin_urls) {
|
||||
auto origin = url::Origin::Create(origin_url);
|
||||
|
||||
// Opaque origins cannot be used with this API
|
||||
if (origin.opaque()) {
|
||||
thrower.ThrowError(
|
||||
base::StringPrintf("Invalid origin: '%s'",
|
||||
origin_url.possibly_invalid_spec().c_str()));
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
origins.push_back(std::move(origin));
|
||||
}
|
||||
}
|
||||
|
||||
if (std::string origin_matching_mode_string;
|
||||
options.Get("originMatchingMode", &origin_matching_mode_string)) {
|
||||
if (origin_matching_mode_string == "third-parties-included") {
|
||||
origin_matching_mode = BrowsingDataFilterBuilder::OriginMatchingMode::
|
||||
kThirdPartiesIncluded;
|
||||
} else if (origin_matching_mode_string == "origin-in-all-contexts") {
|
||||
origin_matching_mode =
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode::kOriginInAllContexts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> promise_handle = promise.GetHandle();
|
||||
|
||||
content::BrowsingDataRemover* remover =
|
||||
browser_context_->GetBrowsingDataRemover();
|
||||
|
||||
auto* observer = new ClearDataObserver(std::move(promise), remover);
|
||||
remover->RemoveAndReply(base::Time::Min(), base::Time::Max(),
|
||||
kClearDataTypeAll, kClearOriginTypeAll, observer);
|
||||
BrowsingDataRemover* remover = browser_context_->GetBrowsingDataRemover();
|
||||
ClearDataTask::Run(remover, std::move(promise), data_type_mask,
|
||||
std::move(origins), filter_mode, origin_matching_mode);
|
||||
|
||||
return promise_handle;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ class Session : public gin::Wrappable<Session>,
|
||||
v8::Local<v8::Value> GetPath(v8::Isolate* isolate);
|
||||
void SetCodeCachePath(gin::Arguments* args);
|
||||
v8::Local<v8::Promise> ClearCodeCaches(const gin_helper::Dictionary& options);
|
||||
v8::Local<v8::Promise> ClearData(gin::Arguments* args);
|
||||
v8::Local<v8::Value> ClearData(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args);
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
base::Value GetSpellCheckerLanguages();
|
||||
void SetSpellCheckerLanguages(gin_helper::ErrorThrower thrower,
|
||||
|
||||
@@ -194,6 +194,22 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
||||
connector_->set_connection_error_handler(base::BindOnce(
|
||||
&UtilityProcessWrapper::CloseConnectorPort, weak_factory_.GetWeakPtr()));
|
||||
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
|
||||
network::mojom::URLLoaderFactoryParamsPtr loader_params =
|
||||
network::mojom::URLLoaderFactoryParams::New();
|
||||
loader_params->process_id = pid_;
|
||||
loader_params->is_orb_enabled = false;
|
||||
loader_params->is_trusted = true;
|
||||
network::mojom::NetworkContext* network_context =
|
||||
g_browser_process->system_network_context_manager()->GetContext();
|
||||
network_context->CreateURLLoaderFactory(
|
||||
url_loader_factory.InitWithNewPipeAndPassReceiver(),
|
||||
std::move(loader_params));
|
||||
params->url_loader_factory = std::move(url_loader_factory);
|
||||
mojo::PendingRemote<network::mojom::HostResolver> host_resolver;
|
||||
network_context->CreateHostResolver(
|
||||
{}, host_resolver.InitWithNewPipeAndPassReceiver());
|
||||
params->host_resolver = std::move(host_resolver);
|
||||
node_service_remote_->Initialize(std::move(params));
|
||||
}
|
||||
|
||||
@@ -234,30 +250,6 @@ void UtilityProcessWrapper::CloseConnectorPort() {
|
||||
}
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::EnsureNetworkServiceInitialized() {
|
||||
if (network_context_initialized_)
|
||||
return;
|
||||
|
||||
network_context_initialized_ = true;
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
|
||||
network::mojom::URLLoaderFactoryParamsPtr loader_params =
|
||||
network::mojom::URLLoaderFactoryParams::New();
|
||||
loader_params->process_id = pid_;
|
||||
loader_params->is_orb_enabled = false;
|
||||
loader_params->is_trusted = true;
|
||||
network::mojom::NetworkContext* network_context =
|
||||
g_browser_process->system_network_context_manager()->GetContext();
|
||||
network_context->CreateURLLoaderFactory(
|
||||
url_loader_factory.InitWithNewPipeAndPassReceiver(),
|
||||
std::move(loader_params));
|
||||
mojo::PendingRemote<network::mojom::HostResolver> host_resolver;
|
||||
network_context->CreateHostResolver(
|
||||
{}, host_resolver.InitWithNewPipeAndPassReceiver());
|
||||
|
||||
node_service_remote_->InitializeNetworkService(std::move(url_loader_factory),
|
||||
std::move(host_resolver));
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::Shutdown(int exit_code) {
|
||||
if (pid_ != base::kNullProcessId)
|
||||
GetAllUtilityProcessWrappers().Remove(pid_);
|
||||
@@ -439,9 +431,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
gin_helper::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("_fork", &electron::api::UtilityProcessWrapper::Create);
|
||||
dict.SetMethod(
|
||||
"_ensureNetworkServiceInitialized",
|
||||
&electron::api::UtilityProcessWrapper::EnsureNetworkServiceInitialized);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -48,7 +48,6 @@ class UtilityProcessWrapper
|
||||
static raw_ptr<UtilityProcessWrapper> FromProcessId(base::ProcessId pid);
|
||||
|
||||
void Shutdown(int exit_code);
|
||||
void EnsureNetworkServiceInitialized();
|
||||
|
||||
// gin::Wrappable
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
@@ -85,7 +84,6 @@ class UtilityProcessWrapper
|
||||
int stdout_read_fd_ = -1;
|
||||
int stderr_read_fd_ = -1;
|
||||
bool connector_closed_ = false;
|
||||
bool network_context_initialized_ = false;
|
||||
std::unique_ptr<mojo::Connector> connector_;
|
||||
blink::MessagePortDescriptor host_port_;
|
||||
mojo::Remote<node::mojom::NodeService> node_service_remote_;
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
#include "mojo/public/cpp/system/platform_handle.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
#include "shell/browser/api/electron_api_browser_window.h"
|
||||
@@ -173,10 +172,10 @@
|
||||
#include "components/printing/browser/print_manager_utils.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
|
||||
#include "printing/backend/print_backend.h" // nogncheck
|
||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||
#include "printing/page_range.h"
|
||||
#include "shell/browser/printing/print_view_manager_electron.h"
|
||||
#include "shell/browser/printing/printing_utils.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "printing/backend/win_helper.h"
|
||||
@@ -530,96 +529,6 @@ std::optional<base::TimeDelta> GetCursorBlinkInterval() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
// This will return false if no printer with the provided device_name can be
|
||||
// found on the network. We need to check this because Chromium does not do
|
||||
// sanity checking of device_name validity and so will crash on invalid names.
|
||||
bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
||||
base::SysUTF16ToCFStringRef(device_name));
|
||||
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
||||
bool printer_exists = new_printer != nullptr;
|
||||
PMRelease(new_printer);
|
||||
return printer_exists;
|
||||
#else
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function returns a validated device name.
|
||||
// If the user passed one to webContents.print(), we check that it's valid and
|
||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||
// pass a device name, we first try to return the system default printer. If one
|
||||
// isn't set, then pull all the printers and use the first one or fail if none
|
||||
// exist.
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
#endif
|
||||
|
||||
if (!device_name.empty()) {
|
||||
if (!IsDeviceNameValid(device_name))
|
||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||
return std::make_pair(std::string(), device_name);
|
||||
}
|
||||
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
std::string printer_name;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
|
||||
// We don't want to return if this fails since some devices won't have a
|
||||
// default printer.
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
if (printers.empty())
|
||||
return std::make_pair("No printers available on the network",
|
||||
std::u16string());
|
||||
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
||||
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
||||
#if !BUILDFLAG(IS_WIN)
|
||||
static constexpr base::TaskTraits kTraits = {
|
||||
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
||||
#endif
|
||||
|
||||
#if defined(USE_CUPS)
|
||||
// CUPS is thread safe.
|
||||
return base::ThreadPool::CreateTaskRunner(kTraits);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// Windows drivers are likely not thread-safe and need to be accessed on the
|
||||
// UI thread.
|
||||
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
||||
#else
|
||||
// Be conservative on unsupported platforms.
|
||||
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
struct UserDataLink : public base::SupportsUserData::Data {
|
||||
explicit UserDataLink(base::WeakPtr<WebContents> contents)
|
||||
: web_contents(contents) {}
|
||||
@@ -2952,7 +2861,6 @@ bool WebContents::IsCurrentlyAudible() {
|
||||
void WebContents::OnGetDeviceNameToUse(
|
||||
base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
bool silent,
|
||||
// <error, device_name>
|
||||
std::pair<std::string, std::u16string> info) {
|
||||
// The content::WebContents might be already deleted at this point, and the
|
||||
@@ -2972,6 +2880,12 @@ void WebContents::OnGetDeviceNameToUse(
|
||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||
print_settings.Set(printing::kSettingDeviceName, info.second);
|
||||
|
||||
if (!print_settings.FindInt(printing::kSettingDpiHorizontal)) {
|
||||
gfx::Size dpi = GetDefaultPrinterDPI(info.second);
|
||||
print_settings.Set(printing::kSettingDpiHorizontal, dpi.width());
|
||||
print_settings.Set(printing::kSettingDpiVertical, dpi.height());
|
||||
}
|
||||
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
if (!print_view_manager)
|
||||
@@ -2982,7 +2896,7 @@ void WebContents::OnGetDeviceNameToUse(
|
||||
? focused_frame
|
||||
: web_contents()->GetPrimaryMainFrame();
|
||||
|
||||
print_view_manager->PrintNow(rfh, silent, std::move(print_settings),
|
||||
print_view_manager->PrintNow(rfh, std::move(print_settings),
|
||||
std::move(print_callback));
|
||||
}
|
||||
|
||||
@@ -3003,9 +2917,10 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set optional silent printing
|
||||
// Set optional silent printing.
|
||||
bool silent = false;
|
||||
options.Get("silent", &silent);
|
||||
settings.Set("silent", silent);
|
||||
|
||||
bool print_background = false;
|
||||
options.Get("printBackground", &print_background);
|
||||
@@ -3133,7 +3048,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
|
||||
// Set custom dots per inch (dpi)
|
||||
gin_helper::Dictionary dpi_settings;
|
||||
int dpi = 72;
|
||||
if (options.Get("dpi", &dpi_settings)) {
|
||||
int horizontal = 72;
|
||||
dpi_settings.Get("horizontal", &horizontal);
|
||||
@@ -3141,16 +3055,13 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
int vertical = 72;
|
||||
dpi_settings.Get("vertical", &vertical);
|
||||
settings.Set(printing::kSettingDpiVertical, vertical);
|
||||
} else {
|
||||
settings.Set(printing::kSettingDpiHorizontal, dpi);
|
||||
settings.Set(printing::kSettingDpiVertical, dpi);
|
||||
}
|
||||
|
||||
print_task_runner_->PostTaskAndReplyWithResult(
|
||||
FROM_HERE, base::BindOnce(&GetDeviceNameToUse, device_name),
|
||||
base::BindOnce(&WebContents::OnGetDeviceNameToUse,
|
||||
weak_factory_.GetWeakPtr(), std::move(settings),
|
||||
std::move(callback), silent));
|
||||
std::move(callback)));
|
||||
}
|
||||
|
||||
// Partially duplicated and modified from
|
||||
|
||||
@@ -236,7 +236,6 @@ class WebContents : public ExclusiveAccessContext,
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
void OnGetDeviceNameToUse(base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
bool silent,
|
||||
// <error, device_name>
|
||||
std::pair<std::string, std::u16string> info);
|
||||
void Print(gin::Arguments* args);
|
||||
|
||||
@@ -42,6 +42,7 @@ void FrameSubscriber::AttachToHost(content::RenderWidgetHost* host) {
|
||||
|
||||
// Create and configure the video capturer.
|
||||
gfx::Size size = GetRenderViewSize();
|
||||
DCHECK(!size.IsEmpty());
|
||||
video_capturer_ = host_->GetView()->CreateVideoCapturer();
|
||||
video_capturer_->SetResolutionConstraints(size, size, true);
|
||||
video_capturer_->SetAutoThrottlingEnabled(false);
|
||||
|
||||
@@ -126,7 +126,7 @@ void BrowserProcessImpl::PreCreateThreads() {
|
||||
// Must be created before the IOThread.
|
||||
// Once IOThread class is no longer needed,
|
||||
// this can be created on first use.
|
||||
if (!SystemNetworkContextManager::HasInstance())
|
||||
if (!SystemNetworkContextManager::GetInstance())
|
||||
SystemNetworkContextManager::CreateInstance(local_state_.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -999,10 +999,6 @@ void ElectronBrowserClient::OnNetworkServiceCreated(
|
||||
if (!g_browser_process)
|
||||
return;
|
||||
|
||||
if (!SystemNetworkContextManager::HasInstance())
|
||||
SystemNetworkContextManager::CreateInstance(
|
||||
g_browser_process->local_state());
|
||||
|
||||
g_browser_process->system_network_context_manager()->OnNetworkServiceCreated(
|
||||
network_service);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "extensions/browser/api/scripting/scripting_utils.h"
|
||||
#include "extensions/browser/extension_api_frame_id_map.h"
|
||||
#include "extensions/browser/extension_file_task_runner.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_system.h"
|
||||
#include "extensions/browser/extension_user_script_loader.h"
|
||||
#include "extensions/browser/extension_util.h"
|
||||
@@ -1061,6 +1062,17 @@ void ScriptingRegisterContentScriptsFunction::OnContentScriptFilesValidated(
|
||||
return;
|
||||
}
|
||||
|
||||
// We cannot proceed if the extension is uninstalled or unloaded in the middle
|
||||
// of validating its script files.
|
||||
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
|
||||
if (!extension() ||
|
||||
!registry->enabled_extensions().Contains(extension_id())) {
|
||||
// Note: a Respond() is not needed if the system is shutting down or if the
|
||||
// extension is no longer enabled.
|
||||
Release(); // Matches the `AddRef()` in `Run()`.
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = std::move(result.second);
|
||||
auto scripts = std::move(result.first);
|
||||
ExtensionUserScriptLoader* loader =
|
||||
@@ -1306,6 +1318,17 @@ void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated(
|
||||
return;
|
||||
}
|
||||
|
||||
// We cannot proceed if the extension is uninstalled or unloaded in the middle
|
||||
// of validating its script files.
|
||||
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
|
||||
if (!extension() ||
|
||||
!registry->enabled_extensions().Contains(extension_id())) {
|
||||
// Note: a Respond() is not needed if the system is shutting down or if the
|
||||
// extension is no longer enabled.
|
||||
Release(); // Matches the `AddRef()` in `Run()`.
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = std::move(result.second);
|
||||
auto scripts = std::move(result.first);
|
||||
ExtensionUserScriptLoader* loader =
|
||||
|
||||
@@ -469,12 +469,12 @@ void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) {
|
||||
|
||||
void NativeWindowViews::SetContentView(views::View* view) {
|
||||
if (content_view()) {
|
||||
root_view_.RemoveChildView(content_view());
|
||||
root_view_.GetMainView()->RemoveChildView(content_view());
|
||||
}
|
||||
set_content_view(view);
|
||||
focused_view_ = view;
|
||||
root_view_.AddChildView(content_view());
|
||||
root_view_.DeprecatedLayoutImmediately();
|
||||
root_view_.GetMainView()->AddChildView(content_view());
|
||||
root_view_.GetMainView()->DeprecatedLayoutImmediately();
|
||||
}
|
||||
|
||||
void NativeWindowViews::Close() {
|
||||
@@ -1664,7 +1664,7 @@ std::u16string NativeWindowViews::GetWindowTitle() const {
|
||||
}
|
||||
|
||||
views::View* NativeWindowViews::GetContentsView() {
|
||||
return &root_view_;
|
||||
return root_view_.GetMainView();
|
||||
}
|
||||
|
||||
bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
|
||||
@@ -1674,7 +1674,7 @@ bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
|
||||
}
|
||||
|
||||
views::ClientView* NativeWindowViews::CreateClientView(views::Widget* widget) {
|
||||
return new NativeWindowClientView{widget, GetContentsView(), this};
|
||||
return new NativeWindowClientView{widget, &root_view_, this};
|
||||
}
|
||||
|
||||
std::unique_ptr<views::NonClientFrameView>
|
||||
|
||||
@@ -214,21 +214,8 @@ SystemNetworkContextManager* SystemNetworkContextManager::CreateInstance(
|
||||
return g_system_network_context_manager;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SystemNetworkContextManager::HasInstance() {
|
||||
return !!g_system_network_context_manager;
|
||||
}
|
||||
|
||||
// static
|
||||
SystemNetworkContextManager* SystemNetworkContextManager::GetInstance() {
|
||||
if (!g_system_network_context_manager) {
|
||||
// Initialize the network service, which will trigger
|
||||
// ElectronBrowserClient::OnNetworkServiceCreated(), which calls
|
||||
// CreateInstance() to initialize |g_system_network_context_manager|.
|
||||
content::GetNetworkService();
|
||||
DCHECK(g_system_network_context_manager);
|
||||
}
|
||||
|
||||
return g_system_network_context_manager;
|
||||
}
|
||||
|
||||
@@ -236,7 +223,6 @@ SystemNetworkContextManager* SystemNetworkContextManager::GetInstance() {
|
||||
void SystemNetworkContextManager::DeleteInstance() {
|
||||
DCHECK(g_system_network_context_manager);
|
||||
delete g_system_network_context_manager;
|
||||
g_system_network_context_manager = nullptr;
|
||||
}
|
||||
|
||||
// c.f.
|
||||
|
||||
@@ -41,16 +41,11 @@ class SystemNetworkContextManager {
|
||||
SystemNetworkContextManager& operator=(const SystemNetworkContextManager&) =
|
||||
delete;
|
||||
|
||||
// Checks if the global SystemNetworkContextManager has been created.
|
||||
static bool HasInstance();
|
||||
|
||||
// Creates the global instance of SystemNetworkContextManager. If an
|
||||
// instance already exists, this will cause a DCHECK failure.
|
||||
static SystemNetworkContextManager* CreateInstance(PrefService* pref_service);
|
||||
|
||||
// Gets the global SystemNetworkContextManager instance. If it has not been
|
||||
// created yet, NetworkService is called, which will cause the
|
||||
// SystemNetworkContextManager to be created.
|
||||
// Gets the global SystemNetworkContextManager instance.
|
||||
static SystemNetworkContextManager* GetInstance();
|
||||
|
||||
// Destroys the global SystemNetworkContextManager instance.
|
||||
|
||||
@@ -159,21 +159,21 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
|
||||
|
||||
void LibnotifyNotification::Dismiss() {
|
||||
if (!notification_) {
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
on_dismissing_ = true;
|
||||
libnotify_loader_.notify_notification_close(notification_, &error);
|
||||
if (error) {
|
||||
log_and_clear_error(error, "notify_notification_close");
|
||||
Destroy();
|
||||
}
|
||||
on_dismissing_ = false;
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationClosed(
|
||||
NotifyNotification* notification) {
|
||||
NotificationDismissed();
|
||||
NotificationDismissed(!on_dismissing_);
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationView(NotifyNotification* notification,
|
||||
|
||||
@@ -33,6 +33,7 @@ class LibnotifyNotification : public Notification {
|
||||
RAW_PTR_EXCLUSION NotifyNotification* notification_ = nullptr;
|
||||
|
||||
ScopedGSignal signal_;
|
||||
bool on_dismissing_ = false;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "content/browser/renderer_host/render_widget_host_delegate.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture_target.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
|
||||
126
shell/browser/printing/printing_utils.cc
Normal file
126
shell/browser/printing/printing_utils.cc
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/printing/printing_utils.h"
|
||||
|
||||
#include "base/apple/scoped_typeref.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "printing/backend/print_backend.h" // nogncheck
|
||||
#include "printing/units.h"
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif BUILDFLAG(LINUX)
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
return gfx::Size(printing::kDefaultMacDpi, printing::kDefaultMacDpi);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
HDC hdc =
|
||||
CreateDC(L"WINSPOOL", base::as_wcstr(device_name), nullptr, nullptr);
|
||||
if (hdc == nullptr)
|
||||
return gfx::Size(kDefaultPdfDpi, kDefaultPdfDpi);
|
||||
|
||||
int dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
int dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
DeleteDC(hdc);
|
||||
|
||||
return gfx::Size(dpi_x, dpi_y);
|
||||
#else
|
||||
GtkPrintSettings* print_settings = gtk_print_settings_new();
|
||||
int dpi = gtk_print_settings_get_resolution(print_settings);
|
||||
g_object_unref(print_settings);
|
||||
return gfx::Size(dpi, dpi);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
||||
base::SysUTF16ToCFStringRef(device_name));
|
||||
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
||||
bool printer_exists = new_printer != nullptr;
|
||||
PMRelease(new_printer);
|
||||
return printer_exists;
|
||||
#else
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
#endif
|
||||
|
||||
if (!device_name.empty()) {
|
||||
if (!IsDeviceNameValid(device_name))
|
||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||
return std::make_pair(std::string(), device_name);
|
||||
}
|
||||
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
std::string printer_name;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
|
||||
// We don't want to return if this fails since some devices won't have a
|
||||
// default printer.
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
if (printers.empty())
|
||||
return std::make_pair("No printers available on the network",
|
||||
std::u16string());
|
||||
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
||||
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
||||
#if !BUILDFLAG(IS_WIN)
|
||||
static constexpr base::TaskTraits kTraits = {
|
||||
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
||||
#endif
|
||||
|
||||
#if defined(USE_CUPS)
|
||||
// CUPS is thread safe.
|
||||
return base::ThreadPool::CreateTaskRunner(kTraits);
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// Windows drivers are likely not thread-safe and need to be accessed on the
|
||||
// UI thread.
|
||||
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
||||
#else
|
||||
// Be conservative on unsupported platforms.
|
||||
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
41
shell/browser/printing/printing_utils.h
Normal file
41
shell/browser/printing/printing_utils.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2024 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_PRINTING_PRINTING_UTILS_H_
|
||||
#define ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/task/task_runner.h"
|
||||
|
||||
namespace gfx {
|
||||
class Size;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
// This function returns the per-platform default printer's DPI.
|
||||
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name);
|
||||
|
||||
// This will return false if no printer with the provided device_name can be
|
||||
// found on the network. We need to check this because Chromium does not do
|
||||
// sanity checking of device_name validity and so will crash on invalid names.
|
||||
bool IsDeviceNameValid(const std::u16string& device_name);
|
||||
|
||||
// This function returns a validated device name.
|
||||
// If the user passed one to webContents.print(), we check that it's valid and
|
||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||
// pass a device name, we first try to return the system default printer. If one
|
||||
// isn't set, then pull all the printers and use the first one or fail if none
|
||||
// exist.
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name);
|
||||
|
||||
// This function creates a task runner for use with printing tasks.
|
||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner();
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||
@@ -35,7 +35,9 @@ std::unique_ptr<content::SerialChooser> ElectronSerialDelegate::RunChooser(
|
||||
if (controller) {
|
||||
DeleteControllerForFrame(frame);
|
||||
}
|
||||
AddControllerForFrame(frame, std::move(filters), std::move(callback));
|
||||
AddControllerForFrame(frame, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids),
|
||||
std::move(callback));
|
||||
|
||||
// Return a nullptr because the return value isn't used for anything, eg
|
||||
// there is no mechanism to cancel navigator.serial.requestPort(). The return
|
||||
@@ -106,12 +108,14 @@ SerialChooserController* ElectronSerialDelegate::ControllerForFrame(
|
||||
SerialChooserController* ElectronSerialDelegate::AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback) {
|
||||
auto* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
auto controller = std::make_unique<SerialChooserController>(
|
||||
render_frame_host, std::move(filters), std::move(callback), web_contents,
|
||||
weak_factory_.GetWeakPtr());
|
||||
render_frame_host, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids), std::move(callback),
|
||||
web_contents, weak_factory_.GetWeakPtr());
|
||||
controller_map_.insert(
|
||||
std::make_pair(render_frame_host, std::move(controller)));
|
||||
return ControllerForFrame(render_frame_host);
|
||||
|
||||
@@ -67,6 +67,7 @@ class ElectronSerialDelegate : public content::SerialDelegate,
|
||||
SerialChooserController* AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback);
|
||||
|
||||
base::ScopedObservation<SerialChooserContext,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
|
||||
#include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
|
||||
#include "services/device/public/mojom/serial.mojom.h"
|
||||
#include "shell/browser/api/electron_api_session.h"
|
||||
#include "shell/browser/serial/serial_chooser_context.h"
|
||||
#include "shell/browser/serial/serial_chooser_context_factory.h"
|
||||
@@ -58,14 +61,57 @@ struct Converter<device::mojom::SerialPortInfoPtr> {
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::device::mojom::SerialPortType;
|
||||
|
||||
bool FilterMatchesPort(const blink::mojom::SerialPortFilter& filter,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (filter.bluetooth_service_class_id) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return false;
|
||||
}
|
||||
return device::BluetoothUUID(*port.bluetooth_service_class_id) ==
|
||||
device::BluetoothUUID(*filter.bluetooth_service_class_id);
|
||||
}
|
||||
if (!filter.has_vendor_id) {
|
||||
return true;
|
||||
}
|
||||
if (!port.has_vendor_id || port.vendor_id != filter.vendor_id) {
|
||||
return false;
|
||||
}
|
||||
if (!filter.has_product_id) {
|
||||
return true;
|
||||
}
|
||||
return port.has_product_id && port.product_id == filter.product_id;
|
||||
}
|
||||
|
||||
bool BluetoothPortIsAllowed(
|
||||
const std::vector<::device::BluetoothUUID>& allowed_ids,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return true;
|
||||
}
|
||||
// Serial Port Profile is allowed by default.
|
||||
if (*port.bluetooth_service_class_id == device::GetSerialPortProfileUUID()) {
|
||||
return true;
|
||||
}
|
||||
return base::Contains(allowed_ids, port.bluetooth_service_class_id.value());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SerialChooserController::SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate)
|
||||
: WebContentsObserver(web_contents),
|
||||
filters_(std::move(filters)),
|
||||
allowed_bluetooth_service_class_ids_(
|
||||
std::move(allowed_bluetooth_service_class_ids)),
|
||||
callback_(std::move(callback)),
|
||||
serial_delegate_(serial_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
@@ -93,10 +139,11 @@ api::Session* SerialChooserController::GetSession() {
|
||||
|
||||
void SerialChooserController::OnPortAdded(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!FilterMatchesAny(port))
|
||||
if (!DisplayDevice(port))
|
||||
return;
|
||||
|
||||
ports_.push_back(port.Clone());
|
||||
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
session->Emit("serial-port-added", port.Clone(), web_contents());
|
||||
@@ -105,9 +152,8 @@ void SerialChooserController::OnPortAdded(
|
||||
|
||||
void SerialChooserController::OnPortRemoved(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
const auto it = std::find_if(
|
||||
ports_.begin(), ports_.end(),
|
||||
[&port](const auto& ptr) { return ptr->token == port.token; });
|
||||
const auto it = base::ranges::find(ports_, port.token,
|
||||
&device::mojom::SerialPortInfo::token);
|
||||
if (it != ports_.end()) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
@@ -152,7 +198,7 @@ void SerialChooserController::OnGetDevices(
|
||||
});
|
||||
|
||||
for (auto& port : ports) {
|
||||
if (FilterMatchesAny(*port))
|
||||
if (DisplayDevice(*port))
|
||||
ports_.push_back(std::move(port));
|
||||
}
|
||||
|
||||
@@ -169,21 +215,17 @@ void SerialChooserController::OnGetDevices(
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialChooserController::FilterMatchesAny(
|
||||
bool SerialChooserController::DisplayDevice(
|
||||
const device::mojom::SerialPortInfo& port) const {
|
||||
if (filters_.empty())
|
||||
return true;
|
||||
if (filters_.empty()) {
|
||||
return BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port);
|
||||
}
|
||||
|
||||
for (const auto& filter : filters_) {
|
||||
if (filter->has_vendor_id &&
|
||||
(!port.has_vendor_id || filter->vendor_id != port.vendor_id)) {
|
||||
continue;
|
||||
if (FilterMatchesPort(*filter, port) &&
|
||||
BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port)) {
|
||||
return true;
|
||||
}
|
||||
if (filter->has_product_id &&
|
||||
(!port.has_product_id || filter->product_id != port.product_id)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -34,6 +34,7 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate);
|
||||
@@ -55,11 +56,12 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
private:
|
||||
api::Session* GetSession();
|
||||
void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports);
|
||||
bool FilterMatchesAny(const device::mojom::SerialPortInfo& port) const;
|
||||
bool DisplayDevice(const device::mojom::SerialPortInfo& port) const;
|
||||
void RunCallback(device::mojom::SerialPortInfoPtr port);
|
||||
void OnDeviceChosen(const std::string& port_id);
|
||||
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters_;
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_;
|
||||
content::SerialChooser::Callback callback_;
|
||||
url::Origin origin_;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/socket/tcp_server_socket.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/common/electron_paths.h"
|
||||
#include "third_party/inspector_protocol/crdtp/dispatch.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
@@ -139,4 +140,8 @@ bool DevToolsManagerDelegate::HasBundledFrontendResources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
content::BrowserContext* DevToolsManagerDelegate::GetDefaultBrowserContext() {
|
||||
return ElectronBrowserContext::From("", false);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "base/compiler_specific.h"
|
||||
#include "content/public/browser/devtools_manager_delegate.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
@@ -33,6 +37,7 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
TargetType target_type) override;
|
||||
std::string GetDiscoveryPageHTML() override;
|
||||
bool HasBundledFrontendResources() override;
|
||||
content::BrowserContext* GetDefaultBrowserContext() override;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -9,18 +9,12 @@
|
||||
#include "content/public/common/input/native_web_keyboard_event.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/ui/views/menu_bar.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
// The menu bar height in pixels.
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
const int kMenuBarHeight = 20;
|
||||
#else
|
||||
const int kMenuBarHeight = 25;
|
||||
#endif
|
||||
|
||||
bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
|
||||
return event.windows_key_code == ui::VKEY_MENU;
|
||||
}
|
||||
@@ -41,6 +35,12 @@ RootView::RootView(NativeWindow* window)
|
||||
: window_(window),
|
||||
last_focused_view_tracker_(std::make_unique<views::ViewTracker>()) {
|
||||
set_owned_by_client();
|
||||
views::BoxLayout* layout =
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical));
|
||||
main_view_ = AddChildView(std::make_unique<views::View>());
|
||||
main_view_->SetUseDefaultFillLayout(true);
|
||||
layout->SetFlexForView(main_view_, 1);
|
||||
}
|
||||
|
||||
RootView::~RootView() = default;
|
||||
@@ -77,7 +77,7 @@ bool RootView::HasMenu() const {
|
||||
}
|
||||
|
||||
int RootView::GetMenuBarHeight() const {
|
||||
return kMenuBarHeight;
|
||||
return menu_bar_ ? menu_bar_->GetPreferredSize().height() : 0;
|
||||
}
|
||||
|
||||
void RootView::SetAutoHideMenuBar(bool auto_hide) {
|
||||
@@ -90,10 +90,8 @@ void RootView::SetMenuBarVisibility(bool visible) {
|
||||
|
||||
menu_bar_visible_ = visible;
|
||||
if (visible) {
|
||||
DCHECK_EQ(children().size(), 1ul);
|
||||
AddChildView(menu_bar_.get());
|
||||
AddChildViewAt(menu_bar_.get(), 0);
|
||||
} else {
|
||||
DCHECK_EQ(children().size(), 2ul);
|
||||
RemoveChildView(menu_bar_.get());
|
||||
}
|
||||
|
||||
@@ -166,21 +164,6 @@ void RootView::ResetAltState() {
|
||||
menu_bar_alt_pressed_ = false;
|
||||
}
|
||||
|
||||
void RootView::Layout(PassKey) {
|
||||
if (!window_->content_view()) // Not ready yet.
|
||||
return;
|
||||
|
||||
const auto menu_bar_bounds =
|
||||
menu_bar_visible_ ? gfx::Rect(0, 0, size().width(), kMenuBarHeight)
|
||||
: gfx::Rect();
|
||||
if (menu_bar_)
|
||||
menu_bar_->SetBoundsRect(menu_bar_bounds);
|
||||
|
||||
window_->content_view()->SetBoundsRect(
|
||||
gfx::Rect(0, menu_bar_visible_ ? menu_bar_bounds.bottom() : 0,
|
||||
size().width(), size().height() - menu_bar_bounds.height()));
|
||||
}
|
||||
|
||||
gfx::Size RootView::GetMinimumSize() const {
|
||||
return window_->GetMinimumSize();
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ class RootView : public views::View {
|
||||
void RegisterAcceleratorsWithFocusManager(ElectronMenuModel* menu_model);
|
||||
void UnregisterAcceleratorsWithFocusManager();
|
||||
|
||||
views::View* GetMainView() { return main_view_; }
|
||||
|
||||
// views::View:
|
||||
void Layout(PassKey) override;
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
gfx::Size GetMaximumSize() const override;
|
||||
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||
@@ -62,6 +63,9 @@ class RootView : public views::View {
|
||||
bool menu_bar_visible_ = false;
|
||||
bool menu_bar_alt_pressed_ = false;
|
||||
|
||||
// Main view area.
|
||||
raw_ptr<views::View> main_view_;
|
||||
|
||||
// Map from accelerator to menu item's command id.
|
||||
accelerator_util::AcceleratorTable accelerator_table_;
|
||||
|
||||
|
||||
@@ -39,6 +39,14 @@
|
||||
"contexts": ["blessed_extension", "unblessed_extension", "content_script"],
|
||||
"max_manifest_version": 2
|
||||
},
|
||||
"extension.lastError": {
|
||||
"contexts": [
|
||||
"blessed_extension",
|
||||
"unblessed_extension",
|
||||
"content_script"
|
||||
],
|
||||
"max_manifest_version": 2
|
||||
},
|
||||
"i18n": {
|
||||
"channel": "stable",
|
||||
"extension_types": ["extension"],
|
||||
@@ -68,5 +76,10 @@
|
||||
"scripting": {
|
||||
"dependencies": ["permission:scripting"],
|
||||
"contexts": ["blessed_extension"]
|
||||
},
|
||||
"scripting.globalParams": {
|
||||
"channel": "trunk",
|
||||
"dependencies": ["permission:scripting"],
|
||||
"contexts": ["content_script"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,9 @@ template <typename ReturnType, typename... ArgTypes>
|
||||
struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
|
||||
static void Go(base::RepeatingCallback<ReturnType(ArgTypes...)> val,
|
||||
gin::Arguments* args) {
|
||||
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
|
||||
Invoker<Indices, ArgTypes...> invoker(args, 0);
|
||||
using Indices = std::index_sequence_for<ArgTypes...>;
|
||||
Invoker<Indices, ArgTypes...> invoker(args,
|
||||
{.holder_is_first_argument = false});
|
||||
if (invoker.IsOK())
|
||||
invoker.DispatchToCallback(val);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ inline WrappableBase* InvokeFactory(
|
||||
gin::Arguments* args,
|
||||
const base::RepeatingCallback<WrappableBase*(P1)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1))
|
||||
return nullptr;
|
||||
return callback.Run(a1);
|
||||
}
|
||||
@@ -37,8 +38,10 @@ inline WrappableBase* InvokeFactory(
|
||||
const base::RepeatingCallback<WrappableBase*(P1, P2)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a2))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a2))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2);
|
||||
}
|
||||
@@ -50,9 +53,12 @@ inline WrappableBase* InvokeFactory(
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a2) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a3))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a2) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a3))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3);
|
||||
}
|
||||
@@ -65,10 +71,14 @@ inline WrappableBase* InvokeFactory(
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a2) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a3) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a4))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a2) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a3) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a4))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4);
|
||||
}
|
||||
@@ -83,11 +93,16 @@ inline WrappableBase* InvokeFactory(
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
typename CallbackParamTraits<P5>::LocalType a5;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a2) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a3) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a4) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a5))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a2) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a3) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a4) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a5))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4, a5);
|
||||
}
|
||||
@@ -108,12 +123,18 @@ inline WrappableBase* InvokeFactory(
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
typename CallbackParamTraits<P5>::LocalType a5;
|
||||
typename CallbackParamTraits<P6>::LocalType a6;
|
||||
if (!gin_helper::GetNextArgument(args, 0, true, &a1) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a2) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a3) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a4) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a5) ||
|
||||
!gin_helper::GetNextArgument(args, 0, false, &a6))
|
||||
if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0,
|
||||
&a1) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a2) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a3) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a4) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a5) ||
|
||||
!gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0,
|
||||
&a6))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,34 @@
|
||||
|
||||
#include "shell/common/gin_helper/function_template.h"
|
||||
|
||||
#include "base/observer_list.h"
|
||||
#include "base/strings/strcat.h"
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
CallbackHolderBase::DisposeObserver::DisposeObserver(
|
||||
gin::PerIsolateData* per_isolate_data,
|
||||
CallbackHolderBase* holder)
|
||||
: per_isolate_data_(per_isolate_data), holder_(*holder) {
|
||||
if (per_isolate_data_)
|
||||
per_isolate_data_->AddDisposeObserver(this);
|
||||
}
|
||||
CallbackHolderBase::DisposeObserver::~DisposeObserver() {
|
||||
if (per_isolate_data_)
|
||||
per_isolate_data_->RemoveDisposeObserver(this);
|
||||
}
|
||||
void CallbackHolderBase::DisposeObserver::OnBeforeDispose(
|
||||
v8::Isolate* isolate) {
|
||||
holder_->v8_ref_.Reset();
|
||||
}
|
||||
void CallbackHolderBase::DisposeObserver::OnDisposed() {
|
||||
// The holder contains the observer, so the observer is destroyed here also.
|
||||
delete &holder_.get();
|
||||
}
|
||||
|
||||
CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate)
|
||||
: v8_ref_(isolate, v8::External::New(isolate, this)) {
|
||||
: v8_ref_(isolate, v8::External::New(isolate, this)),
|
||||
dispose_observer_(gin::PerIsolateData::From(isolate), this) {
|
||||
v8_ref_.SetWeak(this, &CallbackHolderBase::FirstWeakCallback,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
}
|
||||
@@ -33,4 +57,28 @@ void CallbackHolderBase::SecondWeakCallback(
|
||||
delete data.GetParameter();
|
||||
}
|
||||
|
||||
void ThrowConversionError(gin::Arguments* args,
|
||||
const InvokerOptions& invoker_options,
|
||||
size_t index) {
|
||||
if (index == 0 && invoker_options.holder_is_first_argument) {
|
||||
// Failed to get the appropriate `this` object. This can happen if a
|
||||
// method is invoked using Function.prototype.[call|apply] and passed an
|
||||
// invalid (or null) `this` argument.
|
||||
std::string error =
|
||||
invoker_options.holder_type
|
||||
? base::StrCat({"Illegal invocation: Function must be "
|
||||
"called on an object of type ",
|
||||
invoker_options.holder_type})
|
||||
: "Illegal invocation";
|
||||
args->ThrowTypeError(error);
|
||||
} else {
|
||||
// Otherwise, this failed parsing on a different argument.
|
||||
// Arguments::ThrowError() will try to include appropriate information.
|
||||
// Ideally we would include the expected c++ type in the error message
|
||||
// here, too (which we can access via typeid(ArgType).name()), however we
|
||||
// compile with no-rtti, which disables typeid.
|
||||
args->ThrowError();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gin_helper
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "gin/per_isolate_data.h"
|
||||
#include "shell/common/gin_helper/arguments.h"
|
||||
#include "shell/common/gin_helper/destroyable.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
@@ -25,8 +26,9 @@
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
enum CreateFunctionTemplateFlags {
|
||||
HolderIsFirstArgument = 1 << 0,
|
||||
struct InvokerOptions {
|
||||
bool holder_is_first_argument = false;
|
||||
const char* holder_type = nullptr; // Null if unknown or not applicable.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -43,64 +45,91 @@ struct CallbackParamTraits<const T*> {
|
||||
};
|
||||
|
||||
// CallbackHolder and CallbackHolderBase are used to pass a
|
||||
// base::RepeatingCallback from
|
||||
// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
|
||||
// DispatchToCallback, where it is invoked.
|
||||
// base::RepeatingCallback from CreateFunctionTemplate through v8 (via
|
||||
// v8::FunctionTemplate) to DispatchToCallback, where it is invoked.
|
||||
|
||||
// CallbackHolder will clean up the callback in two different scenarios:
|
||||
// - If the garbage collector finds that it's garbage and collects it. (But note
|
||||
// that even _if_ we become garbage, we might never get collected!)
|
||||
// - If the isolate gets disposed.
|
||||
//
|
||||
// TODO(crbug.com/1285119): When gin::Wrappable gets migrated over to using
|
||||
// cppgc, this class should also be considered for migration.
|
||||
|
||||
// This simple base class is used so that we can share a single object template
|
||||
// among every CallbackHolder instance.
|
||||
class CallbackHolderBase {
|
||||
public:
|
||||
v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
|
||||
|
||||
// disable copy
|
||||
CallbackHolderBase(const CallbackHolderBase&) = delete;
|
||||
CallbackHolderBase& operator=(const CallbackHolderBase&) = delete;
|
||||
|
||||
v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
|
||||
|
||||
protected:
|
||||
explicit CallbackHolderBase(v8::Isolate* isolate);
|
||||
virtual ~CallbackHolderBase();
|
||||
|
||||
private:
|
||||
class DisposeObserver : gin::PerIsolateData::DisposeObserver {
|
||||
public:
|
||||
DisposeObserver(gin::PerIsolateData* per_isolate_data,
|
||||
CallbackHolderBase* holder);
|
||||
~DisposeObserver() override;
|
||||
void OnBeforeDispose(v8::Isolate* isolate) override;
|
||||
void OnDisposed() override;
|
||||
|
||||
private:
|
||||
// Unlike in Chromium, it's possible for PerIsolateData to be null
|
||||
// for a given isolate - e.g. in a Node.js Worker. Thus this
|
||||
// needs to be a raw_ptr instead of a raw_ref.
|
||||
const raw_ptr<gin::PerIsolateData> per_isolate_data_;
|
||||
const raw_ref<CallbackHolderBase> holder_;
|
||||
};
|
||||
|
||||
static void FirstWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data);
|
||||
static void SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data);
|
||||
|
||||
v8::Global<v8::External> v8_ref_;
|
||||
DisposeObserver dispose_observer_;
|
||||
};
|
||||
|
||||
template <typename Sig>
|
||||
class CallbackHolder : public CallbackHolderBase {
|
||||
public:
|
||||
CallbackHolder(v8::Isolate* isolate,
|
||||
const base::RepeatingCallback<Sig>& callback,
|
||||
int flags)
|
||||
: CallbackHolderBase(isolate), callback(callback), flags(flags) {}
|
||||
base::RepeatingCallback<Sig> callback,
|
||||
InvokerOptions invoker_options)
|
||||
: CallbackHolderBase(isolate),
|
||||
callback(std::move(callback)),
|
||||
invoker_options(std::move(invoker_options)) {}
|
||||
CallbackHolder(const CallbackHolder&) = delete;
|
||||
CallbackHolder& operator=(const CallbackHolder&) = delete;
|
||||
|
||||
base::RepeatingCallback<Sig> callback;
|
||||
int flags = 0;
|
||||
InvokerOptions invoker_options;
|
||||
|
||||
private:
|
||||
virtual ~CallbackHolder() = default;
|
||||
~CallbackHolder() override = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
T* result) {
|
||||
if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
|
||||
if (is_first && invoker_options.holder_is_first_argument) {
|
||||
return args->GetHolder(result);
|
||||
} else {
|
||||
return args->GetNext(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Support std::optional as output, which would be empty and do not throw error
|
||||
// when conversion to T fails.
|
||||
// Electron-specific GetNextArgument that supports std::optional.
|
||||
template <typename T>
|
||||
bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
std::optional<T>* result) {
|
||||
T converted;
|
||||
@@ -110,10 +139,36 @@ bool GetNextArgument(gin::Arguments* args,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Electron-specific GetNextArgument that supports ErrorThrower.
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
ErrorThrower* result) {
|
||||
*result = ErrorThrower(args->isolate());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Electron-specific GetNextArgument that supports the gin_helper::Arguments.
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
gin_helper::Arguments** result) {
|
||||
*result = static_cast<gin_helper::Arguments*>(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For advanced use cases, we allow callers to request the unparsed Arguments
|
||||
// object and poke around in it directly.
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
gin::Arguments* result) {
|
||||
*result = *args;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
gin::Arguments** result) {
|
||||
*result = args;
|
||||
@@ -122,71 +177,65 @@ inline bool GetNextArgument(gin::Arguments* args,
|
||||
|
||||
// It's common for clients to just need the isolate, so we make that easy.
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
const InvokerOptions& invoker_options,
|
||||
bool is_first,
|
||||
v8::Isolate** result) {
|
||||
*result = args->isolate();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow clients to pass a util::Error to throw errors if they
|
||||
// don't need the full gin::Arguments
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
bool is_first,
|
||||
ErrorThrower* result) {
|
||||
*result = ErrorThrower(args->isolate());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Supports the gin_helper::Arguments.
|
||||
inline bool GetNextArgument(gin::Arguments* args,
|
||||
int create_flags,
|
||||
bool is_first,
|
||||
gin_helper::Arguments** result) {
|
||||
*result = static_cast<gin_helper::Arguments*>(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Classes for generating and storing an argument pack of integer indices
|
||||
// (based on well-known "indices trick", see: http://goo.gl/bKKojn):
|
||||
template <size_t... indices>
|
||||
struct IndicesHolder {};
|
||||
|
||||
template <size_t requested_index, size_t... indices>
|
||||
struct IndicesGenerator {
|
||||
using type = typename IndicesGenerator<requested_index - 1,
|
||||
requested_index - 1,
|
||||
indices...>::type;
|
||||
};
|
||||
template <size_t... indices>
|
||||
struct IndicesGenerator<0, indices...> {
|
||||
using type = IndicesHolder<indices...>;
|
||||
};
|
||||
// Throws an error indicating conversion failure.
|
||||
void ThrowConversionError(gin::Arguments* args,
|
||||
const InvokerOptions& invoker_options,
|
||||
size_t index);
|
||||
|
||||
// Class template for extracting and storing single argument for callback
|
||||
// at position |index|.
|
||||
template <size_t index, typename ArgType>
|
||||
template <size_t index, typename ArgType, typename = void>
|
||||
struct ArgumentHolder {
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
bool ok = false;
|
||||
|
||||
ArgumentHolder(gin::Arguments* args, int create_flags) {
|
||||
ArgumentHolder(gin::Arguments* args, const InvokerOptions& invoker_options) {
|
||||
v8::Local<v8::Object> holder;
|
||||
if (index == 0 && (create_flags & HolderIsFirstArgument) &&
|
||||
if (index == 0 && invoker_options.holder_is_first_argument &&
|
||||
args->GetHolder(&holder) &&
|
||||
gin_helper::Destroyable::IsDestroyed(holder)) {
|
||||
args->ThrowTypeError("Object has been destroyed");
|
||||
return;
|
||||
}
|
||||
ok = GetNextArgument(args, create_flags, index == 0, &value);
|
||||
|
||||
ok = GetNextArgument(args, invoker_options, index == 0, &value);
|
||||
if (!ok) {
|
||||
// Ideally we would include the expected c++ type in the error
|
||||
// message which we can access via typeid(ArgType).name()
|
||||
// however we compile with no-rtti, which disables typeid.
|
||||
args->ThrowError();
|
||||
ThrowConversionError(args, invoker_options, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is required for types such as v8::LocalVector<T>, which don't have
|
||||
// a default constructor. To create an element of such a type, the isolate
|
||||
// has to be provided.
|
||||
template <size_t index, typename ArgType>
|
||||
struct ArgumentHolder<
|
||||
index,
|
||||
ArgType,
|
||||
std::enable_if_t<!std::is_default_constructible_v<
|
||||
typename CallbackParamTraits<ArgType>::LocalType> &&
|
||||
std::is_constructible_v<
|
||||
typename CallbackParamTraits<ArgType>::LocalType,
|
||||
v8::Isolate*>>> {
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
bool ok;
|
||||
|
||||
ArgumentHolder(gin::Arguments* args, const InvokerOptions& invoker_options)
|
||||
: value(args->isolate()),
|
||||
ok(GetNextArgument(args, invoker_options, index == 0, &value)) {
|
||||
if (!ok) {
|
||||
ThrowConversionError(args, invoker_options, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -194,24 +243,19 @@ struct ArgumentHolder {
|
||||
// Class template for converting arguments from JavaScript to C++ and running
|
||||
// the callback with them.
|
||||
template <typename IndicesType, typename... ArgTypes>
|
||||
class Invoker {};
|
||||
class Invoker;
|
||||
|
||||
template <size_t... indices, typename... ArgTypes>
|
||||
class Invoker<IndicesHolder<indices...>, ArgTypes...>
|
||||
class Invoker<std::index_sequence<indices...>, ArgTypes...>
|
||||
: public ArgumentHolder<indices, ArgTypes>... {
|
||||
public:
|
||||
// Invoker<> inherits from ArgumentHolder<> for each argument.
|
||||
// C++ has always been strict about the class initialization order,
|
||||
// so it is guaranteed ArgumentHolders will be initialized (and thus, will
|
||||
// extract arguments from Arguments) in the right order.
|
||||
Invoker(gin::Arguments* args, int create_flags)
|
||||
: ArgumentHolder<indices, ArgTypes>(args, create_flags)..., args_(args) {
|
||||
// GCC thinks that create_flags is going unused, even though the
|
||||
// expansion above clearly makes use of it. Per jyasskin@, casting
|
||||
// to void is the commonly accepted way to convince the compiler
|
||||
// that you're actually using a parameter/variable.
|
||||
(void)create_flags;
|
||||
}
|
||||
Invoker(gin::Arguments* args, const InvokerOptions& invoker_options)
|
||||
: ArgumentHolder<indices, ArgTypes>(args, invoker_options)...,
|
||||
args_(args) {}
|
||||
|
||||
bool IsOK() { return And(ArgumentHolder<indices, ArgTypes>::ok...); }
|
||||
|
||||
@@ -252,46 +296,89 @@ struct Dispatcher {};
|
||||
|
||||
template <typename ReturnType, typename... ArgTypes>
|
||||
struct Dispatcher<ReturnType(ArgTypes...)> {
|
||||
static void DispatchToCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
gin::Arguments args(info);
|
||||
static void DispatchToCallbackImpl(gin::Arguments* args) {
|
||||
v8::Local<v8::External> v8_holder;
|
||||
args.GetData(&v8_holder);
|
||||
CHECK(args->GetData(&v8_holder));
|
||||
CallbackHolderBase* holder_base =
|
||||
reinterpret_cast<CallbackHolderBase*>(v8_holder->Value());
|
||||
|
||||
typedef CallbackHolder<ReturnType(ArgTypes...)> HolderT;
|
||||
HolderT* holder = static_cast<HolderT*>(holder_base);
|
||||
|
||||
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
|
||||
Invoker<Indices, ArgTypes...> invoker(&args, holder->flags);
|
||||
using Indices = std::index_sequence_for<ArgTypes...>;
|
||||
Invoker<Indices, ArgTypes...> invoker(args, holder->invoker_options);
|
||||
if (invoker.IsOK())
|
||||
invoker.DispatchToCallback(holder->callback);
|
||||
}
|
||||
|
||||
static void DispatchToCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
gin::Arguments args(info);
|
||||
DispatchToCallbackImpl(&args);
|
||||
}
|
||||
|
||||
static void DispatchToCallbackForProperty(
|
||||
v8::Local<v8::Name>,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
gin::Arguments args(info);
|
||||
DispatchToCallbackImpl(&args);
|
||||
}
|
||||
};
|
||||
|
||||
// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
|
||||
// JavaScript functions that execute a provided C++ function or
|
||||
// base::RepeatingCallback.
|
||||
// JavaScript arguments are automatically converted via gin::Converter, as is
|
||||
// the return value of the C++ function, if any.
|
||||
// base::RepeatingCallback. JavaScript arguments are automatically converted via
|
||||
// gin::Converter, as is the return value of the C++ function, if any.
|
||||
// |invoker_options| contains additional parameters. If it contains a
|
||||
// holder_type, it will be used to provide a useful conversion error if the
|
||||
// holder is the first argument. If not provided, a generic invocation error
|
||||
// will be used.
|
||||
//
|
||||
// NOTE: V8 caches FunctionTemplates for a lifetime of a web page for its own
|
||||
// internal reasons, thus it is generally a good idea to cache the template
|
||||
// returned by this function. Otherwise, repeated method invocations from JS
|
||||
// will create substantial memory leaks. See http://crbug.com/463487.
|
||||
//
|
||||
// The callback will be destroyed if either the function template gets garbage
|
||||
// collected or _after_ the isolate is disposed. Garbage collection can never be
|
||||
// relied upon. As such, any destructors for objects bound to the callback must
|
||||
// not depend on the isolate being alive at the point they are called. The order
|
||||
// in which callbacks are destroyed is not guaranteed.
|
||||
template <typename Sig>
|
||||
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
|
||||
v8::Isolate* isolate,
|
||||
const base::RepeatingCallback<Sig> callback,
|
||||
int callback_flags = 0) {
|
||||
base::RepeatingCallback<Sig> callback,
|
||||
InvokerOptions invoker_options = {}) {
|
||||
typedef CallbackHolder<Sig> HolderT;
|
||||
HolderT* holder = new HolderT(isolate, callback, callback_flags);
|
||||
HolderT* holder =
|
||||
new HolderT(isolate, std::move(callback), std::move(invoker_options));
|
||||
|
||||
return v8::FunctionTemplate::New(isolate,
|
||||
&Dispatcher<Sig>::DispatchToCallback,
|
||||
gin::ConvertToV8<v8::Local<v8::External>>(
|
||||
isolate, holder->GetHandle(isolate)));
|
||||
v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
|
||||
isolate, &Dispatcher<Sig>::DispatchToCallback,
|
||||
gin::ConvertToV8<v8::Local<v8::External>>(isolate,
|
||||
holder->GetHandle(isolate)),
|
||||
v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow);
|
||||
return tmpl;
|
||||
}
|
||||
|
||||
// CreateDataPropertyCallback creates a v8::AccessorNameGetterCallback and
|
||||
// corresponding data value that will hold and execute the provided
|
||||
// base::RepeatingCallback, using automatic conversions similar to
|
||||
// |CreateFunctionTemplate|.
|
||||
//
|
||||
// It is expected that these will be passed to v8::Template::SetLazyDataProperty
|
||||
// or another similar function.
|
||||
template <typename Sig>
|
||||
std::pair<v8::AccessorNameGetterCallback, v8::Local<v8::Value>>
|
||||
CreateDataPropertyCallback(v8::Isolate* isolate,
|
||||
base::RepeatingCallback<Sig> callback,
|
||||
InvokerOptions invoker_options = {}) {
|
||||
typedef CallbackHolder<Sig> HolderT;
|
||||
HolderT* holder =
|
||||
new HolderT(isolate, std::move(callback), std::move(invoker_options));
|
||||
return {&Dispatcher<Sig>::DispatchToCallbackForProperty,
|
||||
gin::ConvertToV8<v8::Local<v8::External>>(
|
||||
isolate, holder->GetHandle(isolate))};
|
||||
}
|
||||
|
||||
// Base template - used only for non-member function pointers. Other types
|
||||
@@ -326,9 +413,9 @@ struct CallbackTraits<
|
||||
typename std::enable_if<std::is_member_function_pointer<T>::value>::type> {
|
||||
static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate,
|
||||
T callback) {
|
||||
int flags = HolderIsFirstArgument;
|
||||
InvokerOptions invoker_options = {.holder_is_first_argument = true};
|
||||
return gin_helper::CreateFunctionTemplate(
|
||||
isolate, base::BindRepeating(callback), flags);
|
||||
isolate, base::BindRepeating(callback), std::move(invoker_options));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -336,7 +336,8 @@ void ShowItemInFolder(const base::FilePath& full_path) {
|
||||
base::ThreadPool::CreateCOMSTATaskRunner(
|
||||
{base::MayBlock(), base::TaskPriority::USER_BLOCKING})
|
||||
->PostTask(FROM_HERE,
|
||||
base::BindOnce(&ShowItemInFolderOnWorkerThread, full_path));
|
||||
base::BindOnce(&ShowItemInFolderOnWorkerThread,
|
||||
full_path.NormalizePathSeparators()));
|
||||
}
|
||||
|
||||
void OpenPath(const base::FilePath& full_path, OpenCallback callback) {
|
||||
@@ -344,9 +345,11 @@ void OpenPath(const base::FilePath& full_path, OpenCallback callback) {
|
||||
|
||||
base::ThreadPool::CreateCOMSTATaskRunner(
|
||||
{base::MayBlock(), base::TaskPriority::USER_BLOCKING})
|
||||
->PostTaskAndReplyWithResult(FROM_HERE,
|
||||
base::BindOnce(&OpenPathOnThread, full_path),
|
||||
std::move(callback));
|
||||
->PostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&OpenPathOnThread,
|
||||
full_path.NormalizePathSeparators()),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void OpenExternal(const GURL& url,
|
||||
|
||||
@@ -68,19 +68,16 @@ NodeService::~NodeService() {
|
||||
}
|
||||
}
|
||||
|
||||
void NodeService::InitializeNetworkService(
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory,
|
||||
mojo::PendingRemote<network::mojom::HostResolver> host_resolver) {
|
||||
URLLoaderBundle::GetInstance()->SetURLLoaderFactory(
|
||||
std::move(url_loader_factory), mojo::Remote(std::move(host_resolver)));
|
||||
}
|
||||
|
||||
void NodeService::Initialize(node::mojom::NodeServiceParamsPtr params) {
|
||||
if (NodeBindings::IsInitialized())
|
||||
return;
|
||||
|
||||
ParentPort::GetInstance()->Initialize(std::move(params->port));
|
||||
|
||||
URLLoaderBundle::GetInstance()->SetURLLoaderFactory(
|
||||
std::move(params->url_loader_factory),
|
||||
mojo::Remote(std::move(params->host_resolver)));
|
||||
|
||||
js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop());
|
||||
|
||||
v8::HandleScope scope(js_env_->isolate());
|
||||
|
||||
@@ -57,11 +57,8 @@ class NodeService : public node::mojom::NodeService {
|
||||
NodeService(const NodeService&) = delete;
|
||||
NodeService& operator=(const NodeService&) = delete;
|
||||
|
||||
// mojom::NodeService:
|
||||
// mojom::NodeService implementation:
|
||||
void Initialize(node::mojom::NodeServiceParamsPtr params) override;
|
||||
void InitializeNetworkService(
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory,
|
||||
mojo::PendingRemote<network::mojom::HostResolver> host_resolver) override;
|
||||
|
||||
private:
|
||||
// This needs to be initialized first so that it can be destroyed last
|
||||
|
||||
@@ -15,10 +15,11 @@ struct NodeServiceParams {
|
||||
array<string> args;
|
||||
array<string> exec_args;
|
||||
blink.mojom.MessagePortDescriptor port;
|
||||
pending_remote<network.mojom.URLLoaderFactory> url_loader_factory;
|
||||
pending_remote<network.mojom.HostResolver> host_resolver;
|
||||
};
|
||||
|
||||
[ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox]
|
||||
interface NodeService {
|
||||
Initialize(NodeServiceParams params);
|
||||
InitializeNetworkService(pending_remote<network.mojom.URLLoaderFactory> url_loader_factory, pending_remote<network.mojom.HostResolver> host_resolver);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ const isScaleFactorRounding = () => {
|
||||
|
||||
const expectBoundsEqual = (actual: any, expected: any) => {
|
||||
if (!isScaleFactorRounding()) {
|
||||
expect(expected).to.deep.equal(actual);
|
||||
expect(actual).to.deep.equal(expected);
|
||||
} else if (Array.isArray(actual)) {
|
||||
expect(actual[0]).to.be.closeTo(expected[0], 1);
|
||||
expect(actual[1]).to.be.closeTo(expected[1], 1);
|
||||
|
||||
@@ -177,6 +177,38 @@ describe('debugger module', () => {
|
||||
await loadingFinished;
|
||||
});
|
||||
|
||||
it('can get and set cookies using the Storage API', async () => {
|
||||
await w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach('1.1');
|
||||
|
||||
await w.webContents.debugger.sendCommand('Storage.clearCookies', {});
|
||||
await w.webContents.debugger.sendCommand('Storage.setCookies', {
|
||||
cookies: [
|
||||
{
|
||||
name: 'cookieOne',
|
||||
value: 'cookieValueOne',
|
||||
url: 'https://cookieone.com'
|
||||
},
|
||||
{
|
||||
name: 'cookieTwo',
|
||||
value: 'cookieValueTwo',
|
||||
url: 'https://cookietwo.com'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { cookies } = await w.webContents.debugger.sendCommand('Storage.getCookies', {});
|
||||
expect(cookies).to.have.lengthOf(2);
|
||||
|
||||
const cookieOne = cookies.find((cookie: any) => cookie.name === 'cookieOne');
|
||||
expect(cookieOne.domain).to.equal('cookieone.com');
|
||||
expect(cookieOne.value).to.equal('cookieValueOne');
|
||||
|
||||
const cookieTwo = cookies.find((cookie: any) => cookie.name === 'cookieTwo');
|
||||
expect(cookieTwo.domain).to.equal('cookietwo.com');
|
||||
expect(cookieTwo.value).to.equal('cookieValueTwo');
|
||||
});
|
||||
|
||||
it('uses empty sessionId by default', async () => {
|
||||
w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach();
|
||||
|
||||
@@ -616,6 +616,25 @@ describe('net module (session)', () => {
|
||||
});
|
||||
}).to.throw('`partition` should be a string');
|
||||
});
|
||||
|
||||
it('should throw if given a header value that is empty(null/undefined)', () => {
|
||||
const emptyHeaderValues = [null, undefined];
|
||||
const errorMsg = '`value` required in setHeader("foo", value)';
|
||||
|
||||
for (const emptyValue of emptyHeaderValues) {
|
||||
expect(() => {
|
||||
net.request({
|
||||
url: 'https://foo',
|
||||
headers: { foo: emptyValue as any }
|
||||
} as any);
|
||||
}).to.throw(errorMsg);
|
||||
|
||||
const request = net.request({ url: 'https://foo' });
|
||||
expect(() => {
|
||||
request.setHeader('foo', emptyValue as any);
|
||||
}).to.throw(errorMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('net.fetch', () => {
|
||||
|
||||
@@ -935,12 +935,11 @@ describe('net module', () => {
|
||||
response.end();
|
||||
});
|
||||
const serverUrl = url.parse(serverUrlUnparsed);
|
||||
const options = {
|
||||
const urlRequest = net.request({
|
||||
port: serverUrl.port ? parseInt(serverUrl.port, 10) : undefined,
|
||||
hostname: '127.0.0.1',
|
||||
headers: { [customHeaderName]: customHeaderValue }
|
||||
};
|
||||
const urlRequest = net.request(options);
|
||||
});
|
||||
const response = await getResponse(urlRequest);
|
||||
expect(response.statusCode).to.be.equal(200);
|
||||
await collectStreamBody(response);
|
||||
|
||||
@@ -1613,7 +1613,7 @@ describe('session module', () => {
|
||||
|
||||
// NOTE: This API clears more than localStorage, but localStorage is a
|
||||
// convenient test target for this API
|
||||
it('clears localstorage data', async () => {
|
||||
it('clears all data when no options supplied', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
||||
await w.loadFile(path.join(fixtures, 'api', 'localstorage.html'));
|
||||
|
||||
@@ -1623,7 +1623,8 @@ describe('session module', () => {
|
||||
|
||||
expect(await w.webContents.executeJavaScript('localStorage.length')).to.equal(0);
|
||||
});
|
||||
it('clears localstorage data when called twice in parallel', async () => {
|
||||
|
||||
it('clears all data when no options supplied, called twice in parallel', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
||||
await w.loadFile(path.join(fixtures, 'api', 'localstorage.html'));
|
||||
|
||||
@@ -1638,5 +1639,92 @@ describe('session module', () => {
|
||||
// Await the first promise so it doesn't creep into another test
|
||||
await clearDataPromise;
|
||||
});
|
||||
|
||||
it('only clears specified data categories', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: { nodeIntegration: true, contextIsolation: false }
|
||||
});
|
||||
await w.loadFile(
|
||||
path.join(fixtures, 'api', 'localstorage-and-indexeddb.html')
|
||||
);
|
||||
|
||||
const { webContents } = w;
|
||||
const { session } = webContents;
|
||||
|
||||
await once(ipcMain, 'indexeddb-ready');
|
||||
|
||||
async function queryData (channel: string): Promise<string> {
|
||||
const event = once(ipcMain, `result-${channel}`);
|
||||
webContents.send(`get-${channel}`);
|
||||
return (await event)[1];
|
||||
}
|
||||
|
||||
// Data is in localStorage
|
||||
await expect(queryData('localstorage')).to.eventually.equal('hello localstorage');
|
||||
// Data is in indexedDB
|
||||
await expect(queryData('indexeddb')).to.eventually.equal('hello indexeddb');
|
||||
|
||||
// Clear only indexedDB, not localStorage
|
||||
await session.clearData({ dataTypes: ['indexedDB'] });
|
||||
|
||||
// The localStorage data should still be there
|
||||
await expect(queryData('localstorage')).to.eventually.equal('hello localstorage');
|
||||
|
||||
// The indexedDB data should be gone
|
||||
await expect(queryData('indexeddb')).to.eventually.be.undefined();
|
||||
});
|
||||
|
||||
it('only clears the specified origins', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const { session } = w.webContents;
|
||||
const { cookies } = session;
|
||||
|
||||
await Promise.all([
|
||||
cookies.set({
|
||||
url: 'https://example.com/',
|
||||
name: 'testdotcom',
|
||||
value: 'testdotcom'
|
||||
}),
|
||||
cookies.set({
|
||||
url: 'https://example.org/',
|
||||
name: 'testdotorg',
|
||||
value: 'testdotorg'
|
||||
})
|
||||
]);
|
||||
|
||||
await session.clearData({ origins: ['https://example.com'] });
|
||||
|
||||
expect((await cookies.get({ url: 'https://example.com/', name: 'testdotcom' })).length).to.equal(0);
|
||||
expect((await cookies.get({ url: 'https://example.org/', name: 'testdotorg' })).length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('clears all except the specified origins', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const { session } = w.webContents;
|
||||
const { cookies } = session;
|
||||
|
||||
await Promise.all([
|
||||
cookies.set({
|
||||
url: 'https://example.com/',
|
||||
name: 'testdotcom',
|
||||
value: 'testdotcom'
|
||||
}),
|
||||
cookies.set({
|
||||
url: 'https://example.org/',
|
||||
name: 'testdotorg',
|
||||
value: 'testdotorg'
|
||||
})
|
||||
]);
|
||||
|
||||
await session.clearData({ excludeOrigins: ['https://example.com'] });
|
||||
|
||||
expect((await cookies.get({ url: 'https://example.com/', name: 'testdotcom' })).length).to.be.greaterThan(0);
|
||||
expect((await cookies.get({ url: 'https://example.org/', name: 'testdotorg' })).length).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -548,6 +548,9 @@ describe('webContents module', () => {
|
||||
|
||||
describe('navigationHistory', () => {
|
||||
let w: BrowserWindow;
|
||||
const urlPage1 = 'data:text/html,<html><head><script>document.title = "Page 1";</script></head><body></body></html>';
|
||||
const urlPage2 = 'data:text/html,<html><head><script>document.title = "Page 2";</script></head><body></body></html>';
|
||||
const urlPage3 = 'data:text/html,<html><head><script>document.title = "Page 3";</script></head><body></body></html>';
|
||||
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({ show: false });
|
||||
@@ -559,44 +562,32 @@ describe('webContents module', () => {
|
||||
expect(result).to.deep.equal({ url: '', title: '' });
|
||||
});
|
||||
it('should fetch navigation entry given a valid index', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: 'https://www.google.com/', title: 'Google' });
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||
});
|
||||
it('should return null given an invalid index larger than history length', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(5);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(5);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
it('should return null given an invalid negative index', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(-1);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(-1);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory.getActiveIndex() API', () => {
|
||||
it('should return valid active index after a single page visit', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
|
||||
it('should return valid active index after a multiple page visits', async () => {
|
||||
const loadPromise = once(w.webContents, 'did-finish-load');
|
||||
await w.loadURL('https://www.github.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('https://www.google.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('about:blank');
|
||||
await loadPromise;
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(2);
|
||||
});
|
||||
@@ -608,20 +599,14 @@ describe('webContents module', () => {
|
||||
|
||||
describe('navigationHistory.length() API', () => {
|
||||
it('should return valid history length after a single page visit', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
|
||||
it('should return valid history length after a multiple page visits', async () => {
|
||||
const loadPromise = once(w.webContents, 'did-finish-load');
|
||||
await w.loadURL('https://www.github.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('https://www.google.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('about:blank');
|
||||
await loadPromise;
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(3);
|
||||
});
|
||||
|
||||
@@ -1383,6 +1383,138 @@ describe('chromium features', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Storage Access API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
session.defaultSession.setPermissionCheckHandler(null);
|
||||
session.defaultSession.setPermissionRequestHandler(null);
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can grant a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission === 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can deny a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission !== 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
});
|
||||
|
||||
describe('IdleDetection', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
|
||||
@@ -1285,6 +1285,16 @@ describe('chrome extensions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('globalParams', async () => {
|
||||
await w.loadURL(url);
|
||||
|
||||
const message = { method: 'globalParams' };
|
||||
w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
|
||||
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||
const response = JSON.parse(responseString);
|
||||
expect(response).to.deep.equal({ changed: true });
|
||||
});
|
||||
|
||||
it('insertCSS', async () => {
|
||||
await w.loadURL(url);
|
||||
|
||||
|
||||
50
spec/fixtures/api/localstorage-and-indexeddb.html
vendored
Normal file
50
spec/fixtures/api/localstorage-and-indexeddb.html
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const {ipcRenderer} = require('electron');
|
||||
|
||||
window.localStorage.setItem('test', 'hello localstorage');
|
||||
ipcRenderer.on('get-localstorage', () => {
|
||||
const result = window.localStorage.getItem('test');
|
||||
ipcRenderer.send('result-localstorage', result);
|
||||
});
|
||||
|
||||
const deleteRequest = window.indexedDB.deleteDatabase('testdb');
|
||||
deleteRequest.onerror = deleteRequest.onsuccess = (event) => {
|
||||
const openRequest = window.indexedDB.open('testdb');
|
||||
openRequest.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onerror = (event) => {
|
||||
console.error(event);
|
||||
};
|
||||
const objectStore = db.createObjectStore('testdata');
|
||||
objectStore.createIndex('test', '');
|
||||
};
|
||||
openRequest.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
const addRequest = db.transaction("testdata", "readwrite").objectStore("testdata").add("hello indexeddb", 'test');
|
||||
addRequest.onsuccess = () => {
|
||||
ipcRenderer.send('indexeddb-ready');
|
||||
};
|
||||
};
|
||||
};
|
||||
ipcRenderer.on('get-indexeddb', () => {
|
||||
const openRequest = window.indexedDB.open('testdb');
|
||||
openRequest.onerror = (event) => {
|
||||
console.error(event);
|
||||
};
|
||||
openRequest.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains('testdata')) {
|
||||
ipcRenderer.send('result-indexeddb', undefined);
|
||||
return;
|
||||
}
|
||||
const getRequest = db.transaction('testdata', 'readonly').objectStore('testdata').get('test');
|
||||
getRequest.onsuccess = (event) => {
|
||||
ipcRenderer.send('result-indexeddb', event.target.result);
|
||||
};
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -20,6 +20,27 @@ const handleRequest = async (request, sender, sendResponse) => {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'globalParams' : {
|
||||
await chrome.scripting.executeScript({
|
||||
target: { tabId },
|
||||
func: () => {
|
||||
chrome.scripting.globalParams.changed = true;
|
||||
},
|
||||
world: 'ISOLATED'
|
||||
});
|
||||
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId },
|
||||
func: () => JSON.stringify(chrome.scripting.globalParams),
|
||||
world: 'ISOLATED'
|
||||
});
|
||||
|
||||
const result = JSON.parse(results[0].result);
|
||||
|
||||
sendResponse(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'registerContentScripts': {
|
||||
await chrome.scripting.registerContentScripts([{
|
||||
id: 'session-script',
|
||||
|
||||
@@ -19,6 +19,11 @@ const map = {
|
||||
chrome.runtime.sendMessage({ method: 'insertCSS' }, response => {
|
||||
console.log(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
globalParams () {
|
||||
chrome.runtime.sendMessage({ method: 'globalParams' }, response => {
|
||||
console.log(JSON.stringify(response));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ import { HexColors, ScreenCapture } from './lib/screen-helpers';
|
||||
declare let WebView: any;
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
const isMacArm64 = (process.platform === 'darwin' && process.arch === 'arm64');
|
||||
|
||||
async function loadWebView (w: WebContents, attributes: Record<string, string>, opts?: {openDevTools?: boolean}): Promise<void> {
|
||||
const { openDevTools } = {
|
||||
openDevTools: false,
|
||||
@@ -2107,9 +2105,8 @@ describe('<webview> tag', function () {
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(miniak): figure out why this is failing on windows
|
||||
// TODO(vertedinde): figure out why this is failing on mac arm64
|
||||
ifdescribe(process.platform !== 'win32' && !isMacArm64)('<webview>.capturePage()', () => {
|
||||
// FIXME: This test is flaking constantly on Linux and macOS.
|
||||
xdescribe('<webview>.capturePage()', () => {
|
||||
it('returns a Promise with a NativeImage', async function () {
|
||||
this.retries(5);
|
||||
|
||||
|
||||
65
yarn.lock
65
yarn.lock
@@ -199,10 +199,10 @@
|
||||
"@octokit/auth-app" "^4.0.13"
|
||||
"@octokit/rest" "^19.0.11"
|
||||
|
||||
"@electron/lint-roller@^1.9.0":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.11.1.tgz#bf4ab114e8cb3a77e2c634807d4af3d40c3ba863"
|
||||
integrity sha512-LfErOK5MnSmoSyVN5OnHWsQ8p5It8JBE0AmRYCx65WS4BZudnYeRcohlRSOSi+GGqldujdKHyEo+fdY5lREL/Q==
|
||||
"@electron/lint-roller@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.12.1.tgz#3152b9a68815b2ab51cc5a4d462ae6769d5052ce"
|
||||
integrity sha512-TGgVcHUAooM9/dV3iJTxhmKl35x/gzStsClz2/LWtBQZ59cRK+0YwWF5HWhtydGFIpOLEQGzCvUrty5zZLyd4w==
|
||||
dependencies:
|
||||
"@dsanders11/vscode-markdown-languageservice" "^0.3.0"
|
||||
balanced-match "^2.0.0"
|
||||
@@ -1599,13 +1599,13 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
body-parser@1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
|
||||
body-parser@1.20.2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.4"
|
||||
content-type "~1.0.5"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
@@ -1613,7 +1613,7 @@ body-parser@1.20.1:
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
raw-body "2.5.1"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
@@ -2017,20 +2017,20 @@ content-disposition@0.5.4:
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
content-type@~1.0.4, content-type@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
@@ -2071,14 +2071,7 @@ debug@^3.1.0, debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.0.0:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@@ -2808,17 +2801,17 @@ execa@^4.0.1:
|
||||
signal-exit "^3.0.2"
|
||||
strip-final-newline "^2.0.0"
|
||||
|
||||
express@^4.16.4:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
express@^4.19.2:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.1"
|
||||
body-parser "1.20.2"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
@@ -5243,10 +5236,10 @@ range-parser@~1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
|
||||
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
|
||||
raw-body@2.5.2:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
|
||||
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user