diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index a5d51b751898..7e3a95422288 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -14,19 +14,37 @@ const {BrowserWindow} = require('electron'); let win = new BrowserWindow({width: 800, height: 1500}); win.loadURL('http://github.com'); -let webContents = win.webContents; +let contents = win.webContents; ``` -## Events +## Methods -The `webContents` object emits the following events: +These methods can be accessed from the `webContents` module: -### Event: 'did-finish-load' +```js +const {webContents} = require('electron'); +``` + +### `webContents.getAllWebContents()` + +Returns an array of all web contents. This will contain web contents for all +windows, webviews, opened devtools, and devtools extension background pages. + +### `webContents.getFocusedWebContents()` + +Returns the web contents that is focused in this application, otherwise +returns `null`. + +## Class: WebContents + +### Instance Events + +#### Event: 'did-finish-load' Emitted when the navigation is done, i.e. the spinner of the tab has stopped spinning, and the `onload` event was dispatched. -### Event: 'did-fail-load' +#### Event: 'did-fail-load' Returns: @@ -42,7 +60,7 @@ The full list of error codes and their meaning is available [here](https://code. Note that redirect responses will emit `errorCode` -3; you may want to ignore that error explicitly. -### Event: 'did-frame-finish-load' +#### Event: 'did-frame-finish-load' Returns: @@ -51,15 +69,15 @@ Returns: Emitted when a frame has done navigation. -### Event: 'did-start-loading' +#### Event: 'did-start-loading' Corresponds to the points in time when the spinner of the tab started spinning. -### Event: 'did-stop-loading' +#### Event: 'did-stop-loading' Corresponds to the points in time when the spinner of the tab stopped spinning. -### Event: 'did-get-response-details' +#### Event: 'did-get-response-details' Returns: @@ -76,7 +94,7 @@ Returns: Emitted when details regarding a requested resource are available. `status` indicates the socket connection to download the resource. -### Event: 'did-get-redirect-request' +#### Event: 'did-get-redirect-request' Returns: @@ -91,7 +109,7 @@ Returns: Emitted when a redirect is received while requesting a resource. -### Event: 'dom-ready' +#### Event: 'dom-ready' Returns: @@ -99,7 +117,7 @@ Returns: Emitted when the document in the given frame is loaded. -### Event: 'page-favicon-updated' +#### Event: 'page-favicon-updated' Returns: @@ -108,7 +126,7 @@ Returns: Emitted when page receives favicon urls. -### Event: 'new-window' +#### Event: 'new-window' Returns: @@ -127,7 +145,7 @@ By default a new `BrowserWindow` will be created for the `url`. Calling `event.preventDefault()` will prevent creating new windows. -### Event: 'will-navigate' +#### Event: 'will-navigate' Returns: @@ -146,7 +164,7 @@ this purpose. Calling `event.preventDefault()` will prevent the navigation. -### Event: 'did-navigate' +#### Event: 'did-navigate' Returns: @@ -159,7 +177,7 @@ This event is not emitted for in-page navigations, such as clicking anchor links or updating the `window.location.hash`. Use `did-navigate-in-page` event for this purpose. -### Event: 'did-navigate-in-page' +#### Event: 'did-navigate-in-page' Returns: @@ -172,11 +190,11 @@ When in-page navigation happens, the page URL changes but does not cause navigation outside of the page. Examples of this occurring are when anchor links are clicked or when the DOM `hashchange` event is triggered. -### Event: 'crashed' +#### Event: 'crashed' Emitted when the renderer process has crashed. -### Event: 'plugin-crashed' +#### Event: 'plugin-crashed' Returns: @@ -186,23 +204,23 @@ Returns: Emitted when a plugin process has crashed. -### Event: 'destroyed' +#### Event: 'destroyed' Emitted when `webContents` is destroyed. -### Event: 'devtools-opened' +#### Event: 'devtools-opened' Emitted when DevTools is opened. -### Event: 'devtools-closed' +#### Event: 'devtools-closed' Emitted when DevTools is closed. -### Event: 'devtools-focused' +#### Event: 'devtools-focused' Emitted when DevTools is focused / opened. -### Event: 'certificate-error' +#### Event: 'certificate-error' Returns: @@ -224,7 +242,7 @@ Emitted when failed to verify the `certificate` for `url`. The usage is the same with [the `certificate-error` event of `app`](app.md#event-certificate-error). -### Event: 'select-client-certificate' +#### Event: 'select-client-certificate' Returns: @@ -245,7 +263,7 @@ Emitted when a client certificate is requested. The usage is the same with [the `select-client-certificate` event of `app`](app.md#event-select-client-certificate). -### Event: 'login' +#### Event: 'login' Returns: @@ -266,7 +284,7 @@ Emitted when `webContents` wants to do basic auth. The usage is the same with [the `login` event of `app`](app.md#event-login). -### Event: 'found-in-page' +#### Event: 'found-in-page' Returns: @@ -281,15 +299,15 @@ Returns: Emitted when a result is available for [`webContents.findInPage`](web-contents.md#webcontentsfindinpage) request. -### Event: 'media-started-playing' +#### Event: 'media-started-playing' Emitted when media starts playing. -### Event: 'media-paused' +#### Event: 'media-paused' Emitted when media is paused or done playing. -### Event: 'did-change-theme-color' +#### Event: 'did-change-theme-color' Emitted when a page's theme color changes. This is usually due to encountering a meta tag: @@ -298,7 +316,7 @@ a meta tag: ``` -### Event: 'update-target-url' +#### Event: 'update-target-url' Returns: @@ -307,7 +325,7 @@ Returns: Emitted when mouse moves over a link or the keyboard moves the focus to a link. -### Event: 'cursor-changed' +#### Event: 'cursor-changed' Returns: @@ -329,7 +347,7 @@ If the `type` parameter is `custom`, the `image` parameter will hold the custom cursor image in a `NativeImage`, and the `scale` will hold scaling information for the image. -### Event: 'context-menu' +#### Event: 'context-menu' Returns: @@ -394,7 +412,7 @@ The `editFlags` is an object with the following properties: Emitted when there is a new context menu that needs to be handled. -### Event: 'select-bluetooth-device' +#### Event: 'select-bluetooth-device' Returns: @@ -430,11 +448,9 @@ app.on('ready', () => { }) ``` -## Instance Methods +### Instance Methods -The `webContents` object has the following instance methods: - -### `webContents.loadURL(url[, options])` +#### `contents.loadURL(url[, options])` * `url` URL * `options` Object (optional) @@ -451,14 +467,14 @@ const options = {extraHeaders: 'pragma: no-cache\n'}; webContents.loadURL(url, options); ``` -### `webContents.downloadURL(url)` +#### `contents.downloadURL(url)` * `url` URL Initiates a download of the resource at `url` without navigating. The `will-download` event of `session` will be triggered. -### `webContents.getURL()` +#### `contents.getURL()` Returns URL of the current web page. @@ -469,99 +485,99 @@ win.loadURL('http://github.com'); let currentURL = win.webContents.getURL(); ``` -### `webContents.getTitle()` +#### `contents.getTitle()` Returns the title of the current web page. -### `webContents.isFocused()` +#### `contents.isFocused()` Returns a Boolean, whether the web page is focused. -### `webContents.isLoading()` +#### `contents.isLoading()` Returns whether web page is still loading resources. -### `webContents.isLoadingMainFrame()` +#### `contents.isLoadingMainFrame()` Returns whether the main frame (and not just iframes or frames within it) is still loading. -### `webContents.isWaitingForResponse()` +#### `contents.isWaitingForResponse()` Returns whether the web page is waiting for a first-response from the main resource of the page. -### `webContents.stop()` +#### `contents.stop()` Stops any pending navigation. -### `webContents.reload()` +#### `contents.reload()` Reloads the current web page. -### `webContents.reloadIgnoringCache()` +#### `contents.reloadIgnoringCache()` Reloads current page and ignores cache. -### `webContents.canGoBack()` +#### `contents.canGoBack()` Returns whether the browser can go back to previous web page. -### `webContents.canGoForward()` +#### `contents.canGoForward()` Returns whether the browser can go forward to next web page. -### `webContents.canGoToOffset(offset)` +#### `contents.canGoToOffset(offset)` * `offset` Integer Returns whether the web page can go to `offset`. -### `webContents.clearHistory()` +#### `contents.clearHistory()` Clears the navigation history. -### `webContents.goBack()` +#### `contents.goBack()` Makes the browser go back a web page. -### `webContents.goForward()` +#### `contents.goForward()` Makes the browser go forward a web page. -### `webContents.goToIndex(index)` +#### `contents.goToIndex(index)` * `index` Integer Navigates browser to the specified absolute web page index. -### `webContents.goToOffset(offset)` +#### `contents.goToOffset(offset)` * `offset` Integer Navigates to the specified offset from the "current entry". -### `webContents.isCrashed()` +#### `contents.isCrashed()` Whether the renderer process has crashed. -### `webContents.setUserAgent(userAgent)` +#### `contents.setUserAgent(userAgent)` * `userAgent` String Overrides the user agent for this web page. -### `webContents.getUserAgent()` +#### `contents.getUserAgent()` Returns a `String` representing the user agent for this web page. -### `webContents.insertCSS(css)` +#### `contents.insertCSS(css)` * `css` String Injects CSS into the current web page. -### `webContents.executeJavaScript(code[, userGesture, callback])` +#### `contents.executeJavaScript(code[, userGesture, callback])` * `code` String * `userGesture` Boolean (optional) @@ -574,71 +590,71 @@ In the browser window some HTML APIs like `requestFullScreen` can only be invoked by a gesture from the user. Setting `userGesture` to `true` will remove this limitation. -### `webContents.setAudioMuted(muted)` +#### `contents.setAudioMuted(muted)` * `muted` Boolean Mute the audio on the current web page. -### `webContents.isAudioMuted()` +#### `contents.isAudioMuted()` Returns whether this page has been muted. -### `webContents.undo()` +#### `contents.undo()` Executes the editing command `undo` in web page. -### `webContents.redo()` +#### `contents.redo()` Executes the editing command `redo` in web page. -### `webContents.cut()` +#### `contents.cut()` Executes the editing command `cut` in web page. -### `webContents.copy()` +#### `contents.copy()` Executes the editing command `copy` in web page. -### `webContents.paste()` +#### `contents.paste()` Executes the editing command `paste` in web page. -### `webContents.pasteAndMatchStyle()` +#### `contents.pasteAndMatchStyle()` Executes the editing command `pasteAndMatchStyle` in web page. -### `webContents.delete()` +#### `contents.delete()` Executes the editing command `delete` in web page. -### `webContents.selectAll()` +#### `contents.selectAll()` Executes the editing command `selectAll` in web page. -### `webContents.unselect()` +#### `contents.unselect()` Executes the editing command `unselect` in web page. -### `webContents.replace(text)` +#### `contents.replace(text)` * `text` String Executes the editing command `replace` in web page. -### `webContents.replaceMisspelling(text)` +#### `contents.replaceMisspelling(text)` * `text` String Executes the editing command `replaceMisspelling` in web page. -### `webContents.insertText(text)` +#### `contents.insertText(text)` * `text` String Inserts `text` to the focused element. -### `webContents.findInPage(text[, options])` +#### `contents.findInPage(text[, options])` * `text` String - Content to be searched, must not be empty. * `options` Object (optional) @@ -659,7 +675,7 @@ an `Integer` representing the request id used for the request. The result of the request can be obtained by subscribing to [`found-in-page`](web-contents.md#event-found-in-page) event. -### `webContents.stopFindInPage(action)` +#### `contents.stopFindInPage(action)` * `action` String - Specifies the action to take place when ending [`webContents.findInPage`](web-contents.md#webcontentfindinpage) request. @@ -678,7 +694,7 @@ webContents.on('found-in-page', (event, result) => { const requestId = webContents.findInPage('api'); ``` -### `webContents.capturePage([rect, ]callback)` +#### `contents.capturePage([rect, ]callback)` * `rect` Object (optional) - The area of the page to be captured * `x` Integer @@ -692,14 +708,14 @@ be called with `callback(image)`. The `image` is an instance of [NativeImage](native-image.md) that stores data of the snapshot. Omitting `rect` will capture the whole visible page. -### `webContents.hasServiceWorker(callback)` +#### `contents.hasServiceWorker(callback)` * `callback` Function Checks if any ServiceWorker is registered and returns a boolean as response to `callback`. -### `webContents.unregisterServiceWorker(callback)` +#### `contents.unregisterServiceWorker(callback)` * `callback` Function @@ -707,7 +723,7 @@ Unregisters any ServiceWorker if present and returns a boolean as response to `callback` when the JS promise is fulfilled or false when the JS promise is rejected. -### `webContents.print([options])` +#### `contents.print([options])` * `options` Object (optional) * `silent` Boolean - Don't ask user for print settings. Default is `false`. @@ -720,7 +736,7 @@ up system's default printer and default settings for printing. Calling `window.print()` in web page is equivalent to calling `webContents.print({silent: false, printBackground: false})`. -### `webContents.printToPDF(options, callback)` +#### `contents.printToPDF(options, callback)` * `options` Object * `marginsType` Integer - Specifies the type of margins to use. Uses 0 for @@ -772,7 +788,7 @@ win.webContents.on('did-finish-load', () => { }); ``` -### `webContents.addWorkSpace(path)` +#### `contents.addWorkSpace(path)` * `path` String @@ -785,13 +801,13 @@ win.webContents.on('devtools-opened', () => { }); ``` -### `webContents.removeWorkSpace(path)` +#### `contents.removeWorkSpace(path)` * `path` String Removes the specified path from DevTools workspace. -### `webContents.openDevTools([options])` +#### `contents.openDevTools([options])` * `options` Object (optional) * `mode` String - Opens the devtools with specified dock state, can be @@ -800,34 +816,34 @@ Removes the specified path from DevTools workspace. Opens the devtools. -### `webContents.closeDevTools()` +#### `contents.closeDevTools()` Closes the devtools. -### `webContents.isDevToolsOpened()` +#### `contents.isDevToolsOpened()` Returns whether the devtools is opened. -### `webContents.isDevToolsFocused()` +#### `contents.isDevToolsFocused()` Returns whether the devtools view is focused . -### `webContents.toggleDevTools()` +#### `contents.toggleDevTools()` Toggles the developer tools. -### `webContents.inspectElement(x, y)` +#### `contents.inspectElement(x, y)` * `x` Integer * `y` Integer Starts inspecting element at position (`x`, `y`). -### `webContents.inspectServiceWorker()` +#### `contents.inspectServiceWorker()` Opens the developer tools for the service worker context. -### `webContents.send(channel[, arg1][, arg2][, ...])` +#### `contents.send(channel[, arg1][, arg2][, ...])` * `channel` String * `arg` (optional) @@ -867,7 +883,7 @@ app.on('ready', () => { ``` -### `webContents.enableDeviceEmulation(parameters)` +#### `contents.enableDeviceEmulation(parameters)` `parameters` Object, properties: @@ -898,11 +914,11 @@ app.on('ready', () => { Enable device emulation with the given parameters. -### `webContents.disableDeviceEmulation()` +#### `contents.disableDeviceEmulation()` Disable device emulation enabled by `webContents.enableDeviceEmulation`. -### `webContents.sendInputEvent(event)` +#### `contents.sendInputEvent(event)` * `event` Object * `type` String (**required**) - The type of the event, can be `mouseDown`, @@ -943,7 +959,7 @@ For the `mouseWheel` event, the `event` object also have following properties: * `hasPreciseScrollingDeltas` Boolean * `canScroll` Boolean -### `webContents.beginFrameSubscription([onlyDirty ,]callback)` +#### `contents.beginFrameSubscription([onlyDirty ,]callback)` * `onlyDirty` Boolean (optional) - Defaults to `false` * `callback` Function @@ -963,11 +979,11 @@ describes which part of the page was repainted. If `onlyDirty` is set to `true`, `frameBuffer` will only contain the repainted area. `onlyDirty` defaults to `false`. -### `webContents.endFrameSubscription()` +#### `contents.endFrameSubscription()` End subscribing for frame presentation events. -### `webContents.startDrag(item)` +#### `contents.startDrag(item)` * `item` object * `file` String @@ -977,7 +993,7 @@ Sets the `item` as dragging item for current drag-drop operation, `file` is the absolute path of the file to be dragged, and `icon` is the image showing under the cursor when dragging. -### `webContents.savePage(fullPath, saveType, callback)` +#### `contents.savePage(fullPath, saveType, callback)` * `fullPath` String - The full file path. * `saveType` String - Specify the save type. @@ -1000,34 +1016,36 @@ win.webContents.on('did-finish-load', () => { }); ``` -### `webContents.showDefinitionForSelection()` _macOS_ +#### `contents.showDefinitionForSelection()` _macOS_ Shows pop-up dictionary that searches the selected word on the page. -## Instance Properties +### Instance Properties -`WebContents` objects also have the following properties: - -### `webContents.id` +#### `contents.id` The unique ID of this WebContents. -### `webContents.session` +#### `contents.session` Returns the [session](session.md) object used by this webContents. -### `webContents.hostWebContents` +#### `contents.hostWebContents` Returns the `WebContents` that might own this `WebContents`. -### `webContents.devToolsWebContents` +#### `contents.devToolsWebContents` Get the `WebContents` of DevTools for this `WebContents`. **Note:** Users should never store this object because it may become `null` when the DevTools has been closed. -### `webContents.debugger` +#### `contents.debugger` + +Get the debugger instance for this webContents. + +## Class: Debugger Debugger API serves as an alternate transport for [remote debugging protocol][rdp]. @@ -1052,21 +1070,23 @@ win.webContents.debugger.on('message', (event, method, params) => { win.webContents.debugger.sendCommand('Network.enable'); ``` -#### `webContents.debugger.attach([protocolVersion])` +### Instance Methods + +#### `debugger.attach([protocolVersion])` * `protocolVersion` String (optional) - Requested debugging protocol version. Attaches the debugger to the `webContents`. -#### `webContents.debugger.isAttached()` +#### `debugger.isAttached()` Returns a boolean indicating whether a debugger is attached to the `webContents`. -#### `webContents.debugger.detach()` +#### `debugger.detach()` Detaches the debugger from the `webContents`. -#### `webContents.debugger.sendCommand(method[, commandParams, callback])` +#### `debugger.sendCommand(method[, commandParams, callback])` * `method` String - Method name, should be one of the methods defined by the remote debugging protocol. @@ -1078,6 +1098,8 @@ Detaches the debugger from the `webContents`. Send given command to the debugging target. +### Instance Events + #### Event: 'detach' * `event` Event diff --git a/lib/browser/api/exports/electron.js b/lib/browser/api/exports/electron.js index 9d873663029c..c47c04690659 100644 --- a/lib/browser/api/exports/electron.js +++ b/lib/browser/api/exports/electron.js @@ -101,16 +101,17 @@ Object.defineProperties(exports, { return require('../tray') } }, + webContents: { + enumerable: true, + get: function () { + return require('../web-contents') + } + }, // The internal modules, invisible unless you know their names. NavigationController: { get: function () { return require('../navigation-controller') } - }, - webContents: { - get: function () { - return require('../web-contents') - } } }) diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index c17206426aad..55159845715c 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -2,7 +2,6 @@ const {EventEmitter} = require('events') const {app, ipcMain, session, Menu, NavigationController} = require('electron') -const {getAllWebContents} = process.atomBinding('web_contents') // session is not used here, the purpose is to make sure session is initalized // before the webContents module. @@ -249,7 +248,7 @@ module.exports = { getFocusedWebContents () { let focused = null - for (let contents of getAllWebContents()) { + for (let contents of binding.getAllWebContents()) { if (!contents.isFocused()) continue if (focused == null) focused = contents // Return webview web contents which may be embedded inside another @@ -257,5 +256,9 @@ module.exports = { if (contents.getType() === 'webview') return contents } return focused + }, + + getAllWebContents () { + return binding.getAllWebContents() } } diff --git a/spec/api-web-contents-spec.js b/spec/api-web-contents-spec.js new file mode 100644 index 000000000000..a9519de131aa --- /dev/null +++ b/spec/api-web-contents-spec.js @@ -0,0 +1,79 @@ +'use strict' + +const assert = require('assert') +const path = require('path') + +const {remote} = require('electron') +const {BrowserWindow, webContents} = remote + +const isCi = remote.getGlobal('isCi') + +describe('webContents module', function () { + const fixtures = path.resolve(__dirname, 'fixtures') + let w + + beforeEach(function () { + if (w != null) { + w.destroy() + } + w = new BrowserWindow({ + show: false, + width: 400, + height: 400, + webPreferences: { + backgroundThrottling: false + } + }) + }) + + afterEach(function () { + if (w != null) { + w.destroy() + } + w = null + }) + + describe('getAllWebContents() API', function () { + it('returns an array of web contents', function (done) { + w.webContents.on('devtools-opened', function () { + const all = webContents.getAllWebContents().sort(function (a, b) { + return a.getId() - b.getId() + }) + + assert.equal(all.length, 4) + assert.equal(all[0].getType(), 'window') + assert.equal(all[1].getType(), 'window') + assert.equal(all[2].getType(), 'remote') + assert.equal(all[3].getType(), 'webview') + + done() + }) + + w.loadURL('file://' + path.join(fixtures, 'pages', 'webview-zoom-factor.html')) + w.webContents.openDevTools() + }) + }) + + describe('getFocusedWebContents() API', function () { + if (isCi) { + return + } + + it('returns the focused web contents', function (done) { + const specWebContents = remote.getCurrentWebContents() + assert.equal(specWebContents.getId(), webContents.getFocusedWebContents().getId()) + + specWebContents.on('devtools-opened', function () { + assert.equal(specWebContents.devToolsWebContents.getId(), webContents.getFocusedWebContents().getId()) + specWebContents.closeDevTools() + }) + + specWebContents.on('devtools-closed', function () { + assert.equal(specWebContents.getId(), webContents.getFocusedWebContents().getId()) + done() + }) + + specWebContents.openDevTools() + }) + }) +})