toolkit/mozapps/extensions/test/browser/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 * http://creativecommons.org/publicdomain/zero/1.0/
michael@0 3 */
michael@0 4
michael@0 5 Components.utils.import("resource://gre/modules/NetUtil.jsm");
michael@0 6
michael@0 7 let tmp = {};
michael@0 8 Components.utils.import("resource://gre/modules/AddonManager.jsm", tmp);
michael@0 9 Components.utils.import("resource://gre/modules/Log.jsm", tmp);
michael@0 10 let AddonManager = tmp.AddonManager;
michael@0 11 let AddonManagerPrivate = tmp.AddonManagerPrivate;
michael@0 12 let Log = tmp.Log;
michael@0 13
michael@0 14 var pathParts = gTestPath.split("/");
michael@0 15 // Drop the test filename
michael@0 16 pathParts.splice(pathParts.length - 1, pathParts.length);
michael@0 17
michael@0 18 var gTestInWindow = /-window$/.test(pathParts[pathParts.length - 1]);
michael@0 19
michael@0 20 // Drop the UI type
michael@0 21 if (gTestInWindow) {
michael@0 22 pathParts.splice(pathParts.length - 1, pathParts.length);
michael@0 23 }
michael@0 24
michael@0 25 const RELATIVE_DIR = pathParts.slice(4).join("/") + "/";
michael@0 26
michael@0 27 const TESTROOT = "http://example.com/" + RELATIVE_DIR;
michael@0 28 const TESTROOT2 = "http://example.org/" + RELATIVE_DIR;
michael@0 29 const CHROMEROOT = pathParts.join("/") + "/";
michael@0 30 const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
michael@0 31 const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
michael@0 32 const PREF_XPI_ENABLED = "xpinstall.enabled";
michael@0 33 const PREF_UPDATEURL = "extensions.update.url";
michael@0 34 const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
michael@0 35
michael@0 36 const MANAGER_URI = "about:addons";
michael@0 37 const INSTALL_URI = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
michael@0 38 const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
michael@0 39 const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults";
michael@0 40 const PREF_STRICT_COMPAT = "extensions.strictCompatibility";
michael@0 41
michael@0 42 var PREF_CHECK_COMPATIBILITY;
michael@0 43 (function() {
michael@0 44 var channel = "default";
michael@0 45 try {
michael@0 46 channel = Services.prefs.getCharPref("app.update.channel");
michael@0 47 } catch (e) { }
michael@0 48 if (channel != "aurora" &&
michael@0 49 channel != "beta" &&
michael@0 50 channel != "release") {
michael@0 51 var version = "nightly";
michael@0 52 } else {
michael@0 53 version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
michael@0 54 }
michael@0 55 PREF_CHECK_COMPATIBILITY = "extensions.checkCompatibility." + version;
michael@0 56 })();
michael@0 57
michael@0 58 var gPendingTests = [];
michael@0 59 var gTestsRun = 0;
michael@0 60 var gTestStart = null;
michael@0 61
michael@0 62 var gUseInContentUI = !gTestInWindow && ("switchToTabHavingURI" in window);
michael@0 63
michael@0 64 var gRestorePrefs = [{name: PREF_LOGGING_ENABLED},
michael@0 65 {name: "extensions.webservice.discoverURL"},
michael@0 66 {name: "extensions.update.url"},
michael@0 67 {name: "extensions.update.background.url"},
michael@0 68 {name: "extensions.update.enabled"},
michael@0 69 {name: "extensions.update.autoUpdateDefault"},
michael@0 70 {name: "extensions.getAddons.get.url"},
michael@0 71 {name: "extensions.getAddons.getWithPerformance.url"},
michael@0 72 {name: "extensions.getAddons.search.browseURL"},
michael@0 73 {name: "extensions.getAddons.search.url"},
michael@0 74 {name: "extensions.getAddons.cache.enabled"},
michael@0 75 {name: "devtools.chrome.enabled"},
michael@0 76 {name: "devtools.debugger.remote-enabled"},
michael@0 77 {name: PREF_SEARCH_MAXRESULTS},
michael@0 78 {name: PREF_STRICT_COMPAT},
michael@0 79 {name: PREF_CHECK_COMPATIBILITY}];
michael@0 80
michael@0 81 for (let pref of gRestorePrefs) {
michael@0 82 if (!Services.prefs.prefHasUserValue(pref.name)) {
michael@0 83 pref.type = "clear";
michael@0 84 continue;
michael@0 85 }
michael@0 86 pref.type = Services.prefs.getPrefType(pref.name);
michael@0 87 if (pref.type == Services.prefs.PREF_BOOL)
michael@0 88 pref.value = Services.prefs.getBoolPref(pref.name);
michael@0 89 else if (pref.type == Services.prefs.PREF_INT)
michael@0 90 pref.value = Services.prefs.getIntPref(pref.name);
michael@0 91 else if (pref.type == Services.prefs.PREF_STRING)
michael@0 92 pref.value = Services.prefs.getCharPref(pref.name);
michael@0 93 }
michael@0 94
michael@0 95 // Turn logging on for all tests
michael@0 96 Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
michael@0 97
michael@0 98 // Helper to register test failures and close windows if any are left open
michael@0 99 function checkOpenWindows(aWindowID) {
michael@0 100 let windows = Services.wm.getEnumerator(aWindowID);
michael@0 101 let found = false;
michael@0 102 while (windows.hasMoreElements()) {
michael@0 103 let win = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
michael@0 104 if (!win.closed) {
michael@0 105 found = true;
michael@0 106 win.close();
michael@0 107 }
michael@0 108 }
michael@0 109 if (found)
michael@0 110 ok(false, "Found unexpected " + aWindowID + " window still open");
michael@0 111 }
michael@0 112
michael@0 113 registerCleanupFunction(function() {
michael@0 114 // Restore prefs
michael@0 115 for (let pref of gRestorePrefs) {
michael@0 116 if (pref.type == "clear")
michael@0 117 Services.prefs.clearUserPref(pref.name);
michael@0 118 else if (pref.type == Services.prefs.PREF_BOOL)
michael@0 119 Services.prefs.setBoolPref(pref.name, pref.value);
michael@0 120 else if (pref.type == Services.prefs.PREF_INT)
michael@0 121 Services.prefs.setIntPref(pref.name, pref.value);
michael@0 122 else if (pref.type == Services.prefs.PREF_STRING)
michael@0 123 Services.prefs.setCharPref(pref.name, pref.value);
michael@0 124 }
michael@0 125
michael@0 126 // Throw an error if the add-ons manager window is open anywhere
michael@0 127 checkOpenWindows("Addons:Manager");
michael@0 128 checkOpenWindows("Addons:Compatibility");
michael@0 129 checkOpenWindows("Addons:Install");
michael@0 130
michael@0 131 return new Promise((resolve, reject) => AddonManager.getAllInstalls(resolve))
michael@0 132 .then(aInstalls => {
michael@0 133 for (let install of aInstalls) {
michael@0 134 if (install instanceof MockInstall)
michael@0 135 continue;
michael@0 136
michael@0 137 ok(false, "Should not have seen an install of " + install.sourceURI.spec + " in state " + install.state);
michael@0 138 install.cancel();
michael@0 139 }
michael@0 140 });
michael@0 141 });
michael@0 142
michael@0 143 function log_exceptions(aCallback, ...aArgs) {
michael@0 144 try {
michael@0 145 return aCallback.apply(null, aArgs);
michael@0 146 }
michael@0 147 catch (e) {
michael@0 148 info("Exception thrown: " + e);
michael@0 149 throw e;
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 function log_callback(aPromise, aCallback) {
michael@0 154 aPromise.then(aCallback)
michael@0 155 .then(null, e => info("Exception thrown: " + e));
michael@0 156 return aPromise;
michael@0 157 }
michael@0 158
michael@0 159 function add_test(test) {
michael@0 160 gPendingTests.push(test);
michael@0 161 }
michael@0 162
michael@0 163 function run_next_test() {
michael@0 164 if (gTestsRun > 0)
michael@0 165 info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms");
michael@0 166
michael@0 167 if (gPendingTests.length == 0) {
michael@0 168 end_test();
michael@0 169 return;
michael@0 170 }
michael@0 171
michael@0 172 gTestsRun++;
michael@0 173 var test = gPendingTests.shift();
michael@0 174 if (test.name)
michael@0 175 info("Running test " + gTestsRun + " (" + test.name + ")");
michael@0 176 else
michael@0 177 info("Running test " + gTestsRun);
michael@0 178
michael@0 179 gTestStart = Date.now();
michael@0 180 log_exceptions(test);
michael@0 181 }
michael@0 182
michael@0 183 function get_addon_file_url(aFilename) {
michael@0 184 try {
michael@0 185 var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
michael@0 186 getService(Ci.nsIChromeRegistry);
michael@0 187 var fileurl = cr.convertChromeURL(makeURI(CHROMEROOT + "addons/" + aFilename));
michael@0 188 return fileurl.QueryInterface(Ci.nsIFileURL);
michael@0 189 } catch(ex) {
michael@0 190 var jar = getJar(CHROMEROOT + "addons/" + aFilename);
michael@0 191 var tmpDir = extractJarToTmp(jar);
michael@0 192 tmpDir.append(aFilename);
michael@0 193
michael@0 194 return Services.io.newFileURI(tmpDir).QueryInterface(Ci.nsIFileURL);
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 function get_test_items_in_list(aManager) {
michael@0 199 var tests = "@tests.mozilla.org";
michael@0 200
michael@0 201 let view = aManager.document.getElementById("view-port").selectedPanel;
michael@0 202 let listid = view.id == "search-view" ? "search-list" : "addon-list";
michael@0 203 let item = aManager.document.getElementById(listid).firstChild;
michael@0 204 let items = [];
michael@0 205
michael@0 206 while (item) {
michael@0 207 if (item.localName != "richlistitem") {
michael@0 208 item = item.nextSibling;
michael@0 209 continue;
michael@0 210 }
michael@0 211
michael@0 212 if (!item.mAddon || item.mAddon.id.substring(item.mAddon.id.length - tests.length) == tests)
michael@0 213 items.push(item);
michael@0 214 item = item.nextSibling;
michael@0 215 }
michael@0 216
michael@0 217 return items;
michael@0 218 }
michael@0 219
michael@0 220 function check_all_in_list(aManager, aIds, aIgnoreExtras) {
michael@0 221 var doc = aManager.document;
michael@0 222 var view = doc.getElementById("view-port").selectedPanel;
michael@0 223 var listid = view.id == "search-view" ? "search-list" : "addon-list";
michael@0 224 var list = doc.getElementById(listid);
michael@0 225
michael@0 226 var inlist = [];
michael@0 227 var node = list.firstChild;
michael@0 228 while (node) {
michael@0 229 if (node.value)
michael@0 230 inlist.push(node.value);
michael@0 231 node = node.nextSibling;
michael@0 232 }
michael@0 233
michael@0 234 for (let id of aIds) {
michael@0 235 if (inlist.indexOf(id) == -1)
michael@0 236 ok(false, "Should find " + id + " in the list");
michael@0 237 }
michael@0 238
michael@0 239 if (aIgnoreExtras)
michael@0 240 return;
michael@0 241
michael@0 242 for (let inlistItem of inlist) {
michael@0 243 if (aIds.indexOf(inlistItem) == -1)
michael@0 244 ok(false, "Shouldn't have seen " + inlistItem + " in the list");
michael@0 245 }
michael@0 246 }
michael@0 247
michael@0 248 function get_addon_element(aManager, aId) {
michael@0 249 var doc = aManager.document;
michael@0 250 var view = doc.getElementById("view-port").selectedPanel;
michael@0 251 var listid = "addon-list";
michael@0 252 if (view.id == "search-view")
michael@0 253 listid = "search-list";
michael@0 254 else if (view.id == "updates-view")
michael@0 255 listid = "updates-list";
michael@0 256 var list = doc.getElementById(listid);
michael@0 257
michael@0 258 var node = list.firstChild;
michael@0 259 while (node) {
michael@0 260 if (node.value == aId)
michael@0 261 return node;
michael@0 262 node = node.nextSibling;
michael@0 263 }
michael@0 264 return null;
michael@0 265 }
michael@0 266
michael@0 267 function wait_for_view_load(aManagerWindow, aCallback, aForceWait, aLongerTimeout) {
michael@0 268 requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
michael@0 269
michael@0 270 if (!aForceWait && !aManagerWindow.gViewController.isLoading) {
michael@0 271 log_exceptions(aCallback, aManagerWindow);
michael@0 272 return;
michael@0 273 }
michael@0 274
michael@0 275 aManagerWindow.document.addEventListener("ViewChanged", function() {
michael@0 276 aManagerWindow.document.removeEventListener("ViewChanged", arguments.callee, false);
michael@0 277 log_exceptions(aCallback, aManagerWindow);
michael@0 278 }, false);
michael@0 279 }
michael@0 280
michael@0 281 function wait_for_manager_load(aManagerWindow, aCallback) {
michael@0 282 if (!aManagerWindow.gIsInitializing) {
michael@0 283 log_exceptions(aCallback, aManagerWindow);
michael@0 284 return;
michael@0 285 }
michael@0 286
michael@0 287 info("Waiting for initialization");
michael@0 288 aManagerWindow.document.addEventListener("Initialized", function() {
michael@0 289 aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false);
michael@0 290 log_exceptions(aCallback, aManagerWindow);
michael@0 291 }, false);
michael@0 292 }
michael@0 293
michael@0 294 function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) {
michael@0 295 let p = new Promise((resolve, reject) => {
michael@0 296
michael@0 297 function setup_manager(aManagerWindow) {
michael@0 298 if (aLoadCallback)
michael@0 299 log_exceptions(aLoadCallback, aManagerWindow);
michael@0 300
michael@0 301 if (aView)
michael@0 302 aManagerWindow.loadView(aView);
michael@0 303
michael@0 304 ok(aManagerWindow != null, "Should have an add-ons manager window");
michael@0 305 is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI");
michael@0 306
michael@0 307 waitForFocus(function() {
michael@0 308 info("window has focus, waiting for manager load");
michael@0 309 wait_for_manager_load(aManagerWindow, function() {
michael@0 310 info("Manager waiting for view load");
michael@0 311 wait_for_view_load(aManagerWindow, function() {
michael@0 312 resolve(aManagerWindow);
michael@0 313 }, null, aLongerTimeout);
michael@0 314 });
michael@0 315 }, aManagerWindow);
michael@0 316 }
michael@0 317
michael@0 318 if (gUseInContentUI) {
michael@0 319 info("Loading manager window in tab");
michael@0 320 Services.obs.addObserver(function (aSubject, aTopic, aData) {
michael@0 321 Services.obs.removeObserver(arguments.callee, aTopic);
michael@0 322 if (aSubject.location.href != MANAGER_URI) {
michael@0 323 info("Ignoring load event for " + aSubject.location.href);
michael@0 324 return;
michael@0 325 }
michael@0 326 setup_manager(aSubject);
michael@0 327 }, "EM-loaded", false);
michael@0 328
michael@0 329 gBrowser.selectedTab = gBrowser.addTab();
michael@0 330 switchToTabHavingURI(MANAGER_URI, true);
michael@0 331 } else {
michael@0 332 info("Loading manager window in dialog");
michael@0 333 Services.obs.addObserver(function (aSubject, aTopic, aData) {
michael@0 334 Services.obs.removeObserver(arguments.callee, aTopic);
michael@0 335 setup_manager(aSubject);
michael@0 336 }, "EM-loaded", false);
michael@0 337
michael@0 338 openDialog(MANAGER_URI);
michael@0 339 }
michael@0 340 });
michael@0 341
michael@0 342 // The promise resolves with the manager window, so it is passed to the callback
michael@0 343 return log_callback(p, aCallback);
michael@0 344 }
michael@0 345
michael@0 346 function close_manager(aManagerWindow, aCallback, aLongerTimeout) {
michael@0 347 let p = new Promise((resolve, reject) => {
michael@0 348 requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
michael@0 349
michael@0 350 ok(aManagerWindow != null, "Should have an add-ons manager window to close");
michael@0 351 is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
michael@0 352
michael@0 353 aManagerWindow.addEventListener("unload", function() {
michael@0 354 try {
michael@0 355 dump("Manager window unload handler");
michael@0 356 this.removeEventListener("unload", arguments.callee, false);
michael@0 357 resolve();
michael@0 358 } catch(e) {
michael@0 359 reject(e);
michael@0 360 }
michael@0 361 }, false);
michael@0 362 });
michael@0 363
michael@0 364 info("Telling manager window to close");
michael@0 365 aManagerWindow.close();
michael@0 366 info("Manager window close() call returned");
michael@0 367
michael@0 368 return log_callback(p, aCallback);
michael@0 369 }
michael@0 370
michael@0 371 function restart_manager(aManagerWindow, aView, aCallback, aLoadCallback) {
michael@0 372 if (!aManagerWindow) {
michael@0 373 return open_manager(aView, aCallback, aLoadCallback);
michael@0 374 }
michael@0 375
michael@0 376 return close_manager(aManagerWindow)
michael@0 377 .then(() => open_manager(aView, aCallback, aLoadCallback));
michael@0 378 }
michael@0 379
michael@0 380 function wait_for_window_open(aCallback) {
michael@0 381 Services.wm.addListener({
michael@0 382 onOpenWindow: function(aWindow) {
michael@0 383 Services.wm.removeListener(this);
michael@0 384
michael@0 385 let domwindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 386 .getInterface(Ci.nsIDOMWindow);
michael@0 387 domwindow.addEventListener("load", function() {
michael@0 388 domwindow.removeEventListener("load", arguments.callee, false);
michael@0 389 executeSoon(function() {
michael@0 390 aCallback(domwindow);
michael@0 391 });
michael@0 392 }, false);
michael@0 393 },
michael@0 394
michael@0 395 onCloseWindow: function(aWindow) {
michael@0 396 },
michael@0 397
michael@0 398 onWindowTitleChange: function(aWindow, aTitle) {
michael@0 399 }
michael@0 400 });
michael@0 401 }
michael@0 402
michael@0 403 function get_string(aName, ...aArgs) {
michael@0 404 var bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
michael@0 405 if (aArgs.length == 0)
michael@0 406 return bundle.GetStringFromName(aName);
michael@0 407 return bundle.formatStringFromName(aName, aArgs, aArgs.length);
michael@0 408 }
michael@0 409
michael@0 410 function formatDate(aDate) {
michael@0 411 return Cc["@mozilla.org/intl/scriptabledateformat;1"]
michael@0 412 .getService(Ci.nsIScriptableDateFormat)
michael@0 413 .FormatDate("",
michael@0 414 Ci.nsIScriptableDateFormat.dateFormatLong,
michael@0 415 aDate.getFullYear(),
michael@0 416 aDate.getMonth() + 1,
michael@0 417 aDate.getDate()
michael@0 418 );
michael@0 419 }
michael@0 420
michael@0 421 function is_hidden(aElement) {
michael@0 422 var style = aElement.ownerDocument.defaultView.getComputedStyle(aElement, "");
michael@0 423 if (style.display == "none")
michael@0 424 return true;
michael@0 425 if (style.visibility != "visible")
michael@0 426 return true;
michael@0 427
michael@0 428 // Hiding a parent element will hide all its children
michael@0 429 if (aElement.parentNode != aElement.ownerDocument)
michael@0 430 return is_hidden(aElement.parentNode);
michael@0 431
michael@0 432 return false;
michael@0 433 }
michael@0 434
michael@0 435 function is_element_visible(aElement, aMsg) {
michael@0 436 isnot(aElement, null, "Element should not be null, when checking visibility");
michael@0 437 ok(!is_hidden(aElement), aMsg);
michael@0 438 }
michael@0 439
michael@0 440 function is_element_hidden(aElement, aMsg) {
michael@0 441 isnot(aElement, null, "Element should not be null, when checking visibility");
michael@0 442 ok(is_hidden(aElement), aMsg);
michael@0 443 }
michael@0 444
michael@0 445 /**
michael@0 446 * Install an add-on and call a callback when complete.
michael@0 447 *
michael@0 448 * The callback will receive the Addon for the installed add-on.
michael@0 449 */
michael@0 450 function install_addon(path, cb, pathPrefix=TESTROOT) {
michael@0 451 let p = new Promise((resolve, reject) => {
michael@0 452 AddonManager.getInstallForURL(pathPrefix + path, (install) => {
michael@0 453 install.addListener({
michael@0 454 onInstallEnded: () => resolve(install.addon),
michael@0 455 });
michael@0 456
michael@0 457 install.install();
michael@0 458 }, "application/x-xpinstall");
michael@0 459 });
michael@0 460
michael@0 461 return log_callback(p, cb);
michael@0 462 }
michael@0 463
michael@0 464 function CategoryUtilities(aManagerWindow) {
michael@0 465 this.window = aManagerWindow;
michael@0 466
michael@0 467 var self = this;
michael@0 468 this.window.addEventListener("unload", function() {
michael@0 469 self.window.removeEventListener("unload", arguments.callee, false);
michael@0 470 self.window = null;
michael@0 471 }, false);
michael@0 472 }
michael@0 473
michael@0 474 CategoryUtilities.prototype = {
michael@0 475 window: null,
michael@0 476
michael@0 477 get selectedCategory() {
michael@0 478 isnot(this.window, null, "Should not get selected category when manager window is not loaded");
michael@0 479 var selectedItem = this.window.document.getElementById("categories").selectedItem;
michael@0 480 isnot(selectedItem, null, "A category should be selected");
michael@0 481 var view = this.window.gViewController.parseViewId(selectedItem.value);
michael@0 482 return (view.type == "list") ? view.param : view.type;
michael@0 483 },
michael@0 484
michael@0 485 get: function(aCategoryType, aAllowMissing) {
michael@0 486 isnot(this.window, null, "Should not get category when manager window is not loaded");
michael@0 487 var categories = this.window.document.getElementById("categories");
michael@0 488
michael@0 489 var viewId = "addons://list/" + aCategoryType;
michael@0 490 var items = categories.getElementsByAttribute("value", viewId);
michael@0 491 if (items.length)
michael@0 492 return items[0];
michael@0 493
michael@0 494 viewId = "addons://" + aCategoryType + "/";
michael@0 495 items = categories.getElementsByAttribute("value", viewId);
michael@0 496 if (items.length)
michael@0 497 return items[0];
michael@0 498
michael@0 499 if (!aAllowMissing)
michael@0 500 ok(false, "Should have found a category with type " + aCategoryType);
michael@0 501 return null;
michael@0 502 },
michael@0 503
michael@0 504 getViewId: function(aCategoryType) {
michael@0 505 isnot(this.window, null, "Should not get view id when manager window is not loaded");
michael@0 506 return this.get(aCategoryType).value;
michael@0 507 },
michael@0 508
michael@0 509 isVisible: function(aCategory) {
michael@0 510 isnot(this.window, null, "Should not check visible state when manager window is not loaded");
michael@0 511 if (aCategory.hasAttribute("disabled") &&
michael@0 512 aCategory.getAttribute("disabled") == "true")
michael@0 513 return false;
michael@0 514
michael@0 515 return !is_hidden(aCategory);
michael@0 516 },
michael@0 517
michael@0 518 isTypeVisible: function(aCategoryType) {
michael@0 519 return this.isVisible(this.get(aCategoryType));
michael@0 520 },
michael@0 521
michael@0 522 open: function(aCategory, aCallback) {
michael@0 523
michael@0 524 isnot(this.window, null, "Should not open category when manager window is not loaded");
michael@0 525 ok(this.isVisible(aCategory), "Category should be visible if attempting to open it");
michael@0 526
michael@0 527 EventUtils.synthesizeMouse(aCategory, 2, 2, { }, this.window);
michael@0 528 let p = new Promise((resolve, reject) => wait_for_view_load(this.window, resolve));
michael@0 529
michael@0 530 return log_callback(p, aCallback);
michael@0 531 },
michael@0 532
michael@0 533 openType: function(aCategoryType, aCallback) {
michael@0 534 return this.open(this.get(aCategoryType), aCallback);
michael@0 535 }
michael@0 536 }
michael@0 537
michael@0 538 function CertOverrideListener(host, bits) {
michael@0 539 this.host = host;
michael@0 540 this.bits = bits;
michael@0 541 }
michael@0 542
michael@0 543 CertOverrideListener.prototype = {
michael@0 544 host: null,
michael@0 545 bits: null,
michael@0 546
michael@0 547 getInterface: function (aIID) {
michael@0 548 return this.QueryInterface(aIID);
michael@0 549 },
michael@0 550
michael@0 551 QueryInterface: function(aIID) {
michael@0 552 if (aIID.equals(Ci.nsIBadCertListener2) ||
michael@0 553 aIID.equals(Ci.nsIInterfaceRequestor) ||
michael@0 554 aIID.equals(Ci.nsISupports))
michael@0 555 return this;
michael@0 556
michael@0 557 throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
michael@0 558 },
michael@0 559
michael@0 560 notifyCertProblem: function (socketInfo, sslStatus, targetHost) {
michael@0 561 var cert = sslStatus.QueryInterface(Components.interfaces.nsISSLStatus)
michael@0 562 .serverCert;
michael@0 563 var cos = Cc["@mozilla.org/security/certoverride;1"].
michael@0 564 getService(Ci.nsICertOverrideService);
michael@0 565 cos.rememberValidityOverride(this.host, -1, cert, this.bits, false);
michael@0 566 return true;
michael@0 567 }
michael@0 568 }
michael@0 569
michael@0 570 // Add overrides for the bad certificates
michael@0 571 function addCertOverride(host, bits) {
michael@0 572 var req = new XMLHttpRequest();
michael@0 573 try {
michael@0 574 req.open("GET", "https://" + host + "/", false);
michael@0 575 req.channel.notificationCallbacks = new CertOverrideListener(host, bits);
michael@0 576 req.send(null);
michael@0 577 }
michael@0 578 catch (e) {
michael@0 579 // This request will fail since the SSL server is not trusted yet
michael@0 580 }
michael@0 581 }
michael@0 582
michael@0 583 /***** Mock Provider *****/
michael@0 584
michael@0 585 function MockProvider(aUseAsyncCallbacks, aTypes) {
michael@0 586 this.addons = [];
michael@0 587 this.installs = [];
michael@0 588 this.callbackTimers = [];
michael@0 589 this.timerLocations = new Map();
michael@0 590 this.useAsyncCallbacks = (aUseAsyncCallbacks === undefined) ? true : aUseAsyncCallbacks;
michael@0 591 this.types = (aTypes === undefined) ? [{
michael@0 592 id: "extension",
michael@0 593 name: "Extensions",
michael@0 594 uiPriority: 4000,
michael@0 595 flags: AddonManager.TYPE_UI_VIEW_LIST
michael@0 596 }] : aTypes;
michael@0 597
michael@0 598 var self = this;
michael@0 599 registerCleanupFunction(function() {
michael@0 600 if (self.started)
michael@0 601 self.unregister();
michael@0 602 });
michael@0 603
michael@0 604 this.register();
michael@0 605 }
michael@0 606
michael@0 607 MockProvider.prototype = {
michael@0 608 addons: null,
michael@0 609 installs: null,
michael@0 610 started: null,
michael@0 611 apiDelay: 10,
michael@0 612 callbackTimers: null,
michael@0 613 timerLocations: null,
michael@0 614 useAsyncCallbacks: null,
michael@0 615 types: null,
michael@0 616
michael@0 617 /***** Utility functions *****/
michael@0 618
michael@0 619 /**
michael@0 620 * Register this provider with the AddonManager
michael@0 621 */
michael@0 622 register: function MP_register() {
michael@0 623 AddonManagerPrivate.registerProvider(this, this.types);
michael@0 624 },
michael@0 625
michael@0 626 /**
michael@0 627 * Unregister this provider with the AddonManager
michael@0 628 */
michael@0 629 unregister: function MP_unregister() {
michael@0 630 AddonManagerPrivate.unregisterProvider(this);
michael@0 631 },
michael@0 632
michael@0 633 /**
michael@0 634 * Adds an add-on to the list of add-ons that this provider exposes to the
michael@0 635 * AddonManager, dispatching appropriate events in the process.
michael@0 636 *
michael@0 637 * @param aAddon
michael@0 638 * The add-on to add
michael@0 639 */
michael@0 640 addAddon: function MP_addAddon(aAddon) {
michael@0 641 var oldAddons = this.addons.filter(function(aOldAddon) aOldAddon.id == aAddon.id);
michael@0 642 var oldAddon = oldAddons.length > 0 ? oldAddons[0] : null;
michael@0 643
michael@0 644 this.addons = this.addons.filter(function(aOldAddon) aOldAddon.id != aAddon.id);
michael@0 645
michael@0 646 this.addons.push(aAddon);
michael@0 647 aAddon._provider = this;
michael@0 648
michael@0 649 if (!this.started)
michael@0 650 return;
michael@0 651
michael@0 652 let requiresRestart = (aAddon.operationsRequiringRestart &
michael@0 653 AddonManager.OP_NEEDS_RESTART_INSTALL) != 0;
michael@0 654 AddonManagerPrivate.callInstallListeners("onExternalInstall", null, aAddon,
michael@0 655 oldAddon, requiresRestart)
michael@0 656 },
michael@0 657
michael@0 658 /**
michael@0 659 * Removes an add-on from the list of add-ons that this provider exposes to
michael@0 660 * the AddonManager, dispatching the onUninstalled event in the process.
michael@0 661 *
michael@0 662 * @param aAddon
michael@0 663 * The add-on to add
michael@0 664 */
michael@0 665 removeAddon: function MP_removeAddon(aAddon) {
michael@0 666 var pos = this.addons.indexOf(aAddon);
michael@0 667 if (pos == -1) {
michael@0 668 ok(false, "Tried to remove an add-on that wasn't registered with the mock provider");
michael@0 669 return;
michael@0 670 }
michael@0 671
michael@0 672 this.addons.splice(pos, 1);
michael@0 673
michael@0 674 if (!this.started)
michael@0 675 return;
michael@0 676
michael@0 677 AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
michael@0 678 },
michael@0 679
michael@0 680 /**
michael@0 681 * Adds an add-on install to the list of installs that this provider exposes
michael@0 682 * to the AddonManager, dispatching appropriate events in the process.
michael@0 683 *
michael@0 684 * @param aInstall
michael@0 685 * The add-on install to add
michael@0 686 */
michael@0 687 addInstall: function MP_addInstall(aInstall) {
michael@0 688 this.installs.push(aInstall);
michael@0 689 aInstall._provider = this;
michael@0 690
michael@0 691 if (!this.started)
michael@0 692 return;
michael@0 693
michael@0 694 aInstall.callListeners("onNewInstall");
michael@0 695 },
michael@0 696
michael@0 697 removeInstall: function MP_removeInstall(aInstall) {
michael@0 698 var pos = this.installs.indexOf(aInstall);
michael@0 699 if (pos == -1) {
michael@0 700 ok(false, "Tried to remove an install that wasn't registered with the mock provider");
michael@0 701 return;
michael@0 702 }
michael@0 703
michael@0 704 this.installs.splice(pos, 1);
michael@0 705 },
michael@0 706
michael@0 707 /**
michael@0 708 * Creates a set of mock add-on objects and adds them to the list of add-ons
michael@0 709 * managed by this provider.
michael@0 710 *
michael@0 711 * @param aAddonProperties
michael@0 712 * An array of objects containing properties describing the add-ons
michael@0 713 * @return Array of the new MockAddons
michael@0 714 */
michael@0 715 createAddons: function MP_createAddons(aAddonProperties) {
michael@0 716 var newAddons = [];
michael@0 717 for (let addonProp of aAddonProperties) {
michael@0 718 let addon = new MockAddon(addonProp.id);
michael@0 719 for (let prop in addonProp) {
michael@0 720 if (prop == "id")
michael@0 721 continue;
michael@0 722 if (prop == "applyBackgroundUpdates") {
michael@0 723 addon._applyBackgroundUpdates = addonProp[prop];
michael@0 724 continue;
michael@0 725 }
michael@0 726 if (prop == "appDisabled") {
michael@0 727 addon._appDisabled = addonProp[prop];
michael@0 728 continue;
michael@0 729 }
michael@0 730 addon[prop] = addonProp[prop];
michael@0 731 }
michael@0 732 if (!addon.optionsType && !!addon.optionsURL)
michael@0 733 addon.optionsType = AddonManager.OPTIONS_TYPE_DIALOG;
michael@0 734
michael@0 735 // Make sure the active state matches the passed in properties
michael@0 736 addon.isActive = addon.shouldBeActive;
michael@0 737
michael@0 738 this.addAddon(addon);
michael@0 739 newAddons.push(addon);
michael@0 740 }
michael@0 741
michael@0 742 return newAddons;
michael@0 743 },
michael@0 744
michael@0 745 /**
michael@0 746 * Creates a set of mock add-on install objects and adds them to the list
michael@0 747 * of installs managed by this provider.
michael@0 748 *
michael@0 749 * @param aInstallProperties
michael@0 750 * An array of objects containing properties describing the installs
michael@0 751 * @return Array of the new MockInstalls
michael@0 752 */
michael@0 753 createInstalls: function MP_createInstalls(aInstallProperties) {
michael@0 754 var newInstalls = [];
michael@0 755 for (let installProp of aInstallProperties) {
michael@0 756 let install = new MockInstall(installProp.name || null,
michael@0 757 installProp.type || null,
michael@0 758 null);
michael@0 759 for (let prop in installProp) {
michael@0 760 switch (prop) {
michael@0 761 case "name":
michael@0 762 case "type":
michael@0 763 break;
michael@0 764 case "sourceURI":
michael@0 765 install[prop] = NetUtil.newURI(installProp[prop]);
michael@0 766 break;
michael@0 767 default:
michael@0 768 install[prop] = installProp[prop];
michael@0 769 }
michael@0 770 }
michael@0 771 this.addInstall(install);
michael@0 772 newInstalls.push(install);
michael@0 773 }
michael@0 774
michael@0 775 return newInstalls;
michael@0 776 },
michael@0 777
michael@0 778 /***** AddonProvider implementation *****/
michael@0 779
michael@0 780 /**
michael@0 781 * Called to initialize the provider.
michael@0 782 */
michael@0 783 startup: function MP_startup() {
michael@0 784 this.started = true;
michael@0 785 },
michael@0 786
michael@0 787 /**
michael@0 788 * Called when the provider should shutdown.
michael@0 789 */
michael@0 790 shutdown: function MP_shutdown() {
michael@0 791 if (this.callbackTimers.length) {
michael@0 792 info("MockProvider: pending callbacks at shutdown(): calling immediately");
michael@0 793 }
michael@0 794 while (this.callbackTimers.length > 0) {
michael@0 795 // When we notify the callback timer, it removes itself from our array
michael@0 796 let timer = this.callbackTimers[0];
michael@0 797 try {
michael@0 798 let setAt = this.timerLocations.get(timer);
michael@0 799 info("Notifying timer set at " + (setAt || "unknown location"));
michael@0 800 timer.callback.notify(timer);
michael@0 801 timer.cancel();
michael@0 802 } catch(e) {
michael@0 803 info("Timer notify failed: " + e);
michael@0 804 }
michael@0 805 }
michael@0 806 this.callbackTimers = [];
michael@0 807 this.timerLocations = null;
michael@0 808
michael@0 809 this.started = false;
michael@0 810 },
michael@0 811
michael@0 812 /**
michael@0 813 * Called to get an Addon with a particular ID.
michael@0 814 *
michael@0 815 * @param aId
michael@0 816 * The ID of the add-on to retrieve
michael@0 817 * @param aCallback
michael@0 818 * A callback to pass the Addon to
michael@0 819 */
michael@0 820 getAddonByID: function MP_getAddon(aId, aCallback) {
michael@0 821 for (let addon of this.addons) {
michael@0 822 if (addon.id == aId) {
michael@0 823 this._delayCallback(aCallback, addon);
michael@0 824 return;
michael@0 825 }
michael@0 826 }
michael@0 827
michael@0 828 aCallback(null);
michael@0 829 },
michael@0 830
michael@0 831 /**
michael@0 832 * Called to get Addons of a particular type.
michael@0 833 *
michael@0 834 * @param aTypes
michael@0 835 * An array of types to fetch. Can be null to get all types.
michael@0 836 * @param callback
michael@0 837 * A callback to pass an array of Addons to
michael@0 838 */
michael@0 839 getAddonsByTypes: function MP_getAddonsByTypes(aTypes, aCallback) {
michael@0 840 var addons = this.addons.filter(function(aAddon) {
michael@0 841 if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
michael@0 842 return false;
michael@0 843 return true;
michael@0 844 });
michael@0 845 this._delayCallback(aCallback, addons);
michael@0 846 },
michael@0 847
michael@0 848 /**
michael@0 849 * Called to get Addons that have pending operations.
michael@0 850 *
michael@0 851 * @param aTypes
michael@0 852 * An array of types to fetch. Can be null to get all types
michael@0 853 * @param aCallback
michael@0 854 * A callback to pass an array of Addons to
michael@0 855 */
michael@0 856 getAddonsWithOperationsByTypes: function MP_getAddonsWithOperationsByTypes(aTypes, aCallback) {
michael@0 857 var addons = this.addons.filter(function(aAddon) {
michael@0 858 if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
michael@0 859 return false;
michael@0 860 return aAddon.pendingOperations != 0;
michael@0 861 });
michael@0 862 this._delayCallback(aCallback, addons);
michael@0 863 },
michael@0 864
michael@0 865 /**
michael@0 866 * Called to get the current AddonInstalls, optionally restricting by type.
michael@0 867 *
michael@0 868 * @param aTypes
michael@0 869 * An array of types or null to get all types
michael@0 870 * @param aCallback
michael@0 871 * A callback to pass the array of AddonInstalls to
michael@0 872 */
michael@0 873 getInstallsByTypes: function MP_getInstallsByTypes(aTypes, aCallback) {
michael@0 874 var installs = this.installs.filter(function(aInstall) {
michael@0 875 // Appear to have actually removed cancelled installs from the provider
michael@0 876 if (aInstall.state == AddonManager.STATE_CANCELLED)
michael@0 877 return false;
michael@0 878
michael@0 879 if (aTypes && aTypes.length > 0 && aTypes.indexOf(aInstall.type) == -1)
michael@0 880 return false;
michael@0 881
michael@0 882 return true;
michael@0 883 });
michael@0 884 this._delayCallback(aCallback, installs);
michael@0 885 },
michael@0 886
michael@0 887 /**
michael@0 888 * Called when a new add-on has been enabled when only one add-on of that type
michael@0 889 * can be enabled.
michael@0 890 *
michael@0 891 * @param aId
michael@0 892 * The ID of the newly enabled add-on
michael@0 893 * @param aType
michael@0 894 * The type of the newly enabled add-on
michael@0 895 * @param aPendingRestart
michael@0 896 * true if the newly enabled add-on will only become enabled after a
michael@0 897 * restart
michael@0 898 */
michael@0 899 addonChanged: function MP_addonChanged(aId, aType, aPendingRestart) {
michael@0 900 // Not implemented
michael@0 901 },
michael@0 902
michael@0 903 /**
michael@0 904 * Update the appDisabled property for all add-ons.
michael@0 905 */
michael@0 906 updateAddonAppDisabledStates: function MP_updateAddonAppDisabledStates() {
michael@0 907 // Not needed
michael@0 908 },
michael@0 909
michael@0 910 /**
michael@0 911 * Called to get an AddonInstall to download and install an add-on from a URL.
michael@0 912 *
michael@0 913 * @param aUrl
michael@0 914 * The URL to be installed
michael@0 915 * @param aHash
michael@0 916 * A hash for the install
michael@0 917 * @param aName
michael@0 918 * A name for the install
michael@0 919 * @param aIconURL
michael@0 920 * An icon URL for the install
michael@0 921 * @param aVersion
michael@0 922 * A version for the install
michael@0 923 * @param aLoadGroup
michael@0 924 * An nsILoadGroup to associate requests with
michael@0 925 * @param aCallback
michael@0 926 * A callback to pass the AddonInstall to
michael@0 927 */
michael@0 928 getInstallForURL: function MP_getInstallForURL(aUrl, aHash, aName, aIconURL,
michael@0 929 aVersion, aLoadGroup, aCallback) {
michael@0 930 // Not yet implemented
michael@0 931 },
michael@0 932
michael@0 933 /**
michael@0 934 * Called to get an AddonInstall to install an add-on from a local file.
michael@0 935 *
michael@0 936 * @param aFile
michael@0 937 * The file to be installed
michael@0 938 * @param aCallback
michael@0 939 * A callback to pass the AddonInstall to
michael@0 940 */
michael@0 941 getInstallForFile: function MP_getInstallForFile(aFile, aCallback) {
michael@0 942 // Not yet implemented
michael@0 943 },
michael@0 944
michael@0 945 /**
michael@0 946 * Called to test whether installing add-ons is enabled.
michael@0 947 *
michael@0 948 * @return true if installing is enabled
michael@0 949 */
michael@0 950 isInstallEnabled: function MP_isInstallEnabled() {
michael@0 951 return false;
michael@0 952 },
michael@0 953
michael@0 954 /**
michael@0 955 * Called to test whether this provider supports installing a particular
michael@0 956 * mimetype.
michael@0 957 *
michael@0 958 * @param aMimetype
michael@0 959 * The mimetype to check for
michael@0 960 * @return true if the mimetype is supported
michael@0 961 */
michael@0 962 supportsMimetype: function MP_supportsMimetype(aMimetype) {
michael@0 963 return false;
michael@0 964 },
michael@0 965
michael@0 966 /**
michael@0 967 * Called to test whether installing add-ons from a URI is allowed.
michael@0 968 *
michael@0 969 * @param aUri
michael@0 970 * The URI being installed from
michael@0 971 * @return true if installing is allowed
michael@0 972 */
michael@0 973 isInstallAllowed: function MP_isInstallAllowed(aUri) {
michael@0 974 return false;
michael@0 975 },
michael@0 976
michael@0 977
michael@0 978 /***** Internal functions *****/
michael@0 979
michael@0 980 /**
michael@0 981 * Delay calling a callback to fake a time-consuming async operation.
michael@0 982 * The delay is specified by the apiDelay property, in milliseconds.
michael@0 983 * Parameters to send to the callback should be specified as arguments after
michael@0 984 * the aCallback argument.
michael@0 985 *
michael@0 986 * @param aCallback Callback to eventually call
michael@0 987 */
michael@0 988 _delayCallback: function MP_delayCallback(aCallback, ...aArgs) {
michael@0 989 if (!this.useAsyncCallbacks) {
michael@0 990 aCallback(...aArgs);
michael@0 991 return;
michael@0 992 }
michael@0 993
michael@0 994 let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 995 // Need to keep a reference to the timer, so it doesn't get GC'ed
michael@0 996 this.callbackTimers.push(timer);
michael@0 997 // Capture a stack trace where the timer was set
michael@0 998 // needs the 'new Error' hack until bug 1007656
michael@0 999 this.timerLocations.set(timer, Log.stackTrace(new Error("dummy")));
michael@0 1000 timer.initWithCallback(() => {
michael@0 1001 let idx = this.callbackTimers.indexOf(timer);
michael@0 1002 if (idx == -1) {
michael@0 1003 dump("MockProvider._delayCallback lost track of timer set at "
michael@0 1004 + (this.timerLocations.get(timer) || "unknown location") + "\n");
michael@0 1005 } else {
michael@0 1006 this.callbackTimers.splice(idx, 1);
michael@0 1007 }
michael@0 1008 this.timerLocations.delete(timer);
michael@0 1009 aCallback(...aArgs);
michael@0 1010 }, this.apiDelay, timer.TYPE_ONE_SHOT);
michael@0 1011 }
michael@0 1012 };
michael@0 1013
michael@0 1014 /***** Mock Addon object for the Mock Provider *****/
michael@0 1015
michael@0 1016 function MockAddon(aId, aName, aType, aOperationsRequiringRestart) {
michael@0 1017 // Only set required attributes.
michael@0 1018 this.id = aId || "";
michael@0 1019 this.name = aName || "";
michael@0 1020 this.type = aType || "extension";
michael@0 1021 this.version = "";
michael@0 1022 this.isCompatible = true;
michael@0 1023 this.isDebuggable = false;
michael@0 1024 this.providesUpdatesSecurely = true;
michael@0 1025 this.blocklistState = 0;
michael@0 1026 this._appDisabled = false;
michael@0 1027 this._userDisabled = false;
michael@0 1028 this._applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
michael@0 1029 this.scope = AddonManager.SCOPE_PROFILE;
michael@0 1030 this.isActive = true;
michael@0 1031 this.creator = "";
michael@0 1032 this.pendingOperations = 0;
michael@0 1033 this._permissions = AddonManager.PERM_CAN_UNINSTALL |
michael@0 1034 AddonManager.PERM_CAN_ENABLE |
michael@0 1035 AddonManager.PERM_CAN_DISABLE |
michael@0 1036 AddonManager.PERM_CAN_UPGRADE;
michael@0 1037 this.operationsRequiringRestart = aOperationsRequiringRestart ||
michael@0 1038 (AddonManager.OP_NEEDS_RESTART_INSTALL |
michael@0 1039 AddonManager.OP_NEEDS_RESTART_UNINSTALL |
michael@0 1040 AddonManager.OP_NEEDS_RESTART_ENABLE |
michael@0 1041 AddonManager.OP_NEEDS_RESTART_DISABLE);
michael@0 1042 }
michael@0 1043
michael@0 1044 MockAddon.prototype = {
michael@0 1045 get shouldBeActive() {
michael@0 1046 return !this.appDisabled && !this._userDisabled;
michael@0 1047 },
michael@0 1048
michael@0 1049 get appDisabled() {
michael@0 1050 return this._appDisabled;
michael@0 1051 },
michael@0 1052
michael@0 1053 set appDisabled(val) {
michael@0 1054 if (val == this._appDisabled)
michael@0 1055 return val;
michael@0 1056
michael@0 1057 AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["appDisabled"]);
michael@0 1058
michael@0 1059 var currentActive = this.shouldBeActive;
michael@0 1060 this._appDisabled = val;
michael@0 1061 var newActive = this.shouldBeActive;
michael@0 1062 this._updateActiveState(currentActive, newActive);
michael@0 1063
michael@0 1064 return val;
michael@0 1065 },
michael@0 1066
michael@0 1067 get userDisabled() {
michael@0 1068 return this._userDisabled;
michael@0 1069 },
michael@0 1070
michael@0 1071 set userDisabled(val) {
michael@0 1072 if (val == this._userDisabled)
michael@0 1073 return val;
michael@0 1074
michael@0 1075 var currentActive = this.shouldBeActive;
michael@0 1076 this._userDisabled = val;
michael@0 1077 var newActive = this.shouldBeActive;
michael@0 1078 this._updateActiveState(currentActive, newActive);
michael@0 1079
michael@0 1080 return val;
michael@0 1081 },
michael@0 1082
michael@0 1083 get permissions() {
michael@0 1084 let permissions = this._permissions;
michael@0 1085 if (this.appDisabled || !this._userDisabled)
michael@0 1086 permissions &= ~AddonManager.PERM_CAN_ENABLE;
michael@0 1087 if (this.appDisabled || this._userDisabled)
michael@0 1088 permissions &= ~AddonManager.PERM_CAN_DISABLE;
michael@0 1089 return permissions;
michael@0 1090 },
michael@0 1091
michael@0 1092 set permissions(val) {
michael@0 1093 return this._permissions = val;
michael@0 1094 },
michael@0 1095
michael@0 1096 get applyBackgroundUpdates() {
michael@0 1097 return this._applyBackgroundUpdates;
michael@0 1098 },
michael@0 1099
michael@0 1100 set applyBackgroundUpdates(val) {
michael@0 1101 if (val != AddonManager.AUTOUPDATE_DEFAULT &&
michael@0 1102 val != AddonManager.AUTOUPDATE_DISABLE &&
michael@0 1103 val != AddonManager.AUTOUPDATE_ENABLE) {
michael@0 1104 ok(false, "addon.applyBackgroundUpdates set to an invalid value: " + val);
michael@0 1105 }
michael@0 1106 this._applyBackgroundUpdates = val;
michael@0 1107 AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
michael@0 1108 },
michael@0 1109
michael@0 1110 isCompatibleWith: function(aAppVersion, aPlatformVersion) {
michael@0 1111 return true;
michael@0 1112 },
michael@0 1113
michael@0 1114 findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
michael@0 1115 // Tests can implement this if they need to
michael@0 1116 },
michael@0 1117
michael@0 1118 uninstall: function() {
michael@0 1119 if (this.pendingOperations & AddonManager.PENDING_UNINSTALL)
michael@0 1120 throw Components.Exception("Add-on is already pending uninstall");
michael@0 1121
michael@0 1122 var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL);
michael@0 1123 this.pendingOperations |= AddonManager.PENDING_UNINSTALL;
michael@0 1124 AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart);
michael@0 1125 if (!needsRestart) {
michael@0 1126 this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
michael@0 1127 this._provider.removeAddon(this);
michael@0 1128 }
michael@0 1129 },
michael@0 1130
michael@0 1131 cancelUninstall: function() {
michael@0 1132 if (!(this.pendingOperations & AddonManager.PENDING_UNINSTALL))
michael@0 1133 throw Components.Exception("Add-on is not pending uninstall");
michael@0 1134
michael@0 1135 this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
michael@0 1136 AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
michael@0 1137 },
michael@0 1138
michael@0 1139 _updateActiveState: function(currentActive, newActive) {
michael@0 1140 if (currentActive == newActive)
michael@0 1141 return;
michael@0 1142
michael@0 1143 if (newActive == this.isActive) {
michael@0 1144 this.pendingOperations -= (newActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE);
michael@0 1145 AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
michael@0 1146 }
michael@0 1147 else if (newActive) {
michael@0 1148 var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE);
michael@0 1149 this.pendingOperations |= AddonManager.PENDING_ENABLE;
michael@0 1150 AddonManagerPrivate.callAddonListeners("onEnabling", this, needsRestart);
michael@0 1151 if (!needsRestart) {
michael@0 1152 this.isActive = newActive;
michael@0 1153 this.pendingOperations -= AddonManager.PENDING_ENABLE;
michael@0 1154 AddonManagerPrivate.callAddonListeners("onEnabled", this);
michael@0 1155 }
michael@0 1156 }
michael@0 1157 else {
michael@0 1158 var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE);
michael@0 1159 this.pendingOperations |= AddonManager.PENDING_DISABLE;
michael@0 1160 AddonManagerPrivate.callAddonListeners("onDisabling", this, needsRestart);
michael@0 1161 if (!needsRestart) {
michael@0 1162 this.isActive = newActive;
michael@0 1163 this.pendingOperations -= AddonManager.PENDING_DISABLE;
michael@0 1164 AddonManagerPrivate.callAddonListeners("onDisabled", this);
michael@0 1165 }
michael@0 1166 }
michael@0 1167 }
michael@0 1168 };
michael@0 1169
michael@0 1170 /***** Mock AddonInstall object for the Mock Provider *****/
michael@0 1171
michael@0 1172 function MockInstall(aName, aType, aAddonToInstall) {
michael@0 1173 this.name = aName || "";
michael@0 1174 // Don't expose type until download completed
michael@0 1175 this._type = aType || "extension";
michael@0 1176 this.type = null;
michael@0 1177 this.version = "1.0";
michael@0 1178 this.iconURL = "";
michael@0 1179 this.infoURL = "";
michael@0 1180 this.state = AddonManager.STATE_AVAILABLE;
michael@0 1181 this.error = 0;
michael@0 1182 this.sourceURI = null;
michael@0 1183 this.file = null;
michael@0 1184 this.progress = 0;
michael@0 1185 this.maxProgress = -1;
michael@0 1186 this.certificate = null;
michael@0 1187 this.certName = "";
michael@0 1188 this.existingAddon = null;
michael@0 1189 this.addon = null;
michael@0 1190 this._addonToInstall = aAddonToInstall;
michael@0 1191 this.listeners = [];
michael@0 1192
michael@0 1193 // Another type of install listener for tests that want to check the results
michael@0 1194 // of code run from standard install listeners
michael@0 1195 this.testListeners = [];
michael@0 1196 }
michael@0 1197
michael@0 1198 MockInstall.prototype = {
michael@0 1199 install: function() {
michael@0 1200 switch (this.state) {
michael@0 1201 case AddonManager.STATE_AVAILABLE:
michael@0 1202 this.state = AddonManager.STATE_DOWNLOADING;
michael@0 1203 if (!this.callListeners("onDownloadStarted")) {
michael@0 1204 this.state = AddonManager.STATE_CANCELLED;
michael@0 1205 this.callListeners("onDownloadCancelled");
michael@0 1206 return;
michael@0 1207 }
michael@0 1208
michael@0 1209 this.type = this._type;
michael@0 1210
michael@0 1211 // Adding addon to MockProvider to be implemented when needed
michael@0 1212 if (this._addonToInstall)
michael@0 1213 this.addon = this._addonToInstall;
michael@0 1214 else {
michael@0 1215 this.addon = new MockAddon("", this.name, this.type);
michael@0 1216 this.addon.version = this.version;
michael@0 1217 this.addon.pendingOperations = AddonManager.PENDING_INSTALL;
michael@0 1218 }
michael@0 1219 this.addon.install = this;
michael@0 1220 if (this.existingAddon) {
michael@0 1221 if (!this.addon.id)
michael@0 1222 this.addon.id = this.existingAddon.id;
michael@0 1223 this.existingAddon.pendingUpgrade = this.addon;
michael@0 1224 this.existingAddon.pendingOperations |= AddonManager.PENDING_UPGRADE;
michael@0 1225 }
michael@0 1226
michael@0 1227 this.state = AddonManager.STATE_DOWNLOADED;
michael@0 1228 this.callListeners("onDownloadEnded");
michael@0 1229
michael@0 1230 case AddonManager.STATE_DOWNLOADED:
michael@0 1231 this.state = AddonManager.STATE_INSTALLING;
michael@0 1232 if (!this.callListeners("onInstallStarted")) {
michael@0 1233 this.state = AddonManager.STATE_CANCELLED;
michael@0 1234 this.callListeners("onInstallCancelled");
michael@0 1235 return;
michael@0 1236 }
michael@0 1237
michael@0 1238 AddonManagerPrivate.callAddonListeners("onInstalling", this.addon);
michael@0 1239
michael@0 1240 this.state = AddonManager.STATE_INSTALLED;
michael@0 1241 this.callListeners("onInstallEnded");
michael@0 1242 break;
michael@0 1243 case AddonManager.STATE_DOWNLOADING:
michael@0 1244 case AddonManager.STATE_CHECKING:
michael@0 1245 case AddonManger.STATE_INSTALLING:
michael@0 1246 // Installation is already running
michael@0 1247 return;
michael@0 1248 default:
michael@0 1249 ok(false, "Cannot start installing when state = " + this.state);
michael@0 1250 }
michael@0 1251 },
michael@0 1252
michael@0 1253 cancel: function() {
michael@0 1254 switch (this.state) {
michael@0 1255 case AddonManager.STATE_AVAILABLE:
michael@0 1256 this.state = AddonManager.STATE_CANCELLED;
michael@0 1257 break;
michael@0 1258 case AddonManager.STATE_INSTALLED:
michael@0 1259 this.state = AddonManager.STATE_CANCELLED;
michael@0 1260 this._provider.removeInstall(this);
michael@0 1261 this.callListeners("onInstallCancelled");
michael@0 1262 break;
michael@0 1263 default:
michael@0 1264 // Handling cancelling when downloading to be implemented when needed
michael@0 1265 ok(false, "Cannot cancel when state = " + this.state);
michael@0 1266 }
michael@0 1267 },
michael@0 1268
michael@0 1269
michael@0 1270 addListener: function(aListener) {
michael@0 1271 if (!this.listeners.some(function(i) i == aListener))
michael@0 1272 this.listeners.push(aListener);
michael@0 1273 },
michael@0 1274
michael@0 1275 removeListener: function(aListener) {
michael@0 1276 this.listeners = this.listeners.filter(function(i) i != aListener);
michael@0 1277 },
michael@0 1278
michael@0 1279 addTestListener: function(aListener) {
michael@0 1280 if (!this.testListeners.some(function(i) i == aListener))
michael@0 1281 this.testListeners.push(aListener);
michael@0 1282 },
michael@0 1283
michael@0 1284 removeTestListener: function(aListener) {
michael@0 1285 this.testListeners = this.testListeners.filter(function(i) i != aListener);
michael@0 1286 },
michael@0 1287
michael@0 1288 callListeners: function(aMethod) {
michael@0 1289 var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners,
michael@0 1290 this, this.addon);
michael@0 1291
michael@0 1292 // Call test listeners after standard listeners to remove race condition
michael@0 1293 // between standard and test listeners
michael@0 1294 for (let listener of this.testListeners) {
michael@0 1295 try {
michael@0 1296 if (aMethod in listener)
michael@0 1297 if (listener[aMethod].call(listener, this, this.addon) === false)
michael@0 1298 result = false;
michael@0 1299 }
michael@0 1300 catch (e) {
michael@0 1301 ok(false, "Test listener threw exception: " + e);
michael@0 1302 }
michael@0 1303 }
michael@0 1304
michael@0 1305 return result;
michael@0 1306 }
michael@0 1307 };
michael@0 1308
michael@0 1309 function waitForCondition(condition, nextTest, errorMsg) {
michael@0 1310 let tries = 0;
michael@0 1311 let interval = setInterval(function() {
michael@0 1312 if (tries >= 30) {
michael@0 1313 ok(false, errorMsg);
michael@0 1314 moveOn();
michael@0 1315 }
michael@0 1316 var conditionPassed;
michael@0 1317 try {
michael@0 1318 conditionPassed = condition();
michael@0 1319 } catch (e) {
michael@0 1320 ok(false, e + "\n" + e.stack);
michael@0 1321 conditionPassed = false;
michael@0 1322 }
michael@0 1323 if (conditionPassed) {
michael@0 1324 moveOn();
michael@0 1325 }
michael@0 1326 tries++;
michael@0 1327 }, 100);
michael@0 1328 let moveOn = function() { clearInterval(interval); nextTest(); };
michael@0 1329 }
michael@0 1330
michael@0 1331 function getTestPluginTag() {
michael@0 1332 let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
michael@0 1333 let tags = ph.getPluginTags();
michael@0 1334
michael@0 1335 // Find the test plugin
michael@0 1336 for (let i = 0; i < tags.length; i++) {
michael@0 1337 if (tags[i].name == "Test Plug-in")
michael@0 1338 return tags[i];
michael@0 1339 }
michael@0 1340 ok(false, "Unable to find plugin");
michael@0 1341 return null;
michael@0 1342 }

mercurial