Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | const RELATIVE_DIR = "toolkit/mozapps/extensions/test/xpinstall/"; |
michael@0 | 2 | |
michael@0 | 3 | const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; |
michael@0 | 4 | const TESTROOT2 = "http://example.org/browser/" + RELATIVE_DIR; |
michael@0 | 5 | const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; |
michael@0 | 6 | const PROMPT_URL = "chrome://global/content/commonDialog.xul"; |
michael@0 | 7 | const ADDONS_URL = "chrome://mozapps/content/extensions/extensions.xul"; |
michael@0 | 8 | const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; |
michael@0 | 9 | const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; |
michael@0 | 10 | const CHROME_NAME = "mochikit"; |
michael@0 | 11 | |
michael@0 | 12 | function getChromeRoot(path) { |
michael@0 | 13 | if (path === undefined) { |
michael@0 | 14 | return "chrome://" + CHROME_NAME + "/content/browser/" + RELATIVE_DIR |
michael@0 | 15 | } |
michael@0 | 16 | return getRootDirectory(path); |
michael@0 | 17 | } |
michael@0 | 18 | |
michael@0 | 19 | function extractChromeRoot(path) { |
michael@0 | 20 | var chromeRootPath = getChromeRoot(path); |
michael@0 | 21 | var jar = getJar(chromeRootPath); |
michael@0 | 22 | if (jar) { |
michael@0 | 23 | var tmpdir = extractJarToTmp(jar); |
michael@0 | 24 | return "file://" + tmpdir.path + "/"; |
michael@0 | 25 | } |
michael@0 | 26 | return chromeRootPath; |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | /** |
michael@0 | 30 | * This is a test harness designed to handle responding to UI during the process |
michael@0 | 31 | * of installing an XPI. A test can set callbacks to hear about specific parts |
michael@0 | 32 | * of the sequence. |
michael@0 | 33 | * Before use setup must be called and finish must be called afterwards. |
michael@0 | 34 | */ |
michael@0 | 35 | var Harness = { |
michael@0 | 36 | // If set then the callback is called when an install is attempted and |
michael@0 | 37 | // software installation is disabled. |
michael@0 | 38 | installDisabledCallback: null, |
michael@0 | 39 | // If set then the callback is called when an install is attempted and |
michael@0 | 40 | // then canceled. |
michael@0 | 41 | installCancelledCallback: null, |
michael@0 | 42 | // If set then the callback will be called when an install is blocked by the |
michael@0 | 43 | // whitelist. The callback should return true to continue with the install |
michael@0 | 44 | // anyway. |
michael@0 | 45 | installBlockedCallback: null, |
michael@0 | 46 | // If set will be called in the event of authentication being needed to get |
michael@0 | 47 | // the xpi. Should return a 2 element array of username and password, or |
michael@0 | 48 | // null to not authenticate. |
michael@0 | 49 | authenticationCallback: null, |
michael@0 | 50 | // If set this will be called to allow checking the contents of the xpinstall |
michael@0 | 51 | // confirmation dialog. The callback should return true to continue the install. |
michael@0 | 52 | installConfirmCallback: null, |
michael@0 | 53 | // If set will be called when downloading of an item has begun. |
michael@0 | 54 | downloadStartedCallback: null, |
michael@0 | 55 | // If set will be called during the download of an item. |
michael@0 | 56 | downloadProgressCallback: null, |
michael@0 | 57 | // If set will be called when an xpi fails to download. |
michael@0 | 58 | downloadFailedCallback: null, |
michael@0 | 59 | // If set will be called when an xpi download is cancelled. |
michael@0 | 60 | downloadCancelledCallback: null, |
michael@0 | 61 | // If set will be called when downloading of an item has ended. |
michael@0 | 62 | downloadEndedCallback: null, |
michael@0 | 63 | // If set will be called when installation by the extension manager of an xpi |
michael@0 | 64 | // item starts |
michael@0 | 65 | installStartedCallback: null, |
michael@0 | 66 | // If set will be called when an xpi fails to install. |
michael@0 | 67 | installFailedCallback: null, |
michael@0 | 68 | // If set will be called when each xpi item to be installed completes |
michael@0 | 69 | // installation. |
michael@0 | 70 | installEndedCallback: null, |
michael@0 | 71 | // If set will be called when all triggered items are installed or the install |
michael@0 | 72 | // is canceled. |
michael@0 | 73 | installsCompletedCallback: null, |
michael@0 | 74 | |
michael@0 | 75 | pendingCount: null, |
michael@0 | 76 | installCount: null, |
michael@0 | 77 | runningInstalls: null, |
michael@0 | 78 | |
michael@0 | 79 | waitingForFinish: false, |
michael@0 | 80 | |
michael@0 | 81 | // Setup and tear down functions |
michael@0 | 82 | setup: function() { |
michael@0 | 83 | if (!this.waitingForFinish) { |
michael@0 | 84 | waitForExplicitFinish(); |
michael@0 | 85 | this.waitingForFinish = true; |
michael@0 | 86 | |
michael@0 | 87 | Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true); |
michael@0 | 88 | Services.obs.addObserver(this, "addon-install-started", false); |
michael@0 | 89 | Services.obs.addObserver(this, "addon-install-disabled", false); |
michael@0 | 90 | Services.obs.addObserver(this, "addon-install-blocked", false); |
michael@0 | 91 | Services.obs.addObserver(this, "addon-install-failed", false); |
michael@0 | 92 | Services.obs.addObserver(this, "addon-install-complete", false); |
michael@0 | 93 | |
michael@0 | 94 | AddonManager.addInstallListener(this); |
michael@0 | 95 | |
michael@0 | 96 | Services.wm.addListener(this); |
michael@0 | 97 | |
michael@0 | 98 | var self = this; |
michael@0 | 99 | registerCleanupFunction(function() { |
michael@0 | 100 | Services.prefs.clearUserPref(PREF_LOGGING_ENABLED); |
michael@0 | 101 | Services.obs.removeObserver(self, "addon-install-started"); |
michael@0 | 102 | Services.obs.removeObserver(self, "addon-install-disabled"); |
michael@0 | 103 | Services.obs.removeObserver(self, "addon-install-blocked"); |
michael@0 | 104 | Services.obs.removeObserver(self, "addon-install-failed"); |
michael@0 | 105 | Services.obs.removeObserver(self, "addon-install-complete"); |
michael@0 | 106 | |
michael@0 | 107 | AddonManager.removeInstallListener(self); |
michael@0 | 108 | |
michael@0 | 109 | Services.wm.removeListener(self); |
michael@0 | 110 | |
michael@0 | 111 | AddonManager.getAllInstalls(function(aInstalls) { |
michael@0 | 112 | is(aInstalls.length, 0, "Should be no active installs at the end of the test"); |
michael@0 | 113 | aInstalls.forEach(function(aInstall) { |
michael@0 | 114 | info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state); |
michael@0 | 115 | aInstall.cancel(); |
michael@0 | 116 | }); |
michael@0 | 117 | }); |
michael@0 | 118 | }); |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | this.installCount = 0; |
michael@0 | 122 | this.pendingCount = 0; |
michael@0 | 123 | this.runningInstalls = []; |
michael@0 | 124 | }, |
michael@0 | 125 | |
michael@0 | 126 | finish: function() { |
michael@0 | 127 | finish(); |
michael@0 | 128 | }, |
michael@0 | 129 | |
michael@0 | 130 | endTest: function() { |
michael@0 | 131 | // Defer the final notification to allow things like the InstallTrigger |
michael@0 | 132 | // callback to complete |
michael@0 | 133 | var self = this; |
michael@0 | 134 | executeSoon(function() { |
michael@0 | 135 | let callback = self.installsCompletedCallback; |
michael@0 | 136 | let count = self.installCount; |
michael@0 | 137 | |
michael@0 | 138 | is(self.runningInstalls.length, 0, "Should be no running installs left"); |
michael@0 | 139 | self.runningInstalls.forEach(function(aInstall) { |
michael@0 | 140 | info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state); |
michael@0 | 141 | }); |
michael@0 | 142 | |
michael@0 | 143 | self.installBlockedCallback = null; |
michael@0 | 144 | self.authenticationCallback = null; |
michael@0 | 145 | self.installConfirmCallback = null; |
michael@0 | 146 | self.downloadStartedCallback = null; |
michael@0 | 147 | self.downloadProgressCallback = null; |
michael@0 | 148 | self.downloadCancelledCallback = null; |
michael@0 | 149 | self.downloadFailedCallback = null; |
michael@0 | 150 | self.downloadEndedCallback = null; |
michael@0 | 151 | self.installStartedCallback = null; |
michael@0 | 152 | self.installFailedCallback = null; |
michael@0 | 153 | self.installEndedCallback = null; |
michael@0 | 154 | self.installsCompletedCallback = null; |
michael@0 | 155 | self.runningInstalls = null; |
michael@0 | 156 | |
michael@0 | 157 | if (callback) |
michael@0 | 158 | callback(count); |
michael@0 | 159 | }); |
michael@0 | 160 | }, |
michael@0 | 161 | |
michael@0 | 162 | // Window open handling |
michael@0 | 163 | windowReady: function(window) { |
michael@0 | 164 | if (window.document.location.href == XPINSTALL_URL) { |
michael@0 | 165 | if (this.installBlockedCallback) |
michael@0 | 166 | ok(false, "Should have been blocked by the whitelist"); |
michael@0 | 167 | this.pendingCount = window.document.getElementById("itemList").childNodes.length; |
michael@0 | 168 | |
michael@0 | 169 | // If there is a confirm callback then its return status determines whether |
michael@0 | 170 | // to install the items or not. If not the test is over. |
michael@0 | 171 | if (this.installConfirmCallback && !this.installConfirmCallback(window)) { |
michael@0 | 172 | window.document.documentElement.cancelDialog(); |
michael@0 | 173 | } |
michael@0 | 174 | else { |
michael@0 | 175 | // Initially the accept button is disabled on a countdown timer |
michael@0 | 176 | var button = window.document.documentElement.getButton("accept"); |
michael@0 | 177 | button.disabled = false; |
michael@0 | 178 | window.document.documentElement.acceptDialog(); |
michael@0 | 179 | } |
michael@0 | 180 | } |
michael@0 | 181 | else if (window.document.location.href == PROMPT_URL) { |
michael@0 | 182 | var promptType = window.args.promptType; |
michael@0 | 183 | switch (promptType) { |
michael@0 | 184 | case "alert": |
michael@0 | 185 | case "alertCheck": |
michael@0 | 186 | case "confirmCheck": |
michael@0 | 187 | case "confirm": |
michael@0 | 188 | case "confirmEx": |
michael@0 | 189 | window.document.documentElement.acceptDialog(); |
michael@0 | 190 | break; |
michael@0 | 191 | case "promptUserAndPass": |
michael@0 | 192 | // This is a login dialog, hopefully an authentication prompt |
michael@0 | 193 | // for the xpi. |
michael@0 | 194 | if (this.authenticationCallback) { |
michael@0 | 195 | var auth = this.authenticationCallback(); |
michael@0 | 196 | if (auth && auth.length == 2) { |
michael@0 | 197 | window.document.getElementById("loginTextbox").value = auth[0]; |
michael@0 | 198 | window.document.getElementById("password1Textbox").value = auth[1]; |
michael@0 | 199 | window.document.documentElement.acceptDialog(); |
michael@0 | 200 | } |
michael@0 | 201 | else { |
michael@0 | 202 | window.document.documentElement.cancelDialog(); |
michael@0 | 203 | } |
michael@0 | 204 | } |
michael@0 | 205 | else { |
michael@0 | 206 | window.document.documentElement.cancelDialog(); |
michael@0 | 207 | } |
michael@0 | 208 | break; |
michael@0 | 209 | default: |
michael@0 | 210 | ok(false, "prompt type " + promptType + " not handled in test."); |
michael@0 | 211 | break; |
michael@0 | 212 | } |
michael@0 | 213 | } |
michael@0 | 214 | }, |
michael@0 | 215 | |
michael@0 | 216 | // Install blocked handling |
michael@0 | 217 | |
michael@0 | 218 | installDisabled: function(installInfo) { |
michael@0 | 219 | ok(!!this.installDisabledCallback, "Installation shouldn't have been disabled"); |
michael@0 | 220 | if (this.installDisabledCallback) |
michael@0 | 221 | this.installDisabledCallback(installInfo); |
michael@0 | 222 | this.expectingCancelled = true; |
michael@0 | 223 | installInfo.installs.forEach(function(install) { |
michael@0 | 224 | install.cancel(); |
michael@0 | 225 | }); |
michael@0 | 226 | this.expectingCancelled = false; |
michael@0 | 227 | this.endTest(); |
michael@0 | 228 | }, |
michael@0 | 229 | |
michael@0 | 230 | installCancelled: function(installInfo) { |
michael@0 | 231 | if (this.expectingCancelled) |
michael@0 | 232 | return; |
michael@0 | 233 | |
michael@0 | 234 | ok(!!this.installCancelledCallback, "Installation shouldn't have been cancelled"); |
michael@0 | 235 | if (this.installCancelledCallback) |
michael@0 | 236 | this.installCancelledCallback(installInfo); |
michael@0 | 237 | this.endTest(); |
michael@0 | 238 | }, |
michael@0 | 239 | |
michael@0 | 240 | installBlocked: function(installInfo) { |
michael@0 | 241 | ok(!!this.installBlockedCallback, "Shouldn't have been blocked by the whitelist"); |
michael@0 | 242 | if (this.installBlockedCallback && this.installBlockedCallback(installInfo)) { |
michael@0 | 243 | this.installBlockedCallback = null; |
michael@0 | 244 | installInfo.install(); |
michael@0 | 245 | } |
michael@0 | 246 | else { |
michael@0 | 247 | this.expectingCancelled = true; |
michael@0 | 248 | installInfo.installs.forEach(function(install) { |
michael@0 | 249 | install.cancel(); |
michael@0 | 250 | }); |
michael@0 | 251 | this.expectingCancelled = false; |
michael@0 | 252 | this.endTest(); |
michael@0 | 253 | } |
michael@0 | 254 | }, |
michael@0 | 255 | |
michael@0 | 256 | // nsIWindowMediatorListener |
michael@0 | 257 | |
michael@0 | 258 | onWindowTitleChange: function(window, title) { |
michael@0 | 259 | }, |
michael@0 | 260 | |
michael@0 | 261 | onOpenWindow: function(window) { |
michael@0 | 262 | var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
michael@0 | 263 | .getInterface(Components.interfaces.nsIDOMWindow); |
michael@0 | 264 | var self = this; |
michael@0 | 265 | waitForFocus(function() { |
michael@0 | 266 | self.windowReady(domwindow); |
michael@0 | 267 | }, domwindow); |
michael@0 | 268 | }, |
michael@0 | 269 | |
michael@0 | 270 | onCloseWindow: function(window) { |
michael@0 | 271 | }, |
michael@0 | 272 | |
michael@0 | 273 | // Addon Install Listener |
michael@0 | 274 | |
michael@0 | 275 | onNewInstall: function(install) { |
michael@0 | 276 | this.runningInstalls.push(install); |
michael@0 | 277 | }, |
michael@0 | 278 | |
michael@0 | 279 | onDownloadStarted: function(install) { |
michael@0 | 280 | this.pendingCount++; |
michael@0 | 281 | if (this.downloadStartedCallback) |
michael@0 | 282 | this.downloadStartedCallback(install); |
michael@0 | 283 | }, |
michael@0 | 284 | |
michael@0 | 285 | onDownloadProgress: function(install) { |
michael@0 | 286 | if (this.downloadProgressCallback) |
michael@0 | 287 | this.downloadProgressCallback(install); |
michael@0 | 288 | }, |
michael@0 | 289 | |
michael@0 | 290 | onDownloadEnded: function(install) { |
michael@0 | 291 | if (this.downloadEndedCallback) |
michael@0 | 292 | this.downloadEndedCallback(install); |
michael@0 | 293 | }, |
michael@0 | 294 | |
michael@0 | 295 | onDownloadCancelled: function(install) { |
michael@0 | 296 | isnot(this.runningInstalls.indexOf(install), -1, |
michael@0 | 297 | "Should only see cancelations for started installs"); |
michael@0 | 298 | this.runningInstalls.splice(this.runningInstalls.indexOf(install), 1); |
michael@0 | 299 | |
michael@0 | 300 | if (this.downloadCancelledCallback) |
michael@0 | 301 | this.downloadCancelledCallback(install); |
michael@0 | 302 | this.checkTestEnded(); |
michael@0 | 303 | }, |
michael@0 | 304 | |
michael@0 | 305 | onDownloadFailed: function(install) { |
michael@0 | 306 | if (this.downloadFailedCallback) |
michael@0 | 307 | this.downloadFailedCallback(install); |
michael@0 | 308 | this.checkTestEnded(); |
michael@0 | 309 | }, |
michael@0 | 310 | |
michael@0 | 311 | onInstallStarted: function(install) { |
michael@0 | 312 | if (this.installStartedCallback) |
michael@0 | 313 | this.installStartedCallback(install); |
michael@0 | 314 | }, |
michael@0 | 315 | |
michael@0 | 316 | onInstallEnded: function(install, addon) { |
michael@0 | 317 | if (this.installEndedCallback) |
michael@0 | 318 | this.installEndedCallback(install, addon); |
michael@0 | 319 | this.installCount++; |
michael@0 | 320 | this.checkTestEnded(); |
michael@0 | 321 | }, |
michael@0 | 322 | |
michael@0 | 323 | onInstallFailed: function(install) { |
michael@0 | 324 | if (this.installFailedCallback) |
michael@0 | 325 | this.installFailedCallback(install); |
michael@0 | 326 | this.checkTestEnded(); |
michael@0 | 327 | }, |
michael@0 | 328 | |
michael@0 | 329 | checkTestEnded: function() { |
michael@0 | 330 | if (--this.pendingCount == 0) |
michael@0 | 331 | this.endTest(); |
michael@0 | 332 | }, |
michael@0 | 333 | |
michael@0 | 334 | // nsIObserver |
michael@0 | 335 | |
michael@0 | 336 | observe: function(subject, topic, data) { |
michael@0 | 337 | var installInfo = subject.QueryInterface(Components.interfaces.amIWebInstallInfo); |
michael@0 | 338 | switch (topic) { |
michael@0 | 339 | case "addon-install-started": |
michael@0 | 340 | is(this.runningInstalls.length, installInfo.installs.length, |
michael@0 | 341 | "Should have seen the expected number of installs started"); |
michael@0 | 342 | break; |
michael@0 | 343 | case "addon-install-disabled": |
michael@0 | 344 | this.installDisabled(installInfo); |
michael@0 | 345 | break; |
michael@0 | 346 | case "addon-install-cancelled": |
michael@0 | 347 | this.installCancelled(installInfo); |
michael@0 | 348 | break; |
michael@0 | 349 | case "addon-install-blocked": |
michael@0 | 350 | this.installBlocked(installInfo); |
michael@0 | 351 | break; |
michael@0 | 352 | case "addon-install-failed": |
michael@0 | 353 | installInfo.installs.forEach(function(aInstall) { |
michael@0 | 354 | isnot(this.runningInstalls.indexOf(aInstall), -1, |
michael@0 | 355 | "Should only see failures for started installs"); |
michael@0 | 356 | |
michael@0 | 357 | ok(aInstall.error != 0 || aInstall.addon.appDisabled, |
michael@0 | 358 | "Failed installs should have an error or be appDisabled"); |
michael@0 | 359 | |
michael@0 | 360 | this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1); |
michael@0 | 361 | }, this); |
michael@0 | 362 | break; |
michael@0 | 363 | case "addon-install-complete": |
michael@0 | 364 | installInfo.installs.forEach(function(aInstall) { |
michael@0 | 365 | isnot(this.runningInstalls.indexOf(aInstall), -1, |
michael@0 | 366 | "Should only see completed events for started installs"); |
michael@0 | 367 | |
michael@0 | 368 | is(aInstall.error, 0, "Completed installs should have no error"); |
michael@0 | 369 | ok(!aInstall.appDisabled, "Completed installs should not be appDisabled"); |
michael@0 | 370 | |
michael@0 | 371 | // Complete installs are either in the INSTALLED or CANCELLED state |
michael@0 | 372 | // since the test may cancel installs the moment they complete. |
michael@0 | 373 | ok(aInstall.state == AddonManager.STATE_INSTALLED || |
michael@0 | 374 | aInstall.state == AddonManager.STATE_CANCELLED, |
michael@0 | 375 | "Completed installs should be in the right state"); |
michael@0 | 376 | |
michael@0 | 377 | this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1); |
michael@0 | 378 | }, this); |
michael@0 | 379 | break; |
michael@0 | 380 | } |
michael@0 | 381 | }, |
michael@0 | 382 | |
michael@0 | 383 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, |
michael@0 | 384 | Ci.nsIWindowMediatorListener, |
michael@0 | 385 | Ci.nsISupports]) |
michael@0 | 386 | } |