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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/mozapps/extensions/test/xpinstall/head.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,386 @@
     1.4 +const RELATIVE_DIR = "toolkit/mozapps/extensions/test/xpinstall/";
     1.5 +
     1.6 +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
     1.7 +const TESTROOT2 = "http://example.org/browser/" + RELATIVE_DIR;
     1.8 +const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
     1.9 +const PROMPT_URL = "chrome://global/content/commonDialog.xul";
    1.10 +const ADDONS_URL = "chrome://mozapps/content/extensions/extensions.xul";
    1.11 +const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
    1.12 +const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
    1.13 +const CHROME_NAME = "mochikit";
    1.14 +
    1.15 +function getChromeRoot(path) {
    1.16 +  if (path === undefined) {
    1.17 +    return "chrome://" + CHROME_NAME + "/content/browser/" + RELATIVE_DIR
    1.18 +  }
    1.19 +  return getRootDirectory(path);
    1.20 +}
    1.21 +
    1.22 +function extractChromeRoot(path) {
    1.23 +  var chromeRootPath = getChromeRoot(path);
    1.24 +  var jar = getJar(chromeRootPath);
    1.25 +  if (jar) {
    1.26 +    var tmpdir = extractJarToTmp(jar);
    1.27 +    return "file://" + tmpdir.path + "/";
    1.28 +  }
    1.29 +  return chromeRootPath;
    1.30 +}
    1.31 +
    1.32 +/**
    1.33 + * This is a test harness designed to handle responding to UI during the process
    1.34 + * of installing an XPI. A test can set callbacks to hear about specific parts
    1.35 + * of the sequence.
    1.36 + * Before use setup must be called and finish must be called afterwards.
    1.37 + */
    1.38 +var Harness = {
    1.39 +  // If set then the callback is called when an install is attempted and
    1.40 +  // software installation is disabled.
    1.41 +  installDisabledCallback: null,
    1.42 +  // If set then the callback is called when an install is attempted and
    1.43 +  // then canceled.
    1.44 +  installCancelledCallback: null,
    1.45 +  // If set then the callback will be called when an install is blocked by the
    1.46 +  // whitelist. The callback should return true to continue with the install
    1.47 +  // anyway.
    1.48 +  installBlockedCallback: null,
    1.49 +  // If set will be called in the event of authentication being needed to get
    1.50 +  // the xpi. Should return a 2 element array of username and password, or
    1.51 +  // null to not authenticate.
    1.52 +  authenticationCallback: null,
    1.53 +  // If set this will be called to allow checking the contents of the xpinstall
    1.54 +  // confirmation dialog. The callback should return true to continue the install.
    1.55 +  installConfirmCallback: null,
    1.56 +  // If set will be called when downloading of an item has begun.
    1.57 +  downloadStartedCallback: null,
    1.58 +  // If set will be called during the download of an item.
    1.59 +  downloadProgressCallback: null,
    1.60 +  // If set will be called when an xpi fails to download.
    1.61 +  downloadFailedCallback: null,
    1.62 +  // If set will be called when an xpi download is cancelled.
    1.63 +  downloadCancelledCallback: null,
    1.64 +  // If set will be called when downloading of an item has ended.
    1.65 +  downloadEndedCallback: null,
    1.66 +  // If set will be called when installation by the extension manager of an xpi
    1.67 +  // item starts
    1.68 +  installStartedCallback: null,
    1.69 +  // If set will be called when an xpi fails to install.
    1.70 +  installFailedCallback: null,
    1.71 +  // If set will be called when each xpi item to be installed completes
    1.72 +  // installation.
    1.73 +  installEndedCallback: null,
    1.74 +  // If set will be called when all triggered items are installed or the install
    1.75 +  // is canceled.
    1.76 +  installsCompletedCallback: null,
    1.77 +
    1.78 +  pendingCount: null,
    1.79 +  installCount: null,
    1.80 +  runningInstalls: null,
    1.81 +
    1.82 +  waitingForFinish: false,
    1.83 +
    1.84 +  // Setup and tear down functions
    1.85 +  setup: function() {
    1.86 +    if (!this.waitingForFinish) {
    1.87 +      waitForExplicitFinish();
    1.88 +      this.waitingForFinish = true;
    1.89 +
    1.90 +      Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
    1.91 +      Services.obs.addObserver(this, "addon-install-started", false);
    1.92 +      Services.obs.addObserver(this, "addon-install-disabled", false);
    1.93 +      Services.obs.addObserver(this, "addon-install-blocked", false);
    1.94 +      Services.obs.addObserver(this, "addon-install-failed", false);
    1.95 +      Services.obs.addObserver(this, "addon-install-complete", false);
    1.96 +
    1.97 +      AddonManager.addInstallListener(this);
    1.98 +
    1.99 +      Services.wm.addListener(this);
   1.100 +
   1.101 +      var self = this;
   1.102 +      registerCleanupFunction(function() {
   1.103 +        Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
   1.104 +        Services.obs.removeObserver(self, "addon-install-started");
   1.105 +        Services.obs.removeObserver(self, "addon-install-disabled");
   1.106 +        Services.obs.removeObserver(self, "addon-install-blocked");
   1.107 +        Services.obs.removeObserver(self, "addon-install-failed");
   1.108 +        Services.obs.removeObserver(self, "addon-install-complete");
   1.109 +
   1.110 +        AddonManager.removeInstallListener(self);
   1.111 +
   1.112 +        Services.wm.removeListener(self);
   1.113 +
   1.114 +        AddonManager.getAllInstalls(function(aInstalls) {
   1.115 +          is(aInstalls.length, 0, "Should be no active installs at the end of the test");
   1.116 +          aInstalls.forEach(function(aInstall) {
   1.117 +            info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state);
   1.118 +            aInstall.cancel();
   1.119 +          });
   1.120 +        });
   1.121 +      });
   1.122 +    }
   1.123 +
   1.124 +    this.installCount = 0;
   1.125 +    this.pendingCount = 0;
   1.126 +    this.runningInstalls = [];
   1.127 +  },
   1.128 +
   1.129 +  finish: function() {
   1.130 +    finish();
   1.131 +  },
   1.132 +
   1.133 +  endTest: function() {
   1.134 +    // Defer the final notification to allow things like the InstallTrigger
   1.135 +    // callback to complete
   1.136 +    var self = this;
   1.137 +    executeSoon(function() {
   1.138 +      let callback = self.installsCompletedCallback;
   1.139 +      let count = self.installCount;
   1.140 +
   1.141 +      is(self.runningInstalls.length, 0, "Should be no running installs left");
   1.142 +      self.runningInstalls.forEach(function(aInstall) {
   1.143 +        info("Install for " + aInstall.sourceURI + " is in state " + aInstall.state);
   1.144 +      });
   1.145 +
   1.146 +      self.installBlockedCallback = null;
   1.147 +      self.authenticationCallback = null;
   1.148 +      self.installConfirmCallback = null;
   1.149 +      self.downloadStartedCallback = null;
   1.150 +      self.downloadProgressCallback = null;
   1.151 +      self.downloadCancelledCallback = null;
   1.152 +      self.downloadFailedCallback = null;
   1.153 +      self.downloadEndedCallback = null;
   1.154 +      self.installStartedCallback = null;
   1.155 +      self.installFailedCallback = null;
   1.156 +      self.installEndedCallback = null;
   1.157 +      self.installsCompletedCallback = null;
   1.158 +      self.runningInstalls = null;
   1.159 +
   1.160 +      if (callback)
   1.161 +        callback(count);
   1.162 +    });
   1.163 +  },
   1.164 +
   1.165 +  // Window open handling
   1.166 +  windowReady: function(window) {
   1.167 +    if (window.document.location.href == XPINSTALL_URL) {
   1.168 +      if (this.installBlockedCallback)
   1.169 +        ok(false, "Should have been blocked by the whitelist");
   1.170 +      this.pendingCount = window.document.getElementById("itemList").childNodes.length;
   1.171 +
   1.172 +      // If there is a confirm callback then its return status determines whether
   1.173 +      // to install the items or not. If not the test is over.
   1.174 +      if (this.installConfirmCallback && !this.installConfirmCallback(window)) {
   1.175 +        window.document.documentElement.cancelDialog();
   1.176 +      }
   1.177 +      else {
   1.178 +        // Initially the accept button is disabled on a countdown timer
   1.179 +        var button = window.document.documentElement.getButton("accept");
   1.180 +        button.disabled = false;
   1.181 +        window.document.documentElement.acceptDialog();
   1.182 +      }
   1.183 +    }
   1.184 +    else if (window.document.location.href == PROMPT_URL) {
   1.185 +        var promptType = window.args.promptType;
   1.186 +        switch (promptType) {
   1.187 +          case "alert":
   1.188 +          case "alertCheck":
   1.189 +          case "confirmCheck":
   1.190 +          case "confirm":
   1.191 +          case "confirmEx":
   1.192 +                window.document.documentElement.acceptDialog();
   1.193 +                break;
   1.194 +          case "promptUserAndPass":
   1.195 +                  // This is a login dialog, hopefully an authentication prompt
   1.196 +                  // for the xpi.
   1.197 +                  if (this.authenticationCallback) {
   1.198 +                    var auth = this.authenticationCallback();
   1.199 +                    if (auth && auth.length == 2) {
   1.200 +                      window.document.getElementById("loginTextbox").value = auth[0];
   1.201 +                      window.document.getElementById("password1Textbox").value = auth[1];
   1.202 +                      window.document.documentElement.acceptDialog();
   1.203 +                    }
   1.204 +                    else {
   1.205 +                      window.document.documentElement.cancelDialog();
   1.206 +                    }
   1.207 +                  }
   1.208 +                  else {
   1.209 +                    window.document.documentElement.cancelDialog();
   1.210 +                  }
   1.211 +                break;
   1.212 +          default:
   1.213 +                ok(false, "prompt type " + promptType + " not handled in test.");
   1.214 +                break;
   1.215 +      }
   1.216 +    }
   1.217 +  },
   1.218 +
   1.219 +  // Install blocked handling
   1.220 +
   1.221 +  installDisabled: function(installInfo) {
   1.222 +    ok(!!this.installDisabledCallback, "Installation shouldn't have been disabled");
   1.223 +    if (this.installDisabledCallback)
   1.224 +      this.installDisabledCallback(installInfo);
   1.225 +    this.expectingCancelled = true;
   1.226 +    installInfo.installs.forEach(function(install) {
   1.227 +      install.cancel();
   1.228 +    });
   1.229 +    this.expectingCancelled = false;
   1.230 +    this.endTest();
   1.231 +  },
   1.232 +
   1.233 +  installCancelled: function(installInfo) {
   1.234 +    if (this.expectingCancelled)
   1.235 +      return;
   1.236 +
   1.237 +    ok(!!this.installCancelledCallback, "Installation shouldn't have been cancelled");
   1.238 +    if (this.installCancelledCallback)
   1.239 +      this.installCancelledCallback(installInfo);
   1.240 +    this.endTest();
   1.241 +  },
   1.242 +
   1.243 +  installBlocked: function(installInfo) {
   1.244 +    ok(!!this.installBlockedCallback, "Shouldn't have been blocked by the whitelist");
   1.245 +    if (this.installBlockedCallback && this.installBlockedCallback(installInfo)) {
   1.246 +      this.installBlockedCallback = null;
   1.247 +      installInfo.install();
   1.248 +    }
   1.249 +    else {
   1.250 +      this.expectingCancelled = true;
   1.251 +      installInfo.installs.forEach(function(install) {
   1.252 +        install.cancel();
   1.253 +      });
   1.254 +      this.expectingCancelled = false;
   1.255 +      this.endTest();
   1.256 +    }
   1.257 +  },
   1.258 +
   1.259 +  // nsIWindowMediatorListener
   1.260 +
   1.261 +  onWindowTitleChange: function(window, title) {
   1.262 +  },
   1.263 +
   1.264 +  onOpenWindow: function(window) {
   1.265 +    var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
   1.266 +                          .getInterface(Components.interfaces.nsIDOMWindow);
   1.267 +    var self = this;
   1.268 +    waitForFocus(function() {
   1.269 +      self.windowReady(domwindow);
   1.270 +    }, domwindow);
   1.271 +  },
   1.272 +
   1.273 +  onCloseWindow: function(window) {
   1.274 +  },
   1.275 +
   1.276 +  // Addon Install Listener
   1.277 +
   1.278 +  onNewInstall: function(install) {
   1.279 +    this.runningInstalls.push(install);
   1.280 +  },
   1.281 +
   1.282 +  onDownloadStarted: function(install) {
   1.283 +    this.pendingCount++;
   1.284 +    if (this.downloadStartedCallback)
   1.285 +      this.downloadStartedCallback(install);
   1.286 +  },
   1.287 +
   1.288 +  onDownloadProgress: function(install) {
   1.289 +    if (this.downloadProgressCallback)
   1.290 +      this.downloadProgressCallback(install);
   1.291 +  },
   1.292 +
   1.293 +  onDownloadEnded: function(install) {
   1.294 +    if (this.downloadEndedCallback)
   1.295 +      this.downloadEndedCallback(install);
   1.296 +  },
   1.297 +
   1.298 +  onDownloadCancelled: function(install) {
   1.299 +    isnot(this.runningInstalls.indexOf(install), -1,
   1.300 +          "Should only see cancelations for started installs");
   1.301 +    this.runningInstalls.splice(this.runningInstalls.indexOf(install), 1);
   1.302 +
   1.303 +    if (this.downloadCancelledCallback)
   1.304 +      this.downloadCancelledCallback(install);
   1.305 +    this.checkTestEnded();
   1.306 +  },
   1.307 +
   1.308 +  onDownloadFailed: function(install) {
   1.309 +    if (this.downloadFailedCallback)
   1.310 +      this.downloadFailedCallback(install);
   1.311 +    this.checkTestEnded();
   1.312 +  },
   1.313 +
   1.314 +  onInstallStarted: function(install) {
   1.315 +    if (this.installStartedCallback)
   1.316 +      this.installStartedCallback(install);
   1.317 +  },
   1.318 +
   1.319 +  onInstallEnded: function(install, addon) {
   1.320 +    if (this.installEndedCallback)
   1.321 +      this.installEndedCallback(install, addon);
   1.322 +    this.installCount++;
   1.323 +    this.checkTestEnded();
   1.324 +  },
   1.325 +
   1.326 +  onInstallFailed: function(install) {
   1.327 +    if (this.installFailedCallback)
   1.328 +      this.installFailedCallback(install);
   1.329 +    this.checkTestEnded();
   1.330 +  },
   1.331 +
   1.332 +  checkTestEnded: function() {
   1.333 +    if (--this.pendingCount == 0)
   1.334 +      this.endTest();
   1.335 +  },
   1.336 +
   1.337 +  // nsIObserver
   1.338 +
   1.339 +  observe: function(subject, topic, data) {
   1.340 +    var installInfo = subject.QueryInterface(Components.interfaces.amIWebInstallInfo);
   1.341 +    switch (topic) {
   1.342 +    case "addon-install-started":
   1.343 +      is(this.runningInstalls.length, installInfo.installs.length,
   1.344 +         "Should have seen the expected number of installs started");
   1.345 +      break;
   1.346 +    case "addon-install-disabled":
   1.347 +      this.installDisabled(installInfo);
   1.348 +      break;
   1.349 +    case "addon-install-cancelled":
   1.350 +      this.installCancelled(installInfo);
   1.351 +      break;
   1.352 +    case "addon-install-blocked":
   1.353 +      this.installBlocked(installInfo);
   1.354 +      break;
   1.355 +    case "addon-install-failed":
   1.356 +      installInfo.installs.forEach(function(aInstall) {
   1.357 +        isnot(this.runningInstalls.indexOf(aInstall), -1,
   1.358 +              "Should only see failures for started installs");
   1.359 +
   1.360 +        ok(aInstall.error != 0 || aInstall.addon.appDisabled,
   1.361 +           "Failed installs should have an error or be appDisabled");
   1.362 +
   1.363 +        this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1);
   1.364 +      }, this);
   1.365 +      break;
   1.366 +    case "addon-install-complete":
   1.367 +      installInfo.installs.forEach(function(aInstall) {
   1.368 +        isnot(this.runningInstalls.indexOf(aInstall), -1,
   1.369 +              "Should only see completed events for started installs");
   1.370 +
   1.371 +        is(aInstall.error, 0, "Completed installs should have no error");
   1.372 +        ok(!aInstall.appDisabled, "Completed installs should not be appDisabled");
   1.373 +
   1.374 +        // Complete installs are either in the INSTALLED or CANCELLED state
   1.375 +        // since the test may cancel installs the moment they complete.
   1.376 +        ok(aInstall.state == AddonManager.STATE_INSTALLED ||
   1.377 +           aInstall.state == AddonManager.STATE_CANCELLED,
   1.378 +           "Completed installs should be in the right state");
   1.379 +
   1.380 +        this.runningInstalls.splice(this.runningInstalls.indexOf(aInstall), 1);
   1.381 +      }, this);
   1.382 +      break;
   1.383 +    }
   1.384 +  },
   1.385 +
   1.386 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
   1.387 +                                         Ci.nsIWindowMediatorListener,
   1.388 +                                         Ci.nsISupports])
   1.389 +}

mercurial