electron/renderer/api/lib/remote.coffee
Cheng Zhao d377cbfa74 Don't send ATOM_BROWSER_DEREFERENCE message if context is already gone.
It's possible that the object gets destructed after reloading, however
since we already released all objects in browser when unloading, it
would cause troubles when sending message in an unexist context. We just
rely on the closure to mark whether the context is gone.

A more suitable solution, however, is to restart renderer process when
doing reloading.
2013-05-04 23:00:57 +08:00

125 lines
4.1 KiB
CoffeeScript

ipc = require 'ipc'
v8_util = process.atomBinding 'v8_util'
currentContextExist = true
class CallbacksRegistry
@nextId = 0
@callbacks = {}
@add: (callback) ->
@callbacks[++@nextId] = callback
@nextId
@call: (id, args) ->
@callbacks[id].apply global, args
@remove: (id) ->
delete @callbacks[id]
# Convert the arguments object into an array of meta data.
wrapArgs = (args) ->
Array::slice.call(args).map (value) ->
if typeof value is 'object' and v8_util.getHiddenValue value, 'isRemoteObject'
type: 'object', id: value.id
else if typeof value is 'function'
type: 'function', id: CallbacksRegistry.add(value)
else
type: 'value', value: value
# Convert meta data from browser into real value.
metaToValue = (meta) ->
switch meta.type
when 'error' then throw new Error(meta.value)
when 'value' then meta.value
when 'array' then (metaToValue(el) for el in meta.members)
else
if meta.type is 'function'
# A shadow class to represent the remote function object.
ret =
class RemoteFunction
constructor: ->
if @constructor == RemoteFunction
# Constructor call.
obj = ipc.sendChannelSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments)
# 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.
ret = ipc.sendChannelSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments)
return metaToValue ret
else
ret = v8_util.createObjectWithName meta.name
# Polulate delegate members.
for member in meta.members
do (member) ->
if member.type is 'function'
ret[member.name] = ->
# Call member function.
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments)
metaToValue ret
else
ret.__defineSetter__ member.name, (value) ->
# Set member data.
ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_SET', meta.id, member.name, value
ret.__defineGetter__ member.name, ->
# Get member data.
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_GET', meta.id, member.name
metaToValue ret
# Track delegate object's life time, and tell the browser to clean up
# when the object is GCed.
v8_util.setDestructor ret, ->
return unless currentContextExist
ipc.sendChannel 'ATOM_BROWSER_DEREFERENCE', meta.storeId
# Mark this is a remote object.
v8_util.setHiddenValue ret, 'isRemoteObject', true
ret
# Browser calls a callback in renderer.
ipc.on 'ATOM_RENDERER_CALLBACK', (id, args) ->
CallbacksRegistry.call id, metaToValue(args)
# A callback in browser is released.
ipc.on 'ATOM_RENDERER_RELEASE_CALLBACK', (id) ->
CallbacksRegistry.remove id
# Release all resources of current render view when it's going to be unloaded.
window.addEventListener 'unload', (event) ->
currentContextExist = false
ipc.sendChannelSync 'ATOM_BROWSER_RELEASE_RENDER_VIEW'
# Get remote module.
exports.require = (module) ->
meta = ipc.sendChannelSync 'ATOM_BROWSER_REQUIRE', module
metaToValue meta
# Get object with specified id.
exports.getObject = (id) ->
meta = ipc.sendChannelSync 'ATOM_BROWSER_REFERENCE', id
metaToValue meta
# Get current window object.
windowCache = null
exports.getCurrentWindow = ->
return windowCache if windowCache?
meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW'
windowCache = metaToValue meta
# Get a global object in browser.
exports.getGlobal = (name) ->
meta = ipc.sendChannelSync 'ATOM_BROWSER_GLOBAL', name
metaToValue meta
# Get the process object in browser.
processCache = null
exports.__defineGetter__ 'process', ->
processCache = exports.getGlobal('process') unless processCache?
processCache