Bug 1157253: Port ListenerProxy to use Proxy instead of __noSuchMethod__

r=chmanchester

--HG--
extra : rebase_source : 9a8a3e3528cb665519de1f986c48c67ac99c5445
This commit is contained in:
Andreas Tolfsen 2015-04-23 16:59:12 +01:00
Родитель f300e99712
Коммит ee63bc61fa
1 изменённых файлов: 54 добавлений и 12 удалений

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

@ -96,7 +96,7 @@ this.Context.fromString = function(s) {
* passed literally. The latter specialisation is temporary to achieve * passed literally. The latter specialisation is temporary to achieve
* backwards compatibility with listener.js. * backwards compatibility with listener.js.
* *
* @param {function(): nsIMessageManager} mmFn * @param {function(): (nsIMessageSender|nsIMessageBroadcaster)} mmFn
* Function returning the current message manager. * Function returning the current message manager.
* @param {function(string, Object, number)} sendAsyncFn * @param {function(string, Object, number)} sendAsyncFn
* Callback for sending async messages to the current listener. * Callback for sending async messages to the current listener.
@ -104,6 +104,34 @@ this.Context.fromString = function(s) {
* Function that returns the current browser. * Function that returns the current browser.
*/ */
let ListenerProxy = function(mmFn, sendAsyncFn, curBrowserFn) { let ListenerProxy = function(mmFn, sendAsyncFn, curBrowserFn) {
let sender = new ContentSender(mmFn, sendAsyncFn, curBrowserFn);
let handler = {
set: (obj, prop, val) => { obj[prop] = val; return true; },
get: (obj, prop) => (...args) => obj.send(prop, args),
};
return new Proxy(sender, handler);
};
/**
* The ContentSender allows one to make synchronous calls to the
* message listener of the content frame of the current browsing context.
*
* Presumptions about the responses from content space are made so we
* can provide a nicer API on top of the message listener primitives that
* make calls from chrome- to content space seem synchronous by leveraging
* promises.
*
* The promise is guaranteed not to resolve until the execution of the
* command in content space is complete.
*
* @param {function(): (nsIMessageSender|nsIMessageBroadcaster)} mmFn
* Function returning the current message manager.
* @param {function(string, Object, number)} sendAsyncFn
* Callback for sending async messages to the current listener.
* @param {function(): BrowserObj} curBrowserFn
* Function that returns the current browser.
*/
let ContentSender = function(mmFn, sendAsyncFn, curBrowserFn) {
this.curCmdId = null; this.curCmdId = null;
this.sendAsync = sendAsyncFn; this.sendAsync = sendAsyncFn;
@ -111,15 +139,30 @@ let ListenerProxy = function(mmFn, sendAsyncFn, curBrowserFn) {
this.curBrowserFn_ = curBrowserFn; this.curBrowserFn_ = curBrowserFn;
}; };
Object.defineProperty(ListenerProxy.prototype, "mm", { Object.defineProperty(ContentSender.prototype, "mm", {
get: function() { return this.mmFn_(); } get: function() { return this.mmFn_(); }
}); });
Object.defineProperty(ListenerProxy.prototype, "curBrowser", { Object.defineProperty(ContentSender.prototype, "curBrowser", {
get: function() { return this.curBrowserFn_(); } get: function() { return this.curBrowserFn_(); }
}); });
ListenerProxy.prototype.__noSuchMethod__ = function*(name, args) { /**
* Call registered function in the frame script environment of the
* current browsing context's content frame.
*
* @param {string} name
* Function to call in the listener, e.g. for "Marionette:foo8",
* use "foo".
* @param {Array} args
* Argument list to pass the function. If args has a single entry
* that is an object, we assume it's an old style dispatch, and
* the object will passed literally.
*
* @return {Promise}
* A promise that resolves to the result of the command.
*/
ContentSender.prototype.send = function(name, args) {
const ok = "Marionette:ok"; const ok = "Marionette:ok";
const val = "Marionette:done"; const val = "Marionette:done";
const err = "Marionette:error"; const err = "Marionette:error";
@ -169,12 +212,11 @@ ListenerProxy.prototype.__noSuchMethod__ = function*(name, args) {
listeners.add(); listeners.add();
modal.addHandler(handleDialog); modal.addHandler(handleDialog);
// convert to array if passed arguments // new style dispatches are arrays of arguments, old style dispatches
let msg; // are key-value objects
if (args.length == 1 && typeof args[0] == "object" && args[0] !== null) { let msg = args;
if (args.length == 1 && typeof args[0] == "object") {
msg = args[0]; msg = args[0];
} else {
msg = Array.prototype.slice.call(args);
} }
this.sendAsync(name, msg, this.curCmdId); this.sendAsync(name, msg, this.curCmdId);
@ -183,7 +225,7 @@ ListenerProxy.prototype.__noSuchMethod__ = function*(name, args) {
return proxy; return proxy;
}; };
ListenerProxy.prototype.isOutOfSync = function(id) { ContentSender.prototype.isOutOfSync = function(id) {
return this.curCmdId !== id; return this.curCmdId !== id;
}; };
@ -268,7 +310,7 @@ this.GeckoDriver = function(appName, device, emulator) {
}; };
this.mm = globalMessageManager; this.mm = globalMessageManager;
this.listener = new ListenerProxy( this.listener = ListenerProxy(
() => this.mm, () => this.mm,
this.sendAsync.bind(this), this.sendAsync.bind(this),
() => this.curBrowser); () => this.curBrowser);
@ -1281,7 +1323,7 @@ GeckoDriver.prototype.get = function(cmd, resp) {
// We need to re-issue this request to correctly poll for readyState and // We need to re-issue this request to correctly poll for readyState and
// send errors. // send errors.
this.curBrowser.pendingCommands.push(() => { this.curBrowser.pendingCommands.push(() => {
cmd.parameters.command_id = this.listener.curCmdId; cmd.parameters.command_id = cmd.id;
this.mm.broadcastAsyncMessage( this.mm.broadcastAsyncMessage(
"Marionette:pollForReadyState" + this.curBrowser.curFrameId, "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
cmd.parameters); cmd.parameters);