2016-03-25 19:57:17 +00:00
'use strict'
2016-01-13 04:46:13 +00:00
2016-03-25 19:57:17 +00:00
const webFrame = require ( 'electron' ) . webFrame
const remote = require ( 'electron' ) . remote
const ipcRenderer = require ( 'electron' ) . ipcRenderer
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
const v8Util = process . atomBinding ( 'v8_util' )
const guestViewInternal = require ( './guest-view-internal' )
const webViewConstants = require ( './web-view-constants' )
2016-01-12 02:40:23 +00:00
2016-11-03 17:39:40 +00:00
const hasProp = { } . hasOwnProperty
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// ID generator.
2016-11-03 17:39:40 +00:00
let nextId = 0
2016-01-12 02:40:23 +00:00
2016-11-03 17:39:40 +00:00
const getNextId = function ( ) {
2016-03-25 19:57:17 +00:00
return ++ nextId
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Represents the internal state of the WebView node.
2016-11-03 17:39:40 +00:00
const WebViewImpl = ( function ( ) {
2016-03-25 19:57:17 +00:00
function WebViewImpl ( webviewNode ) {
this . webviewNode = webviewNode
v8Util . setHiddenValue ( this . webviewNode , 'internal' , this )
this . attached = false
this . elementAttached = false
this . beforeFirstNavigation = true
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// on* Event handlers.
2016-03-25 19:57:17 +00:00
this . on = { }
this . browserPluginNode = this . createBrowserPluginNode ( )
2016-11-03 17:39:40 +00:00
const shadowRoot = this . webviewNode . createShadowRoot ( )
2016-09-22 21:47:20 +00:00
shadowRoot . innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>'
2016-03-25 19:57:17 +00:00
this . setupWebViewAttributes ( )
this . setupFocusPropagation ( )
this . viewInstanceId = getNextId ( )
shadowRoot . appendChild ( this . browserPluginNode )
2016-01-13 04:46:13 +00:00
// Subscribe to host's zoom level changes.
2016-01-13 06:58:16 +00:00
this . onZoomLevelChanged = ( zoomLevel ) => {
2016-03-25 19:57:17 +00:00
this . webviewNode . setZoomLevel ( zoomLevel )
}
webFrame . on ( 'zoom-level-changed' , this . onZoomLevelChanged )
2016-05-24 19:44:09 +00:00
this . onVisibilityChanged = ( event , visibilityState ) => {
this . webviewNode . send ( 'ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE' , visibilityState )
}
ipcRenderer . on ( 'ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE' , this . onVisibilityChanged )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . createBrowserPluginNode = function ( ) {
2016-01-14 19:10:12 +00:00
// We create BrowserPlugin as a custom element in order to observe changes
// to attributes synchronously.
2016-11-03 17:39:40 +00:00
const browserPluginNode = new WebViewImpl . BrowserPlugin ( )
2016-03-25 19:57:17 +00:00
v8Util . setHiddenValue ( browserPluginNode , 'internal' , this )
return browserPluginNode
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Resets some state upon reattaching <webview> element to the DOM.
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . reset = function ( ) {
2016-01-13 06:58:16 +00:00
// Unlisten the zoom-level-changed event.
2016-03-25 19:57:17 +00:00
webFrame . removeListener ( 'zoom-level-changed' , this . onZoomLevelChanged )
2016-05-24 19:44:09 +00:00
ipcRenderer . removeListener ( 'ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE' , this . onVisibilityChanged )
2016-01-12 02:40:23 +00:00
2016-01-14 19:10:12 +00:00
// If guestInstanceId is defined then the <webview> has navigated and has
// already picked up a partition ID. Thus, we need to reset the initialization
// state. However, it may be the case that beforeFirstNavigation is false BUT
// guestInstanceId has yet to be initialized. This means that we have not
// heard back from createGuest yet. We will not reset the flag in this case so
// that we don't end up allocating a second guest.
2016-01-12 02:40:23 +00:00
if ( this . guestInstanceId ) {
2016-03-25 19:57:17 +00:00
guestViewInternal . destroyGuest ( this . guestInstanceId )
this . guestInstanceId = void 0
2016-01-12 02:40:23 +00:00
}
2016-09-08 17:01:01 +00:00
this . webContents = null
this . attributes [ webViewConstants . ATTRIBUTE _GUESTINSTANCE ] . setValueIgnoreMutation ( undefined )
this . beforeFirstNavigation = true
this . attributes [ webViewConstants . ATTRIBUTE _PARTITION ] . validPartitionId = true
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Sets the <webview>.request property.
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . setRequestPropertyOnWebViewNode = function ( request ) {
2016-11-03 17:19:52 +00:00
Object . defineProperty ( this . webviewNode , 'request' , {
2016-01-12 02:40:23 +00:00
value : request ,
enumerable : true
2016-03-25 19:57:17 +00:00
} )
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . setupFocusPropagation = function ( ) {
2016-01-12 02:40:23 +00:00
if ( ! this . webviewNode . hasAttribute ( 'tabIndex' ) ) {
2016-01-14 19:10:12 +00:00
// <webview> needs a tabIndex in order to be focusable.
// TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
// to allow <webview> to be focusable.
// See http://crbug.com/231664.
2016-03-25 19:57:17 +00:00
this . webviewNode . setAttribute ( 'tabIndex' , - 1 )
2016-01-12 02:40:23 +00:00
}
2016-03-10 19:54:17 +00:00
// Focus the BrowserPlugin when the <webview> takes focus.
this . webviewNode . addEventListener ( 'focus' , ( ) => {
2016-03-25 19:57:17 +00:00
this . browserPluginNode . focus ( )
} )
2016-03-10 19:54:17 +00:00
// Blur the BrowserPlugin when the <webview> loses focus.
this . webviewNode . addEventListener ( 'blur' , ( ) => {
2016-03-25 19:57:17 +00:00
this . browserPluginNode . blur ( )
} )
}
2016-01-12 02:40:23 +00:00
2016-01-14 19:10:12 +00:00
// This observer monitors mutations to attributes of the <webview> and
// updates the BrowserPlugin properties accordingly. In turn, updating
// a BrowserPlugin property will update the corresponding BrowserPlugin
// attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
// details.
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . handleWebviewAttributeMutation = function ( attributeName , oldValue , newValue ) {
2016-01-12 02:40:23 +00:00
if ( ! this . attributes [ attributeName ] || this . attributes [ attributeName ] . ignoreMutation ) {
2016-03-25 19:57:17 +00:00
return
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
// Let the changed attribute handle its own mutation
2016-11-03 17:19:52 +00:00
this . attributes [ attributeName ] . handleMutation ( oldValue , newValue )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . handleBrowserPluginAttributeMutation = function ( attributeName , oldValue , newValue ) {
2016-01-12 02:40:23 +00:00
if ( attributeName === webViewConstants . ATTRIBUTE _INTERNALINSTANCEID && ! oldValue && ! ! newValue ) {
2016-03-25 19:57:17 +00:00
this . browserPluginNode . removeAttribute ( webViewConstants . ATTRIBUTE _INTERNALINSTANCEID )
this . internalInstanceId = parseInt ( newValue )
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Track when the element resizes using the element resize callback.
2016-03-25 19:57:17 +00:00
webFrame . registerElementResizeCallback ( this . internalInstanceId , this . onElementResize . bind ( this ) )
2016-11-03 17:19:52 +00:00
if ( this . guestInstanceId ) {
guestViewInternal . attachGuest ( this . internalInstanceId , this . guestInstanceId , this . buildParams ( ) )
2016-01-12 02:40:23 +00:00
}
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . onSizeChanged = function ( webViewEvent ) {
2016-11-03 17:39:40 +00:00
const { newHeight , newWidth } = webViewEvent
const node = this . webviewNode
const width = node . offsetWidth
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Check the current bounds to make sure we do not resize <webview>
// outside of current constraints.
2016-11-03 17:39:40 +00:00
const maxWidth = this . attributes [ webViewConstants . ATTRIBUTE _MAXWIDTH ] . getValue ( ) | width
const maxHeight = this . attributes [ webViewConstants . ATTRIBUTE _MAXHEIGHT ] . getValue ( ) | width
let minWidth = this . attributes [ webViewConstants . ATTRIBUTE _MINWIDTH ] . getValue ( ) | width
let minHeight = this . attributes [ webViewConstants . ATTRIBUTE _MINHEIGHT ] . getValue ( ) | width
2016-03-25 19:57:17 +00:00
minWidth = Math . min ( minWidth , maxWidth )
minHeight = Math . min ( minHeight , maxHeight )
2016-01-12 02:40:23 +00:00
if ( ! this . attributes [ webViewConstants . ATTRIBUTE _AUTOSIZE ] . getValue ( ) || ( newWidth >= minWidth && newWidth <= maxWidth && newHeight >= minHeight && newHeight <= maxHeight ) ) {
2016-03-25 19:57:17 +00:00
node . style . width = newWidth + 'px'
node . style . height = newHeight + 'px'
2016-01-12 02:40:23 +00:00
2016-01-14 19:10:12 +00:00
// Only fire the DOM event if the size of the <webview> has actually
// changed.
2016-11-03 17:19:52 +00:00
this . dispatchEvent ( webViewEvent )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . onElementResize = function ( newSize ) {
2016-01-14 18:35:29 +00:00
// Dispatch the 'resize' event.
2016-11-03 17:39:40 +00:00
const resizeEvent = new Event ( 'resize' , {
2016-01-12 02:40:23 +00:00
bubbles : true
2016-03-25 19:57:17 +00:00
} )
2016-06-18 22:41:42 +00:00
// Using client size values, because when a webview is transformed `newSize`
// is incorrect
newSize . width = this . webviewNode . clientWidth
newSize . height = this . webviewNode . clientHeight
2016-03-25 19:57:17 +00:00
resizeEvent . newWidth = newSize . width
resizeEvent . newHeight = newSize . height
this . dispatchEvent ( resizeEvent )
2016-01-12 02:40:23 +00:00
if ( this . guestInstanceId ) {
2016-11-03 17:19:52 +00:00
guestViewInternal . setSize ( this . guestInstanceId , {
2016-01-12 02:40:23 +00:00
normal : newSize
2016-03-25 19:57:17 +00:00
} )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . createGuest = function ( ) {
2016-03-10 19:54:17 +00:00
return guestViewInternal . createGuest ( this . buildParams ( ) , ( event , guestInstanceId ) => {
2016-09-08 17:01:01 +00:00
this . attachGuestInstance ( guestInstanceId )
2016-03-25 19:57:17 +00:00
} )
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . dispatchEvent = function ( webViewEvent ) {
2016-11-03 17:19:52 +00:00
this . webviewNode . dispatchEvent ( webViewEvent )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Adds an 'on<event>' property on the webview, which can be used to set/unset
// an event handler.
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . setupEventProperty = function ( eventName ) {
2016-11-03 17:39:40 +00:00
const propertyName = 'on' + eventName . toLowerCase ( )
2016-01-12 02:40:23 +00:00
return Object . defineProperty ( this . webviewNode , propertyName , {
2016-03-10 19:54:17 +00:00
get : ( ) => {
2016-03-25 19:57:17 +00:00
return this . on [ propertyName ]
2016-03-10 19:54:17 +00:00
} ,
set : ( value ) => {
if ( this . on [ propertyName ] ) {
2016-03-25 19:57:17 +00:00
this . webviewNode . removeEventListener ( eventName , this . on [ propertyName ] )
2016-03-10 19:54:17 +00:00
}
2016-03-25 19:57:17 +00:00
this . on [ propertyName ] = value
2016-03-10 19:54:17 +00:00
if ( value ) {
2016-03-25 19:57:17 +00:00
return this . webviewNode . addEventListener ( eventName , value )
2016-03-10 19:54:17 +00:00
}
} ,
2016-01-12 02:40:23 +00:00
enumerable : true
2016-03-25 19:57:17 +00:00
} )
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Updates state upon loadcommit.
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . onLoadCommit = function ( webViewEvent ) {
2016-11-03 17:39:40 +00:00
const oldValue = this . webviewNode . getAttribute ( webViewConstants . ATTRIBUTE _SRC )
const newValue = webViewEvent . url
2016-01-12 02:40:23 +00:00
if ( webViewEvent . isMainFrame && ( oldValue !== newValue ) ) {
2016-01-14 19:10:12 +00:00
// Touching the src attribute triggers a navigation. To avoid
// triggering a page reload on every guest-initiated navigation,
// we do not handle this mutation.
2016-11-03 17:39:40 +00:00
this . attributes [ webViewConstants . ATTRIBUTE _SRC ] . setValueIgnoreMutation ( newValue )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . onAttach = function ( storagePartitionId ) {
return this . attributes [ webViewConstants . ATTRIBUTE _PARTITION ] . setValue ( storagePartitionId )
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
WebViewImpl . prototype . buildParams = function ( ) {
2016-11-03 17:39:40 +00:00
const params = {
2016-01-12 02:40:23 +00:00
instanceId : this . viewInstanceId ,
2016-05-19 21:32:16 +00:00
userAgentOverride : this . userAgentOverride ,
2016-05-25 17:13:12 +00:00
zoomFactor : webFrame . getZoomFactor ( )
2016-03-25 19:57:17 +00:00
}
2016-11-03 17:39:40 +00:00
for ( const attributeName in this . attributes ) {
if ( hasProp . call ( this . attributes , attributeName ) ) {
params [ attributeName ] = this . attributes [ attributeName ] . getValue ( )
}
2016-01-12 02:40:23 +00:00
}
2016-01-14 19:10:12 +00:00
// When the WebView is not participating in layout (display:none)
// then getBoundingClientRect() would report a width and height of 0.
// However, in the case where the WebView has a fixed size we can
// use that value to initially size the guest so as to avoid a relayout of
// the on display:block.
2016-11-03 17:39:40 +00:00
const css = window . getComputedStyle ( this . webviewNode , null )
const elementRect = this . webviewNode . getBoundingClientRect ( )
2016-03-25 19:57:17 +00:00
params . elementWidth = parseInt ( elementRect . width ) || parseInt ( css . getPropertyValue ( 'width' ) )
params . elementHeight = parseInt ( elementRect . height ) || parseInt ( css . getPropertyValue ( 'height' ) )
return params
}
2016-09-08 17:01:01 +00:00
WebViewImpl . prototype . attachGuestInstance = function ( guestInstanceId ) {
2016-03-25 19:57:17 +00:00
this . guestInstanceId = guestInstanceId
2016-09-08 17:01:01 +00:00
this . attributes [ webViewConstants . ATTRIBUTE _GUESTINSTANCE ] . setValueIgnoreMutation ( guestInstanceId )
2016-03-25 19:57:17 +00:00
this . webContents = remote . getGuestWebContents ( this . guestInstanceId )
2016-01-12 02:40:23 +00:00
if ( ! this . internalInstanceId ) {
2016-03-25 19:57:17 +00:00
return true
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
return guestViewInternal . attachGuest ( this . internalInstanceId , this . guestInstanceId , this . buildParams ( ) )
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
return WebViewImpl
} ) ( )
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Registers browser plugin <object> custom element.
2016-11-03 17:39:40 +00:00
const registerBrowserPluginElement = function ( ) {
const proto = Object . create ( HTMLObjectElement . prototype )
2016-03-25 19:57:17 +00:00
proto . createdCallback = function ( ) {
this . setAttribute ( 'type' , 'application/browser-plugin' )
this . setAttribute ( 'id' , 'browser-plugin-' + getNextId ( ) )
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// The <object> node fills in the <webview> container.
2016-03-29 00:16:08 +00:00
this . style . flex = '1 1 auto'
2016-03-25 19:57:17 +00:00
}
proto . attributeChangedCallback = function ( name , oldValue , newValue ) {
var internal
internal = v8Util . getHiddenValue ( this , 'internal' )
2016-11-03 17:19:52 +00:00
if ( internal ) {
internal . handleBrowserPluginAttributeMutation ( name , oldValue , newValue )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
proto . attachedCallback = function ( ) {
2016-01-14 18:35:29 +00:00
// Load the plugin immediately.
2016-03-25 19:57:17 +00:00
return this . nonExistentAttribute
}
2016-01-12 02:40:23 +00:00
WebViewImpl . BrowserPlugin = webFrame . registerEmbedderCustomElement ( 'browserplugin' , {
2016-03-25 19:57:17 +00:00
'extends' : 'object' ,
2016-01-12 02:40:23 +00:00
prototype : proto
2016-03-25 19:57:17 +00:00
} )
delete proto . createdCallback
delete proto . attachedCallback
delete proto . detachedCallback
2016-11-03 17:39:40 +00:00
delete proto . attributeChangedCallback
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Registers <webview> custom element.
2016-03-25 19:57:17 +00:00
var registerWebViewElement = function ( ) {
2016-11-03 17:39:40 +00:00
const proto = Object . create ( HTMLObjectElement . prototype )
2016-03-25 19:57:17 +00:00
proto . createdCallback = function ( ) {
return new WebViewImpl ( this )
}
proto . attributeChangedCallback = function ( name , oldValue , newValue ) {
var internal
internal = v8Util . getHiddenValue ( this , 'internal' )
2016-11-03 17:19:52 +00:00
if ( internal ) {
internal . handleWebviewAttributeMutation ( name , oldValue , newValue )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
proto . detachedCallback = function ( ) {
var internal
internal = v8Util . getHiddenValue ( this , 'internal' )
2016-01-12 02:40:23 +00:00
if ( ! internal ) {
2016-03-25 19:57:17 +00:00
return
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
guestViewInternal . deregisterEvents ( internal . viewInstanceId )
internal . elementAttached = false
2016-09-08 17:01:01 +00:00
internal . reset ( )
this . internalInstanceId = 0
2016-03-25 19:57:17 +00:00
}
proto . attachedCallback = function ( ) {
2016-09-08 17:01:01 +00:00
var internal , instance
2016-03-25 19:57:17 +00:00
internal = v8Util . getHiddenValue ( this , 'internal' )
2016-01-12 02:40:23 +00:00
if ( ! internal ) {
2016-03-25 19:57:17 +00:00
return
2016-01-12 02:40:23 +00:00
}
if ( ! internal . elementAttached ) {
2016-03-25 19:57:17 +00:00
guestViewInternal . registerEvents ( internal , internal . viewInstanceId )
internal . elementAttached = true
2016-09-08 17:01:01 +00:00
instance = internal . attributes [ webViewConstants . ATTRIBUTE _GUESTINSTANCE ] . getValue ( )
if ( instance ) {
2016-11-03 17:19:52 +00:00
internal . attachGuestInstance ( instance )
} else {
internal . attributes [ webViewConstants . ATTRIBUTE _SRC ] . parse ( )
2016-09-08 17:01:01 +00:00
}
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Public-facing API methods.
2016-11-03 17:39:40 +00:00
const methods = [
2016-01-14 19:10:12 +00:00
'getURL' ,
2016-01-21 21:31:35 +00:00
'loadURL' ,
2016-01-14 19:10:12 +00:00
'getTitle' ,
'isLoading' ,
2016-04-18 17:37:08 +00:00
'isLoadingMainFrame' ,
2016-01-14 19:10:12 +00:00
'isWaitingForResponse' ,
'stop' ,
'reload' ,
'reloadIgnoringCache' ,
'canGoBack' ,
'canGoForward' ,
'canGoToOffset' ,
'clearHistory' ,
'goBack' ,
'goForward' ,
'goToIndex' ,
'goToOffset' ,
'isCrashed' ,
'setUserAgent' ,
'getUserAgent' ,
'openDevTools' ,
'closeDevTools' ,
'isDevToolsOpened' ,
'isDevToolsFocused' ,
'inspectElement' ,
'setAudioMuted' ,
'isAudioMuted' ,
'undo' ,
'redo' ,
'cut' ,
'copy' ,
'paste' ,
'pasteAndMatchStyle' ,
'delete' ,
'selectAll' ,
'unselect' ,
'replace' ,
'replaceMisspelling' ,
'findInPage' ,
'stopFindInPage' ,
'getId' ,
'downloadURL' ,
'inspectServiceWorker' ,
'print' ,
2016-06-02 17:12:38 +00:00
'printToPDF' ,
2016-07-05 22:43:57 +00:00
'showDefinitionForSelection' ,
'capturePage'
2016-03-25 19:57:17 +00:00
]
2016-11-03 17:39:40 +00:00
const nonblockMethods = [
2016-01-13 03:55:49 +00:00
'insertCSS' ,
2016-02-25 18:05:01 +00:00
'insertText' ,
2016-01-13 03:55:49 +00:00
'send' ,
2016-02-22 14:00:21 +00:00
'sendInputEvent' ,
2016-01-13 04:46:13 +00:00
'setZoomFactor' ,
'setZoomLevel' ,
2016-03-29 00:16:08 +00:00
'setZoomLevelLimits'
2016-03-25 19:57:17 +00:00
]
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Forward proto.foo* method calls to WebViewImpl.foo*.
2016-11-03 17:39:40 +00:00
const createBlockHandler = function ( m ) {
2016-03-25 19:57:17 +00:00
return function ( ... args ) {
const internal = v8Util . getHiddenValue ( this , 'internal' )
2016-02-05 18:55:32 +00:00
if ( internal . webContents ) {
2016-03-25 19:57:17 +00:00
return internal . webContents [ m ] . apply ( internal . webContents , args )
2016-02-05 18:55:32 +00:00
} else {
2016-03-25 19:57:17 +00:00
throw new Error ( ` Cannot call ${ m } because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called. ` )
2016-02-05 18:55:32 +00:00
}
2016-03-25 19:57:17 +00:00
}
}
2016-11-03 17:39:40 +00:00
for ( let i = 0 , len = methods . length ; i < len ; i ++ ) {
const method = methods [ i ]
proto [ method ] = createBlockHandler ( method )
2016-03-25 19:57:17 +00:00
}
2016-11-03 17:39:40 +00:00
const createNonBlockHandler = function ( m ) {
2016-03-25 19:57:17 +00:00
return function ( ... args ) {
const internal = v8Util . getHiddenValue ( this , 'internal' )
2016-11-03 17:19:52 +00:00
ipcRenderer . send . apply ( ipcRenderer , [ 'ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW' , null , internal . guestInstanceId , m ] . concat ( args ) )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
}
2016-11-03 17:39:40 +00:00
for ( let j = 0 , len1 = nonblockMethods . length ; j < len1 ; j ++ ) {
const method = nonblockMethods [ j ]
proto [ method ] = createNonBlockHandler ( method )
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
proto . executeJavaScript = function ( code , hasUserGesture , callback ) {
2016-11-03 17:39:40 +00:00
const internal = v8Util . getHiddenValue ( this , 'internal' )
2016-03-25 19:57:17 +00:00
if ( typeof hasUserGesture === 'function' ) {
callback = hasUserGesture
hasUserGesture = false
2016-02-25 18:05:01 +00:00
}
2016-11-03 17:39:40 +00:00
const requestId = getNextId ( )
2016-04-06 23:21:26 +00:00
ipcRenderer . send ( 'ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW' , requestId , internal . guestInstanceId , 'executeJavaScript' , code , hasUserGesture )
ipcRenderer . once ( ` ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_ ${ requestId } ` , function ( event , result ) {
2016-03-29 00:35:49 +00:00
if ( callback ) callback ( result )
2016-03-25 19:57:17 +00:00
} )
}
2016-02-17 17:03:27 +00:00
2016-02-17 08:52:19 +00:00
// WebContents associated with this webview.
2016-03-25 19:57:17 +00:00
proto . getWebContents = function ( ) {
2016-11-03 17:39:40 +00:00
const internal = v8Util . getHiddenValue ( this , 'internal' )
2016-03-25 19:57:17 +00:00
return internal . webContents
}
2016-02-17 08:52:19 +00:00
2016-01-12 02:40:23 +00:00
window . WebView = webFrame . registerEmbedderCustomElement ( 'webview' , {
prototype : proto
2016-03-25 19:57:17 +00:00
} )
2016-01-12 02:40:23 +00:00
2016-01-14 19:10:12 +00:00
// Delete the callbacks so developers cannot call them and produce unexpected
// behavior.
2016-03-25 19:57:17 +00:00
delete proto . createdCallback
delete proto . attachedCallback
delete proto . detachedCallback
2016-11-03 17:19:52 +00:00
delete proto . attributeChangedCallback
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-11-03 17:39:40 +00:00
const useCapture = true
2016-01-12 02:40:23 +00:00
2016-11-03 17:39:40 +00:00
const listener = function ( event ) {
2016-01-12 02:40:23 +00:00
if ( document . readyState === 'loading' ) {
2016-03-25 19:57:17 +00:00
return
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
registerBrowserPluginElement ( )
registerWebViewElement ( )
2016-11-03 17:19:52 +00:00
window . removeEventListener ( event . type , listener , useCapture )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
window . addEventListener ( 'readystatechange' , listener , true )
2016-01-12 02:40:23 +00:00
2016-03-25 19:57:17 +00:00
module . exports = WebViewImpl