зеркало из https://github.com/electron/electron.git
docs: rework sandbox guide (#28978)
* docs: rework sandbox guide * update doc name * add missing comment to code sample * Update docs/tutorial/sandbox.md Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com> * Update docs/tutorial/sandbox.md Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com> * Update docs/tutorial/sandbox.md Co-authored-by: Biru Mohanathas <birunthan@mohanathas.com> * load https in the examples * change `process` docs to Electron's * remove bit on chrome://sandbox page * Update docs/tutorial/sandbox.md Co-authored-by: Jeremy Rose <nornagon@nornagon.net> * Update docs/tutorial/sandbox.md Co-authored-by: Jeremy Rose <nornagon@nornagon.net> * clarify sandbox default posture * clarify tasks sandboxed renderers need ipc for * clarify polyfilled preload environment * emphasize that --no-sandbox is bad * clarify preload polyfill `require` * format markdown references properly Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com> Co-authored-by: Biru Mohanathas <birunthan@mohanathas.com> Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
This commit is contained in:
Родитель
a264fc891d
Коммит
8f8708680f
|
@ -59,6 +59,7 @@ an issue:
|
||||||
* [Using Native Node.js Modules](tutorial/using-native-node-modules.md)
|
* [Using Native Node.js Modules](tutorial/using-native-node-modules.md)
|
||||||
* [Performance Strategies](tutorial/performance.md)
|
* [Performance Strategies](tutorial/performance.md)
|
||||||
* [Security Strategies](tutorial/security.md)
|
* [Security Strategies](tutorial/security.md)
|
||||||
|
* [Process Sandboxing](tutorial/sandbox.md)
|
||||||
* [Accessibility](tutorial/accessibility.md)
|
* [Accessibility](tutorial/accessibility.md)
|
||||||
* [Manually Enabling Accessibility Features](tutorial/accessibility.md#manually-enabling-accessibility-features)
|
* [Manually Enabling Accessibility Features](tutorial/accessibility.md#manually-enabling-accessibility-features)
|
||||||
* [Testing and Debugging](tutorial/application-debugging.md)
|
* [Testing and Debugging](tutorial/application-debugging.md)
|
||||||
|
|
|
@ -272,7 +272,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
associated with the window, making it compatible with the Chromium
|
associated with the window, making it compatible with the Chromium
|
||||||
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
||||||
the `nodeIntegration` option and the APIs available to the preload script
|
the `nodeIntegration` option and the APIs available to the preload script
|
||||||
are more limited. Read more about the option [here](sandbox-option.md).
|
are more limited. Read more about the option [here](../tutorial/sandbox.md).
|
||||||
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
||||||
page. Instead of passing the Session object directly, you can also choose to
|
page. Instead of passing the Session object directly, you can also choose to
|
||||||
use the `partition` option instead, which accepts a partition string. When
|
use the `partition` option instead, which accepts a partition string. When
|
||||||
|
|
|
@ -137,7 +137,8 @@ proxy server flags that are passed.
|
||||||
|
|
||||||
### --no-sandbox
|
### --no-sandbox
|
||||||
|
|
||||||
Disables Chromium sandbox, which is now enabled by default.
|
Disables the Chromium [sandbox](https://www.chromium.org/developers/design-documents/sandbox).
|
||||||
|
Forces renderer process and Chromium helper processes to run un-sandboxed.
|
||||||
Should only be used for testing.
|
Should only be used for testing.
|
||||||
|
|
||||||
### --proxy-bypass-list=`hosts`
|
### --proxy-bypass-list=`hosts`
|
||||||
|
|
|
@ -1,182 +0,0 @@
|
||||||
# `sandbox` Option
|
|
||||||
|
|
||||||
> Create a browser window with a sandboxed renderer. With this
|
|
||||||
option enabled, the renderer must communicate via IPC to the main process in order to access node APIs.
|
|
||||||
|
|
||||||
One of the key security features of Chromium is that all blink rendering/JavaScript
|
|
||||||
code is executed within a sandbox. This sandbox uses OS-specific features to ensure
|
|
||||||
that exploits in the renderer process cannot harm the system.
|
|
||||||
|
|
||||||
In other words, when the sandbox is enabled, the renderers can only make changes
|
|
||||||
to the system by delegating tasks to the main process via IPC.
|
|
||||||
[Here's](https://www.chromium.org/developers/design-documents/sandbox) more
|
|
||||||
information about the sandbox.
|
|
||||||
|
|
||||||
Since a major feature in Electron is the ability to run Node.js in the
|
|
||||||
renderer process (making it easier to develop desktop applications using web
|
|
||||||
technologies), the sandbox is disabled by electron. This is because
|
|
||||||
most Node.js APIs require system access. `require()` for example, is not
|
|
||||||
possible without file system permissions, which are not available in a sandboxed
|
|
||||||
environment.
|
|
||||||
|
|
||||||
Usually this is not a problem for desktop applications since the code is always
|
|
||||||
trusted, but it makes Electron less secure than Chromium for displaying
|
|
||||||
untrusted web content. For applications that require more security, the
|
|
||||||
`sandbox` flag will force Electron to spawn a classic Chromium renderer that is
|
|
||||||
compatible with the sandbox.
|
|
||||||
|
|
||||||
A sandboxed renderer doesn't have a Node.js environment running and doesn't
|
|
||||||
expose Node.js JavaScript APIs to client code. The only exception is the preload script,
|
|
||||||
which has access to a subset of the Electron renderer API.
|
|
||||||
|
|
||||||
Another difference is that sandboxed renderers don't modify any of the default
|
|
||||||
JavaScript APIs. Consequently, some APIs such as `window.open` will work as they
|
|
||||||
do in Chromium (i.e. they do not return a [`BrowserWindowProxy`](browser-window-proxy.md)).
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
To create a sandboxed window, pass `sandbox: true` to `webPreferences`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let win
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
win = new BrowserWindow({
|
|
||||||
webPreferences: {
|
|
||||||
sandbox: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
win.loadURL('http://google.com')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
In the above code the [`BrowserWindow`](browser-window.md) that was created has Node.js disabled and can communicate
|
|
||||||
only via IPC. The use of this option stops Electron from creating a Node.js runtime in the renderer. Also,
|
|
||||||
within this new window `window.open` follows the native behavior (by default Electron creates a [`BrowserWindow`](browser-window.md)
|
|
||||||
and returns a proxy to this via `window.open`).
|
|
||||||
|
|
||||||
[`app.enableSandbox`](app.md#appenablesandbox) can be used to force `sandbox: true` for all `BrowserWindow` instances.
|
|
||||||
|
|
||||||
```js
|
|
||||||
let win
|
|
||||||
app.enableSandbox()
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
// no need to pass `sandbox: true` since `app.enableSandbox()` was called.
|
|
||||||
win = new BrowserWindow()
|
|
||||||
win.loadURL('http://google.com')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Preload
|
|
||||||
|
|
||||||
An app can make customizations to sandboxed renderers using a preload script.
|
|
||||||
Here's an example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let win
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
win = new BrowserWindow({
|
|
||||||
webPreferences: {
|
|
||||||
sandbox: true,
|
|
||||||
preload: path.join(app.getAppPath(), 'preload.js')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
win.loadURL('http://google.com')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
and preload.js:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// This file is loaded whenever a javascript context is created. It runs in a
|
|
||||||
// private scope that can access a subset of Electron renderer APIs. Without
|
|
||||||
// contextIsolation enabled, it's possible to accidentally leak privileged
|
|
||||||
// globals like ipcRenderer to web content.
|
|
||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
const defaultWindowOpen = window.open
|
|
||||||
|
|
||||||
window.open = function customWindowOpen (url, ...args) {
|
|
||||||
ipcRenderer.send('report-window-open', location.origin, url, args)
|
|
||||||
return defaultWindowOpen(url + '?from_electron=1', ...args)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Important things to notice in the preload script:
|
|
||||||
|
|
||||||
- Even though the sandboxed renderer doesn't have Node.js running, it still has
|
|
||||||
access to a limited node-like environment: `Buffer`, `process`, `setImmediate`,
|
|
||||||
`clearImmediate` and `require` are available.
|
|
||||||
- The preload script must be contained in a single script, but it is possible to have
|
|
||||||
complex preload code composed with multiple modules by using a tool like
|
|
||||||
webpack or browserify. An example of using browserify is below.
|
|
||||||
|
|
||||||
To create a browserify bundle and use it as a preload script, something like
|
|
||||||
the following should be used:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
browserify preload/index.js \
|
|
||||||
-x electron \
|
|
||||||
--insert-global-vars=__filename,__dirname -o preload.js
|
|
||||||
```
|
|
||||||
|
|
||||||
The `-x` flag should be used with any required module that is already exposed in
|
|
||||||
the preload scope, and tells browserify to use the enclosing `require` function
|
|
||||||
for it. `--insert-global-vars` will ensure that `process`, `Buffer` and
|
|
||||||
`setImmediate` are also taken from the enclosing scope(normally browserify
|
|
||||||
injects code for those).
|
|
||||||
|
|
||||||
Currently the `require` function provided in the preload scope exposes the
|
|
||||||
following modules:
|
|
||||||
|
|
||||||
- `electron`
|
|
||||||
- `crashReporter`
|
|
||||||
- `desktopCapturer`
|
|
||||||
- `ipcRenderer`
|
|
||||||
- `nativeImage`
|
|
||||||
- `webFrame`
|
|
||||||
- `events`
|
|
||||||
- `timers`
|
|
||||||
- `url`
|
|
||||||
|
|
||||||
More may be added as needed to expose more Electron APIs in the sandbox.
|
|
||||||
|
|
||||||
## Rendering untrusted content
|
|
||||||
|
|
||||||
Rendering untrusted content in Electron is still somewhat uncharted territory,
|
|
||||||
though some apps are finding success (e.g. Beaker Browser). Our goal is to get
|
|
||||||
as close to Chrome as we can in terms of the security of sandboxed content, but
|
|
||||||
ultimately we will always be behind due to a few fundamental issues:
|
|
||||||
|
|
||||||
1. We do not have the dedicated resources or expertise that Chromium has to
|
|
||||||
apply to the security of its product. We do our best to make use of what we
|
|
||||||
have, to inherit everything we can from Chromium, and to respond quickly to
|
|
||||||
security issues, but Electron cannot be as secure as Chromium without the
|
|
||||||
resources that Chromium is able to dedicate.
|
|
||||||
2. Some security features in Chrome (such as Safe Browsing and Certificate
|
|
||||||
Transparency) require a centralized authority and dedicated servers, both of
|
|
||||||
which run counter to the goals of the Electron project. As such, we disable
|
|
||||||
those features in Electron, at the cost of the associated security they
|
|
||||||
would otherwise bring.
|
|
||||||
3. There is only one Chromium, whereas there are many thousands of apps built
|
|
||||||
on Electron, all of which behave slightly differently. Accounting for those
|
|
||||||
differences can yield a huge possibility space, and make it challenging to
|
|
||||||
ensure the security of the platform in unusual use cases.
|
|
||||||
4. We can't push security updates to users directly, so we rely on app vendors
|
|
||||||
to upgrade the version of Electron underlying their app in order for
|
|
||||||
security updates to reach users.
|
|
||||||
|
|
||||||
Here are some things to consider before rendering untrusted content:
|
|
||||||
|
|
||||||
- A preload script can accidentally leak privileged APIs to untrusted code,
|
|
||||||
unless [`contextIsolation`](../tutorial/security.md#3-enable-context-isolation-for-remote-content)
|
|
||||||
is also enabled.
|
|
||||||
- Some bug in the V8 engine may allow malicious code to access the renderer
|
|
||||||
preload APIs, effectively granting full access to the system through the
|
|
||||||
`remote` module. Therefore, it is highly recommended to [disable the `remote`
|
|
||||||
module](../tutorial/security.md#15-disable-the-remote-module).
|
|
||||||
If disabling is not feasible, you should selectively [filter the `remote`
|
|
||||||
module](../tutorial/security.md#16-filter-the-remote-module).
|
|
||||||
- While we make our best effort to backport Chromium security fixes to older
|
|
||||||
versions of Electron, we do not make a guarantee that every fix will be
|
|
||||||
backported. Your best chance at staying secure is to be on the latest stable
|
|
||||||
version of Electron.
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
# Process Sandboxing
|
||||||
|
|
||||||
|
One key security feature in Chromium is that processes can be executed within a sandbox.
|
||||||
|
The sandbox limits the harm that malicious code can cause by limiting access to most
|
||||||
|
system resources — sandboxed processes can only freely use CPU cycles and memory.
|
||||||
|
In order to perform operations requiring additional privilege, sandboxed processes
|
||||||
|
use dedicated communication channels to delegate tasks to more privileged processes.
|
||||||
|
|
||||||
|
In Chromium, sandboxing is applied to most processes other than the main process.
|
||||||
|
This includes renderer processes, as well as utility processes such as the audio service,
|
||||||
|
the GPU service and the network service.
|
||||||
|
|
||||||
|
See Chromium's [Sandbox design document][sandbox] for more information.
|
||||||
|
|
||||||
|
## Electron's sandboxing policies
|
||||||
|
|
||||||
|
Electron comes with a mixed sandbox environment, meaning sandboxed processes can run
|
||||||
|
alongside privileged ones. By default, renderer processes are not sandboxed, but
|
||||||
|
utility processes are. Note that as in Chromium, the main (browser) process is
|
||||||
|
privileged and cannot be sandboxed.
|
||||||
|
|
||||||
|
Historically, this mixed sandbox approach was established because having Node.js available
|
||||||
|
in the renderer is an extremely powerful tool for app developers. Unfortunately, this
|
||||||
|
feature is also an equally massive security vulnerability.
|
||||||
|
|
||||||
|
Theoretically, unsandboxed renderers are not a problem for desktop applications that
|
||||||
|
only display trusted code, but they make Electron less secure than Chromium for
|
||||||
|
displaying untrusted web content. However, even purportedly trusted code may be
|
||||||
|
dangerous — there are countless attack vectors that malicious actors can use, from
|
||||||
|
cross-site scripting to content injection to man-in-the-middle attacks on remotely loaded
|
||||||
|
websites, just to name a few. For this reason, we recommend enabling renderer sandboxing
|
||||||
|
for the vast majority of cases under an abundance of caution.
|
||||||
|
|
||||||
|
<!--TODO: update this guide when #28466 is either solved or closed -->
|
||||||
|
Note that there is an active discussion in the issue tracker to enable renderer sandboxing
|
||||||
|
by default. See [#28466][issue-28466]) for details.
|
||||||
|
|
||||||
|
## Sandbox behaviour in Electron
|
||||||
|
|
||||||
|
Sandboxed processes in Electron behave _mostly_ in the same way as Chromium's do, but
|
||||||
|
Electron has a few additional concepts to consider because it interfaces with Node.js.
|
||||||
|
|
||||||
|
### Renderer processes
|
||||||
|
|
||||||
|
When renderer processes in Electron are sandboxed, they behave in the same way as a
|
||||||
|
regular Chrome renderer would. A sandboxed renderer won't have a Node.js
|
||||||
|
environment initialized.
|
||||||
|
|
||||||
|
<!-- TODO(erickzhao): when we have a solid guide for IPC, link it here -->
|
||||||
|
Therefore, when the sandbox is enabled, renderer processes can only perform privileged
|
||||||
|
tasks (such as interacting with the filesystem, making changes to the system, or spawning
|
||||||
|
subprocesses) by delegating these tasks to the main process via inter-process
|
||||||
|
communication (IPC).
|
||||||
|
|
||||||
|
### Preload scripts
|
||||||
|
|
||||||
|
In order to allow renderer processes to communicate with the main process, preload
|
||||||
|
scripts attached to sandboxed renderers will still have a polyfilled subset of Node.js
|
||||||
|
APIs available. A `require` function similar to Node's `require` module is exposed,
|
||||||
|
but can only import a subset of Electron and Node's built-in modules:
|
||||||
|
|
||||||
|
* `electron` (only renderer process modules)
|
||||||
|
* [`events`](https://nodejs.org/api/events.html)
|
||||||
|
* [`timers`](https://nodejs.org/api/timers.html)
|
||||||
|
* [`url`](https://nodejs.org/api/url.html)
|
||||||
|
|
||||||
|
In addition, the preload script also polyfills certain Node.js primitives as globals:
|
||||||
|
|
||||||
|
* [`Buffer`](https://nodejs.org/api/Buffer.html)
|
||||||
|
* [`process`](../api/process.md)
|
||||||
|
* [`clearImmediate`](https://nodejs.org/api/timers.html#timers_clearimmediate_immediate)
|
||||||
|
* [`setImmediate`](https://nodejs.org/api/timers.html#timers_setimmediate_callback_args)
|
||||||
|
|
||||||
|
Because the `require` function is a polyfill with limited functionality, you will not be
|
||||||
|
able to use [CommonJS modules][commonjs] to separate your preload script into multiple
|
||||||
|
files. If you need to split your preload code, use a bundler such as [webpack][webpack]
|
||||||
|
or [Parcel][parcel].
|
||||||
|
|
||||||
|
Note that because the environment presented to the `preload` script is substantially
|
||||||
|
more privileged than that of a sandboxed renderer, it is still possible to leak
|
||||||
|
privileged APIs to untrusted code running in the renderer process unless
|
||||||
|
[`contextIsolation`][contextIsolation] is enabled.
|
||||||
|
|
||||||
|
## Configuring the sandbox
|
||||||
|
|
||||||
|
### Enabling the sandbox for a single process
|
||||||
|
|
||||||
|
In Electron, renderer sandboxing can be enabled on a per-process basis with
|
||||||
|
the `sandbox: true` preference in the [`BrowserWindow`][browser-window] constructor.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// main.js
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
win.loadURL('https://google.com')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enabling the sandbox globally
|
||||||
|
|
||||||
|
If you want to force sandboxing for all renderers, you can also use the
|
||||||
|
[`app.enableSandbox`][enable-sandbox] API. Note that this API has to be called before the
|
||||||
|
app's `ready` event.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// main.js
|
||||||
|
app.enableSandbox()
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
// no need to pass `sandbox: true` since `app.enableSandbox()` was called.
|
||||||
|
const win = new BrowserWindow()
|
||||||
|
win.loadURL('https://google.com')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disabling Chromium's sandbox (testing only)
|
||||||
|
|
||||||
|
You can also disable Chromium's sandbox entirely with the [`--no-sandbox`][no-sandbox]
|
||||||
|
CLI flag, which will disable the sandbox for all processes (including utility processes).
|
||||||
|
We highly recommend that you only use this flag for testing purposes, and **never**
|
||||||
|
in production.
|
||||||
|
|
||||||
|
Note that the `sandbox: true` option will still disable the renderer's Node.js
|
||||||
|
environment.
|
||||||
|
|
||||||
|
## A note on rendering untrusted content
|
||||||
|
|
||||||
|
Rendering untrusted content in Electron is still somewhat uncharted territory,
|
||||||
|
though some apps are finding success (e.g. [Beaker Browser][beaker]).
|
||||||
|
Our goal is to get as close to Chrome as we can in terms of the security of
|
||||||
|
sandboxed content, but ultimately we will always be behind due to a few fundamental
|
||||||
|
issues:
|
||||||
|
|
||||||
|
1. We do not have the dedicated resources or expertise that Chromium has to
|
||||||
|
apply to the security of its product. We do our best to make use of what we
|
||||||
|
have, to inherit everything we can from Chromium, and to respond quickly to
|
||||||
|
security issues, but Electron cannot be as secure as Chromium without the
|
||||||
|
resources that Chromium is able to dedicate.
|
||||||
|
2. Some security features in Chrome (such as Safe Browsing and Certificate
|
||||||
|
Transparency) require a centralized authority and dedicated servers, both of
|
||||||
|
which run counter to the goals of the Electron project. As such, we disable
|
||||||
|
those features in Electron, at the cost of the associated security they
|
||||||
|
would otherwise bring.
|
||||||
|
3. There is only one Chromium, whereas there are many thousands of apps built
|
||||||
|
on Electron, all of which behave slightly differently. Accounting for those
|
||||||
|
differences can yield a huge possibility space, and make it challenging to
|
||||||
|
ensure the security of the platform in unusual use cases.
|
||||||
|
4. We can't push security updates to users directly, so we rely on app vendors
|
||||||
|
to upgrade the version of Electron underlying their app in order for
|
||||||
|
security updates to reach users.
|
||||||
|
|
||||||
|
While we make our best effort to backport Chromium security fixes to older
|
||||||
|
versions of Electron, we do not make a guarantee that every fix will be
|
||||||
|
backported. Your best chance at staying secure is to be on the latest stable
|
||||||
|
version of Electron.
|
||||||
|
|
||||||
|
[sandbox]: https://chromium.googlesource.com/chromium/src/+/master/docs/design/sandbox.md
|
||||||
|
[issue-28466]: https://github.com/electron/electron/issues/28466
|
||||||
|
[browser-window]: ../api/browser-window.md
|
||||||
|
[enable-sandbox]: ../api/app.md#appenablesandbox
|
||||||
|
[no-sandbox]: ../api/command-line-switches.md#--no-sandbox
|
||||||
|
[commonjs]: https://nodejs.org/api/modules.html#modules_modules_commonjs_modules
|
||||||
|
[webpack]: https://webpack.js.org/
|
||||||
|
[parcel]: https://parceljs.org/
|
||||||
|
[context-isolation]: ./context-isolation.md
|
||||||
|
[beaker]: https://github.com/beakerbrowser/beaker
|
|
@ -692,5 +692,5 @@ which potential security issues are not as widely known.
|
||||||
[window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
|
[window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
|
||||||
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
||||||
[open-external]: ../api/shell.md#shellopenexternalurl-options
|
[open-external]: ../api/shell.md#shellopenexternalurl-options
|
||||||
[sandbox]: ../api/sandbox-option.md
|
[sandbox]: ../tutorial/sandbox.md
|
||||||
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
||||||
|
|
|
@ -42,7 +42,6 @@ auto_filenames = {
|
||||||
"docs/api/power-save-blocker.md",
|
"docs/api/power-save-blocker.md",
|
||||||
"docs/api/process.md",
|
"docs/api/process.md",
|
||||||
"docs/api/protocol.md",
|
"docs/api/protocol.md",
|
||||||
"docs/api/sandbox-option.md",
|
|
||||||
"docs/api/screen.md",
|
"docs/api/screen.md",
|
||||||
"docs/api/service-workers.md",
|
"docs/api/service-workers.md",
|
||||||
"docs/api/session.md",
|
"docs/api/session.md",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче