2016-03-25 19:57:17 +00:00
'use strict'
2016-02-22 02:52:21 +00:00
2019-03-18 19:37:06 +00:00
const v8Util = process . electronBinding ( 'v8_util' )
2019-10-04 17:49:09 +00:00
const { hasSwitch } = process . electronBinding ( 'command_line' )
2016-01-12 02:40:23 +00:00
2019-09-18 16:52:06 +00:00
const { CallbacksRegistry } = require ( '@electron/internal/renderer/remote/callbacks-registry' )
2019-11-12 20:56:17 +00:00
const { isPromise , isSerializableObject } = require ( '@electron/internal/common/type-utils' )
2019-02-15 01:24:25 +00:00
const { ipcRendererInternal } = require ( '@electron/internal/renderer/ipc-renderer-internal' )
2018-05-24 12:05:46 +00:00
2016-03-29 00:35:49 +00:00
const callbacksRegistry = new CallbacksRegistry ( )
2016-07-11 17:28:01 +00:00
const remoteObjectCache = v8Util . createIDWeakMap ( )
2016-02-17 07:57:46 +00:00
2018-07-10 08:15:40 +00:00
// An unique ID that can represent current context.
2018-07-19 04:29:47 +00:00
const contextId = v8Util . getHiddenValue ( global , 'contextId' )
2018-07-10 08:15:40 +00:00
2020-01-13 06:23:03 +00:00
ipcRendererInternal . invoke ( 'ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES' ) . then ( preferences => {
if ( ! preferences . enableRemoteModule ) {
console . warn ( '%cElectron Deprecation Warning' , 'font-weight: bold' , "The 'remote' module is deprecated and will be disabled by default in a future version of Electron. To ensure a smooth upgrade and silence this warning, specify {enableRemoteModule: true} in the WebPreferences for this window." )
}
} , ( err ) => {
console . error ( 'Failed to get web preferences:' , err )
} )
2018-07-10 08:15:40 +00:00
// Notify the main process when current context is going to be released.
// Note that when the renderer process is destroyed, the message may not be
// sent, we also listen to the "render-view-deleted" event in the main process
// to guard that situation.
process . on ( 'exit' , ( ) => {
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
2019-10-23 04:44:21 +00:00
ipcRendererInternal . send ( command , contextId )
2018-07-10 08:15:40 +00:00
} )
2016-01-14 18:35:29 +00:00
// Convert the arguments object into an array of meta data.
2017-10-26 03:41:11 +00:00
function wrapArgs ( args , visited = new Set ( ) ) {
const valueToMeta = ( value ) => {
2016-07-11 17:39:21 +00:00
// Check for circular reference.
if ( visited . has ( value ) ) {
2016-01-12 02:40:23 +00:00
return {
2016-07-11 17:28:01 +00:00
type : 'value' ,
value : null
}
}
if ( Array . isArray ( value ) ) {
2016-07-11 17:39:21 +00:00
visited . add ( value )
2018-10-02 01:56:31 +00:00
const meta = {
2016-01-12 02:40:23 +00:00
type : 'array' ,
2016-07-11 17:28:01 +00:00
value : wrapArgs ( value , visited )
2016-03-25 19:57:17 +00:00
}
2016-07-11 17:28:01 +00:00
visited . delete ( value )
return meta
2019-10-10 13:59:08 +00:00
} else if ( value instanceof Buffer ) {
2016-01-12 02:40:23 +00:00
return {
type : 'buffer' ,
2019-10-10 13:59:08 +00:00
value
2016-03-25 19:57:17 +00:00
}
2019-10-10 13:59:08 +00:00
} else if ( isSerializableObject ( value ) ) {
2016-01-12 02:40:23 +00:00
return {
2019-10-10 13:59:08 +00:00
type : 'value' ,
value
2016-03-25 19:57:17 +00:00
}
2019-10-10 13:59:08 +00:00
} else if ( typeof value === 'object' ) {
2016-05-25 05:38:35 +00:00
if ( isPromise ( value ) ) {
2016-04-20 05:26:49 +00:00
return {
type : 'promise' ,
2016-05-23 22:07:01 +00:00
then : valueToMeta ( function ( onFulfilled , onRejected ) {
value . then ( onFulfilled , onRejected )
} )
2016-04-20 05:26:49 +00:00
}
} else if ( v8Util . getHiddenValue ( value , 'atomId' ) ) {
return {
type : 'remote-object' ,
id : v8Util . getHiddenValue ( value , 'atomId' )
}
}
2018-10-02 01:56:31 +00:00
const meta = {
2016-01-12 02:40:23 +00:00
type : 'object' ,
2017-10-26 03:41:11 +00:00
name : value . constructor ? value . constructor . name : '' ,
2016-01-12 02:40:23 +00:00
members : [ ]
2016-03-25 19:57:17 +00:00
}
2016-07-11 17:39:21 +00:00
visited . add ( value )
2018-10-02 01:56:31 +00:00
for ( const prop in value ) {
2016-07-11 17:28:01 +00:00
meta . members . push ( {
2016-01-12 02:40:23 +00:00
name : prop ,
2016-07-11 17:28:01 +00:00
value : valueToMeta ( value [ prop ] )
2016-03-25 19:57:17 +00:00
} )
2016-01-12 02:40:23 +00:00
}
2016-07-11 17:28:01 +00:00
visited . delete ( value )
return meta
2016-01-12 02:40:23 +00:00
} else if ( typeof value === 'function' && v8Util . getHiddenValue ( value , 'returnValue' ) ) {
return {
type : 'function-with-return-value' ,
value : valueToMeta ( value ( ) )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
} else if ( typeof value === 'function' ) {
return {
type : 'function' ,
id : callbacksRegistry . add ( value ) ,
2016-11-09 13:05:46 +00:00
location : v8Util . getHiddenValue ( value , 'location' ) ,
length : value . length
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
} else {
return {
type : 'value' ,
2019-10-10 13:59:08 +00:00
value
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-12-02 01:29:51 +00:00
return args . map ( valueToMeta )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-02-22 02:52:21 +00:00
// Populate object's members from descriptors.
2016-04-01 06:45:36 +00:00
// The |ref| will be kept referenced by |members|.
2016-02-22 02:52:21 +00:00
// This matches |getObjectMemebers| in rpc-server.
2017-10-25 13:51:21 +00:00
function setObjectMembers ( ref , object , metaId , members ) {
2016-11-15 17:44:41 +00:00
if ( ! Array . isArray ( members ) ) return
2018-10-02 01:56:31 +00:00
for ( const member of members ) {
2016-03-29 00:35:49 +00:00
if ( object . hasOwnProperty ( member . name ) ) continue
2016-02-22 02:52:21 +00:00
2018-10-02 01:56:31 +00:00
const descriptor = { enumerable : member . enumerable }
2016-02-22 02:52:21 +00:00
if ( member . type === 'method' ) {
2016-12-02 01:29:51 +00:00
const remoteMemberFunction = function ( ... args ) {
2017-11-03 01:07:40 +00:00
let command
2016-02-22 02:52:21 +00:00
if ( this && this . constructor === remoteMemberFunction ) {
2017-11-03 01:07:40 +00:00
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
2016-02-22 02:52:21 +00:00
} else {
2017-11-03 01:07:40 +00:00
command = 'ELECTRON_BROWSER_MEMBER_CALL'
2016-02-22 02:52:21 +00:00
}
2019-02-15 01:24:25 +00:00
const ret = ipcRendererInternal . sendSync ( command , contextId , metaId , member . name , wrapArgs ( args ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( ret )
2016-03-25 19:57:17 +00:00
}
2016-08-16 20:54:21 +00:00
2016-08-17 20:58:48 +00:00
let descriptorFunction = proxyFunctionProperties ( remoteMemberFunction , metaId , member . name )
2016-08-16 20:54:21 +00:00
2017-11-03 01:07:40 +00:00
descriptor . get = ( ) => {
2018-09-13 16:10:51 +00:00
descriptorFunction . ref = ref // The member should reference its object.
2016-08-16 20:54:21 +00:00
return descriptorFunction
2016-04-01 06:26:30 +00:00
}
// Enable monkey-patch the method
2017-11-03 01:07:40 +00:00
descriptor . set = ( value ) => {
2016-08-16 20:54:21 +00:00
descriptorFunction = value
2016-04-01 06:26:30 +00:00
return value
}
2016-03-25 19:57:17 +00:00
descriptor . configurable = true
2016-02-22 02:52:21 +00:00
} else if ( member . type === 'get' ) {
2017-11-03 01:07:40 +00:00
descriptor . get = ( ) => {
const command = 'ELECTRON_BROWSER_MEMBER_GET'
2019-02-15 01:24:25 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , metaId , member . name )
2017-11-03 01:07:40 +00:00
return metaToValue ( meta )
2016-03-25 19:57:17 +00:00
}
2016-02-22 02:52:21 +00:00
if ( member . writable ) {
2017-11-03 01:07:40 +00:00
descriptor . set = ( value ) => {
2017-04-03 21:18:04 +00:00
const args = wrapArgs ( [ value ] )
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_MEMBER_SET'
2019-02-15 01:24:25 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , metaId , member . name , args )
2017-10-26 03:41:11 +00:00
if ( meta != null ) metaToValue ( meta )
2016-03-25 19:57:17 +00:00
return value
}
2016-02-22 02:52:21 +00:00
}
}
2016-03-25 19:57:17 +00:00
Object . defineProperty ( object , member . name , descriptor )
2016-02-22 02:52:21 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-02-22 02:52:21 +00:00
// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
2017-10-25 13:51:21 +00:00
function setObjectPrototype ( ref , object , metaId , descriptor ) {
2017-11-03 01:07:40 +00:00
if ( descriptor === null ) return
2018-10-02 01:56:31 +00:00
const proto = { }
2016-04-01 06:45:36 +00:00
setObjectMembers ( ref , proto , metaId , descriptor . members )
setObjectPrototype ( ref , proto , metaId , descriptor . proto )
2016-03-25 19:57:17 +00:00
Object . setPrototypeOf ( object , proto )
}
2016-02-22 02:52:21 +00:00
2016-08-17 20:58:48 +00:00
// Wrap function in Proxy for accessing remote properties
2017-10-25 13:51:21 +00:00
function proxyFunctionProperties ( remoteMemberFunction , metaId , name ) {
2016-08-17 20:58:48 +00:00
let loaded = false
2016-08-17 21:21:50 +00:00
// Lazily load function properties
2016-08-17 20:58:48 +00:00
const loadRemoteProperties = ( ) => {
if ( loaded ) return
loaded = true
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_MEMBER_GET'
2019-02-15 01:24:25 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , metaId , name )
2016-11-15 17:44:41 +00:00
setObjectMembers ( remoteMemberFunction , remoteMemberFunction , meta . id , meta . members )
2016-08-17 20:58:48 +00:00
}
return new Proxy ( remoteMemberFunction , {
2016-09-14 20:21:44 +00:00
set : ( target , property , value , receiver ) => {
if ( property !== 'ref' ) loadRemoteProperties ( )
target [ property ] = value
return true
} ,
2016-08-17 20:58:48 +00:00
get : ( target , property , receiver ) => {
if ( ! target . hasOwnProperty ( property ) ) loadRemoteProperties ( )
2017-03-17 17:29:07 +00:00
const value = target [ property ]
if ( property === 'toString' && typeof value === 'function' ) {
return value . bind ( target )
}
return value
2016-08-17 20:58:48 +00:00
} ,
ownKeys : ( target ) => {
loadRemoteProperties ( )
return Object . getOwnPropertyNames ( target )
} ,
getOwnPropertyDescriptor : ( target , property ) => {
2018-10-02 01:56:31 +00:00
const descriptor = Object . getOwnPropertyDescriptor ( target , property )
2017-10-25 13:51:21 +00:00
if ( descriptor ) return descriptor
2016-08-17 20:58:48 +00:00
loadRemoteProperties ( )
return Object . getOwnPropertyDescriptor ( target , property )
}
} )
}
2016-01-14 18:35:29 +00:00
// Convert meta data from browser into real value.
2017-10-25 13:51:21 +00:00
function metaToValue ( meta ) {
const types = {
2017-11-03 01:07:40 +00:00
value : ( ) => meta . value ,
array : ( ) => meta . members . map ( ( member ) => metaToValue ( member ) ) ,
2019-10-10 13:59:08 +00:00
buffer : ( ) => Buffer . from ( meta . value . buffer , meta . value . byteOffset , meta . value . byteLength ) ,
2019-07-08 06:17:50 +00:00
promise : ( ) => Promise . resolve ( { then : metaToValue ( meta . then ) } ) ,
2019-10-10 13:59:08 +00:00
error : ( ) => metaToError ( meta ) ,
exception : ( ) => { throw metaToError ( meta . value ) }
2017-10-25 13:51:21 +00:00
}
if ( meta . type in types ) {
2017-11-03 01:07:40 +00:00
return types [ meta . type ] ( )
2017-10-25 13:51:21 +00:00
} else {
let ret
2017-11-03 01:07:40 +00:00
if ( remoteObjectCache . has ( meta . id ) ) {
2019-04-16 20:08:11 +00:00
v8Util . addRemoteObjectRef ( contextId , meta . id )
2017-11-03 01:07:40 +00:00
return remoteObjectCache . get ( meta . id )
}
2017-10-25 13:51:21 +00:00
// A shadow class to represent the remote function object.
if ( meta . type === 'function' ) {
2018-10-02 01:56:31 +00:00
const remoteFunction = function ( ... args ) {
2017-11-03 01:07:40 +00:00
let command
2017-10-25 13:51:21 +00:00
if ( this && this . constructor === remoteFunction ) {
2017-11-03 01:07:40 +00:00
command = 'ELECTRON_BROWSER_CONSTRUCTOR'
2017-10-25 13:51:21 +00:00
} else {
2017-11-03 01:07:40 +00:00
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
2016-03-25 19:57:17 +00:00
}
2019-02-15 01:24:25 +00:00
const obj = ipcRendererInternal . sendSync ( command , contextId , meta . id , wrapArgs ( args ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( obj )
2016-01-12 02:40:23 +00:00
}
2017-10-25 13:51:21 +00:00
ret = remoteFunction
} else {
ret = { }
}
2016-01-12 02:40:23 +00:00
2017-10-25 13:51:21 +00:00
setObjectMembers ( ret , ret , meta . id , meta . members )
setObjectPrototype ( ret , ret , meta . id , meta . proto )
Object . defineProperty ( ret . constructor , 'name' , { value : meta . name } )
2016-01-12 02:40:23 +00:00
2017-10-25 13:51:21 +00:00
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
2018-07-10 08:15:40 +00:00
v8Util . setRemoteObjectFreer ( ret , contextId , meta . id )
2017-10-25 13:51:21 +00:00
v8Util . setHiddenValue ( ret , 'atomId' , meta . id )
2019-04-16 20:08:11 +00:00
v8Util . addRemoteObjectRef ( contextId , meta . id )
2017-10-25 13:51:21 +00:00
remoteObjectCache . set ( meta . id , ret )
return ret
2016-01-12 02:40:23 +00:00
}
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2019-10-10 13:59:08 +00:00
function metaToError ( meta ) {
const obj = meta . value
for ( const { name , value } of meta . members ) {
obj [ name ] = metaToValue ( value )
2017-11-03 01:29:17 +00:00
}
2016-03-25 19:57:17 +00:00
return obj
}
2016-01-12 02:40:23 +00:00
2018-08-23 23:27:52 +00:00
function handleMessage ( channel , handler ) {
2019-02-15 01:24:25 +00:00
ipcRendererInternal . on ( channel , ( event , passedContextId , id , ... args ) => {
2018-08-23 23:27:52 +00:00
if ( passedContextId === contextId ) {
2018-10-31 15:26:57 +00:00
handler ( id , ... args )
} else {
// Message sent to an un-exist context, notify the error to main process.
2019-02-15 01:24:25 +00:00
ipcRendererInternal . send ( 'ELECTRON_BROWSER_WRONG_CONTEXT_ERROR' , contextId , passedContextId , id )
2018-08-23 23:27:52 +00:00
}
} )
}
2019-10-04 17:49:09 +00:00
const enableStacks = hasSwitch ( 'enable-api-filtering-logging' )
function getCurrentStack ( ) {
const target = { }
if ( enableStacks ) {
Error . captureStackTrace ( target , getCurrentStack )
}
return target . stack
}
2016-01-14 18:35:29 +00:00
// Browser calls a callback in renderer.
2018-08-23 23:27:52 +00:00
handleMessage ( 'ELECTRON_RENDERER_CALLBACK' , ( id , args ) => {
2016-05-19 22:28:08 +00:00
callbacksRegistry . apply ( id , metaToValue ( args ) )
2016-03-25 19:57:17 +00:00
} )
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// A callback in browser is released.
2018-08-23 23:27:52 +00:00
handleMessage ( 'ELECTRON_RENDERER_RELEASE_CALLBACK' , ( id ) => {
2016-05-19 22:28:08 +00:00
callbacksRegistry . remove ( id )
2016-03-25 19:57:17 +00:00
} )
2016-01-12 02:40:23 +00:00
2017-10-26 03:41:11 +00:00
exports . require = ( module ) => {
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_REQUIRE'
2019-10-04 17:49:09 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , module , getCurrentStack ( ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( meta )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Alias to remote.require('electron').xxx.
2017-10-26 03:41:11 +00:00
exports . getBuiltin = ( module ) => {
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
2019-10-04 17:49:09 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , module , getCurrentStack ( ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( meta )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2017-10-26 03:41:11 +00:00
exports . getCurrentWindow = ( ) => {
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
2019-10-04 17:49:09 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , getCurrentStack ( ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( meta )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Get current WebContents object.
2017-10-26 03:41:11 +00:00
exports . getCurrentWebContents = ( ) => {
2018-12-12 21:31:16 +00:00
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'
2019-10-04 17:49:09 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , getCurrentStack ( ) )
2018-12-12 21:31:16 +00:00
return metaToValue ( meta )
2018-03-20 04:54:47 +00:00
}
2016-01-14 18:35:29 +00:00
// Get a global object in browser.
2017-10-26 03:41:11 +00:00
exports . getGlobal = ( name ) => {
2017-11-03 01:07:40 +00:00
const command = 'ELECTRON_BROWSER_GLOBAL'
2019-10-04 17:49:09 +00:00
const meta = ipcRendererInternal . sendSync ( command , contextId , name , getCurrentStack ( ) )
2017-11-03 01:07:40 +00:00
return metaToValue ( meta )
2016-03-25 19:57:17 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Get the process object in browser.
2019-07-09 11:44:46 +00:00
Object . defineProperty ( exports , 'process' , {
get : ( ) => exports . getGlobal ( 'process' )
} )
2016-01-12 02:40:23 +00:00
2017-10-26 03:41:11 +00:00
// Create a function that will return the specified value when called in browser.
exports . createFunctionWithReturnValue = ( returnValue ) => {
2017-10-25 13:51:21 +00:00
const func = ( ) => returnValue
2016-03-25 19:57:17 +00:00
v8Util . setHiddenValue ( func , 'returnValue' , true )
return func
}
2016-01-12 02:40:23 +00:00
2017-02-23 18:01:27 +00:00
const addBuiltinProperty = ( name ) => {
Object . defineProperty ( exports , name , {
2017-11-03 01:07:40 +00:00
get : ( ) => exports . getBuiltin ( name )
2017-02-23 18:01:27 +00:00
} )
}
2019-08-23 09:18:58 +00:00
const { commonModuleList } = require ( '@electron/internal/common/api/module-list' )
const browserModules = commonModuleList . concat ( require ( '@electron/internal/browser/api/module-keys' ) )
2017-03-06 14:58:10 +00:00
// And add a helper receiver for each one.
browserModules
. filter ( ( m ) => ! m . private )
. map ( ( m ) => m . name )
. forEach ( addBuiltinProperty )