зеркало из https://github.com/electron/electron.git
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': [
|
'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
|
|
@ -1 +1 @@
|
||||||
Subproject commit 027d18bde8c24534215095aadceb78e7a61b2b5c
|
Subproject commit 5c651db8ff28c710bedecd0599ce8a1249ae843f
|
Загрузка…
Ссылка в новой задаче