diff --git a/filenames.gni b/filenames.gni index cbdca3c82230..965b36ea77b2 100644 --- a/filenames.gni +++ b/filenames.gni @@ -73,12 +73,12 @@ filenames = { "lib/renderer/security-warnings.ts", "lib/renderer/window-setup.ts", "lib/renderer/web-frame-init.ts", - "lib/renderer/web-view/guest-view-internal.js", - "lib/renderer/web-view/web-view-attributes.js", - "lib/renderer/web-view/web-view-constants.js", - "lib/renderer/web-view/web-view-element.js", - "lib/renderer/web-view/web-view-impl.js", - "lib/renderer/web-view/web-view-init.js", + "lib/renderer/web-view/guest-view-internal.ts", + "lib/renderer/web-view/web-view-attributes.ts", + "lib/renderer/web-view/web-view-constants.ts", + "lib/renderer/web-view/web-view-element.ts", + "lib/renderer/web-view/web-view-impl.ts", + "lib/renderer/web-view/web-view-init.ts", "lib/renderer/api/exports/electron.js", "lib/renderer/api/crash-reporter.js", "lib/renderer/api/ipc-renderer.js", diff --git a/lib/renderer/init.ts b/lib/renderer/init.ts index e00756259bb9..dc8d2f35a52f 100644 --- a/lib/renderer/init.ts +++ b/lib/renderer/init.ts @@ -93,7 +93,8 @@ switch (window.location.protocol) { // Load webview tag implementation. if (process.isMainFrame) { - require('@electron/internal/renderer/web-view/web-view-init')(contextIsolation, webviewTag, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') + webViewInit(contextIsolation, webviewTag, guestInstanceId) } // Pass the arguments to isolatedWorld. diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js deleted file mode 100644 index c66948dc04f9..000000000000 --- a/lib/renderer/web-view/guest-view-internal.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict' - -const { webFrame } = require('electron') -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') - -const WEB_VIEW_EVENTS = { - 'load-commit': ['url', 'isMainFrame'], - 'did-attach': [], - 'did-finish-load': [], - 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], - 'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'], - 'did-start-loading': [], - 'did-stop-loading': [], - 'dom-ready': [], - 'console-message': ['level', 'message', 'line', 'sourceId'], - 'context-menu': ['params'], - 'devtools-opened': [], - 'devtools-closed': [], - 'devtools-focused': [], - 'new-window': ['url', 'frameName', 'disposition', 'options'], - 'will-navigate': ['url'], - 'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], - 'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'], - 'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], - 'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], - 'focus-change': ['focus', 'guestInstanceId'], - 'close': [], - 'crashed': [], - 'gpu-crashed': [], - 'plugin-crashed': ['name', 'version'], - 'destroyed': [], - 'page-title-updated': ['title', 'explicitSet'], - 'page-favicon-updated': ['favicons'], - 'enter-html-full-screen': [], - 'leave-html-full-screen': [], - 'media-started-playing': [], - 'media-paused': [], - 'found-in-page': ['result'], - 'did-change-theme-color': ['themeColor'], - 'update-target-url': ['url'] -} - -const DEPRECATED_EVENTS = { - 'page-title-updated': 'page-title-set' -} - -const dispatchEvent = function (webView, eventName, eventKey, ...args) { - if (DEPRECATED_EVENTS[eventName] != null) { - dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args) - } - const domEvent = new Event(eventName) - WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { - domEvent[prop] = args[index] - }) - webView.dispatchEvent(domEvent) - if (eventName === 'load-commit') { - webView.onLoadCommit(domEvent) - } else if (eventName === 'focus-change') { - webView.onFocusChange(domEvent) - } -} - -module.exports = { - registerEvents: function (webView, viewInstanceId) { - ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () { - webView.guestInstanceId = undefined - webView.reset() - const domEvent = new Event('destroyed') - webView.dispatchEvent(domEvent) - }) - - ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) { - dispatchEvent(webView, eventName, eventName, ...args) - }) - - ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) { - const domEvent = new Event('ipc-message') - domEvent.channel = channel - domEvent.args = args - webView.dispatchEvent(domEvent) - }) - }, - deregisterEvents: function (viewInstanceId) { - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`) - }, - createGuest: function (params) { - return ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) - }, - createGuestSync: function (params) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) - }, - destroyGuest: function (guestInstanceId) { - ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId) - }, - attachGuest: function (elementInstanceId, guestInstanceId, params, contentWindow) { - const embedderFrameId = webFrame.getWebFrameId(contentWindow) - if (embedderFrameId < 0) { // this error should not happen. - throw new Error('Invalid embedder frame') - } - ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params) - } -} diff --git a/lib/renderer/web-view/guest-view-internal.ts b/lib/renderer/web-view/guest-view-internal.ts new file mode 100644 index 000000000000..789e7fdb6f03 --- /dev/null +++ b/lib/renderer/web-view/guest-view-internal.ts @@ -0,0 +1,124 @@ +import { webFrame, IpcMessageEvent } from 'electron' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { invoke, invokeSync } from '@electron/internal/renderer/ipc-renderer-internal-utils' + +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' + +const WEB_VIEW_EVENTS: Record> = { + 'load-commit': ['url', 'isMainFrame'], + 'did-attach': [], + 'did-finish-load': [], + 'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], + 'did-frame-finish-load': ['isMainFrame', 'frameProcessId', 'frameRoutingId'], + 'did-start-loading': [], + 'did-stop-loading': [], + 'dom-ready': [], + 'console-message': ['level', 'message', 'line', 'sourceId'], + 'context-menu': ['params'], + 'devtools-opened': [], + 'devtools-closed': [], + 'devtools-focused': [], + 'new-window': ['url', 'frameName', 'disposition', 'options'], + 'will-navigate': ['url'], + 'did-start-navigation': ['url', 'isInPlace', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], + 'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'], + 'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], + 'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'], + 'focus-change': ['focus', 'guestInstanceId'], + 'close': [], + 'crashed': [], + 'gpu-crashed': [], + 'plugin-crashed': ['name', 'version'], + 'destroyed': [], + 'page-title-updated': ['title', 'explicitSet'], + 'page-favicon-updated': ['favicons'], + 'enter-html-full-screen': [], + 'leave-html-full-screen': [], + 'media-started-playing': [], + 'media-paused': [], + 'found-in-page': ['result'], + 'did-change-theme-color': ['themeColor'], + 'update-target-url': ['url'] +} + +const DEPRECATED_EVENTS: Record = { + 'page-title-updated': 'page-title-set' +} + +const dispatchEvent = function ( + webView: WebViewImpl, eventName: string, eventKey: string, ...args: Array +) { + if (DEPRECATED_EVENTS[eventName] != null) { + dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args) + } + + const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent + WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { + (domEvent as any)[prop] = args[index] + }) + + webView.dispatchEvent(domEvent) + + if (eventName === 'load-commit') { + webView.onLoadCommit(domEvent) + } else if (eventName === 'focus-change') { + webView.onFocusChange() + } +} + +export function registerEvents (webView: WebViewImpl, viewInstanceId: number) { + ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () { + webView.guestInstanceId = undefined + webView.reset() + const domEvent = new Event('destroyed') + webView.dispatchEvent(domEvent) + }) + + ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) { + dispatchEvent(webView, eventName, eventName, ...args) + }) + + ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) { + const domEvent = new Event('ipc-message') as IpcMessageEvent + domEvent.channel = channel + domEvent.args = args + + webView.dispatchEvent(domEvent) + }) +} + +export function deregisterEvents (viewInstanceId: number) { + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`) + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`) + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`) +} + +export function createGuest (params: Record): Promise { + return invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) +} + +export function createGuestSync (params: Record): number { + return invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) +} + +export function destroyGuest (guestInstanceId: number): void { + invoke('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId) +} + +export function attachGuest ( + elementInstanceId: number, guestInstanceId: number, params: Record, contentWindow: Window +) { + const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow) + if (embedderFrameId < 0) { // this error should not happen. + throw new Error('Invalid embedder frame') + } + invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params) +} + +export const guestViewInternalModule = { + deregisterEvents, + createGuest, + createGuestSync, + destroyGuest, + attachGuest +} diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.ts similarity index 58% rename from lib/renderer/web-view/web-view-attributes.js rename to lib/renderer/web-view/web-view-attributes.ts index 818813bd8abc..07030b7015f7 100644 --- a/lib/renderer/web-view/web-view-attributes.js +++ b/lib/renderer/web-view/web-view-attributes.ts @@ -1,14 +1,12 @@ -'use strict' - -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') -const { WebViewImpl } = require('@electron/internal/renderer/web-view/web-view-impl') -const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' // Helper function to resolve url set in attribute. const a = document.createElement('a') -const resolveURL = function (url) { - if (url === '') return '' +const resolveURL = function (url?: string | null) { + if (!url) return '' a.href = url return a.href } @@ -16,33 +14,35 @@ const resolveURL = function (url) { // Attribute objects. // Default implementation of a WebView attribute. class WebViewAttribute { - constructor (name, webViewImpl) { + public value: any; + public ignoreMutation = false; + + constructor (public name: string, public webViewImpl: WebViewImpl) { this.name = name - this.value = webViewImpl.webviewNode[name] || '' + this.value = (webViewImpl.webviewNode as Record)[name] || '' this.webViewImpl = webViewImpl - this.ignoreMutation = false this.defineProperty() } // Retrieves and returns the attribute's value. - getValue () { + public getValue () { return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value } // Sets the attribute's value. - setValue (value) { + public setValue (value: any) { this.webViewImpl.webviewNode.setAttribute(this.name, value || '') } // Changes the attribute's value without triggering its mutation handler. - setValueIgnoreMutation (value) { + public setValueIgnoreMutation (value: any) { this.ignoreMutation = true this.setValue(value) this.ignoreMutation = false } // Defines this attribute as a property on the webview node. - defineProperty () { + public defineProperty () { return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { get: () => { return this.getValue() @@ -55,7 +55,7 @@ class WebViewAttribute { } // Called when the attribute's value changes. - handleMutation () {} + public handleMutation (..._args: Array): any {} } // An attribute that is treated as a Boolean. @@ -64,7 +64,7 @@ class BooleanAttribute extends WebViewAttribute { return this.webViewImpl.webviewNode.hasAttribute(this.name) } - setValue (value) { + setValue (value: boolean) { if (value) { this.webViewImpl.webviewNode.setAttribute(this.name, '') } else { @@ -75,35 +75,38 @@ class BooleanAttribute extends WebViewAttribute { // Attribute representing the state of the storage partition. class PartitionAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl) - this.validPartitionId = true + public validPartitionId = true + + constructor (public webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl) } - handleMutation (oldValue, newValue) { + public handleMutation (oldValue: any, newValue: any) { newValue = newValue || '' // The partition cannot change if the webview has already navigated. if (!this.webViewImpl.beforeFirstNavigation) { - console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED) + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED) this.setValueIgnoreMutation(oldValue) return } if (newValue === 'persist:') { this.validPartitionId = false - console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE) + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE) } } } // Attribute that handles the location and navigation of the webview. class SrcAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_SRC, webViewImpl) + public observer!: MutationObserver; + + constructor (public webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl) this.setupMutationObserver() } - getValue () { + public getValue () { if (this.webViewImpl.webviewNode.hasAttribute(this.name)) { return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) } else { @@ -111,7 +114,7 @@ class SrcAttribute extends WebViewAttribute { } } - setValueIgnoreMutation (value) { + public setValueIgnoreMutation (value: any) { super.setValueIgnoreMutation(value) // takeRecords() is needed to clear queued up src mutations. Without it, it @@ -121,7 +124,7 @@ class SrcAttribute extends WebViewAttribute { this.observer.takeRecords() } - handleMutation (oldValue, newValue) { + public handleMutation (oldValue: any, newValue: any) { // Once we have navigated, we don't allow clearing the src attribute. // Once enters a navigated state, it cannot return to a // placeholder state. @@ -139,7 +142,7 @@ class SrcAttribute extends WebViewAttribute { // attribute without any changes to its value. This is useful in the case // where the webview guest has crashed and navigating to the same address // spawns off a new process. - setupMutationObserver () { + public setupMutationObserver () { this.observer = new MutationObserver((mutations) => { for (const mutation of mutations) { const { oldValue } = mutation @@ -150,16 +153,18 @@ class SrcAttribute extends WebViewAttribute { this.handleMutation(oldValue, newValue) } }) + const params = { attributes: true, attributeOldValue: true, attributeFilter: [this.name] } + this.observer.observe(this.webViewImpl.webviewNode, params) } - parse () { - if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) { + public parse () { + if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) { return } if (this.webViewImpl.guestInstanceId == null) { @@ -171,12 +176,14 @@ class SrcAttribute extends WebViewAttribute { } // Navigate to |this.src|. - const opts = {} - const httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue() + const opts: Record = {} + + const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue() if (httpreferrer) { opts.httpReferrer = httpreferrer } - const useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue() + + const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue() if (useragent) { opts.userAgent = useragent } @@ -191,69 +198,72 @@ class SrcAttribute extends WebViewAttribute { // Attribute specifies HTTP referrer. class HttpReferrerAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl) } } // Attribute specifies user agent class UserAgentAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl) } } // Attribute that set preload script. class PreloadAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl) } - getValue () { + public getValue () { if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { return this.value } + let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) const protocol = preload.substr(0, 5) + if (protocol !== 'file:') { - console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE) + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE) preload = '' } + return preload } } // Attribute that specifies the blink features to be enabled. class BlinkFeaturesAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl) } } // Attribute that specifies the blink features to be disabled. class DisableBlinkFeaturesAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) } } // Attribute that specifies the web preferences to be enabled. class WebPreferencesAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_WEBPREFERENCES, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl) } } class EnableRemoteModuleAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl) + constructor (webViewImpl: WebViewImpl) { + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl) } - getValue () { + public getValue () { return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false' } - setValue (value) { + public setValue (value: any) { this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false') } } @@ -261,17 +271,17 @@ class EnableRemoteModuleAttribute extends WebViewAttribute { // Sets up all of the webview attributes. WebViewImpl.prototype.setupWebViewAttributes = function () { this.attributes = {} - this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this) - this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this) - this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this) - this.attributes[webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this) } diff --git a/lib/renderer/web-view/web-view-constants.js b/lib/renderer/web-view/web-view-constants.js deleted file mode 100644 index 459aafaca28b..000000000000 --- a/lib/renderer/web-view/web-view-constants.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict' - -module.exports = { - // Attributes. - ATTRIBUTE_NAME: 'name', - ATTRIBUTE_PARTITION: 'partition', - ATTRIBUTE_SRC: 'src', - ATTRIBUTE_HTTPREFERRER: 'httpreferrer', - ATTRIBUTE_NODEINTEGRATION: 'nodeintegration', - ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule', - ATTRIBUTE_PLUGINS: 'plugins', - ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity', - ATTRIBUTE_ALLOWPOPUPS: 'allowpopups', - ATTRIBUTE_PRELOAD: 'preload', - ATTRIBUTE_USERAGENT: 'useragent', - ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', - ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures', - ATTRIBUTE_WEBPREFERENCES: 'webpreferences', - - // Internal attribute. - ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', - - // Error messages. - ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.', - ERROR_MSG_CANNOT_INJECT_SCRIPT: ': ' + 'Script cannot be injected into content until the page has loaded.', - ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.', - ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.' -} diff --git a/lib/renderer/web-view/web-view-constants.ts b/lib/renderer/web-view/web-view-constants.ts new file mode 100644 index 000000000000..6a31dbc0d003 --- /dev/null +++ b/lib/renderer/web-view/web-view-constants.ts @@ -0,0 +1,26 @@ +export const enum WEB_VIEW_CONSTANTS { + // Attributes. + ATTRIBUTE_NAME = 'name', + ATTRIBUTE_PARTITION = 'partition', + ATTRIBUTE_SRC = 'src', + ATTRIBUTE_HTTPREFERRER = 'httpreferrer', + ATTRIBUTE_NODEINTEGRATION = 'nodeintegration', + ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule', + ATTRIBUTE_PLUGINS = 'plugins', + ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity', + ATTRIBUTE_ALLOWPOPUPS = 'allowpopups', + ATTRIBUTE_PRELOAD = 'preload', + ATTRIBUTE_USERAGENT = 'useragent', + ATTRIBUTE_BLINKFEATURES = 'blinkfeatures', + ATTRIBUTE_DISABLEBLINKFEATURES = 'disableblinkfeatures', + ATTRIBUTE_WEBPREFERENCES = 'webpreferences', + + // Internal attribute. + ATTRIBUTE_INTERNALINSTANCEID = 'internalinstanceid', + + // Error messages. + ERROR_MSG_ALREADY_NAVIGATED = 'The object has already navigated, so its partition cannot be changed.', + ERROR_MSG_CANNOT_INJECT_SCRIPT = ' = ' + 'Script cannot be injected into content until the page has loaded.', + ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.', + ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE = 'Only "file:" protocol is supported in "preload" attribute.' +} diff --git a/lib/renderer/web-view/web-view-element.js b/lib/renderer/web-view/web-view-element.ts similarity index 52% rename from lib/renderer/web-view/web-view-element.js rename to lib/renderer/web-view/web-view-element.ts index beb0a34d0f88..26ac4e0badf1 100644 --- a/lib/renderer/web-view/web-view-element.js +++ b/lib/renderer/web-view/web-view-element.ts @@ -1,5 +1,3 @@ -'use strict' - // When using context isolation, the WebViewElement and the custom element // methods have to be defined in the main world to be able to be registered. // @@ -10,27 +8,30 @@ // which runs in browserify environment instead of Node environment, all native // modules must be passed from outside, all included files must be plain JS. -const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' +import { WebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl' // Return a WebViewElement class that is defined in this context. -const defineWebViewElement = (v8Util, webViewImpl) => { +const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { const { guestViewInternal, WebViewImpl } = webViewImpl return class WebViewElement extends HTMLElement { + public internalInstanceId?: number; + static get observedAttributes () { return [ - webViewConstants.ATTRIBUTE_PARTITION, - webViewConstants.ATTRIBUTE_SRC, - webViewConstants.ATTRIBUTE_HTTPREFERRER, - webViewConstants.ATTRIBUTE_USERAGENT, - webViewConstants.ATTRIBUTE_NODEINTEGRATION, - webViewConstants.ATTRIBUTE_PLUGINS, - webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, - webViewConstants.ATTRIBUTE_ALLOWPOPUPS, - webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, - webViewConstants.ATTRIBUTE_PRELOAD, - webViewConstants.ATTRIBUTE_BLINKFEATURES, - webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, - webViewConstants.ATTRIBUTE_WEBPREFERENCES + WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, + WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, + WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, + WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, + WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, + WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, + WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, + WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, + WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, + WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, + WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, + WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, + WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES ] } @@ -40,26 +41,26 @@ const defineWebViewElement = (v8Util, webViewImpl) => { } connectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal') if (!internal) { return } if (!internal.elementAttached) { guestViewInternal.registerEvents(internal, internal.viewInstanceId) internal.elementAttached = true - internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse() + internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse() } } - attributeChangedCallback (name, oldValue, newValue) { - const internal = v8Util.getHiddenValue(this, 'internal') + attributeChangedCallback (name: string, oldValue: any, newValue: any) { + const internal = v8Util.getHiddenValue(this, 'internal') if (internal) { internal.handleWebviewAttributeMutation(name, oldValue, newValue) } } disconnectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal') if (!internal) { return } @@ -72,15 +73,18 @@ const defineWebViewElement = (v8Util, webViewImpl) => { } // Register custom element. -const registerWebViewElement = (v8Util, webViewImpl) => { - const WebViewElement = defineWebViewElement(v8Util, webViewImpl) +const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { + // I wish eslint wasn't so stupid, but it is + // eslint-disable-next-line + const WebViewElement = defineWebViewElement(v8Util, webViewImpl) as unknown as typeof ElectronInternal.WebViewElement webViewImpl.setupMethods(WebViewElement) // The customElements.define has to be called in a special scope. - webViewImpl.webFrame.allowGuestViewElementDefinition(window, () => { - window.customElements.define('webview', WebViewElement) - window.WebView = WebViewElement + const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal + webFrame.allowGuestViewElementDefinition(window, () => { + window.customElements.define('webview', WebViewElement); + (window as any).WebView = WebViewElement // Delete the callbacks so developers cannot call them and produce unexpected // behavior. @@ -90,21 +94,24 @@ const registerWebViewElement = (v8Util, webViewImpl) => { // Now that |observedAttributes| has been retrieved, we can hide it from // user code as well. - delete WebViewElement.observedAttributes + // TypeScript is concerned that we're deleting a read-only attribute + delete (WebViewElement as any).observedAttributes }) } // Prepare to register the element. -const setupWebView = (v8Util, webViewImpl) => { +export const setupWebView = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { const useCapture = true - window.addEventListener('readystatechange', function listener (event) { + const listener = (event: Event) => { if (document.readyState === 'loading') { return } + webViewImpl.setupAttributes() registerWebViewElement(v8Util, webViewImpl) - window.removeEventListener(event.type, listener, useCapture) - }, useCapture) -} -module.exports = { setupWebView } + window.removeEventListener(event.type, listener, useCapture) + } + + window.addEventListener('readystatechange', listener, useCapture) +} diff --git a/lib/renderer/web-view/web-view-impl.js b/lib/renderer/web-view/web-view-impl.ts similarity index 66% rename from lib/renderer/web-view/web-view-impl.js rename to lib/renderer/web-view/web-view-impl.ts index ccbdb4cf7d34..d607c0a8b638 100644 --- a/lib/renderer/web-view/web-view-impl.js +++ b/lib/renderer/web-view/web-view-impl.ts @@ -1,16 +1,15 @@ -'use strict' +import { deprecate, webFrame } from 'electron' -const { webFrame, deprecate } = require('electron') - -const v8Util = process.atomBinding('v8_util') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') -const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal') -const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants') -const { +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal' +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' +import { syncMethods, asyncCallbackMethods, asyncPromiseMethods -} = require('@electron/internal/common/web-view-methods') +} from '@electron/internal/common/web-view-methods' + +const v8Util = process.atomBinding('v8_util') // ID generator. let nextId = 0 @@ -20,16 +19,25 @@ const getNextId = function () { } // Represents the internal state of the WebView node. -class WebViewImpl { - constructor (webviewNode) { - this.webviewNode = webviewNode - this.elementAttached = false - this.beforeFirstNavigation = true - this.hasFocus = false +export class WebViewImpl { + public beforeFirstNavigation = true + public elementAttached = false + public guestInstanceId?: number + public hasFocus = false + public internalInstanceId?: number; + public resizeObserver?: ResizeObserver; + public userAgentOverride?: string; + public viewInstanceId: number - // on* Event handlers. - this.on = {} + // on* Event handlers. + public on: Record = {} + public internalElement: HTMLIFrameElement + // Replaced in web-view-attributes + public attributes: Record = {} + public setupWebViewAttributes (): void {} + + constructor (public webviewNode: HTMLElement) { // Create internal iframe element. this.internalElement = this.createInternalElement() const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' }) @@ -70,22 +78,17 @@ class WebViewImpl { } this.beforeFirstNavigation = true - this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true // Since attachment swaps a local frame for a remote frame, we need our // internal iframe element to be local again before we can reattach. const newFrame = this.createInternalElement() const oldFrame = this.internalElement this.internalElement = newFrame - oldFrame.parentNode.replaceChild(newFrame, oldFrame) - } - // Sets the .request property. - setRequestPropertyOnWebViewNode (request) { - Object.defineProperty(this.webviewNode, 'request', { - value: request, - enumerable: true - }) + if (oldFrame && oldFrame.parentNode) { + oldFrame.parentNode.replaceChild(newFrame, oldFrame) + } } // This observer monitors mutations to attributes of the and @@ -93,7 +96,7 @@ class WebViewImpl { // a BrowserPlugin property will update the corresponding BrowserPlugin // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more // details. - handleWebviewAttributeMutation (attributeName, oldValue, newValue) { + handleWebviewAttributeMutation (attributeName: string, oldValue: any, newValue: any) { if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) { return } @@ -103,7 +106,7 @@ class WebViewImpl { } onElementResize () { - const resizeEvent = new Event('resize') + const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent resizeEvent.newWidth = this.webviewNode.clientWidth resizeEvent.newHeight = this.webviewNode.clientHeight this.dispatchEvent(resizeEvent) @@ -120,13 +123,13 @@ class WebViewImpl { this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams())) } - dispatchEvent (webViewEvent) { + dispatchEvent (webViewEvent: Electron.Event) { this.webviewNode.dispatchEvent(webViewEvent) } // Adds an 'on' property on the webview, which can be used to set/unset // an event handler. - setupEventProperty (eventName) { + setupEventProperty (eventName: string) { const propertyName = `on${eventName.toLowerCase()}` return Object.defineProperty(this.webviewNode, propertyName, { get: () => { @@ -146,14 +149,14 @@ class WebViewImpl { } // Updates state upon loadcommit. - onLoadCommit (webViewEvent) { - const oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC) + onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) { + const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) const newValue = webViewEvent.url if (webViewEvent.isMainFrame && (oldValue !== newValue)) { // Touching the src attribute triggers a navigation. To avoid // triggering a page reload on every guest-initiated navigation, // we do not handle this mutation. - this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue) } } @@ -166,52 +169,66 @@ class WebViewImpl { } } - onAttach (storagePartitionId) { - return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId) + onAttach (storagePartitionId: number) { + return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId) } buildParams () { - const params = { + const params: Record = { instanceId: this.viewInstanceId, userAgentOverride: this.userAgentOverride } + for (const attributeName in this.attributes) { if (this.attributes.hasOwnProperty(attributeName)) { params[attributeName] = this.attributes[attributeName].getValue() } } + return params } - attachGuestInstance (guestInstanceId) { + attachGuestInstance (guestInstanceId: number) { if (!this.elementAttached) { // The element could be detached before we got response from browser. return } this.internalInstanceId = getNextId() this.guestInstanceId = guestInstanceId - guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow) + + guestViewInternal.attachGuest( + this.internalInstanceId, + this.guestInstanceId, + this.buildParams(), + this.internalElement.contentWindow! + ) + // ResizeObserver is a browser global not recognized by "standard". /* globals ResizeObserver */ // TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not // even documented. - this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)).observe(this.internalElement) + this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)) + this.resizeObserver.observe(this.internalElement) } } -const setupAttributes = () => { +export const setupAttributes = () => { require('@electron/internal/renderer/web-view/web-view-attributes') } -const setupMethods = (WebViewElement) => { +// I wish eslint wasn't so stupid, but it is +// eslint-disable-next-line +export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => { // WebContents associated with this webview. WebViewElement.prototype.getWebContents = function () { const { getRemote } = require('@electron/internal/renderer/remote') const getGuestWebContents = getRemote('getGuestWebContents') - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal') + if (!internal.guestInstanceId) { internal.createGuestSync() } + return getGuestWebContents(internal.guestInstanceId) } @@ -220,8 +237,8 @@ const setupMethods = (WebViewElement) => { this.contentWindow.focus() } - const getGuestInstanceId = function (self) { - const internal = v8Util.getHiddenValue(self, 'internal') + const getGuestInstanceId = function (self: any) { + const internal = v8Util.getHiddenValue(self, 'internal') if (!internal.guestInstanceId) { throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.') } @@ -229,34 +246,41 @@ const setupMethods = (WebViewElement) => { } // Forward proto.foo* method calls to WebViewImpl.foo*. - const createBlockHandler = function (method) { - return function (...args) { + const createBlockHandler = function (method: string) { + return function (this: any, ...args: Array) { return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', getGuestInstanceId(this), method, args) } } + for (const method of syncMethods) { - WebViewElement.prototype[method] = createBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createBlockHandler(method) } - const createNonBlockHandler = function (method) { - return function (...args) { + const createNonBlockHandler = function (method: string) { + return function (this: any, ...args: Array) { ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', getGuestInstanceId(this), method, args) } } for (const method of asyncCallbackMethods) { - WebViewElement.prototype[method] = createNonBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createNonBlockHandler(method) } - const createPromiseHandler = function (method) { - return function (...args) { + const createPromiseHandler = function (method: string) { + return function (this: any, ...args: Array) { return ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', getGuestInstanceId(this), method, args) } } for (const method of asyncPromiseMethods) { - WebViewElement.prototype[method] = deprecate.promisify(createPromiseHandler(method)) + (WebViewElement.prototype as Record)[method] = deprecate.promisify(createPromiseHandler(method)) } } -module.exports = { setupAttributes, setupMethods, guestViewInternal, webFrame, WebViewImpl } +export const webViewImplModule = { + setupAttributes, + setupMethods, + guestViewInternal, + webFrame, + WebViewImpl +} diff --git a/lib/renderer/web-view/web-view-init.js b/lib/renderer/web-view/web-view-init.ts similarity index 67% rename from lib/renderer/web-view/web-view-init.js rename to lib/renderer/web-view/web-view-init.ts index a57aacb6ae7b..5e2c2fe15262 100644 --- a/lib/renderer/web-view/web-view-init.js +++ b/lib/renderer/web-view/web-view-init.ts @@ -1,9 +1,8 @@ -'use strict' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') const v8Util = process.atomBinding('v8_util') -function handleFocusBlur (guestInstanceId) { +function handleFocusBlur (guestInstanceId: number) { // Note that while Chromium content APIs have observer for focus/blur, they // unfortunately do not work for webview. @@ -16,15 +15,17 @@ function handleFocusBlur (guestInstanceId) { }) } -module.exports = function (contextIsolation, webviewTag, guestInstanceId) { +export function webViewInit ( + contextIsolation: boolean, webviewTag: ElectronInternal.WebViewElement, guestInstanceId: number +) { // Don't allow recursive ``. if (webviewTag && guestInstanceId == null) { - const webViewImpl = require('@electron/internal/renderer/web-view/web-view-impl') + const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl') if (contextIsolation) { - v8Util.setHiddenValue(window, 'web-view-impl', webViewImpl) + v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule) } else { const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImpl) + setupWebView(v8Util, webViewImplModule) } } diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js index d9bf39900499..7537276e40f7 100644 --- a/lib/sandboxed_renderer/init.js +++ b/lib/sandboxed_renderer/init.js @@ -116,7 +116,8 @@ const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanc // Load webview tag implementation. if (process.isMainFrame) { - require('@electron/internal/renderer/web-view/web-view-init')(contextIsolation, isWebViewTagEnabled, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') + webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId) } const errorUtils = require('@electron/internal/common/error-utils') diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 130c181958f6..8c5f53281d34 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -62,5 +62,56 @@ declare interface Window { FileSystemWorkspaceBinding: { completeURL: (project: string, path: string) => string; } - } + }; + ResizeObserver: ResizeObserver; } + +/** + * The ResizeObserver interface is used to observe changes to Element's content + * rect. + * + * It is modeled after MutationObserver and IntersectionObserver. + */ +declare class ResizeObserver { + constructor (callback: ResizeObserverCallback); + + /** + * Adds target to the list of observed elements. + */ + observe: (target: Element) => void; + + /** + * Removes target from the list of observed elements. + */ + unobserve: (target: Element) => void; + + /** + * Clears both the observationTargets and activeTargets lists. + */ + disconnect: () => void; +} + +/** + * This callback delivers ResizeObserver's notifications. It is invoked by a + * broadcast active observations algorithm. + */ +interface ResizeObserverCallback { + (entries: ResizeObserverEntry[], observer: ResizeObserver): void; +} + +interface ResizeObserverEntry { + /** + * @param target The Element whose size has changed. + */ + new (target: Element): ResizeObserverEntry; + + /** + * The Element whose size has changed. + */ + readonly target: Element; + + /** + * Element's content rect when ResizeObserverCallback is invoked. + */ + readonly contentRect: DOMRectReadOnly; +} \ No newline at end of file diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index cc2566b93692..6a73f6d8cca6 100644 --- a/typings/internal-electron.d.ts +++ b/typings/internal-electron.d.ts @@ -80,6 +80,34 @@ declare namespace ElectronInternal { on(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this; once(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this; } + + interface WebFrameInternal extends Electron.WebFrame { + getWebFrameId(window: Window): number; + allowGuestViewElementDefinition(window: Window, context: any): void; + } + + interface WebFrameResizeEvent extends Electron.Event { + newWidth: number; + newHeight: number; + } + + interface WebViewEvent extends Event { + url: string; + isMainFrame: boolean; + } + + abstract class WebViewElement extends HTMLElement { + static observedAttributes: Array; + + public contentWindow: Window; + + public connectedCallback(): void; + public attributeChangedCallback(): void; + public disconnectedCallback(): void; + + // Created in web-view-impl + public getWebContents(): Electron.WebContents; + } } declare namespace Chrome {