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 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const Cu = Components.utils;
6 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
7 let TargetFactory = devtools.TargetFactory;
8 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
9 let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
10 let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
12 // All test are asynchronous
13 waitForExplicitFinish();
15 //Services.prefs.setBoolPref("devtools.dump.emit", true);
17 // Set the testing flag on gDevTools and reset it when the test ends
18 gDevTools.testing = true;
19 registerCleanupFunction(() => gDevTools.testing = false);
21 // Clear preferences that may be set during the course of tests.
22 registerCleanupFunction(() => {
23 Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
24 Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
25 Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
26 Services.prefs.clearUserPref("devtools.dump.emit");
27 Services.prefs.clearUserPref("devtools.markup.pagesize");
28 });
30 // Auto close the toolbox and close the test tabs when the test ends
31 registerCleanupFunction(() => {
32 try {
33 let target = TargetFactory.forTab(gBrowser.selectedTab);
34 gDevTools.closeToolbox(target);
35 } catch (ex) {
36 dump(ex);
37 }
38 while (gBrowser.tabs.length > 1) {
39 gBrowser.removeCurrentTab();
40 }
41 });
43 const TEST_URL_ROOT = "http://mochi.test:8888/browser/browser/devtools/markupview/test/";
45 /**
46 * Define an async test based on a generator function
47 */
48 function asyncTest(generator) {
49 return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
50 }
52 /**
53 * Add a new test tab in the browser and load the given url.
54 * @param {String} url The url to be loaded in the new tab
55 * @return a promise that resolves to the tab object when the url is loaded
56 */
57 function addTab(url) {
58 info("Adding a new tab with URL: '" + url + "'");
59 let def = promise.defer();
61 let tab = gBrowser.selectedTab = gBrowser.addTab();
62 gBrowser.selectedBrowser.addEventListener("load", function onload() {
63 gBrowser.selectedBrowser.removeEventListener("load", onload, true);
64 info("URL '" + url + "' loading complete");
65 waitForFocus(() => {
66 def.resolve(tab);
67 }, content);
68 }, true);
69 content.location = url;
71 return def.promise;
72 }
74 /**
75 * Some tests may need to import one or more of the test helper scripts.
76 * A test helper script is simply a js file that contains common test code that
77 * is either not common-enough to be in head.js, or that is located in a separate
78 * directory.
79 * The script will be loaded synchronously and in the test's scope.
80 * @param {String} filePath The file path, relative to the current directory.
81 * Examples:
82 * - "helper_attributes_test_runner.js"
83 * - "../../../commandline/test/helpers.js"
84 */
85 function loadHelperScript(filePath) {
86 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
87 Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
88 }
90 /**
91 * Reload the current page
92 * @return a promise that resolves when the inspector has emitted the event
93 * new-root
94 */
95 function reloadPage(inspector) {
96 info("Reloading the page");
97 let newRoot = inspector.once("new-root");
98 content.location.reload();
99 return newRoot;
100 }
102 /**
103 * Open the toolbox, with the inspector tool visible.
104 * @return a promise that resolves when the inspector is ready
105 */
106 function openInspector() {
107 info("Opening the inspector panel");
108 let def = promise.defer();
110 let target = TargetFactory.forTab(gBrowser.selectedTab);
111 gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
112 info("The toolbox is open");
113 let inspector = toolbox.getCurrentPanel();
114 inspector.once("inspector-updated", () => {
115 info("The inspector panel is active and ready");
116 def.resolve({toolbox: toolbox, inspector: inspector});
117 });
118 }).then(null, console.error);
120 return def.promise;
121 }
123 /**
124 * Simple DOM node accesor function that takes either a node or a string css
125 * selector as argument and returns the corresponding node
126 * @param {String|DOMNode} nodeOrSelector
127 * @return {DOMNode}
128 */
129 function getNode(nodeOrSelector) {
130 info("Getting the node for '" + nodeOrSelector + "'");
131 return typeof nodeOrSelector === "string" ?
132 content.document.querySelector(nodeOrSelector) :
133 nodeOrSelector;
134 }
136 /**
137 * Set the inspector's current selection to a node or to the first match of the
138 * given css selector
139 * @param {String|DOMNode} nodeOrSelector
140 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
141 * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
142 * @return a promise that resolves when the inspector is updated with the new
143 * node
144 */
145 function selectNode(nodeOrSelector, inspector, reason="test") {
146 info("Selecting the node for '" + nodeOrSelector + "'");
147 let node = getNode(nodeOrSelector);
148 let updated = inspector.once("inspector-updated");
149 inspector.selection.setNode(node, reason);
150 return updated;
151 }
153 /**
154 * Get the MarkupContainer object instance that corresponds to the given
155 * HTML node
156 * @param {DOMNode|String} nodeOrSelector The DOM node for which the
157 * container is required
158 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
159 * loaded in the toolbox
160 * @return {MarkupContainer}
161 */
162 function getContainerForRawNode(nodeOrSelector, {markup}) {
163 let front = markup.walker.frontForRawNode(getNode(nodeOrSelector));
164 let container = markup.getContainer(front);
165 info("Markup-container object for " + nodeOrSelector + " " + container);
166 return container;
167 }
169 /**
170 * Using the markupview's _waitForChildren function, wait for all queued
171 * children updates to be handled.
172 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
173 * loaded in the toolbox
174 * @return a promise that resolves when all queued children updates have been
175 * handled
176 */
177 function waitForChildrenUpdated({markup}) {
178 info("Waiting for queued children updates to be handled");
179 let def = promise.defer();
180 markup._waitForChildren().then(() => {
181 executeSoon(def.resolve);
182 });
183 return def.promise;
184 }
186 /**
187 * Simulate a mouse-over on the markup-container (a line in the markup-view)
188 * that corresponds to the node or selector passed.
189 * @param {String|DOMNode} nodeOrSelector
190 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
191 * @return a promise that resolves when the container is hovered and the higlighter
192 * is shown on the corresponding node
193 */
194 function hoverContainer(nodeOrSelector, inspector) {
195 info("Hovering over the markup-container for node " + nodeOrSelector);
196 let highlit = inspector.toolbox.once("node-highlight");
197 let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
198 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousemove"},
199 inspector.markup.doc.defaultView);
200 return highlit;
201 }
203 /**
204 * Simulate a click on the markup-container (a line in the markup-view)
205 * that corresponds to the node or selector passed.
206 * @param {String|DOMNode} nodeOrSelector
207 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
208 * @return a promise that resolves when the node has been selected.
209 */
210 function clickContainer(nodeOrSelector, inspector) {
211 info("Clicking on the markup-container for node " + nodeOrSelector);
212 let updated = inspector.once("inspector-updated");
213 let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
214 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
215 inspector.markup.doc.defaultView);
216 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
217 inspector.markup.doc.defaultView);
218 return updated;
219 }
221 /**
222 * Checks if the highlighter is visible currently
223 * @return {Boolean}
224 */
225 function isHighlighterVisible() {
226 let highlighter = gBrowser.selectedBrowser.parentNode
227 .querySelector(".highlighter-container .box-model-root");
228 return highlighter && !highlighter.hasAttribute("hidden");
229 }
231 /**
232 * Simulate the mouse leaving the markup-view area
233 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
234 * @return a promise when done
235 */
236 function mouseLeaveMarkupView(inspector) {
237 info("Leaving the markup-view area");
238 let def = promise.defer();
240 // Find another element to mouseover over in order to leave the markup-view
241 let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
243 EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
244 inspector.toolbox.doc.defaultView);
245 executeSoon(def.resolve);
247 return def.promise;
248 }
250 /**
251 * Focus a given editable element, enter edit mode, set value, and commit
252 * @param {DOMNode} field The element that gets editable after receiving focus
253 * and <ENTER> keypress
254 * @param {String} value The string value to be set into the edited field
255 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
256 * loaded in the toolbox
257 */
258 function setEditableFieldValue(field, value, inspector) {
259 field.focus();
260 EventUtils.sendKey("return", inspector.panelWin);
261 let input = inplaceEditor(field).input;
262 ok(input, "Found editable field for setting value: " + value);
263 input.value = value;
264 EventUtils.sendKey("return", inspector.panelWin);
265 }
267 /**
268 * Focus the new-attribute inplace-editor field of the nodeOrSelector's markup
269 * container, and enters the given text, then wait for it to be applied and the
270 * for the node to mutates (when new attribute(s) is(are) created)
271 * @param {DOMNode|String} nodeOrSelector The node or node selector to edit.
272 * @param {String} text The new attribute text to be entered (e.g. "id='test'")
273 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
274 * loaded in the toolbox
275 * @return a promise that resolves when the node has mutated
276 */
277 function addNewAttributes(nodeOrSelector, text, inspector) {
278 info("Entering text '" + text + "' in node '" + nodeOrSelector + "''s new attribute field");
280 let container = getContainerForRawNode(nodeOrSelector, inspector);
281 ok(container, "The container for '" + nodeOrSelector + "' was found");
283 info("Listening for the markupmutation event");
284 let nodeMutated = inspector.once("markupmutation");
285 setEditableFieldValue(container.editor.newAttr, text, inspector);
286 return nodeMutated;
287 }
289 /**
290 * Checks that a node has the given attributes
291 *
292 * @param {DOMNode|String} nodeOrSelector The node or node selector to check.
293 * @param {Object} attrs An object containing the attributes to check.
294 * e.g. {id: "id1", class: "someclass"}
295 *
296 * Note that node.getAttribute() returns attribute values provided by the HTML
297 * parser. The parser only provides unescaped entities so & will return &.
298 */
299 function assertAttributes(nodeOrSelector, attrs) {
300 let node = getNode(nodeOrSelector);
302 is(node.attributes.length, Object.keys(attrs).length,
303 "Node has the correct number of attributes.");
304 for (let attr in attrs) {
305 is(node.getAttribute(attr), attrs[attr],
306 "Node has the correct " + attr + " attribute.");
307 }
308 }
310 /**
311 * Undo the last markup-view action and wait for the corresponding mutation to
312 * occur
313 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
314 * loaded in the toolbox
315 * @return a promise that resolves when the markup-mutation has been treated or
316 * rejects if no undo action is possible
317 */
318 function undoChange(inspector) {
319 let canUndo = inspector.markup.undo.canUndo();
320 ok(canUndo, "The last change in the markup-view can be undone");
321 if (!canUndo) {
322 return promise.reject();
323 }
325 let mutated = inspector.once("markupmutation");
326 inspector.markup.undo.undo();
327 return mutated;
328 }
330 /**
331 * Redo the last markup-view action and wait for the corresponding mutation to
332 * occur
333 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
334 * loaded in the toolbox
335 * @return a promise that resolves when the markup-mutation has been treated or
336 * rejects if no redo action is possible
337 */
338 function redoChange(inspector) {
339 let canRedo = inspector.markup.undo.canRedo();
340 ok(canRedo, "The last change in the markup-view can be redone");
341 if (!canRedo) {
342 return promise.reject();
343 }
345 let mutated = inspector.once("markupmutation");
346 inspector.markup.undo.redo();
347 return mutated;
348 }
350 /**
351 * Get the selector-search input box from the inspector panel
352 * @return {DOMNode}
353 */
354 function getSelectorSearchBox(inspector) {
355 return inspector.panelWin.document.getElementById("inspector-searchbox");
356 }
358 /**
359 * Using the inspector panel's selector search box, search for a given selector.
360 * The selector input string will be entered in the input field and the <ENTER>
361 * keypress will be simulated.
362 * This function won't wait for any events and is not async. It's up to callers
363 * to subscribe to events and react accordingly.
364 */
365 function searchUsingSelectorSearch(selector, inspector) {
366 info("Entering \"" + selector + "\" into the selector-search input field");
367 let field = getSelectorSearchBox(inspector);
368 field.focus();
369 field.value = selector;
370 EventUtils.sendKey("return", inspector.panelWin);
371 }
373 /**
374 * This shouldn't be used in the tests, but is useful when writing new tests or
375 * debugging existing tests in order to introduce delays in the test steps
376 * @param {Number} ms The time to wait
377 * @return A promise that resolves when the time is passed
378 */
379 function wait(ms) {
380 let def = promise.defer();
381 content.setTimeout(def.resolve, ms);
382 return def.promise;
383 }