From 0a9438dbba36f741d442fc74569126363eedfb9e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 28 Jun 2019 16:25:30 +0900 Subject: [PATCH] docs: documentation of NetworkService-based protocol module (#18952) * docs: NetworkService-based protocol module * docs: separate ProtocolRequest * docs: separate ProtocolResponse * docs: fix lint warning * docs: fix electron.d.ts * fix: print deprecation warnings for protocol module * docs: fix links * Apply suggestions from code review Co-Authored-By: Felix Rieseberg * Apply suggestions from code review Co-Authored-By: Samuel Attard * Do not publish NetworkService changes draft * Apply suggestions from code review Co-Authored-By: Samuel Attard * docs: filePath must be absolute --- docs/api/breaking-changes-ns.md | 61 +++++ docs/api/protocol-ns.md | 309 ++++++++++++++++++++++ docs/api/structures/protocol-request.md | 6 + docs/api/structures/protocol-response.md | 36 +++ filenames.auto.gni | 4 + package.json | 4 +- shell/browser/api/atom_api_protocol_ns.cc | 17 +- shell/browser/api/atom_api_protocol_ns.h | 3 +- yarn.lock | 17 +- 9 files changed, 451 insertions(+), 6 deletions(-) create mode 100644 docs/api/breaking-changes-ns.md create mode 100644 docs/api/protocol-ns.md create mode 100644 docs/api/structures/protocol-request.md create mode 100644 docs/api/structures/protocol-response.md diff --git a/docs/api/breaking-changes-ns.md b/docs/api/breaking-changes-ns.md new file mode 100644 index 000000000000..1914c1f43f44 --- /dev/null +++ b/docs/api/breaking-changes-ns.md @@ -0,0 +1,61 @@ +# Breaking changes (NetworkService) (Draft) + +This document describes changes to Electron APIs after migrating network code +to NetworkService API. + +We don't currently have an estimate of when we will enable `NetworkService` by +default in Electron, but as Chromium is already removing non-`NetworkService` +code, we might switch before Electron 10. + +The content of this document should be moved to `breaking-changes.md` once we have +determined when to enable `NetworkService` in Electron. + +## Planned Breaking API Changes + +### `protocol.unregisterProtocol` +### `protocol.uninterceptProtocol` + +The APIs are now synchronous and the optional callback is no longer needed. + +```javascript +// Deprecated +protocol.unregisterProtocol(scheme, () => { /* ... */ }) +// Replace with +protocol.unregisterProtocol(scheme) +``` + +### `protocol.registerFileProtocol` +### `protocol.registerBufferProtocol` +### `protocol.registerStringProtocol` +### `protocol.registerHttpProtocol` +### `protocol.registerStreamProtocol` +### `protocol.interceptFileProtocol` +### `protocol.interceptStringProtocol` +### `protocol.interceptBufferProtocol` +### `protocol.interceptHttpProtocol` +### `protocol.interceptStreamProtocol` + +The APIs are now synchronous and the optional callback is no longer needed. + +```javascript +// Deprecated +protocol.registerFileProtocol(scheme, handler, () => { /* ... */ }) +// Replace with +protocol.registerFileProtocol(scheme, handler) +``` + +The registered or intercepted protocol does not have effect on current page +until navigation happens. + +### `protocol.isProtocolHandled` + +This API is deprecated and users should use `protocol.isProtocolRegistered` +and `protocol.isProtocolIntercepted` instead. + +```javascript +// Deprecated +protocol.isProtocolHandled(scheme).then(() => { /* ... */ }) +// Replace with +const isRegistered = protocol.isProtocolRegistered(scheme) +const isIntercepted = protocol.isProtocolIntercepted(scheme) +``` diff --git a/docs/api/protocol-ns.md b/docs/api/protocol-ns.md new file mode 100644 index 000000000000..45a96f4511da --- /dev/null +++ b/docs/api/protocol-ns.md @@ -0,0 +1,309 @@ +# protocol (NetworkService) (Draft) + +This document describes the new protocol APIs based on the [NetworkService](https://www.chromium.org/servicification). + +We don't currently have an estimate of when we will enable the `NetworkService` by +default in Electron, but as Chromium is already removing non-`NetworkService` +code, we will probably switch before Electron 10. + +The content of this document should be moved to `protocol.md` after we have +enabled the `NetworkService` by default in Electron. + +> Register a custom protocol and intercept existing protocol requests. + +Process: [Main](../glossary.md#main-process) + +An example of implementing a protocol that has the same effect as the +`file://` protocol: + +```javascript +const { app, protocol } = require('electron') +const path = require('path') + +app.on('ready', () => { + protocol.registerFileProtocol('atom', (request, callback) => { + const url = request.url.substr(7) + callback({ path: path.normalize(`${__dirname}/${url}`) }) + }) +}) +``` + +**Note:** All methods unless specified can only be used after the `ready` event +of the `app` module gets emitted. + +## Using `protocol` with a custom `partition` or `session` + +A protocol is registered to a specific Electron [`session`](./session.md) +object. If you don't specify a session, then your `protocol` will be applied to +the default session that Electron uses. However, if you define a `partition` or +`session` on your `browserWindow`'s `webPreferences`, then that window will use +a different session and your custom protocol will not work if you just use +`electron.protocol.XXX`. + +To have your custom protocol work in combination with a custom session, you need +to register it to that session explicitly. + +```javascript +const { session, app, protocol } = require('electron') +const path = require('path') + +app.on('ready', () => { + const partition = 'persist:example' + const ses = session.fromPartition(partition) + + ses.protocol.registerFileProtocol('atom', (request, callback) => { + const url = request.url.substr(7) + callback({ path: path.normalize(`${__dirname}/${url}`) }) + }) + + mainWindow = new BrowserWindow({ webPreferences: { partition } }) +}) +``` + +## Methods + +The `protocol` module has the following methods: + +### `protocol.registerSchemesAsPrivileged(customSchemes)` + +* `customSchemes` [CustomScheme[]](structures/custom-scheme.md) + +**Note:** This method can only be used before the `ready` event of the `app` +module gets emitted and can be called only once. + +Registers the `scheme` as standard, secure, bypasses content security policy for +resources, allows registering ServiceWorker and supports fetch API. Specify a +privilege with the value of `true` to enable the capability. + +An example of registering a privileged scheme, that bypasses Content Security +Policy: + +```javascript +const { protocol } = require('electron') +protocol.registerSchemesAsPrivileged([ + { scheme: 'foo', privileges: { bypassCSP: true } } +]) +``` + +A standard scheme adheres to what RFC 3986 calls [generic URI +syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and +`https` are standard schemes, while `file` is not. + +Registering a scheme as standard allows relative and absolute resources to +be resolved correctly when served. Otherwise the scheme will behave like the +`file` protocol, but without the ability to resolve relative URLs. + +For example when you load following page with custom protocol without +registering it as standard scheme, the image will not be loaded because +non-standard schemes can not recognize relative URLs: + +```html + + + +``` + +Registering a scheme as standard will allow access to files through the +[FileSystem API][file-system-api]. Otherwise the renderer will throw a security +error for the scheme. + +By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB, +cookies) are disabled for non standard schemes. So in general if you want to +register a custom protocol to replace the `http` protocol, you have to register +it as a standard scheme. + +### `protocol.registerFileProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Registers a protocol of `scheme` that will send a file as the response. The +`handler` will be called with `request` and `callback` where `request` is +an incoming request for the `scheme`. + +To handle the `request`, the `callback` should be called with either the file's +path or an object that has a `path` property, e.g. `callback(filePath)` or +`callback({ path: filePath })`. The `filePath` must be an absolute path. + +By default the `scheme` is treated like `http:`, which is parsed differently +from protocols that follow the "generic URI syntax" like `file:`. + +### `protocol.registerBufferProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (Buffer | [ProtocolResponse](structures/protocol-response.md)) + +Registers a protocol of `scheme` that will send a `Buffer` as a response. + +The usage is the same with `registerFileProtocol`, except that the `callback` +should be called with either a `Buffer` object or an object that has the `data` +property. + +Example: + +```javascript +protocol.registerBufferProtocol('atom', (request, callback) => { + callback({ mimeType: 'text/html', data: Buffer.from('
Response
') }) +}) +``` + +### `protocol.registerStringProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Registers a protocol of `scheme` that will send a `String` as a response. + +The usage is the same with `registerFileProtocol`, except that the `callback` +should be called with either a `String` or an object that has the `data` +property. + +### `protocol.registerHttpProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` ProtocolResponse + +Registers a protocol of `scheme` that will send an HTTP request as a response. + +The usage is the same with `registerFileProtocol`, except that the `callback` +should be called with an object that has the `url` property. + +### `protocol.registerStreamProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md)) + +Registers a protocol of `scheme` that will send a stream as a response. + +The usage is the same with `registerFileProtocol`, except that the +`callback` should be called with either a [`ReadableStream`](https://nodejs.org/api/stream.html#stream_class_stream_readable) object or an object that +has the `data` property. + +Example: + +```javascript +const { protocol } = require('electron') +const { PassThrough } = require('stream') + +function createStream (text) { + const rv = new PassThrough() // PassThrough is also a Readable stream + rv.push(text) + rv.push(null) + return rv +} + +protocol.registerStreamProtocol('atom', (request, callback) => { + callback({ + statusCode: 200, + headers: { + 'content-type': 'text/html' + }, + data: createStream('
Response
') + }) +}) +``` + +It is possible to pass any object that implements the readable stream API (emits +`data`/`end`/`error` events). For example, here's how a file could be returned: + +```javascript +protocol.registerStreamProtocol('atom', (request, callback) => { + callback(fs.createReadStream('index.html')) +}) +``` + +### `protocol.unregisterProtocol(scheme)` + +* `scheme` String + +Unregisters the custom protocol of `scheme`. + +### `protocol.isProtocolRegistered(scheme)` + +* `scheme` String + +Returns `Boolean` - Whether `scheme` is already registered. + +### `protocol.interceptFileProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Intercepts `scheme` protocol and uses `handler` as the protocol's new handler +which sends a file as a response. + +### `protocol.interceptStringProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Intercepts `scheme` protocol and uses `handler` as the protocol's new handler +which sends a `String` as a response. + +### `protocol.interceptBufferProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (Buffer | [ProtocolResponse](structures/protocol-response.md)) + +Intercepts `scheme` protocol and uses `handler` as the protocol's new handler +which sends a `Buffer` as a response. + +### `protocol.interceptHttpProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` ProtocolResponse + +Intercepts `scheme` protocol and uses `handler` as the protocol's new handler +which sends a new HTTP request as a response. + +### `protocol.interceptStreamProtocol(scheme, handler)` + +* `scheme` String +* `handler` Function + * `request` ProtocolRequest + * `callback` Function + * `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md)) + +Same as `protocol.registerStreamProtocol`, except that it replaces an existing +protocol handler. + +### `protocol.uninterceptProtocol(scheme)` + +* `scheme` String + +Remove the interceptor installed for `scheme` and restore its original handler. + +### `protocol.isProtocolIntercepted(scheme)` + +* `scheme` String + +Returns `Boolean` - Whether `scheme` is already intercepted. + +[file-system-api]: https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem diff --git a/docs/api/structures/protocol-request.md b/docs/api/structures/protocol-request.md new file mode 100644 index 000000000000..4251c93e25c9 --- /dev/null +++ b/docs/api/structures/protocol-request.md @@ -0,0 +1,6 @@ +# ProtocolRequest Object + +* `url` String +* `referrer` String +* `method` String +* `uploadData` [UploadData[]](upload-data.md) (optional) diff --git a/docs/api/structures/protocol-response.md b/docs/api/structures/protocol-response.md new file mode 100644 index 000000000000..d3b9c99abf96 --- /dev/null +++ b/docs/api/structures/protocol-response.md @@ -0,0 +1,36 @@ +# ProtocolResponse Object + +* `error` Integer (optional) - When assigned, the `request` will fail with the + `error` number . For the available error numbers you can use, please see the + [net error list][net-error]. +* `statusCode` Number (optional) - The HTTP response code, default is 200. +* `charset` String (optional) - The charset of response body, default is + `"utf-8"`. +* `mimeType` String (optional) - The MIME type of response body, default is + `"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 (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 + the response body. When returning `Buffer` as response, this is a `Buffer`. + When returning `String` as response, this is a `String`. This is ignored for + other types of responses. +* `path` String (optional) - Path to the file which would be sent as response + body. This is only used for file responses. +* `url` String (optional) - Download the `url` and pipe the result as response + body. This is only used for URL responses. +* `referrer` String (optional) - The `referrer` URL. This is only used for file + and URL responses. +* `method` String (optional) - The HTTP `method`. This is only used for file + and URL responses. +* `session` Session (optional) - The session used for requesting URL, by default + the HTTP request will reuse the current session. Setting `session` to `null` + would use a random independent session. This is only used for URL responses. +* `uploadData` Object (optional) - The data used as upload data. This is only + used for URL responses when `method` is `"POST"`. + * `contentType` String - MIME type of the content. + * `data` String - Content to be sent. + +[net-error]: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h diff --git a/filenames.auto.gni b/filenames.auto.gni index 6f2bc9b1d47b..9139ef1054fb 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -4,6 +4,7 @@ auto_filenames = { "docs/api/accelerator.md", "docs/api/app.md", "docs/api/auto-updater.md", + "docs/api/breaking-changes-ns.md", "docs/api/breaking-changes.md", "docs/api/browser-view.md", "docs/api/browser-window-proxy.md", @@ -39,6 +40,7 @@ auto_filenames = { "docs/api/power-monitor.md", "docs/api/power-save-blocker.md", "docs/api/process.md", + "docs/api/protocol-ns.md", "docs/api/protocol.md", "docs/api/remote.md", "docs/api/sandbox-option.md", @@ -92,6 +94,8 @@ auto_filenames = { "docs/api/structures/process-memory-info.md", "docs/api/structures/process-metric.md", "docs/api/structures/product.md", + "docs/api/structures/protocol-request.md", + "docs/api/structures/protocol-response.md", "docs/api/structures/rectangle.md", "docs/api/structures/referrer.md", "docs/api/structures/remove-client-certificate.md", diff --git a/package.json b/package.json index 574a26bfa03f..485882bce68a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { - "@electron/docs-parser": "^0.2.2", + "@electron/docs-parser": "^0.3.0", "@electron/typescript-definitions": "^8.3.1", "@octokit/rest": "^16.3.2", "@types/chai": "^4.1.7", @@ -127,4 +127,4 @@ "git add filenames.auto.gni" ] } -} \ No newline at end of file +} diff --git a/shell/browser/api/atom_api_protocol_ns.cc b/shell/browser/api/atom_api_protocol_ns.cc index fd1e6c35f8f8..4489f12d279f 100644 --- a/shell/browser/api/atom_api_protocol_ns.cc +++ b/shell/browser/api/atom_api_protocol_ns.cc @@ -9,6 +9,7 @@ #include "base/stl_util.h" #include "shell/browser/atom_browser_context.h" +#include "shell/common/deprecate_util.h" #include "shell/common/native_mate_converters/net_converter.h" #include "shell/common/native_mate_converters/once_callback.h" #include "shell/common/promise_util.h" @@ -106,8 +107,15 @@ bool ProtocolNS::IsProtocolIntercepted(const std::string& scheme) { return base::ContainsKey(intercept_handlers_, scheme); } -v8::Local ProtocolNS::IsProtocolHandled( - const std::string& scheme) { +v8::Local ProtocolNS::IsProtocolHandled(const std::string& scheme, + mate::Arguments* args) { + node::Environment* env = node::Environment::GetCurrent(args->isolate()); + EmitDeprecationWarning( + env, + "The protocol.isProtocolHandled API is deprecated, use " + "protocol.isProtocolRegistered or protocol.isProtocolIntercepted " + "instead.", + "ProtocolDeprecateIsProtocolHandled"); util::Promise promise(isolate()); promise.Resolve(IsProtocolRegistered(scheme) || IsProtocolIntercepted(scheme) || @@ -126,6 +134,11 @@ void ProtocolNS::HandleOptionalCallback(mate::Arguments* args, ProtocolError error) { CompletionCallback callback; if (args->GetNext(&callback)) { + node::Environment* env = node::Environment::GetCurrent(args->isolate()); + EmitDeprecationWarning( + env, + "The callback argument of protocol module APIs is no longer needed.", + "ProtocolDeprecateCallback"); if (error == ProtocolError::OK) callback.Run(v8::Null(args->isolate())); else diff --git a/shell/browser/api/atom_api_protocol_ns.h b/shell/browser/api/atom_api_protocol_ns.h index 6031af4a44c9..585e006b59e8 100644 --- a/shell/browser/api/atom_api_protocol_ns.h +++ b/shell/browser/api/atom_api_protocol_ns.h @@ -65,7 +65,8 @@ class ProtocolNS : public mate::TrackableObject { bool IsProtocolIntercepted(const std::string& scheme); // Old async version of IsProtocolRegistered. - v8::Local IsProtocolHandled(const std::string& scheme); + v8::Local IsProtocolHandled(const std::string& scheme, + mate::Arguments* args); // Helper for converting old registration APIs to new RegisterProtocol API. template diff --git a/yarn.lock b/yarn.lock index 4bd4919d4414..72d36a47761b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ dependencies: regenerator-runtime "^0.12.0" -"@electron/docs-parser@^0.2.1", "@electron/docs-parser@^0.2.2": +"@electron/docs-parser@^0.2.1": version "0.2.2" resolved "https://registry.yarnpkg.com/@electron/docs-parser/-/docs-parser-0.2.2.tgz#7c9acd6cc10559c86a27bb0653ec13df10955f02" integrity sha512-FKXktu5i6cHL+AkvWv34j2lpBXNpqfHN7YwhswcBqRFXsj24phpih/sY2NKx6OrFP9R3ReJeg681/luAf/3k8Q== @@ -37,6 +37,21 @@ ora "^3.4.0" pretty-ms "^5.0.0" +"@electron/docs-parser@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@electron/docs-parser/-/docs-parser-0.3.0.tgz#cf8c33ed9cebffe7f3463a1e2d60ccf457b52ec6" + integrity sha512-/q2et0q6eMDItzv1ZCAH5ZJZY8AFGFkK1+wfAJfdarMDJOVs29pH8b0HjTXo2k+kLGlbC2TROZfuCHRgx+l/EQ== + dependencies: + "@types/markdown-it" "^0.0.7" + chai "^4.2.0" + chalk "^2.4.2" + fs-extra "^7.0.1" + lodash.camelcase "^4.3.0" + markdown-it "^8.4.2" + minimist "^1.2.0" + ora "^3.4.0" + pretty-ms "^5.0.0" + "@electron/typescript-definitions@^8.3.1": version "8.3.4" resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.3.4.tgz#2345e4058e66677792f1bc11662b13e6ccc9a17e"