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