Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
3 "use strict";
5 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
7 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
9 // Enable logging for all the tests. Both the debugger server and frontend will
10 // be affected by this pref.
11 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
12 Services.prefs.setBoolPref("devtools.debugger.log", true);
14 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
15 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
16 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
17 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
18 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
20 let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
21 let TargetFactory = devtools.TargetFactory;
23 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
24 const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
25 const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
26 const SIMPLE_NODES_URL = EXAMPLE_URL + "doc_simple-node-creation.html";
28 // All tests are asynchronous.
29 waitForExplicitFinish();
31 let gToolEnabled = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled");
33 registerCleanupFunction(() => {
34 info("finish() was called, cleaning up...");
35 Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
36 Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", gToolEnabled);
37 Cu.forceGC();
38 });
40 function addTab(aUrl, aWindow) {
41 info("Adding tab: " + aUrl);
43 let deferred = Promise.defer();
44 let targetWindow = aWindow || window;
45 let targetBrowser = targetWindow.gBrowser;
47 targetWindow.focus();
48 let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
49 let linkedBrowser = tab.linkedBrowser;
51 linkedBrowser.addEventListener("load", function onLoad() {
52 linkedBrowser.removeEventListener("load", onLoad, true);
53 info("Tab added and finished loading: " + aUrl);
54 deferred.resolve(tab);
55 }, true);
57 return deferred.promise;
58 }
60 function removeTab(aTab, aWindow) {
61 info("Removing tab.");
63 let deferred = Promise.defer();
64 let targetWindow = aWindow || window;
65 let targetBrowser = targetWindow.gBrowser;
66 let tabContainer = targetBrowser.tabContainer;
68 tabContainer.addEventListener("TabClose", function onClose(aEvent) {
69 tabContainer.removeEventListener("TabClose", onClose, false);
70 info("Tab removed and finished closing.");
71 deferred.resolve();
72 }, false);
74 targetBrowser.removeTab(aTab);
75 return deferred.promise;
76 }
78 function handleError(aError) {
79 ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
80 finish();
81 }
83 function once(aTarget, aEventName, aUseCapture = false) {
84 info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
86 let deferred = Promise.defer();
88 for (let [add, remove] of [
89 ["on", "off"], // Use event emitter before DOM events for consistency
90 ["addEventListener", "removeEventListener"],
91 ["addListener", "removeListener"]
92 ]) {
93 if ((add in aTarget) && (remove in aTarget)) {
94 aTarget[add](aEventName, function onEvent(...aArgs) {
95 aTarget[remove](aEventName, onEvent, aUseCapture);
96 deferred.resolve(...aArgs);
97 }, aUseCapture);
98 break;
99 }
100 }
102 return deferred.promise;
103 }
105 function reload(aTarget, aWaitForTargetEvent = "navigate") {
106 aTarget.activeTab.reload();
107 return once(aTarget, aWaitForTargetEvent);
108 }
110 function test () {
111 Task.spawn(spawnTest).then(finish, handleError);
112 }
114 function initBackend(aUrl) {
115 info("Initializing a web audio editor front.");
117 if (!DebuggerServer.initialized) {
118 DebuggerServer.init(() => true);
119 DebuggerServer.addBrowserActors();
120 }
122 return Task.spawn(function*() {
123 let tab = yield addTab(aUrl);
124 let target = TargetFactory.forTab(tab);
125 let debuggee = target.window.wrappedJSObject;
127 yield target.makeRemote();
129 let front = new WebAudioFront(target.client, target.form);
130 return [target, debuggee, front];
131 });
132 }
134 function initWebAudioEditor(aUrl) {
135 info("Initializing a web audio editor pane.");
137 return Task.spawn(function*() {
138 let tab = yield addTab(aUrl);
139 let target = TargetFactory.forTab(tab);
140 let debuggee = target.window.wrappedJSObject;
142 yield target.makeRemote();
144 Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
145 let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
146 let panel = toolbox.getCurrentPanel();
147 return [target, debuggee, panel];
148 });
149 }
151 function teardown(aPanel) {
152 info("Destroying the web audio editor.");
154 return Promise.all([
155 once(aPanel, "destroyed"),
156 removeTab(aPanel.target.tab)
157 ]).then(() => {
158 let gBrowser = window.gBrowser;
159 while (gBrowser.tabs.length > 1) {
160 gBrowser.removeCurrentTab();
161 }
162 gBrowser = null;
163 });
164 }
166 // Due to web audio will fire most events synchronously back-to-back,
167 // and we can't yield them in a chain without missing actors, this allows
168 // us to listen for `n` events and return a promise resolving to them.
169 //
170 // Takes a `front` object that is an event emitter, the number of
171 // programs that should be listened to and waited on, and an optional
172 // `onAdd` function that calls with the entire actors array on program link
173 function getN (front, eventName, count, spread) {
174 let actors = [];
175 let deferred = Promise.defer();
176 front.on(eventName, function onEvent (...args) {
177 let actor = args[0];
178 if (actors.length !== count) {
179 actors.push(spread ? args : actor);
180 }
181 if (actors.length === count) {
182 front.off(eventName, onEvent);
183 deferred.resolve(actors);
184 }
185 });
186 return deferred.promise;
187 }
189 function get (front, eventName) { return getN(front, eventName, 1); }
190 function get2 (front, eventName) { return getN(front, eventName, 2); }
191 function get3 (front, eventName) { return getN(front, eventName, 3); }
192 function getSpread (front, eventName) { return getN(front, eventName, 1, true); }
193 function get2Spread (front, eventName) { return getN(front, eventName, 2, true); }
194 function get3Spread (front, eventName) { return getN(front, eventName, 3, true); }
195 function getNSpread (front, eventName, count) { return getN(front, eventName, count, true); }
197 /**
198 * Waits for the UI_GRAPH_RENDERED event to fire, but only
199 * resolves when the graph was rendered with the correct count of
200 * nodes and edges.
201 */
202 function waitForGraphRendered (front, nodeCount, edgeCount) {
203 let deferred = Promise.defer();
204 let eventName = front.EVENTS.UI_GRAPH_RENDERED;
205 front.on(eventName, function onGraphRendered (_, nodes, edges) {
206 if (nodes === nodeCount && edges === edgeCount) {
207 front.off(eventName, onGraphRendered);
208 deferred.resolve();
209 }
210 });
211 return deferred.promise;
212 }
214 function checkVariableView (view, index, hash) {
215 let scope = view.getScopeAtIndex(index);
216 let variables = Object.keys(hash);
217 variables.forEach(variable => {
218 let aVar = scope.get(variable);
219 is(aVar.target.querySelector(".name").getAttribute("value"), variable,
220 "Correct property name for " + variable);
221 is(aVar.target.querySelector(".value").getAttribute("value"), hash[variable],
222 "Correct property value of " + hash[variable] + " for " + variable);
223 });
224 }
226 function modifyVariableView (win, view, index, prop, value) {
227 let deferred = Promise.defer();
228 let scope = view.getScopeAtIndex(index);
229 let aVar = scope.get(prop);
230 scope.expand();
232 // Must wait for the scope DOM to be available to receive
233 // events
234 executeSoon(() => {
235 let varValue = aVar.target.querySelector(".title > .value");
236 EventUtils.sendMouseEvent({ type: "mousedown" }, varValue, win);
238 win.on(win.EVENTS.UI_SET_PARAM, handleSetting);
239 win.on(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
241 info("Setting " + value + " for " + prop + "....");
242 let varInput = aVar.target.querySelector(".title > .element-value-input");
243 setText(varInput, value);
244 EventUtils.sendKey("RETURN", win);
245 });
247 function handleSetting (eventName) {
248 win.off(win.EVENTS.UI_SET_PARAM, handleSetting);
249 win.off(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
250 if (eventName === win.EVENTS.UI_SET_PARAM)
251 deferred.resolve();
252 if (eventName === win.EVENTS.UI_SET_PARAM_ERROR)
253 deferred.reject();
254 }
256 return deferred.promise;
257 }
259 function clearText (aElement) {
260 info("Clearing text...");
261 aElement.focus();
262 aElement.value = "";
263 }
265 function setText (aElement, aText) {
266 clearText(aElement);
267 info("Setting text: " + aText);
268 aElement.value = aText;
269 }
271 function findGraphEdge (win, source, target) {
272 let selector = ".edgePaths .edgePath[data-source='" + source + "'][data-target='" + target + "']";
273 return win.document.querySelector(selector);
274 }
276 function findGraphNode (win, node) {
277 let selector = ".nodes > g[data-id='" + node + "']";
278 return win.document.querySelector(selector);
279 }
281 function click (win, element) {
282 EventUtils.sendMouseEvent({ type: "click" }, element, win);
283 }
285 function mouseOver (win, element) {
286 EventUtils.sendMouseEvent({ type: "mouseover" }, element, win);
287 }
289 /**
290 * List of audio node properties to test against expectations of the AudioNode actor
291 */
293 const NODE_PROPERTIES = {
294 "OscillatorNode": ["type", "frequency", "detune"],
295 "GainNode": ["gain"],
296 "DelayNode": ["delayTime"],
297 "AudioBufferSourceNode": ["buffer", "playbackRate", "loop", "loopStart", "loopEnd"],
298 "ScriptProcessorNode": ["bufferSize"],
299 "PannerNode": ["panningModel", "distanceModel", "refDistance", "maxDistance", "rolloffFactor", "coneInnerAngle", "coneOuterAngle", "coneOuterGain"],
300 "ConvolverNode": ["buffer", "normalize"],
301 "DynamicsCompressorNode": ["threshold", "knee", "ratio", "reduction", "attack", "release"],
302 "BiquadFilterNode": ["type", "frequency", "Q", "detune", "gain"],
303 "WaveShaperNode": ["curve", "oversample"],
304 "AnalyserNode": ["fftSize", "minDecibels", "maxDecibels", "smoothingTimeConstraint", "frequencyBinCount"],
305 "AudioDestinationNode": [],
306 "ChannelSplitterNode": [],
307 "ChannelMergerNode": []
308 };