From f1e15b49a9945bc6a85c42bbd83607709a8ee79c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 29 Apr 2013 18:24:37 +0800 Subject: [PATCH] Support passing callbacks from renderer to browser. --- browser/atom/rpc_server.coffee | 23 +++++++++++++++++++ renderer/api/lib/remote.coffee | 42 +++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/browser/atom/rpc_server.coffee b/browser/atom/rpc_server.coffee index 69fdfa9e258c..deae80de6cbb 100644 --- a/browser/atom/rpc_server.coffee +++ b/browser/atom/rpc_server.coffee @@ -1,6 +1,23 @@ ipc = require 'ipc' path = require 'path' objectsRegistry = require './objects_registry.js' +v8_util = process.atomBinding 'v8_util' + +# Convert list of meta information into real arguments array, the main +# purpose is to turn remote function's id into delegate function. +argsToValues = (processId, routingId, metas) -> + constructCallback = (meta) -> + return meta.value if meta.type is 'value' + + # Create a delegate function to do asynchronous RPC call. + ret = -> + args = new Meta(processId, routingId, arguments) + ipc.sendChannel processId, routingId, 'ATOM_INTERNAL_FUNCTION_CALL', meta.id, args + v8_util.setDestructor ret, -> + ipc.sendChannel processId, routingId, 'ATOM_INTERNAL_DEREFERENCE', meta.id + ret + + constructCallback meta for meta in metas # Convert a real value into a POD structure which carries information of this # value. @@ -10,6 +27,9 @@ class Meta @type = 'value' if value is null @type = 'array' if Array.isArray value + # Treat the arguments object as array. + @type = 'array' if @type is 'object' and value.callee? and value.length? + if @type is 'array' @members = [] @members.push new Meta(processId, routingId, el) for el in value @@ -46,6 +66,7 @@ ipc.on 'ATOM_INTERNAL_CURRENT_WINDOW', (event, processId, routingId) -> ipc.on 'ATOM_INTERNAL_CONSTRUCTOR', (event, processId, routingId, id, args) -> try + args = argsToValues 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 @@ -56,6 +77,7 @@ ipc.on 'ATOM_INTERNAL_CONSTRUCTOR', (event, processId, routingId, id, args) -> ipc.on 'ATOM_INTERNAL_FUNCTION_CALL', (event, processId, routingId, id, args) -> try + args = argsToValues processId, routingId, args func = objectsRegistry.get id ret = func.apply global, args event.result = new Meta(processId, routingId, ret) @@ -64,6 +86,7 @@ ipc.on 'ATOM_INTERNAL_FUNCTION_CALL', (event, processId, routingId, id, args) -> ipc.on 'ATOM_INTERNAL_MEMBER_CALL', (event, processId, routingId, id, method, args) -> try + args = argsToValues 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 5f0d07563a68..54fdfd16173b 100644 --- a/renderer/api/lib/remote.coffee +++ b/renderer/api/lib/remote.coffee @@ -1,6 +1,33 @@ ipc = require 'ipc' +IDWeakMap = require 'id_weak_map' v8_util = process.atomBinding 'v8_util' +class CallbacksRegistry + constructor: -> + @referencesMap = {} + @weakMap = new IDWeakMap + + get: (id) -> @weakMap.get id + remove: (id) -> delete @referencesMap[id] + + add: (callback) -> + id = @weakMap.add callback + @referencesMap[id] = callback + id + +# Translate arguments object into a list of meta data. +# Unlike the Meta class in browser, this function only create delegate object +# for functions, other types of value are transfered after serialization. +callbacksRegistry = new CallbacksRegistry +argumentsToMetaList = (args) -> + metas = [] + for arg in args + if typeof arg is 'function' + metas.push type: 'function', id: callbacksRegistry.add(arg) + else + metas.push type: 'value', value: arg + metas + # Transform the description of value into a value or delegate object. metaToValue = (meta) -> switch meta.type @@ -15,7 +42,7 @@ metaToValue = (meta) -> constructor: -> if @constructor == RemoteFunction # Constructor call. - obj = ipc.sendChannelSync 'ATOM_INTERNAL_CONSTRUCTOR', meta.id, Array::slice.call(arguments) + obj = ipc.sendChannelSync 'ATOM_INTERNAL_CONSTRUCTOR', meta.id, argumentsToMetaList(arguments) # Returning object in constructor will replace constructed object # with the returned object. @@ -23,7 +50,7 @@ metaToValue = (meta) -> return metaToValue obj else # Function call. - ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', meta.id, Array::slice.call(arguments) + ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', meta.id, argumentsToMetaList(arguments) return metaToValue ret else ret = v8_util.createObjectWithName meta.name @@ -34,7 +61,7 @@ metaToValue = (meta) -> if member.type is 'function' ret[member.name] = -> # Call member function. - ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', meta.id, member.name, Array::slice.call(arguments) + ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', meta.id, member.name, argumentsToMetaList(arguments) metaToValue ret else ret.__defineSetter__ member.name, (value) -> @@ -53,6 +80,15 @@ metaToValue = (meta) -> ret +# Browser calls a callback in renderer. +ipc.on 'ATOM_INTERNAL_FUNCTION_CALL', (callbackId, args) -> + callback = callbacksRegistry.get callbackId + callback.apply global, metaToValue(args) + +# Browser releases a callback in renderer. +ipc.on 'ATOM_INTERNAL_DEREFERENCE', (callbackId) -> + callbacksRegistry.remove callbackId + # Get remote module. exports.require = (module) -> meta = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module