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 /* Shared code for xpcshell and mochitests-chrome */
7 // const Cc, Ci, and Cr are defined in netwerk/test/httpserver/httpd.js so we
8 // need to define unique ones.
9 const AUS_Cc = Components.classes;
10 const AUS_Ci = Components.interfaces;
11 const AUS_Cr = Components.results;
12 const AUS_Cu = Components.utils;
13 const AUS_Cm = Components.manager;
15 const PREF_APP_UPDATE_AUTO = "app.update.auto";
16 const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
17 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
18 const PREF_APP_UPDATE_CERTS_BRANCH = "app.update.certs.";
19 const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes";
20 const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
21 const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors";
22 const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
23 const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
24 const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
25 const PREF_APP_UPDATE_METRO_ENABLED = "app.update.metro.enabled";
26 const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
27 const PREF_APP_UPDATE_LOG = "app.update.log";
28 const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never.";
29 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
30 const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime";
31 const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled";
32 const PREF_APP_UPDATE_SHOW_INSTALLED_UI = "app.update.showInstalledUI";
33 const PREF_APP_UPDATE_SILENT = "app.update.silent";
34 const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled";
35 const PREF_APP_UPDATE_URL = "app.update.url";
36 const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details";
37 const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override";
38 const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors";
39 const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout";
41 const PREF_APP_UPDATE_CERT_INVALID_ATTR_NAME = PREF_APP_UPDATE_CERTS_BRANCH +
42 "1.invalidName";
44 const PREF_APP_PARTNER_BRANCH = "app.partner.";
45 const PREF_DISTRIBUTION_ID = "distribution.id";
46 const PREF_DISTRIBUTION_VERSION = "distribution.version";
48 const PREF_EXTENSIONS_UPDATE_URL = "extensions.update.url";
49 const PREF_EXTENSIONS_STRICT_COMPAT = "extensions.strictCompatibility";
51 const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
52 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
53 const NS_GRE_DIR = "GreD";
54 const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD";
55 const XRE_EXECUTABLE_FILE = "XREExeF";
56 const XRE_UPDATE_ROOT_DIR = "UpdRootD";
58 const CRC_ERROR = 4;
59 const WRITE_ERROR = 7;
61 const DIR_PATCH = "0";
62 const DIR_TOBEDELETED = "tobedeleted";
63 const DIR_UPDATES = "updates";
64 #ifdef XP_MACOSX
65 const DIR_BIN_REL_PATH = "Contents/MacOS/";
66 const DIR_UPDATED = "Updated.app";
67 #else
68 const DIR_BIN_REL_PATH = "";
69 const DIR_UPDATED = "updated";
70 #endif
72 const FILE_BACKUP_LOG = "backup-update.log";
73 const FILE_LAST_LOG = "last-update.log";
74 const FILE_UPDATER_INI = "updater.ini";
75 const FILE_UPDATES_DB = "updates.xml";
76 const FILE_UPDATE_ACTIVE = "active-update.xml";
77 const FILE_UPDATE_ARCHIVE = "update.mar";
78 const FILE_UPDATE_LOG = "update.log";
79 const FILE_UPDATE_SETTINGS_INI = "update-settings.ini";
80 const FILE_UPDATE_SETTINGS_INI_BAK = "update-settings.ini.bak";
81 const FILE_UPDATE_STATUS = "update.status";
82 const FILE_UPDATE_VERSION = "update.version";
84 const UPDATE_SETTINGS_CONTENTS = "[Settings]\n" +
85 "ACCEPTED_MAR_CHANNEL_IDS=xpcshell-test\n"
87 const PR_RDWR = 0x04;
88 const PR_CREATE_FILE = 0x08;
89 const PR_APPEND = 0x10;
90 const PR_TRUNCATE = 0x20;
91 const PR_SYNC = 0x40;
92 const PR_EXCL = 0x80;
94 const DEFAULT_UPDATE_VERSION = "999999.0";
96 var gChannel;
98 #include sharedUpdateXML.js
100 AUS_Cu.import("resource://gre/modules/FileUtils.jsm");
101 AUS_Cu.import("resource://gre/modules/Services.jsm");
102 AUS_Cu.import("resource://gre/modules/XPCOMUtils.jsm");
104 const PERMS_FILE = FileUtils.PERMS_FILE;
105 const PERMS_DIRECTORY = FileUtils.PERMS_DIRECTORY;
107 const MODE_RDONLY = FileUtils.MODE_RDONLY;
108 const MODE_WRONLY = FileUtils.MODE_WRONLY;
109 const MODE_RDWR = FileUtils.MODE_RDWR;
110 const MODE_CREATE = FileUtils.MODE_CREATE;
111 const MODE_APPEND = FileUtils.MODE_APPEND;
112 const MODE_TRUNCATE = FileUtils.MODE_TRUNCATE;
114 const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
115 const gUpdateBundle = Services.strings.createBundle(URI_UPDATES_PROPERTIES);
117 XPCOMUtils.defineLazyGetter(this, "gAUS", function test_gAUS() {
118 return AUS_Cc["@mozilla.org/updates/update-service;1"].
119 getService(AUS_Ci.nsIApplicationUpdateService).
120 QueryInterface(AUS_Ci.nsITimerCallback).
121 QueryInterface(AUS_Ci.nsIObserver).
122 QueryInterface(AUS_Ci.nsIUpdateCheckListener);
123 });
125 XPCOMUtils.defineLazyServiceGetter(this, "gUpdateManager",
126 "@mozilla.org/updates/update-manager;1",
127 "nsIUpdateManager");
129 XPCOMUtils.defineLazyGetter(this, "gUpdateChecker", function test_gUC() {
130 return AUS_Cc["@mozilla.org/updates/update-checker;1"].
131 createInstance(AUS_Ci.nsIUpdateChecker);
132 });
134 XPCOMUtils.defineLazyGetter(this, "gUP", function test_gUP() {
135 return AUS_Cc["@mozilla.org/updates/update-prompt;1"].
136 createInstance(AUS_Ci.nsIUpdatePrompt);
137 });
139 XPCOMUtils.defineLazyGetter(this, "gDefaultPrefBranch", function test_gDPB() {
140 return Services.prefs.getDefaultBranch(null);
141 });
143 XPCOMUtils.defineLazyGetter(this, "gPrefRoot", function test_gPR() {
144 return Services.prefs.getBranch(null);
145 });
147 XPCOMUtils.defineLazyGetter(this, "gZipW", function test_gZipW() {
148 return AUS_Cc["@mozilla.org/zipwriter;1"].
149 createInstance(AUS_Ci.nsIZipWriter);
150 });
152 /* Initializes the update service stub */
153 function initUpdateServiceStub() {
154 AUS_Cc["@mozilla.org/updates/update-service-stub;1"].
155 createInstance(AUS_Ci.nsISupports);
156 }
158 /* Reloads the update metadata from disk */
159 function reloadUpdateManagerData() {
160 gUpdateManager.QueryInterface(AUS_Ci.nsIObserver).
161 observe(null, "um-reload-update-data", "");
162 }
164 /**
165 * Sets the app.update.channel preference.
166 *
167 * @param aChannel
168 * The update channel.
169 */
170 function setUpdateChannel(aChannel) {
171 gChannel = aChannel;
172 debugDump("setting default pref " + PREF_APP_UPDATE_CHANNEL + " to " + gChannel);
173 gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel);
174 gPrefRoot.addObserver(PREF_APP_UPDATE_CHANNEL, observer, false);
175 }
177 var observer = {
178 observe: function(aSubject, aTopic, aData) {
179 if (aTopic == "nsPref:changed" && aData == PREF_APP_UPDATE_CHANNEL) {
180 var channel = gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
181 if (channel != gChannel) {
182 debugDump("Changing channel from " + channel + " to " + gChannel);
183 gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel);
184 }
185 }
186 },
187 QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver])
188 };
190 /**
191 * Sets the app.update.url.override preference.
192 *
193 * @param aURL
194 * The update url. If not specified 'URL_HOST + "/update.xml"' will be
195 * used.
196 */
197 function setUpdateURLOverride(aURL) {
198 let url = aURL ? aURL : URL_HOST + "/update.xml";
199 debugDump("setting " + PREF_APP_UPDATE_URL_OVERRIDE + " to " + url);
200 Services.prefs.setCharPref(PREF_APP_UPDATE_URL_OVERRIDE, url);
201 }
203 /**
204 * Returns either the active or regular update database XML file.
205 *
206 * @param isActiveUpdate
207 * If true this will return the active-update.xml otherwise it will
208 * return the updates.xml file.
209 */
210 function getUpdatesXMLFile(aIsActiveUpdate) {
211 var file = getUpdatesRootDir();
212 file.append(aIsActiveUpdate ? FILE_UPDATE_ACTIVE : FILE_UPDATES_DB);
213 return file;
214 }
216 /**
217 * Writes the updates specified to either the active-update.xml or the
218 * updates.xml.
219 *
220 * @param aContent
221 * The updates represented as a string to write to the XML file.
222 * @param isActiveUpdate
223 * If true this will write to the active-update.xml otherwise it will
224 * write to the updates.xml file.
225 */
226 function writeUpdatesToXMLFile(aContent, aIsActiveUpdate) {
227 writeFile(getUpdatesXMLFile(aIsActiveUpdate), aContent);
228 }
230 /**
231 * Writes the current update operation/state to a file in the patch
232 * directory, indicating to the patching system that operations need
233 * to be performed.
234 *
235 * @param aStatus
236 * The status value to write.
237 */
238 function writeStatusFile(aStatus) {
239 let file = getUpdatesPatchDir();
240 file.append(FILE_UPDATE_STATUS);
241 writeFile(file, aStatus + "\n");
242 }
244 /**
245 * Writes the current update version to a file in the patch directory,
246 * indicating to the patching system the version of the update.
247 *
248 * @param aVersion
249 * The version value to write.
250 */
251 function writeVersionFile(aVersion) {
252 let file = getUpdatesPatchDir();
253 file.append(FILE_UPDATE_VERSION);
254 writeFile(file, aVersion + "\n");
255 }
257 /**
258 * Gets the root directory for the updates directory.
259 *
260 * @return nsIFile for the updates root directory.
261 */
262 function getUpdatesRootDir() {
263 return Services.dirsvc.get(XRE_UPDATE_ROOT_DIR, AUS_Ci.nsIFile);
264 }
266 /**
267 * Gets the updates directory.
268 *
269 * @return nsIFile for the updates directory.
270 */
271 function getUpdatesDir() {
272 var dir = getUpdatesRootDir();
273 dir.append(DIR_UPDATES);
274 return dir;
275 }
277 /**
278 * Gets the directory for update patches.
279 *
280 * @return nsIFile for the updates directory.
281 */
282 function getUpdatesPatchDir() {
283 let dir = getUpdatesDir();
284 dir.append(DIR_PATCH);
285 return dir;
286 }
288 /**
289 * Writes text to a file. This will replace existing text if the file exists
290 * and create the file if it doesn't exist.
291 *
292 * @param aFile
293 * The file to write to. Will be created if it doesn't exist.
294 * @param aText
295 * The text to write to the file. If there is existing text it will be
296 * replaced.
297 */
298 function writeFile(aFile, aText) {
299 var fos = AUS_Cc["@mozilla.org/network/file-output-stream;1"].
300 createInstance(AUS_Ci.nsIFileOutputStream);
301 if (!aFile.exists())
302 aFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
303 fos.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
304 fos.write(aText, aText.length);
305 fos.close();
306 }
308 /**
309 * Reads the current update operation/state in the status file in the patch
310 * directory including the error code if it is present.
311 *
312 * @return The status value.
313 */
314 function readStatusFile() {
315 let file = getUpdatesPatchDir();
316 file.append(FILE_UPDATE_STATUS);
318 if (!file.exists()) {
319 logTestInfo("update status file does not exists! Path: " + file.path);
320 return STATE_NONE;
321 }
323 return readFile(file).split("\n")[0];
324 }
326 /**
327 * Reads the current update operation/state in the status file in the patch
328 * directory without the error code if it is present.
329 *
330 * @return The state value.
331 */
332 function readStatusState() {
333 return readStatusFile().split(": ")[0];
334 }
336 /**
337 * Reads the current update operation/state in the status file in the patch
338 * directory with the error code.
339 *
340 * @return The state value.
341 */
342 function readStatusFailedCode() {
343 return readStatusFile().split(": ")[1];
344 }
346 /**
347 * Reads text from a file and returns the string.
348 *
349 * @param aFile
350 * The file to read from.
351 * @return The string of text read from the file.
352 */
353 function readFile(aFile) {
354 var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"].
355 createInstance(AUS_Ci.nsIFileInputStream);
356 if (!aFile.exists())
357 return null;
358 fis.init(aFile, MODE_RDONLY, PERMS_FILE, 0);
359 var sis = AUS_Cc["@mozilla.org/scriptableinputstream;1"].
360 createInstance(AUS_Ci.nsIScriptableInputStream);
361 sis.init(fis);
362 var text = sis.read(sis.available());
363 sis.close();
364 return text;
365 }
367 /**
368 * Reads the binary contents of a file and returns it as a string.
369 *
370 * @param aFile
371 * The file to read from.
372 * @return The contents of the file as a string.
373 */
374 function readFileBytes(aFile) {
375 var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"].
376 createInstance(AUS_Ci.nsIFileInputStream);
377 fis.init(aFile, -1, -1, false);
378 var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"].
379 createInstance(AUS_Ci.nsIBinaryInputStream);
380 bis.setInputStream(fis);
381 var data = [];
382 var count = fis.available();
383 while (count > 0) {
384 var bytes = bis.readByteArray(Math.min(65535, count));
385 data.push(String.fromCharCode.apply(null, bytes));
386 count -= bytes.length;
387 if (bytes.length == 0)
388 throw "Nothing read from input stream!";
389 }
390 data.join('');
391 fis.close();
392 return data.toString();
393 }
395 /* Returns human readable status text from the updates.properties bundle */
396 function getStatusText(aErrCode) {
397 return getString("check_error-" + aErrCode);
398 }
400 /* Returns a string from the updates.properties bundle */
401 function getString(aName) {
402 try {
403 return gUpdateBundle.GetStringFromName(aName);
404 } catch (e) {
405 }
406 return null;
407 }
409 /**
410 * Gets the file extension for an nsIFile.
411 *
412 * @param aFile
413 * The file to get the file extension for.
414 * @return The file extension.
415 */
416 function getFileExtension(aFile) {
417 return Services.io.newFileURI(aFile).QueryInterface(AUS_Ci.nsIURL).
418 fileExtension;
419 }
421 /**
422 * Removes the updates.xml file, active-update.xml file, and all files and
423 * sub-directories in the updates directory except for the "0" sub-directory.
424 * This prevents some tests from failing due to files being left behind when the
425 * tests are interrupted.
426 */
427 function removeUpdateDirsAndFiles() {
428 var file = getUpdatesXMLFile(true);
429 try {
430 if (file.exists())
431 file.remove(false);
432 } catch (e) {
433 dump("Unable to remove file\nPath: " + file.path +
434 "\nException: " + e + "\n");
435 }
437 file = getUpdatesXMLFile(false);
438 try {
439 if (file.exists())
440 file.remove(false);
441 } catch (e) {
442 dump("Unable to remove file\nPath: " + file.path +
443 "\nException: " + e + "\n");
444 }
446 // This fails sporadically on Mac OS X so wrap it in a try catch
447 var updatesDir = getUpdatesDir();
448 try {
449 cleanUpdatesDir(updatesDir);
450 } catch (e) {
451 dump("Unable to remove files / directories from directory\nPath: " +
452 updatesDir.path + "\nException: " + e + "\n");
453 }
454 }
456 /**
457 * Removes all files and sub-directories in the updates directory except for
458 * the "0" sub-directory.
459 *
460 * @param aDir
461 * nsIFile for the directory to be deleted.
462 */
463 function cleanUpdatesDir(aDir) {
464 if (!aDir.exists())
465 return;
467 var dirEntries = aDir.directoryEntries;
468 while (dirEntries.hasMoreElements()) {
469 var entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile);
471 if (entry.isDirectory()) {
472 if (entry.leafName == DIR_PATCH && entry.parent.leafName == DIR_UPDATES) {
473 cleanUpdatesDir(entry);
474 entry.permissions = PERMS_DIRECTORY;
475 } else {
476 try {
477 entry.remove(true);
478 return;
479 } catch (e) {
480 }
481 cleanUpdatesDir(entry);
482 entry.permissions = PERMS_DIRECTORY;
483 try {
484 entry.remove(true);
485 } catch (e) {
486 dump("cleanUpdatesDir: unable to remove directory\nPath: " +
487 entry.path + "\nException: " + e + "\n");
488 throw(e);
489 }
490 }
491 } else {
492 entry.permissions = PERMS_FILE;
493 try {
494 entry.remove(false);
495 } catch (e) {
496 dump("cleanUpdatesDir: unable to remove file\nPath: " + entry.path +
497 "\nException: " + e + "\n");
498 throw(e);
499 }
500 }
501 }
502 }
504 /**
505 * Deletes a directory and its children. First it tries nsIFile::Remove(true).
506 * If that fails it will fall back to recursing, setting the appropriate
507 * permissions, and deleting the current entry.
508 *
509 * @param aDir
510 * nsIFile for the directory to be deleted.
511 */
512 function removeDirRecursive(aDir) {
513 if (!aDir.exists()) {
514 return;
515 }
517 try {
518 logTestInfo("attempting to remove directory. Path: " + aDir.path);
519 aDir.remove(true);
520 return;
521 } catch (e) {
522 logTestInfo("non-fatal error removing directory. Exception: " + e);
523 }
525 var dirEntries = aDir.directoryEntries;
526 while (dirEntries.hasMoreElements()) {
527 var entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile);
529 if (entry.isDirectory()) {
530 removeDirRecursive(entry);
531 } else {
532 entry.permissions = PERMS_FILE;
533 try {
534 logTestInfo("attempting to remove file. Path: " + entry.path);
535 entry.remove(false);
536 } catch (e) {
537 logTestInfo("error removing file. Exception: " + e);
538 throw(e);
539 }
540 }
541 }
543 aDir.permissions = PERMS_DIRECTORY;
544 try {
545 logTestInfo("attempting to remove directory. Path: " + aDir.path);
546 aDir.remove(true);
547 } catch (e) {
548 logTestInfo("error removing directory. Exception: " + e);
549 throw(e);
550 }
551 }
553 /**
554 * Returns the directory for the currently running process. This is used to
555 * clean up after the tests and to locate the active-update.xml and updates.xml
556 * files.
557 *
558 * @return nsIFile for the current process directory.
559 */
560 function getCurrentProcessDir() {
561 return Services.dirsvc.get(NS_XPCOM_CURRENT_PROCESS_DIR, AUS_Ci.nsIFile);
562 }
564 /**
565 * Gets the application base directory.
566 *
567 * @return nsIFile object for the application base directory.
568 */
569 function getAppBaseDir() {
570 return Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsIFile).parent;
571 }
573 /**
574 * Returns the Gecko Runtime Engine directory. This is used to locate the the
575 * updater binary (Windows and Linux) or updater package (Mac OS X). For
576 * XULRunner applications this is different than the currently running process
577 * directory.
578 *
579 * @return nsIFile for the Gecko Runtime Engine directory.
580 */
581 function getGREDir() {
582 return Services.dirsvc.get(NS_GRE_DIR, AUS_Ci.nsIFile);
583 }
585 /**
586 * Get the "updated" directory inside the directory where we apply the
587 * staged updates.
588 * @return The active updates directory inside the updated directory, as a
589 * nsIFile object.
590 */
591 function getUpdatedDir() {
592 let dir = getAppBaseDir();
593 #ifdef XP_MACOSX
594 dir = dir.parent.parent; // the bundle directory
595 #endif
596 dir.append(DIR_UPDATED);
597 logTestInfo("updated directory path: " + dir.path);
598 return dir;
599 }
601 /**
602 * Logs TEST-INFO messages.
603 *
604 * @param aText
605 * The text to log.
606 * @param aCaller (optional)
607 * An optional Components.stack.caller. If not specified
608 * Components.stack.caller will be used.
609 */
610 function logTestInfo(aText, aCaller) {
611 let caller = (aCaller ? aCaller : Components.stack.caller);
612 let now = new Date;
613 let hh = now.getHours();
614 let mm = now.getMinutes();
615 let ss = now.getSeconds();
616 let ms = now.getMilliseconds();
617 let time = (hh < 10 ? "0" + hh : hh) + ":" +
618 (mm < 10 ? "0" + mm : mm) + ":" +
619 (ss < 10 ? "0" + ss : ss) + ":" +
620 (ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms);
621 dump(time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
622 " : " + caller.lineNumber + "] " + aText + "\n");
623 }
625 /**
626 * Logs TEST-INFO messages when DEBUG_AUS_TEST evaluates to true.
627 *
628 * @param aText
629 * The text to log.
630 * @param aCaller (optional)
631 * An optional Components.stack.caller. If not specified
632 * Components.stack.caller will be used.
633 */
634 function debugDump(aText, aCaller) {
635 if (DEBUG_AUS_TEST) {
636 let caller = aCaller ? aCaller : Components.stack.caller;
637 logTestInfo(aText, caller);
638 }
639 }