From d3fa5ed1e89a0f087b80e96cdae4ca69c9f78b92 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 2 Jun 2020 09:46:18 -0700 Subject: [PATCH] docs: move protocol-ns to protocol.md (#23883) * docs: move protocol-ns to protocol.md * chore: fix up tests and implement missing pieces required for tests --- docs/api/protocol-ns.md | 309 --------------------- docs/api/protocol.md | 296 +++++++------------- docs/api/structures/protocol-request.md | 1 + filenames.auto.gni | 1 - shell/browser/api/electron_api_protocol.cc | 6 +- shell/browser/api/electron_api_protocol.h | 16 +- spec-main/api-browser-window-spec.ts | 24 +- spec-main/api-protocol-spec.ts | 244 ++++++++-------- spec-main/api-session-spec.ts | 32 +-- spec-main/api-web-contents-spec.ts | 48 ++-- spec-main/chromium-spec.ts | 21 +- spec-main/webview-spec.ts | 8 +- spec/ts-smoke/electron/main.ts | 12 +- 13 files changed, 285 insertions(+), 733 deletions(-) delete mode 100644 docs/api/protocol-ns.md diff --git a/docs/api/protocol-ns.md b/docs/api/protocol-ns.md deleted file mode 100644 index 536e77051a..0000000000 --- a/docs/api/protocol-ns.md +++ /dev/null @@ -1,309 +0,0 @@ -# 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.whenReady().then(() => { - 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.whenReady().then(() => { - 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/protocol.md b/docs/api/protocol.md index b93b0b6a49..f37c06802b 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -15,8 +15,6 @@ app.whenReady().then(() => { protocol.registerFileProtocol('atom', (request, callback) => { const url = request.url.substr(7) callback({ path: path.normalize(`${__dirname}/${url}`) }) - }, (error) => { - if (error) console.error('Failed to register protocol') }) }) ``` @@ -26,9 +24,15 @@ 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`. +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. +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') @@ -41,17 +45,9 @@ app.whenReady().then(() => { ses.protocol.registerFileProtocol('atom', (request, callback) => { const url = request.url.substr(7) callback({ path: path.normalize(`${__dirname}/${url}`) }) - }, (error) => { - if (error) console.error('Failed to register protocol') }) - mainWindow = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - partition: partition - } - }) + mainWindow = new BrowserWindow({ webPreferences: { partition } }) }) ``` @@ -63,15 +59,15 @@ The `protocol` module has the following methods: * `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. +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. -Specify a privilege with the value of `true` to enable the capability. -An example of registering a privileged scheme, with bypassing Content Security Policy: +An example of registering a privileged scheme, that bypasses Content Security +Policy: ```javascript const { protocol } = require('electron') @@ -84,7 +80,7 @@ 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, will allow relative and absolute resources to +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. @@ -102,168 +98,102 @@ 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. +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.registerSchemesAsPrivileged` can be used to replicate the functionality of the previous `protocol.registerStandardSchemes`, `webFrame.registerURLSchemeAs*` and `protocol.registerServiceWorkerSchemes` functions that existed prior to Electron 5.0.0, for example: - -**before (<= v4.x)** -```javascript -// Main -protocol.registerStandardSchemes(['scheme1', 'scheme2'], { secure: true }) -// Renderer -webFrame.registerURLSchemeAsPrivileged('scheme1', { secure: true }) -webFrame.registerURLSchemeAsPrivileged('scheme2', { secure: true }) -``` - -**after (>= v5.x)** -```javascript -protocol.registerSchemesAsPrivileged([ - { scheme: 'scheme1', privileges: { standard: true, secure: true } }, - { scheme: 'scheme2', privileges: { standard: true, secure: true } } -]) -``` - -### `protocol.registerFileProtocol(scheme, handler[, completion])` +### `protocol.registerFileProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `filePath` String | [FilePathWithHeaders](structures/file-path-with-headers.md) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) -Registers a protocol of `scheme` that will send the file as a response. The -`handler` will be called with `handler(request, callback)` when a `request` is -going to be created with `scheme`. `completion` will be called with -`completion(null)` when `scheme` is successfully registered or -`completion(error)` when failed. +Returns `Boolean` - Whether the protocol was successfully registered + +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 object may also have a `headers` property -which gives a map of headers to values for the response headers, e.g. -`callback({ path: filePath, headers: {"Content-Security-Policy": "default-src 'none'"]})`. - -When `callback` is called with nothing, a number, or an object that has an -`error` property, the `request` will fail with the `error` number you -specified. For the available error numbers you can use, please see the -[net error list][net-error]. +`callback({ path: filePath })`. The `filePath` must be an absolute path. By default the `scheme` is treated like `http:`, which is parsed differently -than protocols that follow the "generic URI syntax" like `file:`. +from protocols that follow the "generic URI syntax" like `file:`. -### `protocol.registerBufferProtocol(scheme, handler[, completion])` +### `protocol.registerBufferProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `buffer` (Buffer | [MimeTypedBuffer](structures/mime-typed-buffer.md)) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (Buffer | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully registered 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`, -`mimeType`, and `charset` properties. +should be called with either a `Buffer` object or an object that has the `data` +property. Example: ```javascript -const { protocol } = require('electron') - protocol.registerBufferProtocol('atom', (request, callback) => { callback({ mimeType: 'text/html', data: Buffer.from('
Response
') }) -}, (error) => { - if (error) console.error('Failed to register protocol') }) ``` -### `protocol.registerStringProtocol(scheme, handler[, completion])` +### `protocol.registerStringProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `data` (String | [StringProtocolResponse](structures/string-protocol-response.md)) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully registered 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`, -`mimeType`, and `charset` properties. +should be called with either a `String` or an object that has the `data` +property. -### `protocol.registerHttpProtocol(scheme, handler[, completion])` +### `protocol.registerHttpProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `redirectRequest` Object - * `url` String - * `method` String (optional) - * `session` Session | null (optional) - * `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional) -* `completion` Function (optional) - * `error` Error + * `response` ProtocolResponse + +Returns `Boolean` - Whether the protocol was successfully registered 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 a `redirectRequest` object that has the `url`, `method`, -`referrer`, `uploadData` and `session` properties. +should be called with an object that has the `url` property. -By default the HTTP request will reuse the current session. If you want the -request to have a different session you should set `session` to `null`. - -For POST requests the `uploadData` object must be provided. - -### `protocol.registerStreamProtocol(scheme, handler[, completion])` +### `protocol.registerStreamProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md)) -Registers a protocol of `scheme` that will send a `Readable` as a response. +Returns `Boolean` - Whether the protocol was successfully registered -The usage is similar to the other `register{Any}Protocol`, except that the -`callback` should be called with either a `Readable` object or an object that -has the `data`, `statusCode`, and `headers` properties. +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: @@ -286,8 +216,6 @@ protocol.registerStreamProtocol('atom', (request, callback) => { }, data: createStream('
Response
') }) -}, (error) => { - if (error) console.error('Failed to register protocol') }) ``` @@ -295,132 +223,102 @@ 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 -const { protocol } = require('electron') -const fs = require('fs') - protocol.registerStreamProtocol('atom', (request, callback) => { callback(fs.createReadStream('index.html')) -}, (error) => { - if (error) console.error('Failed to register protocol') }) ``` -### `protocol.unregisterProtocol(scheme[, completion])` +### `protocol.unregisterProtocol(scheme)` * `scheme` String -* `completion` Function (optional) - * `error` Error + +Returns `Boolean` - Whether the protocol was successfully unregistered Unregisters the custom protocol of `scheme`. -### `protocol.isProtocolHandled(scheme)` +### `protocol.isProtocolRegistered(scheme)` * `scheme` String -Returns `Promise` - fulfilled with a boolean that indicates whether there is -already a handler for `scheme`. +Returns `Boolean` - Whether `scheme` is already registered. -### `protocol.interceptFileProtocol(scheme, handler[, completion])` +### `protocol.interceptFileProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `filePath` String -* `completion` Function (optional) - * `error` Error + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully intercepted Intercepts `scheme` protocol and uses `handler` as the protocol's new handler which sends a file as a response. -### `protocol.interceptStringProtocol(scheme, handler[, completion])` +### `protocol.interceptStringProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `data` (String | [StringProtocolResponse](structures/string-protocol-response.md)) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (String | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully intercepted Intercepts `scheme` protocol and uses `handler` as the protocol's new handler which sends a `String` as a response. -### `protocol.interceptBufferProtocol(scheme, handler[, completion])` +### `protocol.interceptBufferProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `buffer` Buffer (optional) -* `completion` Function (optional) - * `error` Error + * `response` (Buffer | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully intercepted Intercepts `scheme` protocol and uses `handler` as the protocol's new handler which sends a `Buffer` as a response. -### `protocol.interceptHttpProtocol(scheme, handler[, completion])` +### `protocol.interceptHttpProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `redirectRequest` Object - * `url` String - * `method` String (optional) - * `session` Session | null (optional) - * `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional) -* `completion` Function (optional) - * `error` Error + * `response` [ProtocolResponse](structures/protocol-response.md) + +Returns `Boolean` - Whether the protocol was successfully intercepted 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[, completion])` +### `protocol.interceptStreamProtocol(scheme, handler)` * `scheme` String * `handler` Function - * `request` Object - * `url` String - * `headers` Record - * `referrer` String - * `method` String - * `uploadData` [UploadData[]](structures/upload-data.md) + * `request` ProtocolRequest * `callback` Function - * `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional) -* `completion` Function (optional) - * `error` Error + * `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md)) + +Returns `Boolean` - Whether the protocol was successfully intercepted Same as `protocol.registerStreamProtocol`, except that it replaces an existing protocol handler. -### `protocol.uninterceptProtocol(scheme[, completion])` +### `protocol.uninterceptProtocol(scheme)` * `scheme` String -* `completion` Function (optional) - * `error` Error + +Returns `Boolean` - Whether the protocol was successfully unintercepted Remove the interceptor installed for `scheme` and restore its original handler. -[net-error]: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h +### `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 index 4251c93e25..0030e87f8a 100644 --- a/docs/api/structures/protocol-request.md +++ b/docs/api/structures/protocol-request.md @@ -4,3 +4,4 @@ * `referrer` String * `method` String * `uploadData` [UploadData[]](upload-data.md) (optional) +* `headers` Record diff --git a/filenames.auto.gni b/filenames.auto.gni index a0377f2d41..011c64cfae 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -43,7 +43,6 @@ 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", diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index d1d4539b32..727f8d0209 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -176,11 +176,12 @@ ProtocolError Protocol::RegisterProtocol(ProtocolType type, return added ? ProtocolError::OK : ProtocolError::REGISTERED; } -void Protocol::UnregisterProtocol(const std::string& scheme, +bool Protocol::UnregisterProtocol(const std::string& scheme, gin::Arguments* args) { bool removed = protocol_registry_->UnregisterProtocol(scheme); HandleOptionalCallback( args, removed ? ProtocolError::OK : ProtocolError::NOT_REGISTERED); + return removed; } bool Protocol::IsProtocolRegistered(const std::string& scheme) { @@ -194,11 +195,12 @@ ProtocolError Protocol::InterceptProtocol(ProtocolType type, return added ? ProtocolError::OK : ProtocolError::INTERCEPTED; } -void Protocol::UninterceptProtocol(const std::string& scheme, +bool Protocol::UninterceptProtocol(const std::string& scheme, gin::Arguments* args) { bool removed = protocol_registry_->UninterceptProtocol(scheme); HandleOptionalCallback( args, removed ? ProtocolError::OK : ProtocolError::NOT_INTERCEPTED); + return removed; } bool Protocol::IsProtocolIntercepted(const std::string& scheme) { diff --git a/shell/browser/api/electron_api_protocol.h b/shell/browser/api/electron_api_protocol.h index 3311bdd9d4..ae33d6ee36 100644 --- a/shell/browser/api/electron_api_protocol.h +++ b/shell/browser/api/electron_api_protocol.h @@ -58,13 +58,13 @@ class Protocol : public gin::Wrappable { ProtocolError RegisterProtocol(ProtocolType type, const std::string& scheme, const ProtocolHandler& handler); - void UnregisterProtocol(const std::string& scheme, gin::Arguments* args); + bool UnregisterProtocol(const std::string& scheme, gin::Arguments* args); bool IsProtocolRegistered(const std::string& scheme); ProtocolError InterceptProtocol(ProtocolType type, const std::string& scheme, const ProtocolHandler& handler); - void UninterceptProtocol(const std::string& scheme, gin::Arguments* args); + bool UninterceptProtocol(const std::string& scheme, gin::Arguments* args); bool IsProtocolIntercepted(const std::string& scheme); // Old async version of IsProtocolRegistered. @@ -73,16 +73,20 @@ class Protocol : public gin::Wrappable { // Helper for converting old registration APIs to new RegisterProtocol API. template - void RegisterProtocolFor(const std::string& scheme, + bool RegisterProtocolFor(const std::string& scheme, const ProtocolHandler& handler, gin::Arguments* args) { - HandleOptionalCallback(args, RegisterProtocol(type, scheme, handler)); + auto result = RegisterProtocol(type, scheme, handler); + HandleOptionalCallback(args, result); + return result == ProtocolError::OK; } template - void InterceptProtocolFor(const std::string& scheme, + bool InterceptProtocolFor(const std::string& scheme, const ProtocolHandler& handler, gin::Arguments* args) { - HandleOptionalCallback(args, InterceptProtocol(type, scheme, handler)); + auto result = InterceptProtocol(type, scheme, handler); + HandleOptionalCallback(args, result); + return result == ProtocolError::OK; } // Be compatible with old interface, which accepts optional callback. diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 92aef557f0..3e5bf561c6 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -241,10 +241,10 @@ describe('BrowserWindow module', () => { let w = null as unknown as BrowserWindow; const scheme = 'other'; const srcPath = path.join(fixtures, 'api', 'loaded-from-dataurl.js'); - before((done) => { + before(() => { protocol.registerFileProtocol(scheme, (request, callback) => { callback(srcPath); - }, (error) => done(error)); + }); }); after(() => { @@ -2532,26 +2532,20 @@ describe('BrowserWindow module', () => { ['foo', path.join(fixtures, 'api', 'window-open-location-change.html')], ['bar', path.join(fixtures, 'api', 'window-open-location-final.html')] ]; - beforeEach(async () => { - await Promise.all(protocols.map(([scheme, path]) => new Promise((resolve, reject) => { + beforeEach(() => { + for (const [scheme, path] of protocols) { protocol.registerBufferProtocol(scheme, (request, callback) => { callback({ mimeType: 'text/html', data: fs.readFileSync(path) }); - }, (error) => { - if (error != null) { - reject(error); - } else { - resolve(); - } }); - }))); + } }); - afterEach(async () => { - await Promise.all(protocols.map(([scheme]) => { - return new Promise(resolve => protocol.unregisterProtocol(scheme, () => resolve())); - })); + afterEach(() => { + for (const [scheme] of protocols) { + protocol.unregisterProtocol(scheme); + } }); it('retains the original web preferences when window.location is changed to a new origin', async () => { const w = new BrowserWindow({ diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 4008f44e08..97311eca3e 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -1,6 +1,5 @@ import { expect } from 'chai'; import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main'; -import { promisify } from 'util'; import { AddressInfo } from 'net'; import * as ChildProcess from 'child_process'; import * as path from 'path'; @@ -13,17 +12,17 @@ import { emittedOnce } from './events-helpers'; const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures'); -const registerStringProtocol = promisify(protocol.registerStringProtocol); -const registerBufferProtocol = promisify(protocol.registerBufferProtocol); -const registerFileProtocol = promisify(protocol.registerFileProtocol); -const registerHttpProtocol = promisify(protocol.registerHttpProtocol); -const registerStreamProtocol = promisify(protocol.registerStreamProtocol); -const interceptStringProtocol = promisify(protocol.interceptStringProtocol); -const interceptBufferProtocol = promisify(protocol.interceptBufferProtocol); -const interceptHttpProtocol = promisify(protocol.interceptHttpProtocol); -const interceptStreamProtocol = promisify(protocol.interceptStreamProtocol); -const unregisterProtocol = promisify(protocol.unregisterProtocol); -const uninterceptProtocol = promisify(protocol.uninterceptProtocol); +const registerStringProtocol = protocol.registerStringProtocol; +const registerBufferProtocol = protocol.registerBufferProtocol; +const registerFileProtocol = protocol.registerFileProtocol; +const registerHttpProtocol = protocol.registerHttpProtocol; +const registerStreamProtocol = protocol.registerStreamProtocol; +const interceptStringProtocol = protocol.interceptStringProtocol; +const interceptBufferProtocol = protocol.interceptBufferProtocol; +const interceptHttpProtocol = protocol.interceptHttpProtocol; +const interceptStreamProtocol = protocol.interceptStreamProtocol; +const unregisterProtocol = protocol.unregisterProtocol; +const uninterceptProtocol = protocol.uninterceptProtocol; const text = 'valar morghulis'; const protocolName = 'sp'; @@ -87,22 +86,22 @@ describe('protocol module', () => { return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); } - afterEach(async () => { - await new Promise(resolve => protocol.unregisterProtocol(protocolName, (/* ignore error */) => resolve())); - await new Promise(resolve => protocol.uninterceptProtocol('http', () => resolve())); + afterEach(() => { + protocol.unregisterProtocol(protocolName); + protocol.uninterceptProtocol('http'); }); describe('protocol.register(Any)Protocol', () => { - it('throws error when scheme is already registered', async () => { - await registerStringProtocol(protocolName, (req, cb) => cb()); - await expect(registerBufferProtocol(protocolName, (req, cb) => cb())).to.be.eventually.rejectedWith(Error); + it('fails when scheme is already registered', () => { + expect(registerStringProtocol(protocolName, (req, cb) => cb(''))).to.equal(true); + expect(registerBufferProtocol(protocolName, (req, cb) => cb(Buffer.from('')))).to.equal(false); }); it('does not crash when handler is called twice', async () => { - await registerStringProtocol(protocolName, (request, callback) => { + registerStringProtocol(protocolName, (request, callback) => { try { callback(text); - callback(); + callback(''); } catch (error) { // Ignore error } @@ -112,12 +111,12 @@ describe('protocol module', () => { }); it('sends error when callback is called with nothing', async () => { - await registerBufferProtocol(protocolName, (req, cb) => cb()); - await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); + registerBufferProtocol(protocolName, (req, cb: any) => cb()); + await expect(ajax(protocolName + '://fake-host')).to.eventually.be.rejectedWith(Error, '404'); }); it('does not crash when callback is called in next tick', async () => { - await registerStringProtocol(protocolName, (request, callback) => { + registerStringProtocol(protocolName, (request, callback) => { setImmediate(() => callback(text)); }); const r = await ajax(protocolName + '://fake-host'); @@ -126,27 +125,27 @@ describe('protocol module', () => { }); describe('protocol.unregisterProtocol', () => { - it('returns error when scheme does not exist', async () => { - await expect(unregisterProtocol('not-exist')).to.be.eventually.rejectedWith(Error); + it('returns false when scheme does not exist', () => { + expect(unregisterProtocol('not-exist')).to.equal(false); }); }); describe('protocol.registerStringProtocol', () => { it('sends string as response', async () => { - await registerStringProtocol(protocolName, (request, callback) => callback(text)); + registerStringProtocol(protocolName, (request, callback) => callback(text)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); }); it('sets Access-Control-Allow-Origin', async () => { - await registerStringProtocol(protocolName, (request, callback) => callback(text)); + registerStringProtocol(protocolName, (request, callback) => callback(text)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); expect(r.headers).to.include('access-control-allow-origin: *'); }); it('sends object as response', async () => { - await registerStringProtocol(protocolName, (request, callback) => { + registerStringProtocol(protocolName, (request, callback) => { callback({ data: text, mimeType: 'text/html' @@ -158,7 +157,7 @@ describe('protocol module', () => { it('fails when sending object other than string', async () => { const notAString = () => {}; - await registerStringProtocol(protocolName, (request, callback) => callback(notAString as any)); + registerStringProtocol(protocolName, (request, callback) => callback(notAString as any)); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); }); @@ -166,20 +165,20 @@ describe('protocol module', () => { describe('protocol.registerBufferProtocol', () => { const buffer = Buffer.from(text); it('sends Buffer as response', async () => { - await registerBufferProtocol(protocolName, (request, callback) => callback(buffer)); + registerBufferProtocol(protocolName, (request, callback) => callback(buffer)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); }); it('sets Access-Control-Allow-Origin', async () => { - await registerBufferProtocol(protocolName, (request, callback) => callback(buffer)); + registerBufferProtocol(protocolName, (request, callback) => callback(buffer)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); expect(r.headers).to.include('access-control-allow-origin: *'); }); it('sends object as response', async () => { - await registerBufferProtocol(protocolName, (request, callback) => { + registerBufferProtocol(protocolName, (request, callback) => { callback({ data: buffer, mimeType: 'text/html' @@ -190,7 +189,7 @@ describe('protocol module', () => { }); it('fails when sending string', async () => { - await registerBufferProtocol(protocolName, (request, callback) => callback(text as any)); + registerBufferProtocol(protocolName, (request, callback) => callback(text as any)); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); }); @@ -202,20 +201,20 @@ describe('protocol module', () => { const normalContent = fs.readFileSync(normalPath); it('sends file path as response', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback(filePath)); + registerFileProtocol(protocolName, (request, callback) => callback(filePath)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(String(fileContent)); }); it('sets Access-Control-Allow-Origin', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback(filePath)); + registerFileProtocol(protocolName, (request, callback) => callback(filePath)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(String(fileContent)); expect(r.headers).to.include('access-control-allow-origin: *'); }); it('sets custom headers', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback({ + registerFileProtocol(protocolName, (request, callback) => callback({ path: filePath, headers: { 'X-Great-Header': 'sogreat' } })); @@ -231,31 +230,30 @@ describe('protocol module', () => { headers: { 'X-Great-Header': (42 as any) } })).to.throw(Error, 'Value of \'X-Great-Header\' header has to be a string'); done(); - }).then(() => { - ajax(protocolName + '://fake-host'); }); + ajax(protocolName + '://fake-host'); }); it('sends object as response', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback({ path: filePath })); + registerFileProtocol(protocolName, (request, callback) => callback({ path: filePath })); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(String(fileContent)); }); it('can send normal file', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback(normalPath)); + registerFileProtocol(protocolName, (request, callback) => callback(normalPath)); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(String(normalContent)); }); it('fails when sending unexist-file', async () => { const fakeFilePath = path.join(fixturesPath, 'test.asar', 'a.asar', 'not-exist'); - await registerFileProtocol(protocolName, (request, callback) => callback(fakeFilePath)); + registerFileProtocol(protocolName, (request, callback) => callback(fakeFilePath)); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); it('fails when sending unsupported content', async () => { - await registerFileProtocol(protocolName, (request, callback) => callback(new Date() as any)); + registerFileProtocol(protocolName, (request, callback) => callback(new Date() as any)); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); }); @@ -267,22 +265,22 @@ describe('protocol module', () => { res.end(text); server.close(); }); - await server.listen(0, '127.0.0.1'); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const port = (server.address() as AddressInfo).port; const url = 'http://127.0.0.1:' + port; - await registerHttpProtocol(protocolName, (request, callback) => callback({ url })); + registerHttpProtocol(protocolName, (request, callback) => callback({ url })); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); }); it('fails when sending invalid url', async () => { - await registerHttpProtocol(protocolName, (request, callback) => callback({ url: 'url' })); + registerHttpProtocol(protocolName, (request, callback) => callback({ url: 'url' })); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); it('fails when sending unsupported content', async () => { - await registerHttpProtocol(protocolName, (request, callback) => callback(new Date() as any)); + registerHttpProtocol(protocolName, (request, callback) => callback(new Date() as any)); await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); @@ -297,12 +295,12 @@ describe('protocol module', () => { } }); after(() => server.close()); - await server.listen(0, '127.0.0.1'); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const port = (server.address() as AddressInfo).port; const url = `${protocolName}://fake-host`; const redirectURL = `http://127.0.0.1:${port}/serverRedirect`; - await registerHttpProtocol(protocolName, (request, callback) => callback({ url: redirectURL })); + registerHttpProtocol(protocolName, (request, callback) => callback({ url: redirectURL })); const r = await ajax(url); expect(r.data).to.equal(text); @@ -312,28 +310,27 @@ describe('protocol module', () => { protocol.registerHttpProtocol(protocolName, (request) => { expect(request).to.have.property('headers'); done(); - }, () => { - ajax(protocolName + '://fake-host'); }); + ajax(protocolName + '://fake-host'); }); }); describe('protocol.registerStreamProtocol', () => { it('sends Stream as response', async () => { - await registerStreamProtocol(protocolName, (request, callback) => callback(getStream())); + registerStreamProtocol(protocolName, (request, callback) => callback(getStream())); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); }); it('sends object as response', async () => { - await registerStreamProtocol(protocolName, (request, callback) => callback({ data: getStream() })); + registerStreamProtocol(protocolName, (request, callback) => callback({ data: getStream() })); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.equal(text); expect(r.status).to.equal(200); }); it('sends custom response headers', async () => { - await registerStreamProtocol(protocolName, (request, callback) => callback({ + registerStreamProtocol(protocolName, (request, callback) => callback({ data: getStream(3), headers: { 'x-electron': ['a', 'b'] @@ -346,9 +343,9 @@ describe('protocol module', () => { }); it('sends custom status code', async () => { - await registerStreamProtocol(protocolName, (request, callback) => callback({ + registerStreamProtocol(protocolName, (request, callback) => callback({ statusCode: 204, - data: null + data: null as any })); const r = await ajax(protocolName + '://fake-host'); expect(r.data).to.be.undefined('data'); @@ -356,7 +353,7 @@ describe('protocol module', () => { }); it('receives request headers', async () => { - await registerStreamProtocol(protocolName, (request, callback) => { + registerStreamProtocol(protocolName, (request, callback) => { callback({ headers: { 'content-type': 'application/json' @@ -369,7 +366,7 @@ describe('protocol module', () => { }); it('returns response multiple response headers with the same name', async () => { - await registerStreamProtocol(protocolName, (request, callback) => { + registerStreamProtocol(protocolName, (request, callback) => { callback({ headers: { header1: ['value1', 'value2'], @@ -387,7 +384,7 @@ describe('protocol module', () => { it('can handle large responses', async () => { const data = Buffer.alloc(128 * 1024); - await registerStreamProtocol(protocolName, (request, callback) => { + registerStreamProtocol(protocolName, (request, callback) => { callback(getStream(data.length, data)); }); const r = await ajax(protocolName + '://fake-host'); @@ -402,7 +399,7 @@ describe('protocol module', () => { } }); } - await registerStreamProtocol(protocolName, (request, callback) => { + registerStreamProtocol(protocolName, (request, callback) => { callback({ statusCode: 200, headers: { 'Content-Type': 'text/plain' }, @@ -414,48 +411,38 @@ describe('protocol module', () => { }); }); - describe('protocol.isProtocolHandled', () => { - it('returns true for built-in protocols', async () => { - for (const p of ['about', 'file', 'http', 'https']) { - const handled = await protocol.isProtocolHandled(p); - expect(handled).to.be.true(`${p}: is handled`); - } - }); - - it('returns false when scheme is not registered', async () => { - const result = await protocol.isProtocolHandled('no-exist'); + describe('protocol.isProtocolRegistered', () => { + it('returns false when scheme is not registered', () => { + const result = protocol.isProtocolRegistered('no-exist'); expect(result).to.be.false('no-exist: is handled'); }); - it('returns true for custom protocol', async () => { - await registerStringProtocol(protocolName, (request, callback) => callback()); - const result = await protocol.isProtocolHandled(protocolName); + it('returns true for custom protocol', () => { + registerStringProtocol(protocolName, (request, callback) => callback('')); + const result = protocol.isProtocolRegistered(protocolName); expect(result).to.be.true('custom protocol is handled'); }); + }); - it('returns true for intercepted protocol', async () => { - await interceptStringProtocol('http', (request, callback) => callback()); - const result = await protocol.isProtocolHandled('http'); + describe('protocol.isProtocolIntercepted', () => { + it('returns true for intercepted protocol', () => { + interceptStringProtocol('http', (request, callback) => callback('')); + const result = protocol.isProtocolIntercepted('http'); expect(result).to.be.true('intercepted protocol is handled'); }); }); describe('protocol.intercept(Any)Protocol', () => { - it('throws error when scheme is already intercepted', (done) => { - protocol.interceptStringProtocol('http', (request, callback) => callback(), (error) => { - expect(error).to.be.null('error'); - protocol.interceptBufferProtocol('http', (request, callback) => callback(), (error) => { - expect(error).to.not.be.null('error'); - done(); - }); - }); + it('returns false when scheme is already intercepted', () => { + expect(protocol.interceptStringProtocol('http', (request, callback) => callback(''))).to.equal(true); + expect(protocol.interceptBufferProtocol('http', (request, callback) => callback(Buffer.from('')))).to.equal(false); }); it('does not crash when handler is called twice', async () => { - await interceptStringProtocol('http', (request, callback) => { + interceptStringProtocol('http', (request, callback) => { try { callback(text); - callback(); + callback(''); } catch (error) { // Ignore error } @@ -465,20 +452,20 @@ describe('protocol module', () => { }); it('sends error when callback is called with nothing', async () => { - await interceptStringProtocol('http', (request, callback) => callback()); + interceptStringProtocol('http', (request, callback: any) => callback()); await expect(ajax('http://fake-host')).to.be.eventually.rejectedWith(Error, '404'); }); }); describe('protocol.interceptStringProtocol', () => { it('can intercept http protocol', async () => { - await interceptStringProtocol('http', (request, callback) => callback(text)); + interceptStringProtocol('http', (request, callback) => callback(text)); const r = await ajax('http://fake-host'); expect(r.data).to.equal(text); }); it('can set content-type', async () => { - await interceptStringProtocol('http', (request, callback) => { + interceptStringProtocol('http', (request, callback) => { callback({ mimeType: 'application/json', data: '{"value": 1}' @@ -490,7 +477,7 @@ describe('protocol module', () => { }); it('can set content-type with charset', async () => { - await interceptStringProtocol('http', (request, callback) => { + interceptStringProtocol('http', (request, callback) => { callback({ mimeType: 'application/json; charset=UTF-8', data: '{"value": 1}' @@ -502,8 +489,8 @@ describe('protocol module', () => { }); it('can receive post data', async () => { - await interceptStringProtocol('http', (request, callback) => { - const uploadData = request.uploadData[0].bytes.toString(); + interceptStringProtocol('http', (request, callback) => { + const uploadData = request.uploadData![0].bytes.toString(); callback({ data: uploadData }); }); const r = await ajax('http://fake-host', { type: 'POST', data: postData }); @@ -513,14 +500,14 @@ describe('protocol module', () => { describe('protocol.interceptBufferProtocol', () => { it('can intercept http protocol', async () => { - await interceptBufferProtocol('http', (request, callback) => callback(Buffer.from(text))); + interceptBufferProtocol('http', (request, callback) => callback(Buffer.from(text))); const r = await ajax('http://fake-host'); expect(r.data).to.equal(text); }); it('can receive post data', async () => { - await interceptBufferProtocol('http', (request, callback) => { - const uploadData = request.uploadData[0].bytes; + interceptBufferProtocol('http', (request, callback) => { + const uploadData = request.uploadData![0].bytes; callback(uploadData); }); const r = await ajax('http://fake-host', { type: 'POST', data: postData }); @@ -544,19 +531,19 @@ describe('protocol module', () => { server.close(); }); after(() => server.close()); - await server.listen(0, '127.0.0.1'); + server.listen(0, '127.0.0.1'); const port = (server.address() as AddressInfo).port; const url = `http://127.0.0.1:${port}`; - await interceptHttpProtocol('http', (request, callback) => { - const data: Electron.RedirectRequest = { + interceptHttpProtocol('http', (request, callback) => { + const data: Electron.ProtocolResponse = { url: url, method: 'POST', uploadData: { contentType: 'application/x-www-form-urlencoded', - data: request.uploadData[0].bytes + data: request.uploadData![0].bytes }, - session: null + session: undefined }; callback(data); }); @@ -572,7 +559,7 @@ describe('protocol module', () => { }); after(() => customSession.webRequest.onBeforeRequest(null)); - await interceptHttpProtocol('http', (request, callback) => { + interceptHttpProtocol('http', (request, callback) => { callback({ url: request.url, session: customSession @@ -585,33 +572,32 @@ describe('protocol module', () => { protocol.interceptHttpProtocol('http', (request) => { expect(request).to.have.property('headers'); done(); - }, () => { - ajax('http://fake-host'); }); + ajax('http://fake-host'); }); }); describe('protocol.interceptStreamProtocol', () => { it('can intercept http protocol', async () => { - await interceptStreamProtocol('http', (request, callback) => callback(getStream())); + interceptStreamProtocol('http', (request, callback) => callback(getStream())); const r = await ajax('http://fake-host'); expect(r.data).to.equal(text); }); it('can receive post data', async () => { - await interceptStreamProtocol('http', (request, callback) => { - callback(getStream(3, request.uploadData[0].bytes.toString())); + interceptStreamProtocol('http', (request, callback) => { + callback(getStream(3, request.uploadData![0].bytes.toString())); }); const r = await ajax('http://fake-host', { type: 'POST', data: postData }); expect({ ...qs.parse(r.data) }).to.deep.equal(postData); }); it('can execute redirects', async () => { - await interceptStreamProtocol('http', (request, callback) => { + interceptStreamProtocol('http', (request, callback) => { if (request.url.indexOf('http://fake-host') === 0) { setTimeout(() => { callback({ - data: null, + data: '', statusCode: 302, headers: { Location: 'http://fake-redirect' @@ -629,12 +615,12 @@ describe('protocol module', () => { }); describe('protocol.uninterceptProtocol', () => { - it('returns error when scheme does not exist', async () => { - await expect(uninterceptProtocol('not-exist')).to.be.eventually.rejectedWith(Error); + it('returns false when scheme does not exist', () => { + expect(uninterceptProtocol('not-exist')).to.equal(false); }); - it('returns error when scheme is not intercepted', async () => { - await expect(uninterceptProtocol('http')).to.be.eventually.rejectedWith(Error); + it('returns false when scheme is not intercepted', () => { + expect(uninterceptProtocol('http')).to.equal(false); }); }); @@ -677,14 +663,14 @@ describe('protocol module', () => { afterEach(async () => { await closeWindow(w); - await unregisterProtocol(standardScheme); + unregisterProtocol(standardScheme); w = null as unknown as BrowserWindow; }); it('resolves relative resources', async () => { - await registerFileProtocol(standardScheme, (request, callback) => { + registerFileProtocol(standardScheme, (request, callback) => { if (request.url === imageURL) { - callback(); + callback(''); } else { callback(filePath); } @@ -693,9 +679,9 @@ describe('protocol module', () => { }); it('resolves absolute resources', async () => { - await registerStringProtocol(standardScheme, (request, callback) => { + registerStringProtocol(standardScheme, (request, callback) => { if (request.url === imageURL) { - callback(); + callback(''); } else { callback({ data: fileContent, @@ -716,17 +702,15 @@ describe('protocol module', () => { await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const port = (server.address() as AddressInfo).port; const content = ``; - await registerStringProtocol(standardScheme, (request, callback) => callback({ data: content, mimeType: 'text/html' })); + registerStringProtocol(standardScheme, (request, callback) => callback({ data: content, mimeType: 'text/html' })); await w.loadURL(origin); await requestReceived; }); it.skip('can access files through the FileSystem API', (done) => { const filePath = path.join(fixturesPath, 'pages', 'filesystem.html'); - protocol.registerFileProtocol(standardScheme, (request, callback) => callback({ path: filePath }), (error) => { - if (error) return done(error); - w.loadURL(origin); - }); + protocol.registerFileProtocol(standardScheme, (request, callback) => callback({ path: filePath })); + w.loadURL(origin); ipcMain.once('file-system-error', (event, err) => done(err)); ipcMain.once('file-system-write-end', () => done()); }); @@ -735,10 +719,8 @@ describe('protocol module', () => { const filePath = path.join(fixturesPath, 'pages', 'cache-storage.html'); ipcMain.once('success', () => done()); ipcMain.once('failure', (event, err) => done(err)); - protocol.registerFileProtocol(standardScheme, (request, callback) => callback({ path: filePath }), (error) => { - if (error) return done(error); - w.loadURL(origin); - }); + protocol.registerFileProtocol(standardScheme, (request, callback) => callback({ path: filePath })); + w.loadURL(origin); }); }); @@ -752,11 +734,9 @@ describe('protocol module', () => { afterEach(async () => { await closeWindow(w); w = null as unknown as BrowserWindow; - await Promise.all( - [standardScheme, 'cors', 'no-cors', 'no-fetch'].map(scheme => - new Promise(resolve => protocol.unregisterProtocol(scheme, (/* ignore error */) => resolve())) - ) - ); + for (const scheme of [standardScheme, 'cors', 'no-cors', 'no-fetch']) { + protocol.unregisterProtocol(scheme); + } }); it('supports fetch api by default', async () => { @@ -818,10 +798,10 @@ describe('protocol module', () => { }); async function allowsCORSRequests (corsScheme: string, expected: any, expectedConsole: RegExp, content: Function) { - await registerStringProtocol(standardScheme, (request, callback) => { + registerStringProtocol(standardScheme, (request, callback) => { callback({ data: ``, mimeType: 'text/html' }); }); - await registerStringProtocol(corsScheme, (request, callback) => { + registerStringProtocol(corsScheme, (request, callback) => { callback(''); }); diff --git a/spec-main/api-session-spec.ts b/spec-main/api-session-spec.ts index f9a3865b7f..4a54da3a3c 100644 --- a/spec-main/api-session-spec.ts +++ b/spec-main/api-session-spec.ts @@ -312,11 +312,11 @@ describe('session module', () => { }); afterEach(closeAllWindows); - it('does not affect defaultSession', async () => { - const result1 = await protocol.isProtocolHandled(protocolName); + it('does not affect defaultSession', () => { + const result1 = protocol.isProtocolRegistered(protocolName); expect(result1).to.equal(false); - const result2 = await customSession.protocol.isProtocolHandled(protocolName); + const result2 = customSession.protocol.isProtocolRegistered(protocolName); expect(result2).to.equal(true); }); @@ -424,18 +424,16 @@ describe('session module', () => { if (request.method === 'GET') { callback({ data: content, mimeType: 'text/html' }); } else if (request.method === 'POST') { - const uuid = request.uploadData[1].blobUUID; + const uuid = request.uploadData![1].blobUUID; expect(uuid).to.be.a('string'); session.defaultSession.getBlobData(uuid!).then(result => { expect(result.toString()).to.equal(postData); done(); }); } - }, (error) => { - if (error) return done(error); - const w = new BrowserWindow({ show: false }); - w.loadURL(url); }); + const w = new BrowserWindow({ show: false }); + w.loadURL(url); }); }); @@ -644,18 +642,16 @@ describe('session module', () => { const handler = (ignoredError: any, callback: Function) => { callback({ url: `${url}:${port}` }); }; - protocol.registerHttpProtocol(protocolName, handler, (error) => { - if (error) return done(error); - const w = new BrowserWindow({ show: false }); - w.webContents.session.once('will-download', function (e, item) { - item.savePath = downloadFilePath; - item.on('done', function (e, state) { - assertDownload(state, item, true); - done(); - }); + protocol.registerHttpProtocol(protocolName, handler); + const w = new BrowserWindow({ show: false }); + w.webContents.session.once('will-download', function (e, item) { + item.savePath = downloadFilePath; + item.on('done', function (e, state) { + assertDownload(state, item, true); + done(); }); - w.webContents.downloadURL(`${protocolName}://item`); }); + w.webContents.downloadURL(`${protocolName}://item`); }); it('can download using WebView.downloadURL', async () => { diff --git a/spec-main/api-web-contents-spec.ts b/spec-main/api-web-contents-spec.ts index 1cb26c66cf..8fe8470597 100644 --- a/spec-main/api-web-contents-spec.ts +++ b/spec-main/api-web-contents-spec.ts @@ -830,7 +830,7 @@ describe('webContents module', () => { host3: 0.2 }; - before((done) => { + before(() => { const protocol = session.defaultSession.protocol; protocol.registerStringProtocol(scheme, (request, callback) => { const response = ``; callback({ data: response, mimeType: 'text/html' }); - }, (error) => done(error)); + }); }); - after((done) => { + after(() => { const protocol = session.defaultSession.protocol; - protocol.unregisterProtocol(scheme, (error) => done(error)); + protocol.unregisterProtocol(scheme); }); afterEach(closeAllWindows); @@ -981,29 +981,25 @@ describe('webContents module', () => { const protocol = w2.webContents.session.protocol; protocol.registerStringProtocol(scheme, (request, callback) => { callback('hello'); - }, (error) => { - if (error) return done(error); - w2.webContents.on('did-finish-load', () => { - const zoomLevel1 = w.webContents.zoomLevel; - expect(zoomLevel1).to.equal(hostZoomMap.host3); - - const zoomLevel2 = w2.webContents.zoomLevel; - expect(zoomLevel2).to.equal(0); - expect(zoomLevel1).to.not.equal(zoomLevel2); - - protocol.unregisterProtocol(scheme, (error) => { - if (error) return done(error); - w2.setClosable(true); - w2.close(); - done(); - }); - }); - w.webContents.on('did-finish-load', () => { - w.webContents.zoomLevel = hostZoomMap.host3; - w2.loadURL(`${scheme}://host3`); - }); - w.loadURL(`${scheme}://host3`); }); + w2.webContents.on('did-finish-load', () => { + const zoomLevel1 = w.webContents.zoomLevel; + expect(zoomLevel1).to.equal(hostZoomMap.host3); + + const zoomLevel2 = w2.webContents.zoomLevel; + expect(zoomLevel2).to.equal(0); + expect(zoomLevel1).to.not.equal(zoomLevel2); + + protocol.unregisterProtocol(scheme); + w2.setClosable(true); + w2.close(); + done(); + }); + w.webContents.on('did-finish-load', () => { + w.webContents.zoomLevel = hostZoomMap.host3; + w2.loadURL(`${scheme}://host3`); + }); + w.loadURL(`${scheme}://host3`); }); it('can persist when it contains iframe', (done) => { diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 89691fbfce..a39454e972 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -410,8 +410,6 @@ describe('chromium features', () => { if (ext === '.js') type = 'application/javascript'; callback({ data: content, mimeType: type } as any); - }, (error) => { - if (error) done(error); }); const w = new BrowserWindow({ @@ -431,7 +429,8 @@ describe('chromium features', () => { customSession.clearStorageData({ storages: ['serviceworkers'] }).then(() => { - customSession.protocol.uninterceptProtocol('file', error => done(error)); + customSession.protocol.uninterceptProtocol('file'); + done(); }); } }); @@ -840,8 +839,8 @@ describe('chromium features', () => { ]; const s = (url: string) => url.startsWith('file') ? 'file://...' : url; - before(async () => { - await promisify(protocol.registerFileProtocol)(scheme, (request, callback) => { + before(() => { + protocol.registerFileProtocol(scheme, (request, callback) => { if (request.url.includes('blank')) { callback(`${fixturesPath}/pages/blank.html`); } else { @@ -849,8 +848,8 @@ describe('chromium features', () => { } }); }); - after(async () => { - await promisify(protocol.unregisterProtocol)(scheme); + after(() => { + protocol.unregisterProtocol(scheme); }); afterEach(closeAllWindows); @@ -929,7 +928,7 @@ describe('chromium features', () => { describe('custom non standard schemes', () => { const protocolName = 'storage'; let contents: WebContents; - before((done) => { + before(() => { protocol.registerFileProtocol(protocolName, (request, callback) => { const parsedUrl = url.parse(request.url); let filename; @@ -942,11 +941,11 @@ describe('chromium features', () => { default : filename = ''; } callback({ path: `${fixturesPath}/pages/storage/${filename}` }); - }, (error) => done(error)); + }); }); - after((done) => { - protocol.unregisterProtocol(protocolName, () => done()); + after(() => { + protocol.unregisterProtocol(protocolName); }); beforeEach(() => { diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index 1a8f133b74..238438daaa 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -217,16 +217,16 @@ describe(' tag', function () { const zoomScheme = standardScheme; const webviewSession = session.fromPartition('webview-temp'); - before((done) => { + before(() => { const protocol = webviewSession.protocol; protocol.registerStringProtocol(zoomScheme, (request, callback) => { callback('hello'); - }, (error) => done(error)); + }); }); - after((done) => { + after(() => { const protocol = webviewSession.protocol; - protocol.unregisterProtocol(zoomScheme, (error) => done(error)); + protocol.unregisterProtocol(zoomScheme); }); it('inherits the zoomFactor of the parent window', async () => { diff --git a/spec/ts-smoke/electron/main.ts b/spec/ts-smoke/electron/main.ts index 650b6ae7f2..1a6e0afd68 100644 --- a/spec/ts-smoke/electron/main.ts +++ b/spec/ts-smoke/electron/main.ts @@ -897,13 +897,9 @@ app.whenReady().then(() => { callback({ url: request.url, method: request.method }) }) - protocol.unregisterProtocol('atom', (error) => { - console.log(error ? error.message : 'ok') - }) + protocol.unregisterProtocol('atom') - protocol.isProtocolHandled('atom').then(handled => { - console.log(handled) - }) + const registered: boolean = protocol.isProtocolRegistered('atom') }) // tray @@ -1195,10 +1191,6 @@ app.whenReady().then(function () { protocol.registerFileProtocol('atom', function (request, callback) { const url = request.url.substr(7) callback(path.normalize(__dirname + '/' + url)) - }, function (error) { - if (error) { - console.error('Failed to register protocol') - } }) })