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:
Родитель
f8899242c5
Коммит
d723173bc7
4
atom.gyp
4
atom.gyp
|
@ -8,9 +8,13 @@
|
|||
'coffee_sources': [
|
||||
'browser/api/lib/atom.coffee',
|
||||
'browser/api/lib/ipc.coffee',
|
||||
'browser/api/lib/.coffee',
|
||||
'browser/api/lib/window.coffee',
|
||||
'browser/atom/atom.coffee',
|
||||
'browser/atom/objects_registry.coffee',
|
||||
'browser/atom/rpc_server.coffee',
|
||||
'renderer/api/lib/ipc.coffee',
|
||||
'renderer/api/lib/remote.coffee',
|
||||
],
|
||||
'lib_sources': [
|
||||
'app/atom_main_delegate.cc',
|
||||
|
|
|
@ -3,24 +3,4 @@ EventEmitter = require('events').EventEmitter
|
|||
Window = process.atom_binding('window').Window
|
||||
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
|
||||
|
|
|
@ -14,8 +14,12 @@ atom.browserMainParts =
|
|||
global.__atom = atom
|
||||
|
||||
# Add Atom.app/Contents/Resources/browser/api/lib to require's search paths,
|
||||
# which contains javascript of Atom's built-in libraries.
|
||||
require('module').globalPaths.push path.join(__dirname, '..', 'api', 'lib')
|
||||
# which contains javascript part of Atom's built-in libraries.
|
||||
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.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
|
@ -24,6 +28,9 @@ process.on 'uncaughtException', (error) ->
|
|||
console.error 'uncaughtException:'
|
||||
console.error message
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc_server.js'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
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() {
|
||||
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) {
|
||||
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
|
|
@ -1 +1 @@
|
|||
Subproject commit 027d18bde8c24534215095aadceb78e7a61b2b5c
|
||||
Subproject commit 5c651db8ff28c710bedecd0599ce8a1249ae843f
|
Загрузка…
Ссылка в новой задаче