1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/extensions/test/browser/head.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1342 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ 1.6 + */ 1.7 + 1.8 +Components.utils.import("resource://gre/modules/NetUtil.jsm"); 1.9 + 1.10 +let tmp = {}; 1.11 +Components.utils.import("resource://gre/modules/AddonManager.jsm", tmp); 1.12 +Components.utils.import("resource://gre/modules/Log.jsm", tmp); 1.13 +let AddonManager = tmp.AddonManager; 1.14 +let AddonManagerPrivate = tmp.AddonManagerPrivate; 1.15 +let Log = tmp.Log; 1.16 + 1.17 +var pathParts = gTestPath.split("/"); 1.18 +// Drop the test filename 1.19 +pathParts.splice(pathParts.length - 1, pathParts.length); 1.20 + 1.21 +var gTestInWindow = /-window$/.test(pathParts[pathParts.length - 1]); 1.22 + 1.23 +// Drop the UI type 1.24 +if (gTestInWindow) { 1.25 + pathParts.splice(pathParts.length - 1, pathParts.length); 1.26 +} 1.27 + 1.28 +const RELATIVE_DIR = pathParts.slice(4).join("/") + "/"; 1.29 + 1.30 +const TESTROOT = "http://example.com/" + RELATIVE_DIR; 1.31 +const TESTROOT2 = "http://example.org/" + RELATIVE_DIR; 1.32 +const CHROMEROOT = pathParts.join("/") + "/"; 1.33 +const PREF_DISCOVERURL = "extensions.webservice.discoverURL"; 1.34 +const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane"; 1.35 +const PREF_XPI_ENABLED = "xpinstall.enabled"; 1.36 +const PREF_UPDATEURL = "extensions.update.url"; 1.37 +const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled"; 1.38 + 1.39 +const MANAGER_URI = "about:addons"; 1.40 +const INSTALL_URI = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; 1.41 +const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; 1.42 +const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults"; 1.43 +const PREF_STRICT_COMPAT = "extensions.strictCompatibility"; 1.44 + 1.45 +var PREF_CHECK_COMPATIBILITY; 1.46 +(function() { 1.47 + var channel = "default"; 1.48 + try { 1.49 + channel = Services.prefs.getCharPref("app.update.channel"); 1.50 + } catch (e) { } 1.51 + if (channel != "aurora" && 1.52 + channel != "beta" && 1.53 + channel != "release") { 1.54 + var version = "nightly"; 1.55 + } else { 1.56 + version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1"); 1.57 + } 1.58 + PREF_CHECK_COMPATIBILITY = "extensions.checkCompatibility." + version; 1.59 +})(); 1.60 + 1.61 +var gPendingTests = []; 1.62 +var gTestsRun = 0; 1.63 +var gTestStart = null; 1.64 + 1.65 +var gUseInContentUI = !gTestInWindow && ("switchToTabHavingURI" in window); 1.66 + 1.67 +var gRestorePrefs = [{name: PREF_LOGGING_ENABLED}, 1.68 + {name: "extensions.webservice.discoverURL"}, 1.69 + {name: "extensions.update.url"}, 1.70 + {name: "extensions.update.background.url"}, 1.71 + {name: "extensions.update.enabled"}, 1.72 + {name: "extensions.update.autoUpdateDefault"}, 1.73 + {name: "extensions.getAddons.get.url"}, 1.74 + {name: "extensions.getAddons.getWithPerformance.url"}, 1.75 + {name: "extensions.getAddons.search.browseURL"}, 1.76 + {name: "extensions.getAddons.search.url"}, 1.77 + {name: "extensions.getAddons.cache.enabled"}, 1.78 + {name: "devtools.chrome.enabled"}, 1.79 + {name: "devtools.debugger.remote-enabled"}, 1.80 + {name: PREF_SEARCH_MAXRESULTS}, 1.81 + {name: PREF_STRICT_COMPAT}, 1.82 + {name: PREF_CHECK_COMPATIBILITY}]; 1.83 + 1.84 +for (let pref of gRestorePrefs) { 1.85 + if (!Services.prefs.prefHasUserValue(pref.name)) { 1.86 + pref.type = "clear"; 1.87 + continue; 1.88 + } 1.89 + pref.type = Services.prefs.getPrefType(pref.name); 1.90 + if (pref.type == Services.prefs.PREF_BOOL) 1.91 + pref.value = Services.prefs.getBoolPref(pref.name); 1.92 + else if (pref.type == Services.prefs.PREF_INT) 1.93 + pref.value = Services.prefs.getIntPref(pref.name); 1.94 + else if (pref.type == Services.prefs.PREF_STRING) 1.95 + pref.value = Services.prefs.getCharPref(pref.name); 1.96 +} 1.97 + 1.98 +// Turn logging on for all tests 1.99 +Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true); 1.100 + 1.101 +// Helper to register test failures and close windows if any are left open 1.102 +function checkOpenWindows(aWindowID) { 1.103 + let windows = Services.wm.getEnumerator(aWindowID); 1.104 + let found = false; 1.105 + while (windows.hasMoreElements()) { 1.106 + let win = windows.getNext().QueryInterface(Ci.nsIDOMWindow); 1.107 + if (!win.closed) { 1.108 + found = true; 1.109 + win.close(); 1.110 + } 1.111 + } 1.112 + if (found) 1.113 + ok(false, "Found unexpected " + aWindowID + " window still open"); 1.114 +} 1.115 + 1.116 +registerCleanupFunction(function() { 1.117 + // Restore prefs 1.118 + for (let pref of gRestorePrefs) { 1.119 + if (pref.type == "clear") 1.120 + Services.prefs.clearUserPref(pref.name); 1.121 + else if (pref.type == Services.prefs.PREF_BOOL) 1.122 + Services.prefs.setBoolPref(pref.name, pref.value); 1.123 + else if (pref.type == Services.prefs.PREF_INT) 1.124 + Services.prefs.setIntPref(pref.name, pref.value); 1.125 + else if (pref.type == Services.prefs.PREF_STRING) 1.126 + Services.prefs.setCharPref(pref.name, pref.value); 1.127 + } 1.128 + 1.129 + // Throw an error if the add-ons manager window is open anywhere 1.130 + checkOpenWindows("Addons:Manager"); 1.131 + checkOpenWindows("Addons:Compatibility"); 1.132 + checkOpenWindows("Addons:Install"); 1.133 + 1.134 + return new Promise((resolve, reject) => AddonManager.getAllInstalls(resolve)) 1.135 + .then(aInstalls => { 1.136 + for (let install of aInstalls) { 1.137 + if (install instanceof MockInstall) 1.138 + continue; 1.139 + 1.140 + ok(false, "Should not have seen an install of " + install.sourceURI.spec + " in state " + install.state); 1.141 + install.cancel(); 1.142 + } 1.143 + }); 1.144 +}); 1.145 + 1.146 +function log_exceptions(aCallback, ...aArgs) { 1.147 + try { 1.148 + return aCallback.apply(null, aArgs); 1.149 + } 1.150 + catch (e) { 1.151 + info("Exception thrown: " + e); 1.152 + throw e; 1.153 + } 1.154 +} 1.155 + 1.156 +function log_callback(aPromise, aCallback) { 1.157 + aPromise.then(aCallback) 1.158 + .then(null, e => info("Exception thrown: " + e)); 1.159 + return aPromise; 1.160 +} 1.161 + 1.162 +function add_test(test) { 1.163 + gPendingTests.push(test); 1.164 +} 1.165 + 1.166 +function run_next_test() { 1.167 + if (gTestsRun > 0) 1.168 + info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms"); 1.169 + 1.170 + if (gPendingTests.length == 0) { 1.171 + end_test(); 1.172 + return; 1.173 + } 1.174 + 1.175 + gTestsRun++; 1.176 + var test = gPendingTests.shift(); 1.177 + if (test.name) 1.178 + info("Running test " + gTestsRun + " (" + test.name + ")"); 1.179 + else 1.180 + info("Running test " + gTestsRun); 1.181 + 1.182 + gTestStart = Date.now(); 1.183 + log_exceptions(test); 1.184 +} 1.185 + 1.186 +function get_addon_file_url(aFilename) { 1.187 + try { 1.188 + var cr = Cc["@mozilla.org/chrome/chrome-registry;1"]. 1.189 + getService(Ci.nsIChromeRegistry); 1.190 + var fileurl = cr.convertChromeURL(makeURI(CHROMEROOT + "addons/" + aFilename)); 1.191 + return fileurl.QueryInterface(Ci.nsIFileURL); 1.192 + } catch(ex) { 1.193 + var jar = getJar(CHROMEROOT + "addons/" + aFilename); 1.194 + var tmpDir = extractJarToTmp(jar); 1.195 + tmpDir.append(aFilename); 1.196 + 1.197 + return Services.io.newFileURI(tmpDir).QueryInterface(Ci.nsIFileURL); 1.198 + } 1.199 +} 1.200 + 1.201 +function get_test_items_in_list(aManager) { 1.202 + var tests = "@tests.mozilla.org"; 1.203 + 1.204 + let view = aManager.document.getElementById("view-port").selectedPanel; 1.205 + let listid = view.id == "search-view" ? "search-list" : "addon-list"; 1.206 + let item = aManager.document.getElementById(listid).firstChild; 1.207 + let items = []; 1.208 + 1.209 + while (item) { 1.210 + if (item.localName != "richlistitem") { 1.211 + item = item.nextSibling; 1.212 + continue; 1.213 + } 1.214 + 1.215 + if (!item.mAddon || item.mAddon.id.substring(item.mAddon.id.length - tests.length) == tests) 1.216 + items.push(item); 1.217 + item = item.nextSibling; 1.218 + } 1.219 + 1.220 + return items; 1.221 +} 1.222 + 1.223 +function check_all_in_list(aManager, aIds, aIgnoreExtras) { 1.224 + var doc = aManager.document; 1.225 + var view = doc.getElementById("view-port").selectedPanel; 1.226 + var listid = view.id == "search-view" ? "search-list" : "addon-list"; 1.227 + var list = doc.getElementById(listid); 1.228 + 1.229 + var inlist = []; 1.230 + var node = list.firstChild; 1.231 + while (node) { 1.232 + if (node.value) 1.233 + inlist.push(node.value); 1.234 + node = node.nextSibling; 1.235 + } 1.236 + 1.237 + for (let id of aIds) { 1.238 + if (inlist.indexOf(id) == -1) 1.239 + ok(false, "Should find " + id + " in the list"); 1.240 + } 1.241 + 1.242 + if (aIgnoreExtras) 1.243 + return; 1.244 + 1.245 + for (let inlistItem of inlist) { 1.246 + if (aIds.indexOf(inlistItem) == -1) 1.247 + ok(false, "Shouldn't have seen " + inlistItem + " in the list"); 1.248 + } 1.249 +} 1.250 + 1.251 +function get_addon_element(aManager, aId) { 1.252 + var doc = aManager.document; 1.253 + var view = doc.getElementById("view-port").selectedPanel; 1.254 + var listid = "addon-list"; 1.255 + if (view.id == "search-view") 1.256 + listid = "search-list"; 1.257 + else if (view.id == "updates-view") 1.258 + listid = "updates-list"; 1.259 + var list = doc.getElementById(listid); 1.260 + 1.261 + var node = list.firstChild; 1.262 + while (node) { 1.263 + if (node.value == aId) 1.264 + return node; 1.265 + node = node.nextSibling; 1.266 + } 1.267 + return null; 1.268 +} 1.269 + 1.270 +function wait_for_view_load(aManagerWindow, aCallback, aForceWait, aLongerTimeout) { 1.271 + requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2); 1.272 + 1.273 + if (!aForceWait && !aManagerWindow.gViewController.isLoading) { 1.274 + log_exceptions(aCallback, aManagerWindow); 1.275 + return; 1.276 + } 1.277 + 1.278 + aManagerWindow.document.addEventListener("ViewChanged", function() { 1.279 + aManagerWindow.document.removeEventListener("ViewChanged", arguments.callee, false); 1.280 + log_exceptions(aCallback, aManagerWindow); 1.281 + }, false); 1.282 +} 1.283 + 1.284 +function wait_for_manager_load(aManagerWindow, aCallback) { 1.285 + if (!aManagerWindow.gIsInitializing) { 1.286 + log_exceptions(aCallback, aManagerWindow); 1.287 + return; 1.288 + } 1.289 + 1.290 + info("Waiting for initialization"); 1.291 + aManagerWindow.document.addEventListener("Initialized", function() { 1.292 + aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false); 1.293 + log_exceptions(aCallback, aManagerWindow); 1.294 + }, false); 1.295 +} 1.296 + 1.297 +function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { 1.298 + let p = new Promise((resolve, reject) => { 1.299 + 1.300 + function setup_manager(aManagerWindow) { 1.301 + if (aLoadCallback) 1.302 + log_exceptions(aLoadCallback, aManagerWindow); 1.303 + 1.304 + if (aView) 1.305 + aManagerWindow.loadView(aView); 1.306 + 1.307 + ok(aManagerWindow != null, "Should have an add-ons manager window"); 1.308 + is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI"); 1.309 + 1.310 + waitForFocus(function() { 1.311 + info("window has focus, waiting for manager load"); 1.312 + wait_for_manager_load(aManagerWindow, function() { 1.313 + info("Manager waiting for view load"); 1.314 + wait_for_view_load(aManagerWindow, function() { 1.315 + resolve(aManagerWindow); 1.316 + }, null, aLongerTimeout); 1.317 + }); 1.318 + }, aManagerWindow); 1.319 + } 1.320 + 1.321 + if (gUseInContentUI) { 1.322 + info("Loading manager window in tab"); 1.323 + Services.obs.addObserver(function (aSubject, aTopic, aData) { 1.324 + Services.obs.removeObserver(arguments.callee, aTopic); 1.325 + if (aSubject.location.href != MANAGER_URI) { 1.326 + info("Ignoring load event for " + aSubject.location.href); 1.327 + return; 1.328 + } 1.329 + setup_manager(aSubject); 1.330 + }, "EM-loaded", false); 1.331 + 1.332 + gBrowser.selectedTab = gBrowser.addTab(); 1.333 + switchToTabHavingURI(MANAGER_URI, true); 1.334 + } else { 1.335 + info("Loading manager window in dialog"); 1.336 + Services.obs.addObserver(function (aSubject, aTopic, aData) { 1.337 + Services.obs.removeObserver(arguments.callee, aTopic); 1.338 + setup_manager(aSubject); 1.339 + }, "EM-loaded", false); 1.340 + 1.341 + openDialog(MANAGER_URI); 1.342 + } 1.343 + }); 1.344 + 1.345 + // The promise resolves with the manager window, so it is passed to the callback 1.346 + return log_callback(p, aCallback); 1.347 +} 1.348 + 1.349 +function close_manager(aManagerWindow, aCallback, aLongerTimeout) { 1.350 + let p = new Promise((resolve, reject) => { 1.351 + requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2); 1.352 + 1.353 + ok(aManagerWindow != null, "Should have an add-ons manager window to close"); 1.354 + is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI"); 1.355 + 1.356 + aManagerWindow.addEventListener("unload", function() { 1.357 + try { 1.358 + dump("Manager window unload handler"); 1.359 + this.removeEventListener("unload", arguments.callee, false); 1.360 + resolve(); 1.361 + } catch(e) { 1.362 + reject(e); 1.363 + } 1.364 + }, false); 1.365 + }); 1.366 + 1.367 + info("Telling manager window to close"); 1.368 + aManagerWindow.close(); 1.369 + info("Manager window close() call returned"); 1.370 + 1.371 + return log_callback(p, aCallback); 1.372 +} 1.373 + 1.374 +function restart_manager(aManagerWindow, aView, aCallback, aLoadCallback) { 1.375 + if (!aManagerWindow) { 1.376 + return open_manager(aView, aCallback, aLoadCallback); 1.377 + } 1.378 + 1.379 + return close_manager(aManagerWindow) 1.380 + .then(() => open_manager(aView, aCallback, aLoadCallback)); 1.381 +} 1.382 + 1.383 +function wait_for_window_open(aCallback) { 1.384 + Services.wm.addListener({ 1.385 + onOpenWindow: function(aWindow) { 1.386 + Services.wm.removeListener(this); 1.387 + 1.388 + let domwindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.389 + .getInterface(Ci.nsIDOMWindow); 1.390 + domwindow.addEventListener("load", function() { 1.391 + domwindow.removeEventListener("load", arguments.callee, false); 1.392 + executeSoon(function() { 1.393 + aCallback(domwindow); 1.394 + }); 1.395 + }, false); 1.396 + }, 1.397 + 1.398 + onCloseWindow: function(aWindow) { 1.399 + }, 1.400 + 1.401 + onWindowTitleChange: function(aWindow, aTitle) { 1.402 + } 1.403 + }); 1.404 +} 1.405 + 1.406 +function get_string(aName, ...aArgs) { 1.407 + var bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/extensions.properties"); 1.408 + if (aArgs.length == 0) 1.409 + return bundle.GetStringFromName(aName); 1.410 + return bundle.formatStringFromName(aName, aArgs, aArgs.length); 1.411 +} 1.412 + 1.413 +function formatDate(aDate) { 1.414 + return Cc["@mozilla.org/intl/scriptabledateformat;1"] 1.415 + .getService(Ci.nsIScriptableDateFormat) 1.416 + .FormatDate("", 1.417 + Ci.nsIScriptableDateFormat.dateFormatLong, 1.418 + aDate.getFullYear(), 1.419 + aDate.getMonth() + 1, 1.420 + aDate.getDate() 1.421 + ); 1.422 +} 1.423 + 1.424 +function is_hidden(aElement) { 1.425 + var style = aElement.ownerDocument.defaultView.getComputedStyle(aElement, ""); 1.426 + if (style.display == "none") 1.427 + return true; 1.428 + if (style.visibility != "visible") 1.429 + return true; 1.430 + 1.431 + // Hiding a parent element will hide all its children 1.432 + if (aElement.parentNode != aElement.ownerDocument) 1.433 + return is_hidden(aElement.parentNode); 1.434 + 1.435 + return false; 1.436 +} 1.437 + 1.438 +function is_element_visible(aElement, aMsg) { 1.439 + isnot(aElement, null, "Element should not be null, when checking visibility"); 1.440 + ok(!is_hidden(aElement), aMsg); 1.441 +} 1.442 + 1.443 +function is_element_hidden(aElement, aMsg) { 1.444 + isnot(aElement, null, "Element should not be null, when checking visibility"); 1.445 + ok(is_hidden(aElement), aMsg); 1.446 +} 1.447 + 1.448 +/** 1.449 + * Install an add-on and call a callback when complete. 1.450 + * 1.451 + * The callback will receive the Addon for the installed add-on. 1.452 + */ 1.453 +function install_addon(path, cb, pathPrefix=TESTROOT) { 1.454 + let p = new Promise((resolve, reject) => { 1.455 + AddonManager.getInstallForURL(pathPrefix + path, (install) => { 1.456 + install.addListener({ 1.457 + onInstallEnded: () => resolve(install.addon), 1.458 + }); 1.459 + 1.460 + install.install(); 1.461 + }, "application/x-xpinstall"); 1.462 + }); 1.463 + 1.464 + return log_callback(p, cb); 1.465 +} 1.466 + 1.467 +function CategoryUtilities(aManagerWindow) { 1.468 + this.window = aManagerWindow; 1.469 + 1.470 + var self = this; 1.471 + this.window.addEventListener("unload", function() { 1.472 + self.window.removeEventListener("unload", arguments.callee, false); 1.473 + self.window = null; 1.474 + }, false); 1.475 +} 1.476 + 1.477 +CategoryUtilities.prototype = { 1.478 + window: null, 1.479 + 1.480 + get selectedCategory() { 1.481 + isnot(this.window, null, "Should not get selected category when manager window is not loaded"); 1.482 + var selectedItem = this.window.document.getElementById("categories").selectedItem; 1.483 + isnot(selectedItem, null, "A category should be selected"); 1.484 + var view = this.window.gViewController.parseViewId(selectedItem.value); 1.485 + return (view.type == "list") ? view.param : view.type; 1.486 + }, 1.487 + 1.488 + get: function(aCategoryType, aAllowMissing) { 1.489 + isnot(this.window, null, "Should not get category when manager window is not loaded"); 1.490 + var categories = this.window.document.getElementById("categories"); 1.491 + 1.492 + var viewId = "addons://list/" + aCategoryType; 1.493 + var items = categories.getElementsByAttribute("value", viewId); 1.494 + if (items.length) 1.495 + return items[0]; 1.496 + 1.497 + viewId = "addons://" + aCategoryType + "/"; 1.498 + items = categories.getElementsByAttribute("value", viewId); 1.499 + if (items.length) 1.500 + return items[0]; 1.501 + 1.502 + if (!aAllowMissing) 1.503 + ok(false, "Should have found a category with type " + aCategoryType); 1.504 + return null; 1.505 + }, 1.506 + 1.507 + getViewId: function(aCategoryType) { 1.508 + isnot(this.window, null, "Should not get view id when manager window is not loaded"); 1.509 + return this.get(aCategoryType).value; 1.510 + }, 1.511 + 1.512 + isVisible: function(aCategory) { 1.513 + isnot(this.window, null, "Should not check visible state when manager window is not loaded"); 1.514 + if (aCategory.hasAttribute("disabled") && 1.515 + aCategory.getAttribute("disabled") == "true") 1.516 + return false; 1.517 + 1.518 + return !is_hidden(aCategory); 1.519 + }, 1.520 + 1.521 + isTypeVisible: function(aCategoryType) { 1.522 + return this.isVisible(this.get(aCategoryType)); 1.523 + }, 1.524 + 1.525 + open: function(aCategory, aCallback) { 1.526 + 1.527 + isnot(this.window, null, "Should not open category when manager window is not loaded"); 1.528 + ok(this.isVisible(aCategory), "Category should be visible if attempting to open it"); 1.529 + 1.530 + EventUtils.synthesizeMouse(aCategory, 2, 2, { }, this.window); 1.531 + let p = new Promise((resolve, reject) => wait_for_view_load(this.window, resolve)); 1.532 + 1.533 + return log_callback(p, aCallback); 1.534 + }, 1.535 + 1.536 + openType: function(aCategoryType, aCallback) { 1.537 + return this.open(this.get(aCategoryType), aCallback); 1.538 + } 1.539 +} 1.540 + 1.541 +function CertOverrideListener(host, bits) { 1.542 + this.host = host; 1.543 + this.bits = bits; 1.544 +} 1.545 + 1.546 +CertOverrideListener.prototype = { 1.547 + host: null, 1.548 + bits: null, 1.549 + 1.550 + getInterface: function (aIID) { 1.551 + return this.QueryInterface(aIID); 1.552 + }, 1.553 + 1.554 + QueryInterface: function(aIID) { 1.555 + if (aIID.equals(Ci.nsIBadCertListener2) || 1.556 + aIID.equals(Ci.nsIInterfaceRequestor) || 1.557 + aIID.equals(Ci.nsISupports)) 1.558 + return this; 1.559 + 1.560 + throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE); 1.561 + }, 1.562 + 1.563 + notifyCertProblem: function (socketInfo, sslStatus, targetHost) { 1.564 + var cert = sslStatus.QueryInterface(Components.interfaces.nsISSLStatus) 1.565 + .serverCert; 1.566 + var cos = Cc["@mozilla.org/security/certoverride;1"]. 1.567 + getService(Ci.nsICertOverrideService); 1.568 + cos.rememberValidityOverride(this.host, -1, cert, this.bits, false); 1.569 + return true; 1.570 + } 1.571 +} 1.572 + 1.573 +// Add overrides for the bad certificates 1.574 +function addCertOverride(host, bits) { 1.575 + var req = new XMLHttpRequest(); 1.576 + try { 1.577 + req.open("GET", "https://" + host + "/", false); 1.578 + req.channel.notificationCallbacks = new CertOverrideListener(host, bits); 1.579 + req.send(null); 1.580 + } 1.581 + catch (e) { 1.582 + // This request will fail since the SSL server is not trusted yet 1.583 + } 1.584 +} 1.585 + 1.586 +/***** Mock Provider *****/ 1.587 + 1.588 +function MockProvider(aUseAsyncCallbacks, aTypes) { 1.589 + this.addons = []; 1.590 + this.installs = []; 1.591 + this.callbackTimers = []; 1.592 + this.timerLocations = new Map(); 1.593 + this.useAsyncCallbacks = (aUseAsyncCallbacks === undefined) ? true : aUseAsyncCallbacks; 1.594 + this.types = (aTypes === undefined) ? [{ 1.595 + id: "extension", 1.596 + name: "Extensions", 1.597 + uiPriority: 4000, 1.598 + flags: AddonManager.TYPE_UI_VIEW_LIST 1.599 + }] : aTypes; 1.600 + 1.601 + var self = this; 1.602 + registerCleanupFunction(function() { 1.603 + if (self.started) 1.604 + self.unregister(); 1.605 + }); 1.606 + 1.607 + this.register(); 1.608 +} 1.609 + 1.610 +MockProvider.prototype = { 1.611 + addons: null, 1.612 + installs: null, 1.613 + started: null, 1.614 + apiDelay: 10, 1.615 + callbackTimers: null, 1.616 + timerLocations: null, 1.617 + useAsyncCallbacks: null, 1.618 + types: null, 1.619 + 1.620 + /***** Utility functions *****/ 1.621 + 1.622 + /** 1.623 + * Register this provider with the AddonManager 1.624 + */ 1.625 + register: function MP_register() { 1.626 + AddonManagerPrivate.registerProvider(this, this.types); 1.627 + }, 1.628 + 1.629 + /** 1.630 + * Unregister this provider with the AddonManager 1.631 + */ 1.632 + unregister: function MP_unregister() { 1.633 + AddonManagerPrivate.unregisterProvider(this); 1.634 + }, 1.635 + 1.636 + /** 1.637 + * Adds an add-on to the list of add-ons that this provider exposes to the 1.638 + * AddonManager, dispatching appropriate events in the process. 1.639 + * 1.640 + * @param aAddon 1.641 + * The add-on to add 1.642 + */ 1.643 + addAddon: function MP_addAddon(aAddon) { 1.644 + var oldAddons = this.addons.filter(function(aOldAddon) aOldAddon.id == aAddon.id); 1.645 + var oldAddon = oldAddons.length > 0 ? oldAddons[0] : null; 1.646 + 1.647 + this.addons = this.addons.filter(function(aOldAddon) aOldAddon.id != aAddon.id); 1.648 + 1.649 + this.addons.push(aAddon); 1.650 + aAddon._provider = this; 1.651 + 1.652 + if (!this.started) 1.653 + return; 1.654 + 1.655 + let requiresRestart = (aAddon.operationsRequiringRestart & 1.656 + AddonManager.OP_NEEDS_RESTART_INSTALL) != 0; 1.657 + AddonManagerPrivate.callInstallListeners("onExternalInstall", null, aAddon, 1.658 + oldAddon, requiresRestart) 1.659 + }, 1.660 + 1.661 + /** 1.662 + * Removes an add-on from the list of add-ons that this provider exposes to 1.663 + * the AddonManager, dispatching the onUninstalled event in the process. 1.664 + * 1.665 + * @param aAddon 1.666 + * The add-on to add 1.667 + */ 1.668 + removeAddon: function MP_removeAddon(aAddon) { 1.669 + var pos = this.addons.indexOf(aAddon); 1.670 + if (pos == -1) { 1.671 + ok(false, "Tried to remove an add-on that wasn't registered with the mock provider"); 1.672 + return; 1.673 + } 1.674 + 1.675 + this.addons.splice(pos, 1); 1.676 + 1.677 + if (!this.started) 1.678 + return; 1.679 + 1.680 + AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon); 1.681 + }, 1.682 + 1.683 + /** 1.684 + * Adds an add-on install to the list of installs that this provider exposes 1.685 + * to the AddonManager, dispatching appropriate events in the process. 1.686 + * 1.687 + * @param aInstall 1.688 + * The add-on install to add 1.689 + */ 1.690 + addInstall: function MP_addInstall(aInstall) { 1.691 + this.installs.push(aInstall); 1.692 + aInstall._provider = this; 1.693 + 1.694 + if (!this.started) 1.695 + return; 1.696 + 1.697 + aInstall.callListeners("onNewInstall"); 1.698 + }, 1.699 + 1.700 + removeInstall: function MP_removeInstall(aInstall) { 1.701 + var pos = this.installs.indexOf(aInstall); 1.702 + if (pos == -1) { 1.703 + ok(false, "Tried to remove an install that wasn't registered with the mock provider"); 1.704 + return; 1.705 + } 1.706 + 1.707 + this.installs.splice(pos, 1); 1.708 + }, 1.709 + 1.710 + /** 1.711 + * Creates a set of mock add-on objects and adds them to the list of add-ons 1.712 + * managed by this provider. 1.713 + * 1.714 + * @param aAddonProperties 1.715 + * An array of objects containing properties describing the add-ons 1.716 + * @return Array of the new MockAddons 1.717 + */ 1.718 + createAddons: function MP_createAddons(aAddonProperties) { 1.719 + var newAddons = []; 1.720 + for (let addonProp of aAddonProperties) { 1.721 + let addon = new MockAddon(addonProp.id); 1.722 + for (let prop in addonProp) { 1.723 + if (prop == "id") 1.724 + continue; 1.725 + if (prop == "applyBackgroundUpdates") { 1.726 + addon._applyBackgroundUpdates = addonProp[prop]; 1.727 + continue; 1.728 + } 1.729 + if (prop == "appDisabled") { 1.730 + addon._appDisabled = addonProp[prop]; 1.731 + continue; 1.732 + } 1.733 + addon[prop] = addonProp[prop]; 1.734 + } 1.735 + if (!addon.optionsType && !!addon.optionsURL) 1.736 + addon.optionsType = AddonManager.OPTIONS_TYPE_DIALOG; 1.737 + 1.738 + // Make sure the active state matches the passed in properties 1.739 + addon.isActive = addon.shouldBeActive; 1.740 + 1.741 + this.addAddon(addon); 1.742 + newAddons.push(addon); 1.743 + } 1.744 + 1.745 + return newAddons; 1.746 + }, 1.747 + 1.748 + /** 1.749 + * Creates a set of mock add-on install objects and adds them to the list 1.750 + * of installs managed by this provider. 1.751 + * 1.752 + * @param aInstallProperties 1.753 + * An array of objects containing properties describing the installs 1.754 + * @return Array of the new MockInstalls 1.755 + */ 1.756 + createInstalls: function MP_createInstalls(aInstallProperties) { 1.757 + var newInstalls = []; 1.758 + for (let installProp of aInstallProperties) { 1.759 + let install = new MockInstall(installProp.name || null, 1.760 + installProp.type || null, 1.761 + null); 1.762 + for (let prop in installProp) { 1.763 + switch (prop) { 1.764 + case "name": 1.765 + case "type": 1.766 + break; 1.767 + case "sourceURI": 1.768 + install[prop] = NetUtil.newURI(installProp[prop]); 1.769 + break; 1.770 + default: 1.771 + install[prop] = installProp[prop]; 1.772 + } 1.773 + } 1.774 + this.addInstall(install); 1.775 + newInstalls.push(install); 1.776 + } 1.777 + 1.778 + return newInstalls; 1.779 + }, 1.780 + 1.781 + /***** AddonProvider implementation *****/ 1.782 + 1.783 + /** 1.784 + * Called to initialize the provider. 1.785 + */ 1.786 + startup: function MP_startup() { 1.787 + this.started = true; 1.788 + }, 1.789 + 1.790 + /** 1.791 + * Called when the provider should shutdown. 1.792 + */ 1.793 + shutdown: function MP_shutdown() { 1.794 + if (this.callbackTimers.length) { 1.795 + info("MockProvider: pending callbacks at shutdown(): calling immediately"); 1.796 + } 1.797 + while (this.callbackTimers.length > 0) { 1.798 + // When we notify the callback timer, it removes itself from our array 1.799 + let timer = this.callbackTimers[0]; 1.800 + try { 1.801 + let setAt = this.timerLocations.get(timer); 1.802 + info("Notifying timer set at " + (setAt || "unknown location")); 1.803 + timer.callback.notify(timer); 1.804 + timer.cancel(); 1.805 + } catch(e) { 1.806 + info("Timer notify failed: " + e); 1.807 + } 1.808 + } 1.809 + this.callbackTimers = []; 1.810 + this.timerLocations = null; 1.811 + 1.812 + this.started = false; 1.813 + }, 1.814 + 1.815 + /** 1.816 + * Called to get an Addon with a particular ID. 1.817 + * 1.818 + * @param aId 1.819 + * The ID of the add-on to retrieve 1.820 + * @param aCallback 1.821 + * A callback to pass the Addon to 1.822 + */ 1.823 + getAddonByID: function MP_getAddon(aId, aCallback) { 1.824 + for (let addon of this.addons) { 1.825 + if (addon.id == aId) { 1.826 + this._delayCallback(aCallback, addon); 1.827 + return; 1.828 + } 1.829 + } 1.830 + 1.831 + aCallback(null); 1.832 + }, 1.833 + 1.834 + /** 1.835 + * Called to get Addons of a particular type. 1.836 + * 1.837 + * @param aTypes 1.838 + * An array of types to fetch. Can be null to get all types. 1.839 + * @param callback 1.840 + * A callback to pass an array of Addons to 1.841 + */ 1.842 + getAddonsByTypes: function MP_getAddonsByTypes(aTypes, aCallback) { 1.843 + var addons = this.addons.filter(function(aAddon) { 1.844 + if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1) 1.845 + return false; 1.846 + return true; 1.847 + }); 1.848 + this._delayCallback(aCallback, addons); 1.849 + }, 1.850 + 1.851 + /** 1.852 + * Called to get Addons that have pending operations. 1.853 + * 1.854 + * @param aTypes 1.855 + * An array of types to fetch. Can be null to get all types 1.856 + * @param aCallback 1.857 + * A callback to pass an array of Addons to 1.858 + */ 1.859 + getAddonsWithOperationsByTypes: function MP_getAddonsWithOperationsByTypes(aTypes, aCallback) { 1.860 + var addons = this.addons.filter(function(aAddon) { 1.861 + if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1) 1.862 + return false; 1.863 + return aAddon.pendingOperations != 0; 1.864 + }); 1.865 + this._delayCallback(aCallback, addons); 1.866 + }, 1.867 + 1.868 + /** 1.869 + * Called to get the current AddonInstalls, optionally restricting by type. 1.870 + * 1.871 + * @param aTypes 1.872 + * An array of types or null to get all types 1.873 + * @param aCallback 1.874 + * A callback to pass the array of AddonInstalls to 1.875 + */ 1.876 + getInstallsByTypes: function MP_getInstallsByTypes(aTypes, aCallback) { 1.877 + var installs = this.installs.filter(function(aInstall) { 1.878 + // Appear to have actually removed cancelled installs from the provider 1.879 + if (aInstall.state == AddonManager.STATE_CANCELLED) 1.880 + return false; 1.881 + 1.882 + if (aTypes && aTypes.length > 0 && aTypes.indexOf(aInstall.type) == -1) 1.883 + return false; 1.884 + 1.885 + return true; 1.886 + }); 1.887 + this._delayCallback(aCallback, installs); 1.888 + }, 1.889 + 1.890 + /** 1.891 + * Called when a new add-on has been enabled when only one add-on of that type 1.892 + * can be enabled. 1.893 + * 1.894 + * @param aId 1.895 + * The ID of the newly enabled add-on 1.896 + * @param aType 1.897 + * The type of the newly enabled add-on 1.898 + * @param aPendingRestart 1.899 + * true if the newly enabled add-on will only become enabled after a 1.900 + * restart 1.901 + */ 1.902 + addonChanged: function MP_addonChanged(aId, aType, aPendingRestart) { 1.903 + // Not implemented 1.904 + }, 1.905 + 1.906 + /** 1.907 + * Update the appDisabled property for all add-ons. 1.908 + */ 1.909 + updateAddonAppDisabledStates: function MP_updateAddonAppDisabledStates() { 1.910 + // Not needed 1.911 + }, 1.912 + 1.913 + /** 1.914 + * Called to get an AddonInstall to download and install an add-on from a URL. 1.915 + * 1.916 + * @param aUrl 1.917 + * The URL to be installed 1.918 + * @param aHash 1.919 + * A hash for the install 1.920 + * @param aName 1.921 + * A name for the install 1.922 + * @param aIconURL 1.923 + * An icon URL for the install 1.924 + * @param aVersion 1.925 + * A version for the install 1.926 + * @param aLoadGroup 1.927 + * An nsILoadGroup to associate requests with 1.928 + * @param aCallback 1.929 + * A callback to pass the AddonInstall to 1.930 + */ 1.931 + getInstallForURL: function MP_getInstallForURL(aUrl, aHash, aName, aIconURL, 1.932 + aVersion, aLoadGroup, aCallback) { 1.933 + // Not yet implemented 1.934 + }, 1.935 + 1.936 + /** 1.937 + * Called to get an AddonInstall to install an add-on from a local file. 1.938 + * 1.939 + * @param aFile 1.940 + * The file to be installed 1.941 + * @param aCallback 1.942 + * A callback to pass the AddonInstall to 1.943 + */ 1.944 + getInstallForFile: function MP_getInstallForFile(aFile, aCallback) { 1.945 + // Not yet implemented 1.946 + }, 1.947 + 1.948 + /** 1.949 + * Called to test whether installing add-ons is enabled. 1.950 + * 1.951 + * @return true if installing is enabled 1.952 + */ 1.953 + isInstallEnabled: function MP_isInstallEnabled() { 1.954 + return false; 1.955 + }, 1.956 + 1.957 + /** 1.958 + * Called to test whether this provider supports installing a particular 1.959 + * mimetype. 1.960 + * 1.961 + * @param aMimetype 1.962 + * The mimetype to check for 1.963 + * @return true if the mimetype is supported 1.964 + */ 1.965 + supportsMimetype: function MP_supportsMimetype(aMimetype) { 1.966 + return false; 1.967 + }, 1.968 + 1.969 + /** 1.970 + * Called to test whether installing add-ons from a URI is allowed. 1.971 + * 1.972 + * @param aUri 1.973 + * The URI being installed from 1.974 + * @return true if installing is allowed 1.975 + */ 1.976 + isInstallAllowed: function MP_isInstallAllowed(aUri) { 1.977 + return false; 1.978 + }, 1.979 + 1.980 + 1.981 + /***** Internal functions *****/ 1.982 + 1.983 + /** 1.984 + * Delay calling a callback to fake a time-consuming async operation. 1.985 + * The delay is specified by the apiDelay property, in milliseconds. 1.986 + * Parameters to send to the callback should be specified as arguments after 1.987 + * the aCallback argument. 1.988 + * 1.989 + * @param aCallback Callback to eventually call 1.990 + */ 1.991 + _delayCallback: function MP_delayCallback(aCallback, ...aArgs) { 1.992 + if (!this.useAsyncCallbacks) { 1.993 + aCallback(...aArgs); 1.994 + return; 1.995 + } 1.996 + 1.997 + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.998 + // Need to keep a reference to the timer, so it doesn't get GC'ed 1.999 + this.callbackTimers.push(timer); 1.1000 + // Capture a stack trace where the timer was set 1.1001 + // needs the 'new Error' hack until bug 1007656 1.1002 + this.timerLocations.set(timer, Log.stackTrace(new Error("dummy"))); 1.1003 + timer.initWithCallback(() => { 1.1004 + let idx = this.callbackTimers.indexOf(timer); 1.1005 + if (idx == -1) { 1.1006 + dump("MockProvider._delayCallback lost track of timer set at " 1.1007 + + (this.timerLocations.get(timer) || "unknown location") + "\n"); 1.1008 + } else { 1.1009 + this.callbackTimers.splice(idx, 1); 1.1010 + } 1.1011 + this.timerLocations.delete(timer); 1.1012 + aCallback(...aArgs); 1.1013 + }, this.apiDelay, timer.TYPE_ONE_SHOT); 1.1014 + } 1.1015 +}; 1.1016 + 1.1017 +/***** Mock Addon object for the Mock Provider *****/ 1.1018 + 1.1019 +function MockAddon(aId, aName, aType, aOperationsRequiringRestart) { 1.1020 + // Only set required attributes. 1.1021 + this.id = aId || ""; 1.1022 + this.name = aName || ""; 1.1023 + this.type = aType || "extension"; 1.1024 + this.version = ""; 1.1025 + this.isCompatible = true; 1.1026 + this.isDebuggable = false; 1.1027 + this.providesUpdatesSecurely = true; 1.1028 + this.blocklistState = 0; 1.1029 + this._appDisabled = false; 1.1030 + this._userDisabled = false; 1.1031 + this._applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE; 1.1032 + this.scope = AddonManager.SCOPE_PROFILE; 1.1033 + this.isActive = true; 1.1034 + this.creator = ""; 1.1035 + this.pendingOperations = 0; 1.1036 + this._permissions = AddonManager.PERM_CAN_UNINSTALL | 1.1037 + AddonManager.PERM_CAN_ENABLE | 1.1038 + AddonManager.PERM_CAN_DISABLE | 1.1039 + AddonManager.PERM_CAN_UPGRADE; 1.1040 + this.operationsRequiringRestart = aOperationsRequiringRestart || 1.1041 + (AddonManager.OP_NEEDS_RESTART_INSTALL | 1.1042 + AddonManager.OP_NEEDS_RESTART_UNINSTALL | 1.1043 + AddonManager.OP_NEEDS_RESTART_ENABLE | 1.1044 + AddonManager.OP_NEEDS_RESTART_DISABLE); 1.1045 +} 1.1046 + 1.1047 +MockAddon.prototype = { 1.1048 + get shouldBeActive() { 1.1049 + return !this.appDisabled && !this._userDisabled; 1.1050 + }, 1.1051 + 1.1052 + get appDisabled() { 1.1053 + return this._appDisabled; 1.1054 + }, 1.1055 + 1.1056 + set appDisabled(val) { 1.1057 + if (val == this._appDisabled) 1.1058 + return val; 1.1059 + 1.1060 + AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["appDisabled"]); 1.1061 + 1.1062 + var currentActive = this.shouldBeActive; 1.1063 + this._appDisabled = val; 1.1064 + var newActive = this.shouldBeActive; 1.1065 + this._updateActiveState(currentActive, newActive); 1.1066 + 1.1067 + return val; 1.1068 + }, 1.1069 + 1.1070 + get userDisabled() { 1.1071 + return this._userDisabled; 1.1072 + }, 1.1073 + 1.1074 + set userDisabled(val) { 1.1075 + if (val == this._userDisabled) 1.1076 + return val; 1.1077 + 1.1078 + var currentActive = this.shouldBeActive; 1.1079 + this._userDisabled = val; 1.1080 + var newActive = this.shouldBeActive; 1.1081 + this._updateActiveState(currentActive, newActive); 1.1082 + 1.1083 + return val; 1.1084 + }, 1.1085 + 1.1086 + get permissions() { 1.1087 + let permissions = this._permissions; 1.1088 + if (this.appDisabled || !this._userDisabled) 1.1089 + permissions &= ~AddonManager.PERM_CAN_ENABLE; 1.1090 + if (this.appDisabled || this._userDisabled) 1.1091 + permissions &= ~AddonManager.PERM_CAN_DISABLE; 1.1092 + return permissions; 1.1093 + }, 1.1094 + 1.1095 + set permissions(val) { 1.1096 + return this._permissions = val; 1.1097 + }, 1.1098 + 1.1099 + get applyBackgroundUpdates() { 1.1100 + return this._applyBackgroundUpdates; 1.1101 + }, 1.1102 + 1.1103 + set applyBackgroundUpdates(val) { 1.1104 + if (val != AddonManager.AUTOUPDATE_DEFAULT && 1.1105 + val != AddonManager.AUTOUPDATE_DISABLE && 1.1106 + val != AddonManager.AUTOUPDATE_ENABLE) { 1.1107 + ok(false, "addon.applyBackgroundUpdates set to an invalid value: " + val); 1.1108 + } 1.1109 + this._applyBackgroundUpdates = val; 1.1110 + AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]); 1.1111 + }, 1.1112 + 1.1113 + isCompatibleWith: function(aAppVersion, aPlatformVersion) { 1.1114 + return true; 1.1115 + }, 1.1116 + 1.1117 + findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) { 1.1118 + // Tests can implement this if they need to 1.1119 + }, 1.1120 + 1.1121 + uninstall: function() { 1.1122 + if (this.pendingOperations & AddonManager.PENDING_UNINSTALL) 1.1123 + throw Components.Exception("Add-on is already pending uninstall"); 1.1124 + 1.1125 + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL); 1.1126 + this.pendingOperations |= AddonManager.PENDING_UNINSTALL; 1.1127 + AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart); 1.1128 + if (!needsRestart) { 1.1129 + this.pendingOperations -= AddonManager.PENDING_UNINSTALL; 1.1130 + this._provider.removeAddon(this); 1.1131 + } 1.1132 + }, 1.1133 + 1.1134 + cancelUninstall: function() { 1.1135 + if (!(this.pendingOperations & AddonManager.PENDING_UNINSTALL)) 1.1136 + throw Components.Exception("Add-on is not pending uninstall"); 1.1137 + 1.1138 + this.pendingOperations -= AddonManager.PENDING_UNINSTALL; 1.1139 + AddonManagerPrivate.callAddonListeners("onOperationCancelled", this); 1.1140 + }, 1.1141 + 1.1142 + _updateActiveState: function(currentActive, newActive) { 1.1143 + if (currentActive == newActive) 1.1144 + return; 1.1145 + 1.1146 + if (newActive == this.isActive) { 1.1147 + this.pendingOperations -= (newActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE); 1.1148 + AddonManagerPrivate.callAddonListeners("onOperationCancelled", this); 1.1149 + } 1.1150 + else if (newActive) { 1.1151 + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE); 1.1152 + this.pendingOperations |= AddonManager.PENDING_ENABLE; 1.1153 + AddonManagerPrivate.callAddonListeners("onEnabling", this, needsRestart); 1.1154 + if (!needsRestart) { 1.1155 + this.isActive = newActive; 1.1156 + this.pendingOperations -= AddonManager.PENDING_ENABLE; 1.1157 + AddonManagerPrivate.callAddonListeners("onEnabled", this); 1.1158 + } 1.1159 + } 1.1160 + else { 1.1161 + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE); 1.1162 + this.pendingOperations |= AddonManager.PENDING_DISABLE; 1.1163 + AddonManagerPrivate.callAddonListeners("onDisabling", this, needsRestart); 1.1164 + if (!needsRestart) { 1.1165 + this.isActive = newActive; 1.1166 + this.pendingOperations -= AddonManager.PENDING_DISABLE; 1.1167 + AddonManagerPrivate.callAddonListeners("onDisabled", this); 1.1168 + } 1.1169 + } 1.1170 + } 1.1171 +}; 1.1172 + 1.1173 +/***** Mock AddonInstall object for the Mock Provider *****/ 1.1174 + 1.1175 +function MockInstall(aName, aType, aAddonToInstall) { 1.1176 + this.name = aName || ""; 1.1177 + // Don't expose type until download completed 1.1178 + this._type = aType || "extension"; 1.1179 + this.type = null; 1.1180 + this.version = "1.0"; 1.1181 + this.iconURL = ""; 1.1182 + this.infoURL = ""; 1.1183 + this.state = AddonManager.STATE_AVAILABLE; 1.1184 + this.error = 0; 1.1185 + this.sourceURI = null; 1.1186 + this.file = null; 1.1187 + this.progress = 0; 1.1188 + this.maxProgress = -1; 1.1189 + this.certificate = null; 1.1190 + this.certName = ""; 1.1191 + this.existingAddon = null; 1.1192 + this.addon = null; 1.1193 + this._addonToInstall = aAddonToInstall; 1.1194 + this.listeners = []; 1.1195 + 1.1196 + // Another type of install listener for tests that want to check the results 1.1197 + // of code run from standard install listeners 1.1198 + this.testListeners = []; 1.1199 +} 1.1200 + 1.1201 +MockInstall.prototype = { 1.1202 + install: function() { 1.1203 + switch (this.state) { 1.1204 + case AddonManager.STATE_AVAILABLE: 1.1205 + this.state = AddonManager.STATE_DOWNLOADING; 1.1206 + if (!this.callListeners("onDownloadStarted")) { 1.1207 + this.state = AddonManager.STATE_CANCELLED; 1.1208 + this.callListeners("onDownloadCancelled"); 1.1209 + return; 1.1210 + } 1.1211 + 1.1212 + this.type = this._type; 1.1213 + 1.1214 + // Adding addon to MockProvider to be implemented when needed 1.1215 + if (this._addonToInstall) 1.1216 + this.addon = this._addonToInstall; 1.1217 + else { 1.1218 + this.addon = new MockAddon("", this.name, this.type); 1.1219 + this.addon.version = this.version; 1.1220 + this.addon.pendingOperations = AddonManager.PENDING_INSTALL; 1.1221 + } 1.1222 + this.addon.install = this; 1.1223 + if (this.existingAddon) { 1.1224 + if (!this.addon.id) 1.1225 + this.addon.id = this.existingAddon.id; 1.1226 + this.existingAddon.pendingUpgrade = this.addon; 1.1227 + this.existingAddon.pendingOperations |= AddonManager.PENDING_UPGRADE; 1.1228 + } 1.1229 + 1.1230 + this.state = AddonManager.STATE_DOWNLOADED; 1.1231 + this.callListeners("onDownloadEnded"); 1.1232 + 1.1233 + case AddonManager.STATE_DOWNLOADED: 1.1234 + this.state = AddonManager.STATE_INSTALLING; 1.1235 + if (!this.callListeners("onInstallStarted")) { 1.1236 + this.state = AddonManager.STATE_CANCELLED; 1.1237 + this.callListeners("onInstallCancelled"); 1.1238 + return; 1.1239 + } 1.1240 + 1.1241 + AddonManagerPrivate.callAddonListeners("onInstalling", this.addon); 1.1242 + 1.1243 + this.state = AddonManager.STATE_INSTALLED; 1.1244 + this.callListeners("onInstallEnded"); 1.1245 + break; 1.1246 + case AddonManager.STATE_DOWNLOADING: 1.1247 + case AddonManager.STATE_CHECKING: 1.1248 + case AddonManger.STATE_INSTALLING: 1.1249 + // Installation is already running 1.1250 + return; 1.1251 + default: 1.1252 + ok(false, "Cannot start installing when state = " + this.state); 1.1253 + } 1.1254 + }, 1.1255 + 1.1256 + cancel: function() { 1.1257 + switch (this.state) { 1.1258 + case AddonManager.STATE_AVAILABLE: 1.1259 + this.state = AddonManager.STATE_CANCELLED; 1.1260 + break; 1.1261 + case AddonManager.STATE_INSTALLED: 1.1262 + this.state = AddonManager.STATE_CANCELLED; 1.1263 + this._provider.removeInstall(this); 1.1264 + this.callListeners("onInstallCancelled"); 1.1265 + break; 1.1266 + default: 1.1267 + // Handling cancelling when downloading to be implemented when needed 1.1268 + ok(false, "Cannot cancel when state = " + this.state); 1.1269 + } 1.1270 + }, 1.1271 + 1.1272 + 1.1273 + addListener: function(aListener) { 1.1274 + if (!this.listeners.some(function(i) i == aListener)) 1.1275 + this.listeners.push(aListener); 1.1276 + }, 1.1277 + 1.1278 + removeListener: function(aListener) { 1.1279 + this.listeners = this.listeners.filter(function(i) i != aListener); 1.1280 + }, 1.1281 + 1.1282 + addTestListener: function(aListener) { 1.1283 + if (!this.testListeners.some(function(i) i == aListener)) 1.1284 + this.testListeners.push(aListener); 1.1285 + }, 1.1286 + 1.1287 + removeTestListener: function(aListener) { 1.1288 + this.testListeners = this.testListeners.filter(function(i) i != aListener); 1.1289 + }, 1.1290 + 1.1291 + callListeners: function(aMethod) { 1.1292 + var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners, 1.1293 + this, this.addon); 1.1294 + 1.1295 + // Call test listeners after standard listeners to remove race condition 1.1296 + // between standard and test listeners 1.1297 + for (let listener of this.testListeners) { 1.1298 + try { 1.1299 + if (aMethod in listener) 1.1300 + if (listener[aMethod].call(listener, this, this.addon) === false) 1.1301 + result = false; 1.1302 + } 1.1303 + catch (e) { 1.1304 + ok(false, "Test listener threw exception: " + e); 1.1305 + } 1.1306 + } 1.1307 + 1.1308 + return result; 1.1309 + } 1.1310 +}; 1.1311 + 1.1312 +function waitForCondition(condition, nextTest, errorMsg) { 1.1313 + let tries = 0; 1.1314 + let interval = setInterval(function() { 1.1315 + if (tries >= 30) { 1.1316 + ok(false, errorMsg); 1.1317 + moveOn(); 1.1318 + } 1.1319 + var conditionPassed; 1.1320 + try { 1.1321 + conditionPassed = condition(); 1.1322 + } catch (e) { 1.1323 + ok(false, e + "\n" + e.stack); 1.1324 + conditionPassed = false; 1.1325 + } 1.1326 + if (conditionPassed) { 1.1327 + moveOn(); 1.1328 + } 1.1329 + tries++; 1.1330 + }, 100); 1.1331 + let moveOn = function() { clearInterval(interval); nextTest(); }; 1.1332 +} 1.1333 + 1.1334 +function getTestPluginTag() { 1.1335 + let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); 1.1336 + let tags = ph.getPluginTags(); 1.1337 + 1.1338 + // Find the test plugin 1.1339 + for (let i = 0; i < tags.length; i++) { 1.1340 + if (tags[i].name == "Test Plug-in") 1.1341 + return tags[i]; 1.1342 + } 1.1343 + ok(false, "Unable to find plugin"); 1.1344 + return null; 1.1345 +}