Initial RPC API implementation.

Basic usage is:
remote = require 'remote'
Window = remote.require 'window'
w = new Window { width: 800, height: 600 }

Still need to do:
* Beter support for Array type.
* Remote objects should cheat devtools.
* Support cross-process callbacks.
This commit is contained in:
Cheng Zhao 2013-04-24 16:43:01 +08:00
Родитель f8899242c5
Коммит d723173bc7
8 изменённых файлов: 145 добавлений и 25 удалений

Просмотреть файл

@ -8,9 +8,13 @@
'coffee_sources': [ 'coffee_sources': [
'browser/api/lib/atom.coffee', 'browser/api/lib/atom.coffee',
'browser/api/lib/ipc.coffee', 'browser/api/lib/ipc.coffee',
'browser/api/lib/.coffee',
'browser/api/lib/window.coffee', 'browser/api/lib/window.coffee',
'browser/atom/atom.coffee', 'browser/atom/atom.coffee',
'browser/atom/objects_registry.coffee',
'browser/atom/rpc_server.coffee',
'renderer/api/lib/ipc.coffee', 'renderer/api/lib/ipc.coffee',
'renderer/api/lib/remote.coffee',
], ],
'lib_sources': [ 'lib_sources': [
'app/atom_main_delegate.cc', 'app/atom_main_delegate.cc',

Просмотреть файл

@ -3,24 +3,4 @@ EventEmitter = require('events').EventEmitter
Window = process.atom_binding('window').Window Window = process.atom_binding('window').Window
Window.prototype.__proto__ = EventEmitter.prototype Window.prototype.__proto__ = EventEmitter.prototype
# Convient accessors.
setupGetterAndSetter = (constructor, name, getter, setter) ->
if getter?
constructor.prototype.__defineGetter__ name, ->
this[getter].apply(this, arguments)
if setter?
constructor.prototype.__defineSetter__ name, ->
this[setter].apply(this, arguments)
setupGetterAndSetter Window, 'fullscreen', 'isFullscreen', 'setFullscreen'
setupGetterAndSetter Window, 'size', 'getSize', 'setSize'
setupGetterAndSetter Window, 'maximumSize', 'getMaximumSize', 'setMaximumSize'
setupGetterAndSetter Window, 'minimumSize', 'getMinimumSize', 'setMinimumSize'
setupGetterAndSetter Window, 'resizable', 'isResizable', 'setResizable'
setupGetterAndSetter Window, 'alwaysOnTop', 'isAlwaysOnTop', 'setAlwaysOnTop'
setupGetterAndSetter Window, 'position', 'getPosition', 'setPosition'
setupGetterAndSetter Window, 'title', 'getTitle', 'setTitle'
setupGetterAndSetter Window, 'kiosk', 'isKiosk', 'setKiosk'
setupGetterAndSetter Window, 'url', 'getURL', 'loadURL'
module.exports = Window module.exports = Window

Просмотреть файл

@ -14,8 +14,12 @@ atom.browserMainParts =
global.__atom = atom global.__atom = atom
# Add Atom.app/Contents/Resources/browser/api/lib to require's search paths, # Add Atom.app/Contents/Resources/browser/api/lib to require's search paths,
# which contains javascript of Atom's built-in libraries. # which contains javascript part of Atom's built-in libraries.
require('module').globalPaths.push path.join(__dirname, '..', 'api', 'lib') globalPaths = require('module').globalPaths
globalPaths.push path.join(__dirname, '..', 'api', 'lib')
# And Atom.app/Contents/Resources/common/api/lib
globalPaths.push path.join(__dirname, '..', '..', 'common', 'api', 'lib')
# Don't quit on fatal error. # Don't quit on fatal error.
process.on 'uncaughtException', (error) -> process.on 'uncaughtException', (error) ->
@ -24,6 +28,9 @@ process.on 'uncaughtException', (error) ->
console.error 'uncaughtException:' console.error 'uncaughtException:'
console.error message console.error message
# Load the RPC server.
require './rpc_server.js'
# Now we try to load app's package.json. # Now we try to load app's package.json.
packageJson = null packageJson = null

Просмотреть файл

@ -0,0 +1,22 @@
module.exports =
class ObjectsRegistry
@nextId = 0
constructor: ->
@objects = []
getNextId: ->
++ObjectsRegistry.nextId
add: (obj) ->
id = @getNextId()
@objects[id] = obj
id
remove: (id) ->
obj = @objects[id]
delete @objects[id]
obj
get: (id) ->
@objects[id]

Просмотреть файл

@ -0,0 +1,61 @@
ipc = require 'ipc'
path = require 'path'
ObjectsRegistry = require './objects_registry.js'
objectsRegistry = new ObjectsRegistry
class PlainObject
constructor: (value) ->
@type = typeof value
@type = 'value' if value is null
if @type is 'object' or @type is 'function'
@name = value.constructor.name
@id = objectsRegistry.add value
@members = []
for prop, field of value
@members.push { name: prop, type: typeof field }
else
@type = 'value'
@value = value
ipc.on 'ATOM_INTERNAL_REQUIRE', (event, process_id, routing_id, module) ->
event.result = new PlainObject(require(module))
ipc.on 'ATOM_INTERNAL_CONSTRUCTOR', (event, process_id, routing_id, id, args) ->
try
# Call new with array of arguments.
# TODO(zcbenz): Paste the URL of the StackOverflow question.
constructor = objectsRegistry.get id
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.result = new PlainObject(obj)
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_FUNCTION_CALL', (event, process_id, routing_id, id, args) ->
try
ret = objectsRegistry.get(id).apply global, args
event.result = new PlainObject(ret)
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_MEMBER_CALL', (event, process_id, routing_id, id, method, args) ->
try
obj = objectsRegistry.get id
ret = obj[method].apply(obj, args)
event.result = new PlainObject(ret)
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_MEMBER_SET', (event, process_id, routing_id, id, name, value) ->
try
objectsRegistry.get(id)[name] = value
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_MEMBER_GET', (event, process_id, routing_id, id, name) ->
try
event.result = new PlainObject(objectsRegistry.get(id)[name])
catch e
event.result = type: 'error', value: e.message

Просмотреть файл

@ -15,11 +15,11 @@ ipc.on('sync-message', function(event, process_id, routing_id) {
atom.browserMainParts.preMainMessageLoopRun = function() { atom.browserMainParts.preMainMessageLoopRun = function() {
mainWindow = new Window({ width: 800, height: 600 }); mainWindow = new Window({ width: 800, height: 600 });
mainWindow.url = 'file://' + __dirname + '/index.html'; mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.on('page-title-updated', function(event, title) { mainWindow.on('page-title-updated', function(event, title) {
event.preventDefault(); event.preventDefault();
this.title = 'Atom Shell - ' + title; this.setTitle('Atom Shell - ' + title);
}); });
} }

Просмотреть файл

@ -0,0 +1,46 @@
ipc = require 'ipc'
generateFromPainObject = (plain) ->
if plain.type is 'value'
return plain.value
else if plain.type is 'error'
throw new Error('Remote Error: ' + plain.value)
else
# A shadow class to represent the remote object.
class RemoteObject
constructor: () ->
if @constructor == RemoteObject
# Constructor call.
obj = ipc.sendChannelSync 'ATOM_INTERNAL_CONSTRUCTOR', plain.id, Array::slice.call(arguments)
# Returning object in constructor will replace constructed object
# with returned object.
return generateFromPainObject obj
else
# Function call.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', plain.id, Array::slice.call(arguments)
generateFromPainObject ret
# Polulate shadow members.
for member in plain.members
do (member) ->
if member.type is 'function'
RemoteObject[member.name] = ->
# Call member function.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', plain.id, member.name, Array::slice.call(arguments)
generateFromPainObject ret
else
RemoteObject.__defineSetter__ member.name, (value) ->
# Set member data.
ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_SET', plain.id, member.name, value
undefined
RemoteObject.__defineGetter__ member.name, ->
# Get member data.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_GET', plain.id, member.name
generateFromPainObject ret
RemoteObject
exports.require = (module) ->
plain = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module
generateFromPainObject plain

2
vendor/node поставляемый

@ -1 +1 @@
Subproject commit 027d18bde8c24534215095aadceb78e7a61b2b5c Subproject commit 5c651db8ff28c710bedecd0599ce8a1249ae843f