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 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 "use strict";
8 const {Cc, Ci, Cu} = require("chrome");
10 Cu.import("resource://gre/modules/Services.jsm");
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
12 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
14 const STACK_THICKNESS = 15;
16 /**
17 * Module containing various helper functions used throughout Tilt.
18 */
19 this.TiltUtils = {};
20 module.exports = this.TiltUtils;
22 /**
23 * Various console/prompt output functions required by the engine.
24 */
25 TiltUtils.Output = {
27 /**
28 * Logs a message to the console.
29 *
30 * @param {String} aMessage
31 * the message to be logged
32 */
33 log: function TUO_log(aMessage)
34 {
35 if (this.suppressLogs) {
36 return;
37 }
38 // get the console service
39 let consoleService = Cc["@mozilla.org/consoleservice;1"]
40 .getService(Ci.nsIConsoleService);
42 // log the message
43 consoleService.logStringMessage(aMessage);
44 },
46 /**
47 * Logs an error to the console.
48 *
49 * @param {String} aMessage
50 * the message to be logged
51 * @param {Object} aProperties
52 * and object containing script error initialization details
53 */
54 error: function TUO_error(aMessage, aProperties)
55 {
56 if (this.suppressErrors) {
57 return;
58 }
59 // make sure the properties parameter is a valid object
60 aProperties = aProperties || {};
62 // get the console service
63 let consoleService = Cc["@mozilla.org/consoleservice;1"]
64 .getService(Ci.nsIConsoleService);
66 // get the script error service
67 let scriptError = Cc["@mozilla.org/scripterror;1"]
68 .createInstance(Ci.nsIScriptError);
70 // initialize a script error
71 scriptError.init(aMessage,
72 aProperties.sourceName || "",
73 aProperties.sourceLine || "",
74 aProperties.lineNumber || 0,
75 aProperties.columnNumber || 0,
76 aProperties.flags || 0,
77 aProperties.category || "");
79 // log the error
80 consoleService.logMessage(scriptError);
81 },
83 /**
84 * Shows a modal alert message popup.
85 *
86 * @param {String} aTitle
87 * the title of the popup
88 * @param {String} aMessage
89 * the message to be logged
90 */
91 alert: function TUO_alert(aTitle, aMessage)
92 {
93 if (this.suppressAlerts) {
94 return;
95 }
96 if (!aMessage) {
97 aMessage = aTitle;
98 aTitle = "";
99 }
101 // get the prompt service
102 let prompt = Cc["@mozilla.org/embedcomp/prompt-service;1"]
103 .getService(Ci.nsIPromptService);
105 // show the alert message
106 prompt.alert(null, aTitle, aMessage);
107 }
108 };
110 /**
111 * Helper functions for managing preferences.
112 */
113 TiltUtils.Preferences = {
115 /**
116 * Gets a custom Tilt preference.
117 * If the preference does not exist, undefined is returned. If it does exist,
118 * but the type is not correctly specified, null is returned.
119 *
120 * @param {String} aPref
121 * the preference name
122 * @param {String} aType
123 * either "boolean", "string" or "integer"
124 *
125 * @return {Boolean | String | Number} the requested preference
126 */
127 get: function TUP_get(aPref, aType)
128 {
129 if (!aPref || !aType) {
130 return;
131 }
133 try {
134 let prefs = this._branch;
136 switch(aType) {
137 case "boolean":
138 return prefs.getBoolPref(aPref);
139 case "string":
140 return prefs.getCharPref(aPref);
141 case "integer":
142 return prefs.getIntPref(aPref);
143 }
144 return null;
146 } catch(e) {
147 // handle any unexpected exceptions
148 TiltUtils.Output.error(e.message);
149 return undefined;
150 }
151 },
153 /**
154 * Sets a custom Tilt preference.
155 * If the preference already exists, it is overwritten.
156 *
157 * @param {String} aPref
158 * the preference name
159 * @param {String} aType
160 * either "boolean", "string" or "integer"
161 * @param {String} aValue
162 * a new preference value
163 *
164 * @return {Boolean} true if the preference was set successfully
165 */
166 set: function TUP_set(aPref, aType, aValue)
167 {
168 if (!aPref || !aType || aValue === undefined || aValue === null) {
169 return;
170 }
172 try {
173 let prefs = this._branch;
175 switch(aType) {
176 case "boolean":
177 return prefs.setBoolPref(aPref, aValue);
178 case "string":
179 return prefs.setCharPref(aPref, aValue);
180 case "integer":
181 return prefs.setIntPref(aPref, aValue);
182 }
183 } catch(e) {
184 // handle any unexpected exceptions
185 TiltUtils.Output.error(e.message);
186 }
187 return false;
188 },
190 /**
191 * Creates a custom Tilt preference.
192 * If the preference already exists, it is left unchanged.
193 *
194 * @param {String} aPref
195 * the preference name
196 * @param {String} aType
197 * either "boolean", "string" or "integer"
198 * @param {String} aValue
199 * the initial preference value
200 *
201 * @return {Boolean} true if the preference was initialized successfully
202 */
203 create: function TUP_create(aPref, aType, aValue)
204 {
205 if (!aPref || !aType || aValue === undefined || aValue === null) {
206 return;
207 }
209 try {
210 let prefs = this._branch;
212 if (!prefs.prefHasUserValue(aPref)) {
213 switch(aType) {
214 case "boolean":
215 return prefs.setBoolPref(aPref, aValue);
216 case "string":
217 return prefs.setCharPref(aPref, aValue);
218 case "integer":
219 return prefs.setIntPref(aPref, aValue);
220 }
221 }
222 } catch(e) {
223 // handle any unexpected exceptions
224 TiltUtils.Output.error(e.message);
225 }
226 return false;
227 },
229 /**
230 * The preferences branch for this extension.
231 */
232 _branch: (function(aBranch) {
233 return Cc["@mozilla.org/preferences-service;1"]
234 .getService(Ci.nsIPrefService)
235 .getBranch(aBranch);
237 }("devtools.tilt."))
238 };
240 /**
241 * Easy way to access the string bundle.
242 */
243 TiltUtils.L10n = {
245 /**
246 * The string bundle element.
247 */
248 stringBundle: null,
250 /**
251 * Returns a string in the string bundle.
252 * If the string bundle is not found, null is returned.
253 *
254 * @param {String} aName
255 * the string name in the bundle
256 *
257 * @return {String} the equivalent string from the bundle
258 */
259 get: function TUL_get(aName)
260 {
261 // check to see if the parent string bundle document element is valid
262 if (!this.stringBundle || !aName) {
263 return null;
264 }
265 return this.stringBundle.GetStringFromName(aName);
266 },
268 /**
269 * Returns a formatted string using the string bundle.
270 * If the string bundle is not found, null is returned.
271 *
272 * @param {String} aName
273 * the string name in the bundle
274 * @param {Array} aArgs
275 * an array of arguments for the formatted string
276 *
277 * @return {String} the equivalent formatted string from the bundle
278 */
279 format: function TUL_format(aName, aArgs)
280 {
281 // check to see if the parent string bundle document element is valid
282 if (!this.stringBundle || !aName || !aArgs) {
283 return null;
284 }
285 return this.stringBundle.formatStringFromName(aName, aArgs, aArgs.length);
286 }
287 };
289 /**
290 * Utilities for accessing and manipulating a document.
291 */
292 TiltUtils.DOM = {
294 /**
295 * Current parent node object used when creating canvas elements.
296 */
297 parentNode: null,
299 /**
300 * Helper method, allowing to easily create and manage a canvas element.
301 * If the width and height params are falsy, they default to the parent node
302 * client width and height.
303 *
304 * @param {Document} aParentNode
305 * the parent node used to create the canvas
306 * if not specified, it will be reused from the cache
307 * @param {Object} aProperties
308 * optional, object containing some of the following props:
309 * {Boolean} focusable
310 * optional, true to make the canvas focusable
311 * {Boolean} append
312 * optional, true to append the canvas to the parent node
313 * {Number} width
314 * optional, specifies the width of the canvas
315 * {Number} height
316 * optional, specifies the height of the canvas
317 * {String} id
318 * optional, id for the created canvas element
319 *
320 * @return {HTMLCanvasElement} the newly created canvas element
321 */
322 initCanvas: function TUD_initCanvas(aParentNode, aProperties)
323 {
324 // check to see if the parent node element is valid
325 if (!(aParentNode = aParentNode || this.parentNode)) {
326 return null;
327 }
329 // make sure the properties parameter is a valid object
330 aProperties = aProperties || {};
332 // cache this parent node so that it can be reused
333 this.parentNode = aParentNode;
335 // create the canvas element
336 let canvas = aParentNode.ownerDocument.
337 createElementNS("http://www.w3.org/1999/xhtml", "canvas");
339 let width = aProperties.width || aParentNode.clientWidth;
340 let height = aProperties.height || aParentNode.clientHeight;
341 let id = aProperties.id || null;
343 canvas.setAttribute("style", "min-width: 1px; min-height: 1px;");
344 canvas.setAttribute("width", width);
345 canvas.setAttribute("height", height);
346 canvas.setAttribute("id", id);
348 // the canvas is unfocusable by default, we may require otherwise
349 if (aProperties.focusable) {
350 canvas.setAttribute("tabindex", "1");
351 canvas.style.outline = "none";
352 }
354 // append the canvas element to the current parent node, if specified
355 if (aProperties.append) {
356 aParentNode.appendChild(canvas);
357 }
359 return canvas;
360 },
362 /**
363 * Gets the full webpage dimensions (width and height).
364 *
365 * @param {Window} aContentWindow
366 * the content window holding the document
367 *
368 * @return {Object} an object containing the width and height coords
369 */
370 getContentWindowDimensions: function TUD_getContentWindowDimensions(
371 aContentWindow)
372 {
373 return {
374 width: aContentWindow.innerWidth + aContentWindow.scrollMaxX,
375 height: aContentWindow.innerHeight + aContentWindow.scrollMaxY
376 };
377 },
379 /**
380 * Calculates the position and depth to display a node, this can be overriden
381 * to change the visualization.
382 *
383 * @param {Window} aContentWindow
384 * the window content holding the document
385 * @param {Node} aNode
386 * the node to get the position for
387 * @param {Object} aParentPosition
388 * the position of the parent node, as returned by this
389 * function
390 *
391 * @return {Object} an object describing the node's position in 3D space
392 * containing the following properties:
393 * {Number} top
394 * distance along the x axis
395 * {Number} left
396 * distance along the y axis
397 * {Number} depth
398 * distance along the z axis
399 * {Number} width
400 * width of the node
401 * {Number} height
402 * height of the node
403 * {Number} thickness
404 * thickness of the node
405 */
406 getNodePosition: function TUD_getNodePosition(aContentWindow, aNode,
407 aParentPosition) {
408 let lh = new LayoutHelpers(aContentWindow);
409 // get the x, y, width and height coordinates of the node
410 let coord = lh.getRect(aNode, aContentWindow);
411 if (!coord) {
412 return null;
413 }
415 coord.depth = aParentPosition ? (aParentPosition.depth + aParentPosition.thickness) : 0;
416 coord.thickness = STACK_THICKNESS;
418 return coord;
419 },
421 /**
422 * Traverses a document object model & calculates useful info for each node.
423 *
424 * @param {Window} aContentWindow
425 * the window content holding the document
426 * @param {Object} aProperties
427 * optional, an object containing the following properties:
428 * {Function} nodeCallback
429 * a function to call instead of TiltUtils.DOM.getNodePosition
430 * to get the position and depth to display nodes
431 * {Object} invisibleElements
432 * elements which should be ignored
433 * {Number} minSize
434 * the minimum dimensions needed for a node to be traversed
435 * {Number} maxX
436 * the maximum left position of an element
437 * {Number} maxY
438 * the maximum top position of an element
439 *
440 * @return {Array} list containing nodes positions and local names
441 */
442 traverse: function TUD_traverse(aContentWindow, aProperties)
443 {
444 // make sure the properties parameter is a valid object
445 aProperties = aProperties || {};
447 let aInvisibleElements = aProperties.invisibleElements || {};
448 let aMinSize = aProperties.minSize || -1;
449 let aMaxX = aProperties.maxX || Number.MAX_VALUE;
450 let aMaxY = aProperties.maxY || Number.MAX_VALUE;
452 let nodeCallback = aProperties.nodeCallback || this.getNodePosition.bind(this);
454 let nodes = aContentWindow.document.childNodes;
455 let store = { info: [], nodes: [] };
456 let depth = 0;
458 let queue = [
459 { parentPosition: null, nodes: aContentWindow.document.childNodes }
460 ]
462 while (queue.length) {
463 let { nodes, parentPosition } = queue.shift();
465 for (let node of nodes) {
466 // skip some nodes to avoid visualization meshes that are too bloated
467 let name = node.localName;
468 if (!name || aInvisibleElements[name]) {
469 continue;
470 }
472 let coord = nodeCallback(aContentWindow, node, parentPosition);
473 if (!coord) {
474 continue;
475 }
477 // the maximum size slices the traversal where needed
478 if (coord.left > aMaxX || coord.top > aMaxY) {
479 continue;
480 }
482 // use this node only if it actually has visible dimensions
483 if (coord.width > aMinSize && coord.height > aMinSize) {
485 // save the necessary details into a list to be returned later
486 store.info.push({ coord: coord, name: name });
487 store.nodes.push(node);
488 }
490 let childNodes = (name === "iframe" || name === "frame") ? node.contentDocument.childNodes : node.childNodes;
491 if (childNodes.length > 0)
492 queue.push({ parentPosition: coord, nodes: childNodes });
493 }
494 }
496 return store;
497 }
498 };
500 /**
501 * Binds a new owner object to the child functions.
502 * If the new parent is not specified, it will default to the passed scope.
503 *
504 * @param {Object} aScope
505 * the object from which all functions will be rebound
506 * @param {String} aRegex
507 * a regular expression to identify certain functions
508 * @param {Object} aParent
509 * the new parent for the object's functions
510 */
511 TiltUtils.bindObjectFunc = function TU_bindObjectFunc(aScope, aRegex, aParent)
512 {
513 if (!aScope) {
514 return;
515 }
517 for (let i in aScope) {
518 try {
519 if ("function" === typeof aScope[i] && (aRegex ? i.match(aRegex) : 1)) {
520 aScope[i] = aScope[i].bind(aParent || aScope);
521 }
522 } catch(e) {
523 TiltUtils.Output.error(e);
524 }
525 }
526 };
528 /**
529 * Destroys an object and deletes all members.
530 *
531 * @param {Object} aScope
532 * the object from which all children will be destroyed
533 */
534 TiltUtils.destroyObject = function TU_destroyObject(aScope)
535 {
536 if (!aScope) {
537 return;
538 }
540 // objects in Tilt usually use a function to handle internal destruction
541 if ("function" === typeof aScope._finalize) {
542 aScope._finalize();
543 }
544 for (let i in aScope) {
545 if (aScope.hasOwnProperty(i)) {
546 delete aScope[i];
547 }
548 }
549 };
551 /**
552 * Retrieve the unique ID of a window object.
553 *
554 * @param {Window} aWindow
555 * the window to get the ID from
556 *
557 * @return {Number} the window ID
558 */
559 TiltUtils.getWindowId = function TU_getWindowId(aWindow)
560 {
561 if (!aWindow) {
562 return;
563 }
565 return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
566 .getInterface(Ci.nsIDOMWindowUtils)
567 .currentInnerWindowID;
568 };
570 /**
571 * Sets the markup document viewer zoom for the currently selected browser.
572 *
573 * @param {Window} aChromeWindow
574 * the top-level browser window
575 *
576 * @param {Number} the zoom ammount
577 */
578 TiltUtils.setDocumentZoom = function TU_setDocumentZoom(aChromeWindow, aZoom) {
579 aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
580 };
582 /**
583 * Performs a garbage collection.
584 *
585 * @param {Window} aChromeWindow
586 * the top-level browser window
587 */
588 TiltUtils.gc = function TU_gc(aChromeWindow)
589 {
590 aChromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
591 .getInterface(Ci.nsIDOMWindowUtils)
592 .garbageCollect();
593 };
595 /**
596 * Clears the cache and sets all the variables to null.
597 */
598 TiltUtils.clearCache = function TU_clearCache()
599 {
600 TiltUtils.DOM.parentNode = null;
601 };
603 // bind the owner object to the necessary functions
604 TiltUtils.bindObjectFunc(TiltUtils.Output);
605 TiltUtils.bindObjectFunc(TiltUtils.Preferences);
606 TiltUtils.bindObjectFunc(TiltUtils.L10n);
607 TiltUtils.bindObjectFunc(TiltUtils.DOM);
609 // set the necessary string bundle
610 XPCOMUtils.defineLazyGetter(TiltUtils.L10n, "stringBundle", function() {
611 return Services.strings.createBundle(
612 "chrome://browser/locale/devtools/tilt.properties");
613 });