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