diff options
Diffstat (limited to 'testing/mochitest/BrowserTestUtils/ContentTask.jsm')
-rw-r--r-- | testing/mochitest/BrowserTestUtils/ContentTask.jsm | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/testing/mochitest/BrowserTestUtils/ContentTask.jsm b/testing/mochitest/BrowserTestUtils/ContentTask.jsm new file mode 100644 index 000000000..a52dd90d3 --- /dev/null +++ b/testing/mochitest/BrowserTestUtils/ContentTask.jsm @@ -0,0 +1,128 @@ +/* -*- 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const FRAME_SCRIPT = "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js"; + +/** + * Keeps track of whether the frame script was already loaded. + */ +var gFrameScriptLoaded = false; + +/** + * Mapping from message id to associated promise. + */ +var gPromises = new Map(); + +/** + * Incrementing integer to generate unique message id. + */ +var gMessageID = 1; + +/** + * This object provides the public module functions. + */ +this.ContentTask = { + /** + * _testScope saves the current testScope from + * browser-test.js. This is used to implement SimpleTest functions + * like ok() and is() in the content process. The scope is only + * valid for tasks spawned in the current test, so we keep track of + * the ID of the first task spawned in this test (_scopeValidId). + */ + _testScope: null, + _scopeValidId: 0, + + /** + * 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) { + // Load the frame script if needed. + if (!gFrameScriptLoaded) { + Services.mm.loadFrameScript(FRAME_SCRIPT, true); + gFrameScriptLoaded = true; + } + + 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; + }, + + setTestScope(scope) { + this._testScope = scope; + this._scopeValidId = gMessageID; + }, +}; + +var ContentMessageListener = { + receiveMessage(aMessage) { + let id = aMessage.data.id; + + if (id < ContentTask._scopeValidId) { + throw new Error("test result returned after test finished"); + } + + if (aMessage.name == "content-task:complete") { + let deferred = gPromises.get(id); + gPromises.delete(id); + + if (aMessage.data.error) { + deferred.reject(aMessage.data.error); + } else { + deferred.resolve(aMessage.data.result); + } + } else if (aMessage.name == "content-task:test-result") { + let data = aMessage.data; + ContentTask._testScope.ok(data.condition, data.name, null, data.stack); + } else if (aMessage.name == "content-task:test-info") { + ContentTask._testScope.info(aMessage.data.name); + } else if (aMessage.name == "content-task:test-todo") { + ContentTask._testScope.todo(aMessage.data.expr, aMessage.data.name); + } + }, +}; + +Services.mm.addMessageListener("content-task:complete", ContentMessageListener); +Services.mm.addMessageListener("content-task:test-result", ContentMessageListener); +Services.mm.addMessageListener("content-task:test-info", ContentMessageListener); |