Bug 1107609 - Implement ContentTask.spawn; r=Gijs r=mconley

This introduces a new medule ContentTask, which includes a spawn method.
This new method can be used to spawn a task in the content process of a
browser. When called, a promise will be returned which resolves to the
value returned by the task.

This allows you to quickly write test code which can touch the content
and return information without having to write custom framescripts all
the time (The content code can be written inline as a simple generator
definition). ContentTask is automatically included in the scope of
mochitests.

An example use follows:

yield ContentTask.spawn(browser, {}, function* gen_replaceState() {
  content.window.history.replaceState({}, "", 'test-entry/');
  return "Value that the promise will resolve with";
});

--HG--
extra : rebase_source : 9b6ae71407da582cdaa8087b5e367c72fa08a337
This commit is contained in:
Steven MacLeod 2015-02-11 17:28:44 -05:00
Родитель 7524e5d2ab
Коммит 4d239d923a
6 изменённых файлов: 149 добавлений и 0 удалений

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

@ -0,0 +1,95 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"ContentTask"
];
const Cu = Components.utils;
Cu.import("resource://gre/modules/Promise.jsm");
/**
* Set of browsers which have loaded the content-task frame script.
*/
let gScriptLoadedSet = new WeakSet();
/**
* Mapping from message id to associated promise.
*/
let gPromises = new Map();
/**
* Incrementing integer to generate unique message id.
*/
let gMessageID = 1;
/**
* This object provides the public module functions.
*/
this.ContentTask = {
/**
* Creates and starts a new task in a browser's content.
*
* @param browser A xul:browser
* @param arg A single serializable argument that will be passed to the
* task when executed on the content process.
* @param task
* - A generator or function which will be serialized and sent to
* the remote browser to be executed. Unlike Task.spawn, this
* argument may not be an iterator as it will be serialized and
* sent to the remote browser.
* @return A promise object where you can register completion callbacks to be
* called when the task terminates.
* @resolves With the final returned value of the task if it executes
* successfully.
* @rejects An error message if execution fails.
*/
spawn: function ContentTask_spawn(browser, arg, task) {
if(!gScriptLoadedSet.has(browser)) {
let mm = browser.messageManager;
mm.addMessageListener("content-task:complete", ContentMessageListener);
mm.loadFrameScript(
"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js", true);
gScriptLoadedSet.add(browser);
}
let deferred = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
let id = gMessageID++;
gPromises.set(id, deferred);
browser.messageManager.sendAsyncMessage(
"content-task:spawn",
{
id: id,
runnable: task.toString(),
arg: arg,
});
return deferred.promise;
},
};
let ContentMessageListener = {
receiveMessage(aMessage) {
let id = aMessage.data.id
let deferred = gPromises.get(id);
gPromises.delete(id);
if (aMessage.data.error) {
deferred.reject(aMessage.data.error);
} else {
deferred.resolve(aMessage.data.result);
}
},
};

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

@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let Cu = Components.utils;
Cu.import("resource://gre/modules/Task.jsm", this);
addMessageListener("content-task:spawn", function (msg) {
let id = msg.data.id;
let source = msg.data.runnable || "()=>{}";
try {
let runnablestr = `
(() => {
return (${source});
})();`
let runnable = eval(runnablestr);
let iterator = runnable(msg.data.arg);
Task.spawn(iterator).then((val) => {
sendAsyncMessage("content-task:complete", {
id: id,
result: val,
});
}, (e) => {
sendAsyncMessage("content-task:complete", {
id: id,
error: e.toString(),
});
});
} catch (e) {
sendAsyncMessage("content-task:complete", {
id: id,
error: e.toString(),
});
}
});

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

@ -0,0 +1,9 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TESTING_JS_MODULES += [
'ContentTask.jsm',
]

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

@ -110,6 +110,7 @@ function Tester(aTests, aDumper, aCallback) {
this.MemoryStats = simpleTestScope.MemoryStats;
this.Task = Task;
this.ContentTask = Components.utils.import("resource://testing-common/ContentTask.jsm", null).ContentTask;
this.Task.Debugging.maintainStack = true;
this.Promise = Components.utils.import("resource://gre/modules/Promise.jsm", null).Promise;
this.Assert = Components.utils.import("resource://testing-common/Assert.jsm", null).Assert;
@ -154,6 +155,7 @@ Tester.prototype = {
EventUtils: {},
SimpleTest: {},
Task: null,
ContentTask: null,
Assert: null,
repeat: 0,
@ -608,6 +610,7 @@ Tester.prototype = {
this.currentTest.scope.SimpleTest = this.SimpleTest;
this.currentTest.scope.gTestPath = this.currentTest.path;
this.currentTest.scope.Task = this.Task;
this.currentTest.scope.ContentTask = this.ContentTask;
// Pass a custom report function for mochitest style reporting.
this.currentTest.scope.Assert = new this.Assert(function(err, message, stack) {
let res;
@ -1003,6 +1006,7 @@ testScope.prototype = {
EventUtils: {},
SimpleTest: {},
Task: null,
ContentTask: null,
Assert: null,
/**

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

@ -37,4 +37,5 @@ mochikit.jar:
content/tests/SimpleTest/NativeKeyCodes.js (tests/SimpleTest/NativeKeyCodes.js)
content/tests/SimpleTest/paint_listener.js (tests/SimpleTest/paint_listener.js)
content/tests/SimpleTest/docshell_helpers.js (../../docshell/test/chrome/docshell_helpers.js)
content/tests/BrowserTestUtils/content-task.js (BrowserTestUtils/content/content-task.js)

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

@ -8,6 +8,7 @@ DIRS += [
'manifests',
'tests',
'ssltunnel',
'BrowserTestUtils',
]
if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':