From 36c260f4d55125bd4dd667bbaf622ee08a264948 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 4 May 2013 17:01:28 +0800 Subject: [PATCH] Resupport cross-process callback. It seems that it's avoidable to make callback cross-process when we start to emitting events for GUI elements, without this feature our implementation will be much more complicated. I will find a way to warn about resources leak in browser. --- browser/atom/rpc_server.coffee | 27 +++++++++++++++++---------- renderer/api/lib/remote.coffee | 32 ++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/browser/atom/rpc_server.coffee b/browser/atom/rpc_server.coffee index aa492e7c9d4b..42566e9904cf 100644 --- a/browser/atom/rpc_server.coffee +++ b/browser/atom/rpc_server.coffee @@ -1,15 +1,22 @@ ipc = require 'ipc' path = require 'path' objectsRegistry = require './objects_registry.js' +v8_util = process.atomBinding 'v8_util' -metaToArgs = (metas) -> - metas.map (meta) -> - if meta.type is 'value' - meta.value - else if meta.type is 'remoteObject' - objectsRegistry.get meta.id +unwrapArgs = (processId, routingId, args) -> + args.map (arg) -> + if arg.type is 'value' + arg.value + else if arg.type is 'remoteObject' + objectsRegistry.get arg.id + else if arg.type is 'function' + ret = -> + ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', arg.id, new Meta(processId, routingId, arguments) + v8_util.setDestructor ret, -> + ipc.sendChannel processId, routingId, 'ATOM_RENDERER_RELEASE_CALLBACK', arg.id + ret else - throw new TypeError("Unknown type: #{meta.type}") + throw new TypeError("Unknown type: #{arg.type}") # Convert a real value into a POD structure which carries information of this # value. @@ -67,7 +74,7 @@ ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) -> ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) -> try - args = metaToArgs args + args = unwrapArgs processId, routingId, args constructor = objectsRegistry.get id # Call new with array of arguments. # http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible @@ -78,7 +85,7 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) -> ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) -> try - args = metaToArgs args + args = unwrapArgs processId, routingId, args func = objectsRegistry.get id ret = func.apply global, args event.result = new Meta(processId, routingId, ret) @@ -87,7 +94,7 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) -> ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) -> try - args = metaToArgs args + args = unwrapArgs processId, routingId, args obj = objectsRegistry.get id ret = obj[method].apply(obj, args) event.result = new Meta(processId, routingId, ret) diff --git a/renderer/api/lib/remote.coffee b/renderer/api/lib/remote.coffee index 8fbccc4128a3..54f253ad42b7 100644 --- a/renderer/api/lib/remote.coffee +++ b/renderer/api/lib/remote.coffee @@ -1,16 +1,30 @@ ipc = require 'ipc' v8_util = process.atomBinding 'v8_util' +nextCallbackId = 0 +storedCallbacks = {} + +storeCallback = (callback) -> + ++nextCallbackId + storedCallbacks[nextCallbackId] = callback + nextCallbackId + +makeCallback = (id, args) -> + storedCallbacks[id].apply global, args + +releaseCallback = (id) -> + delete storedCallbacks[id] + # Transform the arguments passed to browser into list of descriptions. # # This function assumes an array is passed, and it only converts remote objects # and functions, other types of values are passed as it is. -argsToMeta = (args) -> - args.map (value) -> +wrapArgs = (args) -> + Array::slice.call(args).map (value) -> if typeof value is 'object' and v8_util.getHiddenValue value, 'isRemoteObject' type: 'remoteObject', id: value.id else if typeof value is 'function' - throw new TypeError('functions is not supported yet') + type: 'function', id: storeCallback(value) else type: 'value', value: value @@ -28,7 +42,7 @@ metaToValue = (meta) -> constructor: -> if @constructor == RemoteFunction # Constructor call. - obj = ipc.sendChannelSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, Array::slice.call(arguments) + obj = ipc.sendChannelSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments) # Returning object in constructor will replace constructed object # with the returned object. @@ -36,7 +50,7 @@ metaToValue = (meta) -> return metaToValue obj else # Function call. - ret = ipc.sendChannelSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, Array::slice.call(arguments) + ret = ipc.sendChannelSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments) return metaToValue ret else ret = v8_util.createObjectWithName meta.name @@ -47,7 +61,7 @@ metaToValue = (meta) -> if member.type is 'function' ret[member.name] = -> # Call member function. - ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, Array::slice.call(arguments) + ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments) metaToValue ret else ret.__defineSetter__ member.name, (value) -> @@ -69,6 +83,12 @@ metaToValue = (meta) -> ret +ipc.on 'ATOM_RENDERER_CALLBACK', (id, args) -> + makeCallback id, metaToValue(args) + +ipc.on 'ATOM_RENDERER_RELEASE_CALLBACK', (id) -> + releaseCallback id + # Release all resources of current render view when it's going to be unloaded. window.addEventListener 'unload', (event) -> ipc.sendChannelSync 'ATOM_BROWSER_RELEASE_RENDER_VIEW'