1/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ 3/* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7"use strict"; 8 9var EXPORTED_SYMBOLS = ["ContentTask"]; 10 11const { Promise } = ChromeUtils.import("resource://gre/modules/Promise.jsm"); 12const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 13 14const FRAME_SCRIPT = "resource://testing-common/content-task.js"; 15 16/** 17 * Keeps track of whether the frame script was already loaded. 18 */ 19var gFrameScriptLoaded = false; 20 21/** 22 * Mapping from message id to associated promise. 23 */ 24var gPromises = new Map(); 25 26/** 27 * Incrementing integer to generate unique message id. 28 */ 29var gMessageID = 1; 30 31/** 32 * This object provides the public module functions. 33 */ 34var ContentTask = { 35 /** 36 * _testScope saves the current testScope from 37 * browser-test.js. This is used to implement SimpleTest functions 38 * like ok() and is() in the content process. The scope is only 39 * valid for tasks spawned in the current test, so we keep track of 40 * the ID of the first task spawned in this test (_scopeValidId). 41 */ 42 _testScope: null, 43 _scopeValidId: 0, 44 45 /** 46 * Creates and starts a new task in a browser's content. 47 * 48 * @param browser A xul:browser 49 * @param arg A single serializable argument that will be passed to the 50 * task when executed on the content process. 51 * @param task 52 * - A generator or function which will be serialized and sent to 53 * the remote browser to be executed. Unlike Task.spawn, this 54 * argument may not be an iterator as it will be serialized and 55 * sent to the remote browser. 56 * @return A promise object where you can register completion callbacks to be 57 * called when the task terminates. 58 * @resolves With the final returned value of the task if it executes 59 * successfully. 60 * @rejects An error message if execution fails. 61 */ 62 spawn: function ContentTask_spawn(browser, arg, task) { 63 // Load the frame script if needed. 64 if (!gFrameScriptLoaded) { 65 Services.mm.loadFrameScript(FRAME_SCRIPT, true); 66 gFrameScriptLoaded = true; 67 } 68 69 let deferred = {}; 70 deferred.promise = new Promise((resolve, reject) => { 71 deferred.resolve = resolve; 72 deferred.reject = reject; 73 }); 74 75 let id = gMessageID++; 76 gPromises.set(id, deferred); 77 78 browser.messageManager.sendAsyncMessage("content-task:spawn", { 79 id, 80 runnable: task.toString(), 81 arg, 82 }); 83 84 return deferred.promise; 85 }, 86 87 setTestScope(scope) { 88 this._testScope = scope; 89 this._scopeValidId = gMessageID; 90 }, 91}; 92 93var ContentMessageListener = { 94 receiveMessage(aMessage) { 95 let id = aMessage.data.id; 96 97 if (id < ContentTask._scopeValidId) { 98 throw new Error("test result returned after test finished"); 99 } 100 101 if (aMessage.name == "content-task:complete") { 102 let deferred = gPromises.get(id); 103 gPromises.delete(id); 104 105 if (aMessage.data.error) { 106 deferred.reject(aMessage.data.error); 107 } else { 108 deferred.resolve(aMessage.data.result); 109 } 110 } else if (aMessage.name == "content-task:test-result") { 111 let data = aMessage.data; 112 ContentTask._testScope.record( 113 data.condition, 114 data.name, 115 null, 116 data.stack 117 ); 118 } else if (aMessage.name == "content-task:test-info") { 119 ContentTask._testScope.info(aMessage.data.name); 120 } else if (aMessage.name == "content-task:test-todo") { 121 ContentTask._testScope.todo(aMessage.data.expr, aMessage.data.name); 122 } else if (aMessage.name == "content-task:test-todo_is") { 123 ContentTask._testScope.todo_is( 124 aMessage.data.a, 125 aMessage.data.b, 126 aMessage.data.name 127 ); 128 } 129 }, 130}; 131 132Services.mm.addMessageListener("content-task:complete", ContentMessageListener); 133Services.mm.addMessageListener( 134 "content-task:test-result", 135 ContentMessageListener 136); 137Services.mm.addMessageListener( 138 "content-task:test-info", 139 ContentMessageListener 140); 141Services.mm.addMessageListener( 142 "content-task:test-todo", 143 ContentMessageListener 144); 145Services.mm.addMessageListener( 146 "content-task:test-todo_is", 147 ContentMessageListener 148); 149