From f129622446a6cd819e8c1c73c9762825847b382d Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 25 Oct 2017 09:51:21 -0400 Subject: [PATCH 1/7] clean up remote --- lib/renderer/api/remote.js | 198 ++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 104 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index fdc95cdb5cdd..d48a1fc22d6a 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -9,14 +9,30 @@ const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron') const resolvePromise = Promise.resolve.bind(Promise) const callbacksRegistry = new CallbacksRegistry() - const remoteObjectCache = v8Util.createIDWeakMap() +// lookup for ipc renderer send calls +const ipcs = { + 'b_mem_con': 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', + 'b_mem_call': 'ELECTRON_BROWSER_MEMBER_CALL', + 'b_mem_get': 'ELECTRON_BROWSER_MEMBER_GET', + 'b_mem_set': 'ELECTRON_BROWSER_MEMBER_SET', + 'b_con': 'ELECTRON_BROWSER_CONSTRUCTOR', + 'b_func_call': 'ELECTRON_BROWSER_FUNCTION_CALL', + 'rend_call': 'ELECTRON_RENDERER_CALLBACK', + 'rend_rel_call': 'ELECTRON_RENDERER_RELEASE_CALLBACK', + 'b_con_rel': 'ELECTRON_BROWSER_CONTEXT_RELEASE' + 'b_req': 'ELECTRON_BROWSER_REQUIRE', + 'b_get_built': 'ELECTRON_BROWSER_GET_BUILTIN', + 'b_curr_win': 'ELECTRON_BROWSER_CURRENT_WINDOW', + 'b_curr_web_con': 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', + 'b_glob': 'ELECTRON_BROWSER_GLOBAL', + 'b_guest_web_con': 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS' +} + // Convert the arguments object into an array of meta data. -const wrapArgs = function (args, visited) { - if (visited == null) { - visited = new Set() - } +function wrapArgs (args, visited) { + if (visited) visited = new Set() const valueToMeta = function (value) { // Check for circular reference. @@ -45,7 +61,7 @@ const wrapArgs = function (args, visited) { type: 'date', value: value.getTime() } - } else if ((value != null) && typeof value === 'object') { + } else if (value && typeof value === 'object') { if (isPromise(value)) { return { type: 'promise', @@ -62,7 +78,7 @@ const wrapArgs = function (args, visited) { let meta = { type: 'object', - name: value.constructor != null ? value.constructor.name : '', + name: (value.constructor) ? value.constructor.name : '', members: [] } visited.add(value) @@ -99,7 +115,7 @@ const wrapArgs = function (args, visited) { // Populate object's members from descriptors. // The |ref| will be kept referenced by |members|. // This matches |getObjectMemebers| in rpc-server. -const setObjectMembers = function (ref, object, metaId, members) { +function setObjectMembers (ref, object, metaId, members) { if (!Array.isArray(members)) return for (let member of members) { @@ -110,11 +126,11 @@ const setObjectMembers = function (ref, object, metaId, members) { const remoteMemberFunction = function (...args) { if (this && this.constructor === remoteMemberFunction) { // Constructor call. - let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args)) + const ret = ipcRenderer.sendSync(ipcs['b_mem_con'], metaId, member.name, wrapArgs(args)) return metaToValue(ret) } else { // Call member function. - let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args)) + const ret = ipcRenderer.sendSync(ipcs['b_mem_call'], metaId, member.name, wrapArgs(args)) return metaToValue(ret) } } @@ -133,19 +149,17 @@ const setObjectMembers = function (ref, object, metaId, members) { descriptor.configurable = true } else if (member.type === 'get') { descriptor.get = function () { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name)) + return metaToValue(ipcRenderer.sendSync(ipcs['b_mem_get'], metaId, member.name)) } // Only set setter when it is writable. if (member.writable) { descriptor.set = function (value) { const args = wrapArgs([value]) - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, args) + const meta = ipcRenderer.sendSync(ipcs['b_mem_set'], metaId, member.name, args) // Meta will be non-null when a setter error occurred so parse it // to a value so it gets re-thrown. - if (meta != null) { - metaToValue(meta) - } + if (meta) metaToValue(meta) return value } } @@ -157,8 +171,8 @@ const setObjectMembers = function (ref, object, metaId, members) { // Populate object's prototype from descriptor. // This matches |getObjectPrototype| in rpc-server. -const setObjectPrototype = function (ref, object, metaId, descriptor) { - if (descriptor === null) return +function setObjectPrototype (ref, object, metaId, descriptor) { + if (!descriptor) return let proto = {} setObjectMembers(ref, proto, metaId, descriptor.members) setObjectPrototype(ref, proto, metaId, descriptor.proto) @@ -166,14 +180,14 @@ const setObjectPrototype = function (ref, object, metaId, descriptor) { } // Wrap function in Proxy for accessing remote properties -const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) { +function proxyFunctionProperties (remoteMemberFunction, metaId, name) { let loaded = false // Lazily load function properties const loadRemoteProperties = () => { if (loaded) return loaded = true - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name) + const meta = ipcRenderer.sendSync(ipcs['b_mem_get'], metaId, name) setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members) } @@ -201,7 +215,7 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) { }, getOwnPropertyDescriptor: (target, property) => { let descriptor = Object.getOwnPropertyDescriptor(target, property) - if (descriptor != null) return descriptor + if (descriptor) return descriptor loadRemoteProperties() return Object.getOwnPropertyDescriptor(target, property) } @@ -209,128 +223,106 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) { } // Convert meta data from browser into real value. -const metaToValue = function (meta) { - var el, i, len, ref1, results, ret - switch (meta.type) { - case 'value': - return meta.value - case 'array': - ref1 = meta.members - results = [] - for (i = 0, len = ref1.length; i < len; i++) { - el = ref1[i] - results.push(metaToValue(el)) - } - return results - case 'buffer': - return Buffer.from(meta.value) - case 'promise': - return resolvePromise({then: metaToValue(meta.then)}) - case 'error': - return metaToPlainObject(meta) - case 'date': - return new Date(meta.value) - case 'exception': - throw new Error(meta.message + '\n' + meta.stack) - default: - if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id) +function metaToValue (meta) { + const types = { + 'value': () => meta.value, + 'array': () => { + const members = meta.members + return members.map((member) => metaToValue(member)) + }, + 'buffer': () => Buffer.from(meta.value), + 'promise': () => resolvePromise({then: metaToValue(meta.then)}) + 'error': () => metaToPlainObject(meta), + 'date': () => new Date(meta.value), + 'exception': () => new Error(`${meta.message}\n${meta.stack}`) + } - if (meta.type === 'function') { - // A shadow class to represent the remote function object. - let remoteFunction = function (...args) { - if (this && this.constructor === remoteFunction) { - // Constructor call. - let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args)) - // Returning object in constructor will replace constructed object - // with the returned object. - // http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this - return metaToValue(obj) - } else { - // Function call. - let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args)) - return metaToValue(obj) - } + if (meta.type in types) { + types[meta.type]() + } else { + let ret + if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id) + + // A shadow class to represent the remote function object. + if (meta.type === 'function') { + let remoteFunction = function (...args) { + if (this && this.constructor === remoteFunction) { + return metaToValue(ipcRenderer.sendSync( + ipcs['b_con'], meta.id, wrapArgs(args) + ) + } else { + return metaToValue(ipcRenderer.sendSync( + ipcs['b_func_call'], meta.id, wrapArgs(args) + ) } - ret = remoteFunction - } else { - ret = {} } + ret = remoteFunction + } else { + ret = {} + } - // Populate delegate members. - setObjectMembers(ret, ret, meta.id, meta.members) - // Populate delegate prototype. - setObjectPrototype(ret, ret, meta.id, meta.proto) + setObjectMembers(ret, ret, meta.id, meta.members) + setObjectPrototype(ret, ret, meta.id, meta.proto) - // Set constructor.name to object's name. - Object.defineProperty(ret.constructor, 'name', { value: meta.name }) + // Set constructor.name to object's name. + Object.defineProperty(ret.constructor, 'name', { value: meta.name }) - // Track delegate object's life time, and tell the browser to clean up - // when the object is GCed. - v8Util.setRemoteObjectFreer(ret, meta.id) - - // Remember object's id. - v8Util.setHiddenValue(ret, 'atomId', meta.id) - remoteObjectCache.set(meta.id, ret) - return ret + // Track delegate obj's lifetime & tell browser to clean up when object is GCed. + v8Util.setRemoteObjectFreer(ret, meta.id) + v8Util.setHiddenValue(ret, 'atomId', meta.id) + remoteObjectCache.set(meta.id, ret) + return ret } } // Construct a plain object from the meta. -const metaToPlainObject = function (meta) { - var i, len, obj, ref1 - obj = (function () { - switch (meta.type) { - case 'error': - return new Error() - default: - return {} - } - })() - ref1 = meta.members - for (i = 0, len = ref1.length; i < len; i++) { - let {name, value} = ref1[i] - obj[name] = value - } +function metaToPlainObject (meta) { + if (meta.type === 'error') return new Error() + + const obj = {} + const members = meta.members + + members.forEach((key, val) => { obj[key] = val }) return obj } // Browser calls a callback in renderer. -ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) { +ipcRenderer.on(ipcs['rend_call'], (event, id, args) => { callbacksRegistry.apply(id, metaToValue(args)) }) // A callback in browser is released. -ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) { +ipcRenderer.on(ipcs['rend_rel_call'], (event, id) => { callbacksRegistry.remove(id) }) process.on('exit', () => { - ipcRenderer.sendSync('ELECTRON_BROWSER_CONTEXT_RELEASE') + ipcRenderer.sendSync(ipcs['b_con_rel']) }) // Get remote module. exports.require = function (module) { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) + return metaToValue(ipcRenderer.sendSync(ipcs['b_req'], module)) } // Alias to remote.require('electron').xxx. exports.getBuiltin = function (module) { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module)) + return metaToValue(ipcRenderer.sendSync(ipcs['b_get_built'], module)) } // Get current BrowserWindow. exports.getCurrentWindow = function () { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW')) + return metaToValue(ipcRenderer.sendSync(ipcs['b_curr_win'])) } // Get current WebContents object. exports.getCurrentWebContents = function () { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS')) + return metaToValue(ipcRenderer.sendSync(ipcs['b_curr_web_cont'])) } // Get a global object in browser. exports.getGlobal = function (name) { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name)) + return metaToValue(ipcRenderer.sendSync(ipcs['b_glob'], name)) } // Get the process object in browser. @@ -340,16 +332,14 @@ exports.__defineGetter__('process', function () { // Create a funtion that will return the specifed value when called in browser. exports.createFunctionWithReturnValue = function (returnValue) { - const func = function () { - return returnValue - } + const func = () => returnValue v8Util.setHiddenValue(func, 'returnValue', true) return func } // Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = function (guestInstanceId) { - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId) + const meta = ipcRenderer.sendSync(ipcs['b_guest_web_con'], guestInstanceId) return metaToValue(meta) } From c0f2a7b44a690962adda77fe9e182f7416ad8c83 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 25 Oct 2017 09:56:02 -0400 Subject: [PATCH 2/7] fix standard issues --- lib/renderer/api/remote.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index d48a1fc22d6a..ecbc7f7f4b8a 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -21,7 +21,7 @@ const ipcs = { 'b_func_call': 'ELECTRON_BROWSER_FUNCTION_CALL', 'rend_call': 'ELECTRON_RENDERER_CALLBACK', 'rend_rel_call': 'ELECTRON_RENDERER_RELEASE_CALLBACK', - 'b_con_rel': 'ELECTRON_BROWSER_CONTEXT_RELEASE' + 'b_con_rel': 'ELECTRON_BROWSER_CONTEXT_RELEASE', 'b_req': 'ELECTRON_BROWSER_REQUIRE', 'b_get_built': 'ELECTRON_BROWSER_GET_BUILTIN', 'b_curr_win': 'ELECTRON_BROWSER_CURRENT_WINDOW', @@ -231,7 +231,7 @@ function metaToValue (meta) { return members.map((member) => metaToValue(member)) }, 'buffer': () => Buffer.from(meta.value), - 'promise': () => resolvePromise({then: metaToValue(meta.then)}) + 'promise': () => resolvePromise({then: metaToValue(meta.then)}), 'error': () => metaToPlainObject(meta), 'date': () => new Date(meta.value), 'exception': () => new Error(`${meta.message}\n${meta.stack}`) @@ -247,13 +247,9 @@ function metaToValue (meta) { if (meta.type === 'function') { let remoteFunction = function (...args) { if (this && this.constructor === remoteFunction) { - return metaToValue(ipcRenderer.sendSync( - ipcs['b_con'], meta.id, wrapArgs(args) - ) + return metaToValue(ipcRenderer.sendSync(ipcs['b_con'], meta.id, wrapArgs(args))) } else { - return metaToValue(ipcRenderer.sendSync( - ipcs['b_func_call'], meta.id, wrapArgs(args) - ) + return metaToValue(ipcRenderer.sendSync(ipcs['b_func_call'], meta.id, wrapArgs(args))) } } ret = remoteFunction From d4880b135a0148dd70dcf0e9d33d146e6305bdae Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 25 Oct 2017 15:36:16 -0400 Subject: [PATCH 3/7] revert ipc lookup table --- lib/renderer/api/remote.js | 51 ++++++++++++-------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index ecbc7f7f4b8a..f115215f311b 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -11,25 +11,6 @@ const resolvePromise = Promise.resolve.bind(Promise) const callbacksRegistry = new CallbacksRegistry() const remoteObjectCache = v8Util.createIDWeakMap() -// lookup for ipc renderer send calls -const ipcs = { - 'b_mem_con': 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', - 'b_mem_call': 'ELECTRON_BROWSER_MEMBER_CALL', - 'b_mem_get': 'ELECTRON_BROWSER_MEMBER_GET', - 'b_mem_set': 'ELECTRON_BROWSER_MEMBER_SET', - 'b_con': 'ELECTRON_BROWSER_CONSTRUCTOR', - 'b_func_call': 'ELECTRON_BROWSER_FUNCTION_CALL', - 'rend_call': 'ELECTRON_RENDERER_CALLBACK', - 'rend_rel_call': 'ELECTRON_RENDERER_RELEASE_CALLBACK', - 'b_con_rel': 'ELECTRON_BROWSER_CONTEXT_RELEASE', - 'b_req': 'ELECTRON_BROWSER_REQUIRE', - 'b_get_built': 'ELECTRON_BROWSER_GET_BUILTIN', - 'b_curr_win': 'ELECTRON_BROWSER_CURRENT_WINDOW', - 'b_curr_web_con': 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', - 'b_glob': 'ELECTRON_BROWSER_GLOBAL', - 'b_guest_web_con': 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS' -} - // Convert the arguments object into an array of meta data. function wrapArgs (args, visited) { if (visited) visited = new Set() @@ -126,11 +107,11 @@ function setObjectMembers (ref, object, metaId, members) { const remoteMemberFunction = function (...args) { if (this && this.constructor === remoteMemberFunction) { // Constructor call. - const ret = ipcRenderer.sendSync(ipcs['b_mem_con'], metaId, member.name, wrapArgs(args)) + const ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args)) return metaToValue(ret) } else { // Call member function. - const ret = ipcRenderer.sendSync(ipcs['b_mem_call'], metaId, member.name, wrapArgs(args)) + const ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args)) return metaToValue(ret) } } @@ -149,14 +130,14 @@ function setObjectMembers (ref, object, metaId, members) { descriptor.configurable = true } else if (member.type === 'get') { descriptor.get = function () { - return metaToValue(ipcRenderer.sendSync(ipcs['b_mem_get'], metaId, member.name)) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name)) } // Only set setter when it is writable. if (member.writable) { descriptor.set = function (value) { const args = wrapArgs([value]) - const meta = ipcRenderer.sendSync(ipcs['b_mem_set'], metaId, member.name, args) + const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, args) // Meta will be non-null when a setter error occurred so parse it // to a value so it gets re-thrown. if (meta) metaToValue(meta) @@ -187,7 +168,7 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) { const loadRemoteProperties = () => { if (loaded) return loaded = true - const meta = ipcRenderer.sendSync(ipcs['b_mem_get'], metaId, name) + const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name) setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members) } @@ -247,9 +228,9 @@ function metaToValue (meta) { if (meta.type === 'function') { let remoteFunction = function (...args) { if (this && this.constructor === remoteFunction) { - return metaToValue(ipcRenderer.sendSync(ipcs['b_con'], meta.id, wrapArgs(args))) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))) } else { - return metaToValue(ipcRenderer.sendSync(ipcs['b_func_call'], meta.id, wrapArgs(args))) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))) } } ret = remoteFunction @@ -283,42 +264,42 @@ function metaToPlainObject (meta) { } // Browser calls a callback in renderer. -ipcRenderer.on(ipcs['rend_call'], (event, id, args) => { +ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => { callbacksRegistry.apply(id, metaToValue(args)) }) // A callback in browser is released. -ipcRenderer.on(ipcs['rend_rel_call'], (event, id) => { +ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => { callbacksRegistry.remove(id) }) process.on('exit', () => { - ipcRenderer.sendSync(ipcs['b_con_rel']) + ipcRenderer.sendSync('ELECTRON_BROWSER_CONTEXT_RELEASE') }) // Get remote module. exports.require = function (module) { - return metaToValue(ipcRenderer.sendSync(ipcs['b_req'], module)) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) } // Alias to remote.require('electron').xxx. exports.getBuiltin = function (module) { - return metaToValue(ipcRenderer.sendSync(ipcs['b_get_built'], module)) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module)) } // Get current BrowserWindow. exports.getCurrentWindow = function () { - return metaToValue(ipcRenderer.sendSync(ipcs['b_curr_win'])) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW')) } // Get current WebContents object. exports.getCurrentWebContents = function () { - return metaToValue(ipcRenderer.sendSync(ipcs['b_curr_web_cont'])) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS')) } // Get a global object in browser. exports.getGlobal = function (name) { - return metaToValue(ipcRenderer.sendSync(ipcs['b_glob'], name)) + return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name)) } // Get the process object in browser. @@ -335,7 +316,7 @@ exports.createFunctionWithReturnValue = function (returnValue) { // Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = function (guestInstanceId) { - const meta = ipcRenderer.sendSync(ipcs['b_guest_web_con'], guestInstanceId) + const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId) return metaToValue(meta) } From 5f6f117bad369c59e9109b58af25ad0a678d06a8 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 25 Oct 2017 23:41:11 -0400 Subject: [PATCH 4/7] changes from review --- lib/renderer/api/remote.js | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index f115215f311b..e38c531c90a5 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -12,10 +12,8 @@ const callbacksRegistry = new CallbacksRegistry() const remoteObjectCache = v8Util.createIDWeakMap() // Convert the arguments object into an array of meta data. -function wrapArgs (args, visited) { - if (visited) visited = new Set() - - const valueToMeta = function (value) { +function wrapArgs (args, visited = new Set()) { + const valueToMeta = (value) => { // Check for circular reference. if (visited.has(value)) { return { @@ -59,7 +57,7 @@ function wrapArgs (args, visited) { let meta = { type: 'object', - name: (value.constructor) ? value.constructor.name : '', + name: value.constructor ? value.constructor.name : '', members: [] } visited.add(value) @@ -140,7 +138,7 @@ function setObjectMembers (ref, object, metaId, members) { const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, args) // Meta will be non-null when a setter error occurred so parse it // to a value so it gets re-thrown. - if (meta) metaToValue(meta) + if (meta != null) metaToValue(meta) return value } } @@ -207,10 +205,7 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) { function metaToValue (meta) { const types = { 'value': () => meta.value, - 'array': () => { - const members = meta.members - return members.map((member) => metaToValue(member)) - }, + 'array': () => meta.members.map((member) => metaToValue(member)), 'buffer': () => Buffer.from(meta.value), 'promise': () => resolvePromise({then: metaToValue(meta.then)}), 'error': () => metaToPlainObject(meta), @@ -219,7 +214,7 @@ function metaToValue (meta) { } if (meta.type in types) { - types[meta.type]() + return types[meta.type] } else { let ret if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id) @@ -278,44 +273,42 @@ process.on('exit', () => { }) // Get remote module. -exports.require = function (module) { +exports.require = (module) => { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) } // Alias to remote.require('electron').xxx. -exports.getBuiltin = function (module) { +exports.getBuiltin = (module) => { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module)) } // Get current BrowserWindow. -exports.getCurrentWindow = function () { +exports.getCurrentWindow = () => { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW')) } // Get current WebContents object. -exports.getCurrentWebContents = function () { +exports.getCurrentWebContents = () => { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS')) } // Get a global object in browser. -exports.getGlobal = function (name) { +exports.getGlobal = (name) => { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name)) } // Get the process object in browser. -exports.__defineGetter__('process', function () { - return exports.getGlobal('process') -}) +exports.__defineGetter__('process', () => exports.getGlobal('process')) -// Create a funtion that will return the specifed value when called in browser. -exports.createFunctionWithReturnValue = function (returnValue) { +// Create a function that will return the specified value when called in browser. +exports.createFunctionWithReturnValue = (returnValue) => { const func = () => returnValue v8Util.setHiddenValue(func, 'returnValue', true) return func } // Get the guest WebContents from guestInstanceId. -exports.getGuestWebContents = function (guestInstanceId) { +exports.getGuestWebContents = (guestInstanceId) => { const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId) return metaToValue(meta) } From 5c318932c2e8992612c3eeee0be17b33cc032e3c Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 2 Nov 2017 21:07:40 -0400 Subject: [PATCH 5/7] add some structural changes --- lib/renderer/api/remote.js | 100 ++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index e38c531c90a5..1e4c292ac834 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -103,41 +103,40 @@ function setObjectMembers (ref, object, metaId, members) { let descriptor = { enumerable: member.enumerable } if (member.type === 'method') { const remoteMemberFunction = function (...args) { + let command if (this && this.constructor === remoteMemberFunction) { - // Constructor call. - const ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args)) - return metaToValue(ret) + command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR' } else { - // Call member function. - const ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args)) - return metaToValue(ret) + command = 'ELECTRON_BROWSER_MEMBER_CALL' } + const ret = ipcRenderer.sendSync(command, metaId, member.name, wrapArgs(args)) + return metaToValue(ret) } let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name) - descriptor.get = function () { + descriptor.get = () => { descriptorFunction.ref = ref // The member should reference its object. return descriptorFunction } // Enable monkey-patch the method - descriptor.set = function (value) { + descriptor.set = (value) => { descriptorFunction = value return value } descriptor.configurable = true } else if (member.type === 'get') { - descriptor.get = function () { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name)) + descriptor.get = () => { + const command = 'ELECTRON_BROWSER_MEMBER_GET' + const meta = ipcRenderer.sendSync(command, metaId, member.name) + return metaToValue(meta) } - // Only set setter when it is writable. if (member.writable) { - descriptor.set = function (value) { + descriptor.set = (value) => { const args = wrapArgs([value]) - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, args) - // Meta will be non-null when a setter error occurred so parse it - // to a value so it gets re-thrown. + const command = 'ELECTRON_BROWSER_MEMBER_SET' + const meta = ipcRenderer.sendSync(command, metaId, member.name, args) if (meta != null) metaToValue(meta) return value } @@ -151,7 +150,7 @@ function setObjectMembers (ref, object, metaId, members) { // Populate object's prototype from descriptor. // This matches |getObjectPrototype| in rpc-server. function setObjectPrototype (ref, object, metaId, descriptor) { - if (!descriptor) return + if (descriptor === null) return let proto = {} setObjectMembers(ref, proto, metaId, descriptor.members) setObjectPrototype(ref, proto, metaId, descriptor.proto) @@ -166,7 +165,8 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) { const loadRemoteProperties = () => { if (loaded) return loaded = true - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name) + const command = 'ELECTRON_BROWSER_MEMBER_GET' + const meta = ipcRenderer.sendSync(command, metaId, name) setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members) } @@ -179,13 +179,9 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) { get: (target, property, receiver) => { if (!target.hasOwnProperty(property)) loadRemoteProperties() const value = target[property] - - // Bind toString to target if it is a function to avoid - // Function.prototype.toString is not generic errors if (property === 'toString' && typeof value === 'function') { return value.bind(target) } - return value }, ownKeys: (target) => { @@ -204,29 +200,34 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) { // Convert meta data from browser into real value. function metaToValue (meta) { const types = { - 'value': () => meta.value, - 'array': () => meta.members.map((member) => metaToValue(member)), - 'buffer': () => Buffer.from(meta.value), - 'promise': () => resolvePromise({then: metaToValue(meta.then)}), - 'error': () => metaToPlainObject(meta), - 'date': () => new Date(meta.value), - 'exception': () => new Error(`${meta.message}\n${meta.stack}`) + value: () => meta.value, + array: () => meta.members.map((member) => metaToValue(member)), + buffer: () => Buffer.from(meta.value), + promise: () => resolvePromise({then: metaToValue(meta.then)}), + error: () => metaToPlainObject(meta), + date: () => new Date(meta.value), + exception: () => { throw new Error(`${meta.message}\n${meta.stack}`) } } if (meta.type in types) { - return types[meta.type] + return types[meta.type]() } else { let ret - if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id) + if (remoteObjectCache.has(meta.id)) { + return remoteObjectCache.get(meta.id) + } // A shadow class to represent the remote function object. if (meta.type === 'function') { let remoteFunction = function (...args) { + let command if (this && this.constructor === remoteFunction) { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))) + command = 'ELECTRON_BROWSER_CONSTRUCTOR' } else { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))) + command = 'ELECTRON_BROWSER_FUNCTION_CALL' } + const obj = ipcRenderer.sendSync(command, meta.id, wrapArgs(args)) + return metaToValue(obj) } ret = remoteFunction } else { @@ -235,8 +236,6 @@ function metaToValue (meta) { setObjectMembers(ret, ret, meta.id, meta.members) setObjectPrototype(ret, ret, meta.id, meta.proto) - - // Set constructor.name to object's name. Object.defineProperty(ret.constructor, 'name', { value: meta.name }) // Track delegate obj's lifetime & tell browser to clean up when object is GCed. @@ -254,7 +253,10 @@ function metaToPlainObject (meta) { const obj = {} const members = meta.members - members.forEach((key, val) => { obj[key] = val }) + members.forEach((member) => { + const {key, value} = member + obj[key] = value + }) return obj } @@ -269,22 +271,27 @@ ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => { }) process.on('exit', () => { - ipcRenderer.sendSync('ELECTRON_BROWSER_CONTEXT_RELEASE') + const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE' + ipcRenderer.sendSync(command) }) -// Get remote module. exports.require = (module) => { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) + const command = 'ELECTRON_BROWSER_REQUIRE' + const meta = ipcRenderer.sendSync(command, module) + return metaToValue(meta) } // Alias to remote.require('electron').xxx. exports.getBuiltin = (module) => { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module)) + const command = 'ELECTRON_BROWSER_GET_BUILTIN' + const meta = ipcRenderer.sendSync(command, module) + return metaToValue(meta) } -// Get current BrowserWindow. exports.getCurrentWindow = () => { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW')) + const command = 'ELECTRON_BROWSER_CURRENT_WINDOW' + const meta = ipcRenderer.sendSync(command) + return metaToValue(meta) } // Get current WebContents object. @@ -294,7 +301,9 @@ exports.getCurrentWebContents = () => { // Get a global object in browser. exports.getGlobal = (name) => { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name)) + const command = 'ELECTRON_BROWSER_GLOBAL' + const meta = ipcRenderer.sendSync(command, name) + return metaToValue(meta) } // Get the process object in browser. @@ -309,15 +318,14 @@ exports.createFunctionWithReturnValue = (returnValue) => { // Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = (guestInstanceId) => { - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId) + const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS' + const meta = ipcRenderer.sendSync(command, guestInstanceId) return metaToValue(meta) } const addBuiltinProperty = (name) => { Object.defineProperty(exports, name, { - get: function () { - return exports.getBuiltin(name) - } + get: () => exports.getBuiltin(name) }) } From 2b2c7d7f9f587562d269b7669daa103120ccaab5 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 2 Nov 2017 21:29:17 -0400 Subject: [PATCH 6/7] fix object serialization test issue --- lib/renderer/api/remote.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 1e4c292ac834..01a30c44d6ce 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -248,15 +248,11 @@ function metaToValue (meta) { // Construct a plain object from the meta. function metaToPlainObject (meta) { - if (meta.type === 'error') return new Error() - - const obj = {} - const members = meta.members - - members.forEach((member) => { - const {key, value} = member - obj[key] = value - }) + const obj = (() => meta.type === 'error' ? new Error() : {})() + for (let i = 0; i < meta.members.length; i++) { + let {name, value} = meta.members[i] + obj[name] = value + } return obj } From 803fa35484302b9ffc5de499bd21288471dc406e Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Fri, 3 Nov 2017 08:47:21 -0400 Subject: [PATCH 7/7] fix null value check --- lib/renderer/api/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 01a30c44d6ce..a4f8072ed576 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -40,7 +40,7 @@ function wrapArgs (args, visited = new Set()) { type: 'date', value: value.getTime() } - } else if (value && typeof value === 'object') { + } else if ((value != null) && typeof value === 'object') { if (isPromise(value)) { return { type: 'promise',