Browse Source

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
Samuel Attard 4 years ago
parent
commit
d3fa5ed1e8

+ 0 - 309
docs/api/protocol-ns.md

@@ -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
-<body>
-  <img src='test.png'>
-</body>
-```
-
-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('<h5>Response</h5>') })
-})
-```
-
-### `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('<h5>Response</h5>')
-  })
-})
-```
-
-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

+ 97 - 199
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.
-
-`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:
+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.
 
-**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<String, String>
-    * `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<String, String>
-    * `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('<h5>Response</h5>') })
-}, (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<String, String>
-    * `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<String, String>
-    * `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.
-
-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.
+should be called with an object that has the `url` property.
 
-### `protocol.registerStreamProtocol(scheme, handler[, completion])`
+### `protocol.registerStreamProtocol(scheme, handler)`
 
 * `scheme` String
 * `handler` Function
-  * `request` Object
-    * `url` String
-    * `headers` Record<String, String>
-    * `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('<h5>Response</h5>')
   })
-}, (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<Boolean>` - 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<String, String>
-    * `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<String, String>
-    * `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<String, String>
-    * `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<String, String>
-    * `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<String, String>
-    * `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

+ 1 - 0
docs/api/structures/protocol-request.md

@@ -4,3 +4,4 @@
 * `referrer` String
 * `method` String
 * `uploadData` [UploadData[]](upload-data.md) (optional)
+* `headers` Record<String, String>

+ 0 - 1
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",

+ 4 - 2
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) {

+ 10 - 6
shell/browser/api/electron_api_protocol.h

@@ -58,13 +58,13 @@ class Protocol : public gin::Wrappable<Protocol> {
   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<Protocol> {
 
   // Helper for converting old registration APIs to new RegisterProtocol API.
   template <ProtocolType type>
-  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 <ProtocolType type>
-  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.

+ 9 - 15
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({

+ 112 - 132
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 = `<script>fetch("http://127.0.0.1:${port}")</script>`;
-      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: `<script>(${content})()</script>`, mimeType: 'text/html' });
       });
-      await registerStringProtocol(corsScheme, (request, callback) => {
+      registerStringProtocol(corsScheme, (request, callback) => {
         callback('');
       });
 

+ 14 - 18
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 () => {

+ 22 - 26
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 = `<script>
@@ -841,12 +841,12 @@ describe('webContents module', () => {
                             })
                           </script>`;
         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) => {

+ 10 - 11
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(() => {

+ 4 - 4
spec-main/webview-spec.ts

@@ -217,16 +217,16 @@ describe('<webview> 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 () => {

+ 2 - 10
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')
-    }
   })
 })