services/sync/tps/extensions/mozmill/resource/stdlib/utils.js

branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
equal deleted inserted replaced
-1:000000000000 0:9a3ff4042429
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/. */
4
5 var EXPORTED_SYMBOLS = ["applicationName", "assert", "Copy", "getBrowserObject",
6 "getChromeWindow", "getWindows", "getWindowByTitle",
7 "getWindowByType", "getWindowId", "getMethodInWindows",
8 "getPreference", "saveDataURL", "setPreference",
9 "sleep", "startTimer", "stopTimer", "takeScreenshot",
10 "unwrapNode", "waitFor"
11 ];
12
13 const Cc = Components.classes;
14 const Ci = Components.interfaces;
15 const Cu = Components.utils;
16
17
18 Cu.import("resource://gre/modules/NetUtil.jsm");
19 Cu.import("resource://gre/modules/Services.jsm");
20
21 const applicationIdMap = {
22 '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'Firefox',
23 '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}': 'MetroFirefox'
24 }
25 const applicationName = applicationIdMap[Services.appinfo.ID] || Services.appinfo.name;
26
27 var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
28 var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
29 var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
30
31 var assert = new assertions.Assert();
32
33 var hwindow = Services.appShell.hiddenDOMWindow;
34
35 var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
36
37 function Copy (obj) {
38 for (var n in obj) {
39 this[n] = obj[n];
40 }
41 }
42
43 /**
44 * Returns the browser object of the specified window
45 *
46 * @param {Window} aWindow
47 * Window to get the browser element from.
48 *
49 * @returns {Object} The browser element
50 */
51 function getBrowserObject(aWindow) {
52 switch(applicationName) {
53 case "MetroFirefox":
54 return aWindow.Browser;
55 case "Firefox":
56 default:
57 return aWindow.gBrowser;
58 }
59 }
60
61 function getChromeWindow(aWindow) {
62 var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
63 .getInterface(Ci.nsIWebNavigation)
64 .QueryInterface(Ci.nsIDocShellTreeItem)
65 .rootTreeItem
66 .QueryInterface(Ci.nsIInterfaceRequestor)
67 .getInterface(Ci.nsIDOMWindow)
68 .QueryInterface(Ci.nsIDOMChromeWindow);
69
70 return chromeWin;
71 }
72
73 function getWindows(type) {
74 if (type == undefined) {
75 type = "";
76 }
77
78 var windows = [];
79 var enumerator = Services.wm.getEnumerator(type);
80
81 while (enumerator.hasMoreElements()) {
82 windows.push(enumerator.getNext());
83 }
84
85 if (type == "") {
86 windows.push(hwindow);
87 }
88
89 return windows;
90 }
91
92 function getMethodInWindows(methodName) {
93 for each (var w in getWindows()) {
94 if (w[methodName] != undefined) {
95 return w[methodName];
96 }
97 }
98
99 throw new Error("Method with name: '" + methodName + "' is not in any open window.");
100 }
101
102 function getWindowByTitle(title) {
103 for each (var w in getWindows()) {
104 if (w.document.title && w.document.title == title) {
105 return w;
106 }
107 }
108
109 throw new Error("Window with title: '" + title + "' not found.");
110 }
111
112 function getWindowByType(type) {
113 return Services.wm.getMostRecentWindow(type);
114 }
115
116 /**
117 * Retrieve the outer window id for the given window.
118 *
119 * @param {Number} aWindow
120 * Window to retrieve the id from.
121 * @returns {Boolean} The outer window id
122 **/
123 function getWindowId(aWindow) {
124 try {
125 // Normally we can retrieve the id via window utils
126 return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
127 getInterface(Ci.nsIDOMWindowUtils).
128 outerWindowID;
129 } catch (e) {
130 // ... but for observer notifications we need another interface
131 return aWindow.QueryInterface(Ci.nsISupportsPRUint64).data;
132 }
133 }
134
135 var checkChrome = function () {
136 var loc = window.document.location.href;
137 try {
138 loc = window.top.document.location.href;
139 } catch (e) {
140 }
141
142 return /^chrome:\/\//.test(loc);
143 }
144
145 /**
146 * Called to get the state of an individual preference.
147 *
148 * @param aPrefName string The preference to get the state of.
149 * @param aDefaultValue any The default value if preference was not found.
150 *
151 * @returns any The value of the requested preference
152 *
153 * @see setPref
154 * Code by Henrik Skupin: <hskupin@gmail.com>
155 */
156 function getPreference(aPrefName, aDefaultValue) {
157 try {
158 var branch = Services.prefs;
159
160 switch (typeof aDefaultValue) {
161 case ('boolean'):
162 return branch.getBoolPref(aPrefName);
163 case ('string'):
164 return branch.getCharPref(aPrefName);
165 case ('number'):
166 return branch.getIntPref(aPrefName);
167 default:
168 return branch.getComplexValue(aPrefName);
169 }
170 } catch (e) {
171 return aDefaultValue;
172 }
173 }
174
175 /**
176 * Called to set the state of an individual preference.
177 *
178 * @param aPrefName string The preference to set the state of.
179 * @param aValue any The value to set the preference to.
180 *
181 * @returns boolean Returns true if value was successfully set.
182 *
183 * @see getPref
184 * Code by Henrik Skupin: <hskupin@gmail.com>
185 */
186 function setPreference(aName, aValue) {
187 try {
188 var branch = Services.prefs;
189
190 switch (typeof aValue) {
191 case ('boolean'):
192 branch.setBoolPref(aName, aValue);
193 break;
194 case ('string'):
195 branch.setCharPref(aName, aValue);
196 break;
197 case ('number'):
198 branch.setIntPref(aName, aValue);
199 break;
200 default:
201 branch.setComplexValue(aName, aValue);
202 }
203 } catch (e) {
204 return false;
205 }
206
207 return true;
208 }
209
210 /**
211 * Sleep for the given amount of milliseconds
212 *
213 * @param {number} milliseconds
214 * Sleeps the given number of milliseconds
215 */
216 function sleep(milliseconds) {
217 var timeup = false;
218
219 hwindow.setTimeout(function () { timeup = true; }, milliseconds);
220 var thread = Services.tm.currentThread;
221
222 while (!timeup) {
223 thread.processNextEvent(true);
224 }
225
226 broker.pass({'function':'utils.sleep()'});
227 }
228
229 /**
230 * Check if the callback function evaluates to true
231 */
232 function assert(callback, message, thisObject) {
233 var result = callback.call(thisObject);
234
235 if (!result) {
236 throw new Error(message || arguments.callee.name + ": Failed for '" + callback + "'");
237 }
238
239 return true;
240 }
241
242 /**
243 * Unwraps a node which is wrapped into a XPCNativeWrapper or XrayWrapper
244 *
245 * @param {DOMnode} Wrapped DOM node
246 * @returns {DOMNode} Unwrapped DOM node
247 */
248 function unwrapNode(aNode) {
249 var node = aNode;
250 if (node) {
251 // unwrap is not available on older branches (3.5 and 3.6) - Bug 533596
252 if ("unwrap" in XPCNativeWrapper) {
253 node = XPCNativeWrapper.unwrap(node);
254 }
255 else if (node.wrappedJSObject != null) {
256 node = node.wrappedJSObject;
257 }
258 }
259
260 return node;
261 }
262
263 /**
264 * Waits for the callback evaluates to true
265 */
266 function waitFor(callback, message, timeout, interval, thisObject) {
267 broker.log({'function': 'utils.waitFor() - DEPRECATED',
268 'message': 'utils.waitFor() is deprecated. Use assert.waitFor() instead'});
269 assert.waitFor(callback, message, timeout, interval, thisObject);
270 }
271
272 /**
273 * Calculates the x and y chrome offset for an element
274 * See https://developer.mozilla.org/en/DOM/window.innerHeight
275 *
276 * Note this function will not work if the user has custom toolbars (via extension) at the bottom or left/right of the screen
277 */
278 function getChromeOffset(elem) {
279 var win = elem.ownerDocument.defaultView;
280 // Calculate x offset
281 var chromeWidth = 0;
282
283 if (win["name"] != "sidebar") {
284 chromeWidth = win.outerWidth - win.innerWidth;
285 }
286
287 // Calculate y offset
288 var chromeHeight = win.outerHeight - win.innerHeight;
289 // chromeHeight == 0 means elem is already in the chrome and doesn't need the addonbar offset
290 if (chromeHeight > 0) {
291 // window.innerHeight doesn't include the addon or find bar, so account for these if present
292 var addonbar = win.document.getElementById("addon-bar");
293 if (addonbar) {
294 chromeHeight -= addonbar.scrollHeight;
295 }
296
297 var findbar = win.document.getElementById("FindToolbar");
298 if (findbar) {
299 chromeHeight -= findbar.scrollHeight;
300 }
301 }
302
303 return {'x':chromeWidth, 'y':chromeHeight};
304 }
305
306 /**
307 * Takes a screenshot of the specified DOM node
308 */
309 function takeScreenshot(node, highlights) {
310 var rect, win, width, height, left, top, needsOffset;
311 // node can be either a window or an arbitrary DOM node
312 try {
313 // node is an arbitrary DOM node
314 win = node.ownerDocument.defaultView;
315 rect = node.getBoundingClientRect();
316 width = rect.width;
317 height = rect.height;
318 top = rect.top;
319 left = rect.left;
320 // offset for highlights not needed as they will be relative to this node
321 needsOffset = false;
322 } catch (e) {
323 // node is a window
324 win = node;
325 width = win.innerWidth;
326 height = win.innerHeight;
327 top = 0;
328 left = 0;
329 // offset needed for highlights to take 'outerHeight' of window into account
330 needsOffset = true;
331 }
332
333 var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
334 canvas.width = width;
335 canvas.height = height;
336
337 var ctx = canvas.getContext("2d");
338 // Draws the DOM contents of the window to the canvas
339 ctx.drawWindow(win, left, top, width, height, "rgb(255,255,255)");
340
341 // This section is for drawing a red rectangle around each element passed in via the highlights array
342 if (highlights) {
343 ctx.lineWidth = "2";
344 ctx.strokeStyle = "red";
345 ctx.save();
346
347 for (var i = 0; i < highlights.length; ++i) {
348 var elem = highlights[i];
349 rect = elem.getBoundingClientRect();
350
351 var offsetY = 0, offsetX = 0;
352 if (needsOffset) {
353 var offset = getChromeOffset(elem);
354 offsetX = offset.x;
355 offsetY = offset.y;
356 } else {
357 // Don't need to offset the window chrome, just make relative to containing node
358 offsetY = -top;
359 offsetX = -left;
360 }
361
362 // Draw the rectangle
363 ctx.strokeRect(rect.left + offsetX, rect.top + offsetY, rect.width, rect.height);
364 }
365 }
366
367 return canvas.toDataURL("image/jpeg", 0.5);
368 }
369
370 /**
371 * Save the dataURL content to the specified file. It will be stored in either the persisted screenshot or temporary folder.
372 *
373 * @param {String} aDataURL
374 * The dataURL to save
375 * @param {String} aFilename
376 * Target file name without extension
377 *
378 * @returns {Object} The hash containing the path of saved file, and the failure bit
379 */
380 function saveDataURL(aDataURL, aFilename) {
381 var frame = {}; Cu.import('resource://mozmill/modules/frame.js', frame);
382 const FILE_PERMISSIONS = parseInt("0644", 8);
383
384 var file;
385 file = Cc['@mozilla.org/file/local;1']
386 .createInstance(Ci.nsILocalFile);
387 file.initWithPath(frame.persisted['screenshots']['path']);
388 file.append(aFilename + ".jpg");
389 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FILE_PERMISSIONS);
390
391 // Create an output stream to write to file
392 let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
393 .createInstance(Ci.nsIFileOutputStream);
394 foStream.init(file, 0x02 | 0x08 | 0x10, FILE_PERMISSIONS, foStream.DEFER_OPEN);
395
396 let dataURI = NetUtil.newURI(aDataURL, "UTF8", null);
397 if (!dataURI.schemeIs("data")) {
398 throw TypeError("aDataURL parameter has to have 'data'" +
399 " scheme instead of '" + dataURI.scheme + "'");
400 }
401
402 // Write asynchronously to buffer;
403 // Input and output streams are closed after write
404
405 let ready = false;
406 let failure = false;
407
408 function sync(aStatus) {
409 if (!Components.isSuccessCode(aStatus)) {
410 failure = true;
411 }
412 ready = true;
413 }
414
415 NetUtil.asyncFetch(dataURI, function (aInputStream, aAsyncFetchResult) {
416 if (!Components.isSuccessCode(aAsyncFetchResult)) {
417 // An error occurred!
418 sync(aAsyncFetchResult);
419 } else {
420 // Consume the input stream.
421 NetUtil.asyncCopy(aInputStream, foStream, function (aAsyncCopyResult) {
422 sync(aAsyncCopyResult);
423 });
424 }
425 });
426
427 assert.waitFor(function () {
428 return ready;
429 }, "DataURL has been saved to '" + file.path + "'");
430
431 return {filename: file.path, failure: failure};
432 }
433
434 /**
435 * Some very brain-dead timer functions useful for performance optimizations
436 * This is only enabled in debug mode
437 *
438 **/
439 var gutility_mzmltimer = 0;
440 /**
441 * Starts timer initializing with current EPOC time in milliseconds
442 *
443 * @returns none
444 **/
445 function startTimer(){
446 dump("TIMERCHECK:: starting now: " + Date.now() + "\n");
447 gutility_mzmltimer = Date.now();
448 }
449
450 /**
451 * Checks the timer and outputs current elapsed time since start of timer. It
452 * will print out a message you provide with its "time check" so you can
453 * correlate in the log file and figure out elapsed time of specific functions.
454 *
455 * @param aMsg string The debug message to print with the timer check
456 *
457 * @returns none
458 **/
459 function checkTimer(aMsg){
460 var end = Date.now();
461 dump("TIMERCHECK:: at " + aMsg + " is: " + (end - gutility_mzmltimer) + "\n");
462 }

mercurial