1. main patch - Bug 843497 - Update check says 'up to date' for out of date versions on unsupported OS versions. r=bbondy
authorRobert Strong <robert.bugzilla@gmail.com>
Sat, 22 Jun 2013 13:37:07 -0700
changeset 147672 340646bf7e85b427ad6f95e6eb32749dab7b0cbc
parent 147671 df8a743fcab7e0a27293b7208a17a71f2f506275
child 147673 b1c91eba5eaa19517b862f4b3b1f97aa97f7123c
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy
bugs1, 843497
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
1. main patch - Bug 843497 - Update check says 'up to date' for out of date versions on unsupported OS versions. r=bbondy
toolkit/locales/en-US/chrome/mozapps/update/updates.dtd
toolkit/mozapps/update/content/updates.js
toolkit/mozapps/update/content/updates.xul
toolkit/mozapps/update/nsIUpdateService.idl
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/nsUpdateTimerManager.js
toolkit/themes/linux/mozapps/update/updates.css
toolkit/themes/osx/mozapps/update/updates.css
toolkit/themes/windows/mozapps/update/updates.css
--- a/toolkit/locales/en-US/chrome/mozapps/update/updates.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/update/updates.dtd
@@ -20,16 +20,22 @@
 <!ENTITY  manualUpdate.desc               "A recommended security and stability update is available, but you do
                                            not have the system permissions required to install it. Please contact your
                                            system administrator, or try again from an account that has permission to
                                            install software on this computer.">
 <!ENTITY  manualUpdate.space.desc         "A recommended security and stability update is available, but you do
                                            not have enough space to install it.">
 <!ENTITY  manualUpdateGetMsg.label        "You can always get the latest version of &brandShortName; at:">
 
+<!ENTITY  unsupported.title               "System Unsupported">
+<!ENTITY  unsupported.label               "Your &brandShortName; is out of date, but the latest version is not
+                                           supported on your system. Please upgrade your system, then try again.
+                                           You will not see this notice again, but you can">
+<!ENTITY  unsupportedLink.label           "learn more.">
+
 <!ENTITY  incompatibleCheck.title         "Checking Add-on Compatibility">
 <!ENTITY  incompatibleCheck.label         "Looking for newer versions of your add-ons…">
 
 <!ENTITY  clickHere.label                 "View more information about this update">
 
 <!ENTITY  evangelism.desc                 "It is strongly recommended that you apply this 
                                            update for &brandShortName; as soon as possible.">
 
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -8,29 +8,30 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 // Firefox's macBrowserOverlay.xul includes scripts that define Cc, Ci, and Cr
 // so we have to use different names.
 const CoC = Components.classes;
 const CoI = Components.interfaces;
 const CoR = Components.results;
 
-const XMLNS_XUL               = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
-const PREF_APP_UPDATE_BACKGROUNDERRORS   = "app.update.backgroundErrors";
-const PREF_APP_UPDATE_BILLBOARD_TEST_URL = "app.update.billboard.test_url";
-const PREF_APP_UPDATE_CERT_ERRORS        = "app.update.cert.errors";
-const PREF_APP_UPDATE_ENABLED            = "app.update.enabled";
-const PREF_APP_UPDATE_LOG                = "app.update.log";
-const PREF_APP_UPDATE_MANUAL_URL         = "app.update.url.manual";
-const PREF_APP_UPDATE_NEVER_BRANCH       = "app.update.never.";
-const PREF_APP_UPDATE_TEST_LOOP          = "app.update.test.loop";
-const PREF_PLUGINS_UPDATEURL             = "plugins.update.url";
+const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
+const PREF_APP_UPDATE_BILLBOARD_TEST_URL  = "app.update.billboard.test_url";
+const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
+const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
+const PREF_APP_UPDATE_LOG                 = "app.update.log";
+const PREF_APP_UPDATE_MANUAL_URL          = "app.update.url.manual";
+const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
+const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
+const PREF_APP_UPDATE_TEST_LOOP           = "app.update.test.loop";
+const PREF_PLUGINS_UPDATEURL              = "plugins.update.url";
 
-const PREF_EM_HOTFIX_ID                  = "extensions.hotfix.id";
+const PREF_EM_HOTFIX_ID                   = "extensions.hotfix.id";
 
 const UPDATE_TEST_LOOP_INTERVAL     = 2000;
 
 const URI_UPDATES_PROPERTIES  = "chrome://mozapps/locale/update/updates.properties";
 
 const STATE_DOWNLOADING       = "downloading";
 const STATE_PENDING           = "pending";
 const STATE_PENDING_SVC       = "pending-service";
@@ -377,16 +378,21 @@ var gUpdates = {
         this.setUpdate(arg0);
         if (this.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
             this.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE ||
             this.update.errorCode == BACKGROUNDCHECK_MULTIPLE_FAILURES) {
           aCallback("errorextra");
           return;
         }
 
+        if (this.update.unsupported) {
+          aCallback("unsupported");
+          return;
+        }
+
         var p = this.update.selectedPatch;
         if (p) {
           var state = p.state;
           var patchFailed;
           try {
             patchFailed = this.update.getProperty("patchingFailed");
           }
           catch (e) {
@@ -589,16 +595,21 @@ var gCheckingPage = {
     // notifications will never happen.
     Services.prefs.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH);
 
     // The user will be notified if there is an error so clear the background
     // check error count.
     if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
       Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
 
+    // The preference will be set back to true if the system is still
+    // unsupported.
+    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED))
+      Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
+
     this._checker = CoC["@mozilla.org/updates/update-checker;1"].
                     createInstance(CoI.nsIUpdateChecker);
     this._checker.checkForUpdates(this.updateListener, true);
   },
 
   /**
    * The user has closed the window, either by pressing cancel or using a Window
    * Manager control, so stop checking for updates.
@@ -616,16 +627,21 @@ var gCheckingPage = {
      * See nsIUpdateCheckListener
      */
     onCheckComplete: function(request, updates, updateCount) {
       var aus = CoC["@mozilla.org/updates/update-service;1"].
                 getService(CoI.nsIApplicationUpdateService);
       gUpdates.setUpdate(aus.selectUpdate(updates, updates.length));
       if (gUpdates.update) {
         LOG("gCheckingPage", "onCheckComplete - update found");
+        if (gUpdates.update.unsupported) {
+          gUpdates.wiz.goTo("unsupported");
+          return;
+        }
+
         if (!aus.canApplyUpdates) {
           // Prevent multiple notifications for the same update when the user is
           // unable to apply updates.
           gUpdates.never();
           gUpdates.wiz.goTo("manualUpdate");
           return;
         }
 
@@ -864,16 +880,33 @@ var gManualUpdatePage = {
     manualUpdateLinkLabel.setAttribute("url", manualURL);
 
     gUpdates.setButtons(null, null, "okButton", true);
     gUpdates.wiz.getButton("finish").focus();
   }
 };
 
 /**
+ * The "System Unsupported" page. Provides the user with information about their
+ * system no longer being supported and an url for more information.
+ */
+var gUnsupportedPage = {
+  onPageShow: function() {
+    Services.prefs.setBoolPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, true);
+    if (gUpdates.update.detailsURL) {
+      let unsupportedLinkLabel = document.getElementById("unsupportedLinkLabel");
+      unsupportedLinkLabel.setAttribute("url", gUpdates.update.detailsURL);
+    }
+
+    gUpdates.setButtons(null, null, "okButton", true);
+    gUpdates.wiz.getButton("finish").focus();
+  }
+};
+
+/**
  * The "Updates Are Available" page. Provides the user information about the
  * available update.
  */
 var gUpdatesFoundBasicPage = {
   /**
    * Initialize
    */
   onPageShow: function() {
--- a/toolkit/mozapps/update/content/updates.xul
+++ b/toolkit/mozapps/update/content/updates.xul
@@ -77,16 +77,29 @@
       <label>&manualUpdateGetMsg.label;</label>
       <hbox>
         <label class="text-link" id="manualUpdateLinkLabel" value=""
                onclick="openUpdateURL(event);"/>
       </hbox>
     </vbox>
   </wizardpage>
 
+  <wizardpage id="unsupported" pageid="unsupported"
+              object="gUnsupportedPage"
+              onpageshow="gUnsupportedPage.onPageShow();">
+    <updateheader label="&unsupported.title;"/>
+    <vbox class="update-content" flex="1">
+      <description flex="1">&unsupported.label;
+        <label id="unsupportedLinkLabel" class="text-link inline-link" onclick="openUpdateURL(event);">
+          &unsupportedLink.label;
+        </label>
+      </description>
+    </vbox>
+  </wizardpage>
+
   <wizardpage id="incompatibleCheck" pageid="incompatibleCheck"
               next="updatesfoundbasic" object="gIncompatibleCheckPage"
               onpageshow="gIncompatibleCheckPage.onPageShow();">
     <updateheader label="&incompatibleCheck.title;"/>
     <vbox class="update-content" flex="1">
       <label>&incompatibleCheck.label;</label>
       <separator class="thin"/>
       <progressmeter id="incompatibleCheckProgress" mode="undetermined"/>
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -82,17 +82,17 @@ interface nsIUpdatePatch : nsISupports
  * the current application - this update may have several available patches
  * from which one must be selected to download and install, for example we
  * might select a binary difference patch first and attempt to apply that,
  * then if the application process fails fall back to downloading a complete
  * file-replace patch. This object also contains information about the update
  * that the front end and other application services can use to learn more
  * about what is going on.
  */
-[scriptable, uuid(8f7185a7-056a-45a8-985c-1cb39cf7b7a8)]
+[scriptable, uuid(6b0b7721-6746-443d-8cb0-c6199d7f28a6)]
 interface nsIUpdate : nsISupports
 {
   /**
    * The type of update:
    *   "major"  A major new version of the Application
    *   "minor"  A minor update to the Application (e.g. security update)
    */
   attribute AString type;
@@ -173,20 +173,19 @@ interface nsIUpdate : nsISupports
   /**
    * Whether to show the "No Thanks" button in the update prompt. This allows
    * the user to never receive a notification for that specific update version
    * again.
    */
   attribute boolean showNeverForVersion;
 
   /**
-   * Whether to show the survey link in the update prompt. The url must also be
-   * present in the app.update.surveyURL preference.
+   * Whether the update is no longer supported on this system.
    */
-  attribute boolean showSurvey;
+  attribute boolean unsupported;
   
   /**
    * Allows overriding the default amount of time in seconds before prompting the
    * user to apply an update. If not specified, the value of
    * app.update.promptWaitTime will be used.
    */
   attribute long long promptWaitTime;
 
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -39,16 +39,17 @@ const PREF_APP_UPDATE_INTERVAL          
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_MODE                = "app.update.mode";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_POSTUPDATE          = "app.update.postupdate";
 const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_SHOW_INSTALLED_UI   = "app.update.showInstalledUI";
 const PREF_APP_UPDATE_SILENT              = "app.update.silent";
 const PREF_APP_UPDATE_STAGING_ENABLED     = "app.update.staging.enabled";
+const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
 const PREF_APP_UPDATE_URL                 = "app.update.url";
 const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
 const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
 const PREF_APP_UPDATE_SERVICE_ENABLED     = "app.update.service.enabled";
 const PREF_APP_UPDATE_SERVICE_ERRORS      = "app.update.service.errors";
 const PREF_APP_UPDATE_SERVICE_MAX_ERRORS  = "app.update.service.maxErrors";
 const PREF_APP_UPDATE_SOCKET_ERRORS       = "app.update.socket.maxErrors";
 const PREF_APP_UPDATE_RETRY_TIMEOUT       = "app.update.socket.retryTimeout";
@@ -176,16 +177,17 @@ const DEFAULT_SERVICE_MAX_ERRORS = 10;
 // The number of consecutive socket errors to allow before falling back to
 // downloading a different MAR file or failing if already downloading the full.
 const DEFAULT_SOCKET_MAX_ERRORS = 10;
 
 // The number of milliseconds to wait before retrying a connection error.
 const DEFAULT_UPDATE_RETRY_TIMEOUT = 2000;
 
 var gLocale     = null;
+
 #ifdef MOZ_B2G
 var gVolumeMountLock = null;
 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
     return Services.env.get("EXTERNAL_STORAGE");
 });
 
 var gSDCardMountLock = null;
 #endif
@@ -1555,18 +1557,18 @@ UpdatePatch.prototype = {
  * @constructor
  */
 function Update(update) {
   this._properties = {};
   this._patches = [];
   this.isCompleteUpdate = false;
   this.isOSUpdate = false;
   this.showPrompt = false;
-  this.showSurvey = false;
   this.showNeverForVersion = false;
+  this.unsupported = false;
   this.channel = "default";
   this.promptWaitTime = getPref("getIntPref", PREF_APP_UPDATE_PROMPTWAITTIME, 43200);
 
   // Null <update>, assume this is a message container and do no
   // further initialization
   if (!update)
     return;
 
@@ -1581,17 +1583,17 @@ function Update(update) {
     try {
       var patch = new UpdatePatch(patchElement);
     } catch (e) {
       continue;
     }
     this._patches.push(patch);
   }
 
-  if (0 == this._patches.length)
+  if (this._patches.length == 0 && !update.hasAttribute("unsupported"))
     throw Cr.NS_ERROR_ILLEGAL_VALUE;
 
   // Fallback to the behavior prior to bug 530872 if the update does not have an
   // appVersion attribute.
   if (!update.hasAttribute("appVersion")) {
     if (update.getAttribute("type") == "major") {
       if (update.hasAttribute("detailsURL")) {
         this.billboardURL = update.getAttribute("detailsURL");
@@ -1626,18 +1628,18 @@ function Update(update) {
       this.showNeverForVersion = attr.value == "true";
     else if (attr.name == "showPrompt")
       this.showPrompt = attr.value == "true";
     else if (attr.name == "promptWaitTime")
     {
       if(!isNaN(attr.value))
         this.promptWaitTime = parseInt(attr.value);
     }
-    else if (attr.name == "showSurvey")
-      this.showSurvey = attr.value == "true";
+    else if (attr.name == "unsupported")
+      this.unsupported = attr.value == "true";
     else if (attr.name == "version") {
       // Prevent version from replacing displayVersion if displayVersion is
       // present in the update xml.
       if (!this.displayVersion)
         this.displayVersion = attr.value;
     }
     else {
       this[attr.name] = attr.value;
@@ -1766,17 +1768,16 @@ Update.prototype = {
     update.setAttribute("installDate", this.installDate);
     update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
     update.setAttribute("isOSUpdate", this.isOSUpdate);
     update.setAttribute("name", this.name);
     update.setAttribute("serviceURL", this.serviceURL);
     update.setAttribute("showNeverForVersion", this.showNeverForVersion);
     update.setAttribute("showPrompt", this.showPrompt);
     update.setAttribute("promptWaitTime", this.promptWaitTime);
-    update.setAttribute("showSurvey", this.showSurvey);
     update.setAttribute("type", this.type);
     // for backwards compatibility in case the user downgrades
     update.setAttribute("version", this.displayVersion);
 
     // Optional attributes
     if (this.billboardURL)
       update.setAttribute("billboardURL", this.billboardURL);
     if (this.detailsURL)
@@ -1784,16 +1785,18 @@ Update.prototype = {
     if (this.licenseURL)
       update.setAttribute("licenseURL", this.licenseURL);
     if (this.platformVersion)
       update.setAttribute("platformVersion", this.platformVersion);
     if (this.previousAppVersion)
       update.setAttribute("previousAppVersion", this.previousAppVersion);
     if (this.statusText)
       update.setAttribute("statusText", this.statusText);
+    if (this.unsupported)
+      update.setAttribute("unsupported", this.unsupported);
     updates.documentElement.appendChild(update);
 
     for (var p in this._properties) {
       if (this._properties[p].present)
         update.setAttribute(p, this._properties[p].data);
     }
 
     for (var i = 0; i < this.patchCount; ++i)
@@ -2371,16 +2374,19 @@ UpdateService.prototype = {
    * @param   updates
    *          An array of available nsIUpdate items
    * @return  The nsIUpdate to offer.
    */
   selectUpdate: function AUS_selectUpdate(updates) {
     if (updates.length == 0)
       return null;
 
+    if (updates.length == 1 && updates[0].unsupported)
+      return updates[0];
+
     // Choose the newest of the available minor and major updates.
     var majorUpdate = null;
     var minorUpdate = null;
     var vc = Services.vc;
 
     updates.forEach(function(aUpdate) {
       // Ignore updates for older versions of the application and updates for
       // the same version of the application with the same build ID.
@@ -2448,31 +2454,43 @@ UpdateService.prototype = {
 #ifdef MOZ_WIDGET_GONK
       // For gonk, the user isn't necessarily aware of the update, so we need
       // to show the prompt to make sure.
       this._showPrompt(um.activeUpdate);
 #endif
       return;
     }
 
-    var update = this.selectUpdate(updates, updates.length);
-    if (!update)
-      return;
-
     var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
     if (!updateEnabled) {
       LOG("UpdateService:_selectAndInstallUpdate - not prompting because " +
           "update is disabled");
       return;
     }
 
     if (!gMetroUpdatesEnabled) {
       return;
     }
 
+    var update = this.selectUpdate(updates, updates.length);
+    if (!update) {
+      return;
+    }
+
+    if (update.unsupported) {
+      LOG("UpdateService:_selectAndInstallUpdate - update not supported for " +
+          "this system");
+      if (!getPref("getBoolPref", PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, false)) {
+        LOG("UpdateService:_selectAndInstallUpdate - notifying that the " +
+            "update is not supported for this system");
+        this._showPrompt(update);
+      }
+      return;
+    }
+
     if (!gCanApplyUpdates) {
       LOG("UpdateService:_selectAndInstallUpdate - the user is unable to " +
           "apply updates... prompting");
       this._showPrompt(update);
       return;
     }
 
     /**
@@ -3296,34 +3314,34 @@ Checker.prototype = {
   get _updates() {
     var updatesElement = this._request.responseXML.documentElement;
     if (!updatesElement) {
       LOG("Checker:_updates get - empty updates document?!");
       return [];
     }
 
     if (updatesElement.nodeName != "updates") {
-      LOG("Checker:updates get - unexpected node name!");
+      LOG("Checker:_updates get - unexpected node name!");
       throw new Error("Unexpected node name, expected: updates, got: " +
                       updatesElement.nodeName);
     }
 
     const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
     var updates = [];
     for (var i = 0; i < updatesElement.childNodes.length; ++i) {
       var updateElement = updatesElement.childNodes.item(i);
       if (updateElement.nodeType != ELEMENT_NODE ||
           updateElement.localName != "update")
         continue;
 
       updateElement.QueryInterface(Ci.nsIDOMElement);
       try {
         var update = new Update(updateElement);
       } catch (e) {
-        LOG("Checker:updates get - invalid <update/>, ignoring...");
+        LOG("Checker:_updates get - invalid <update/>, ignoring...");
         continue;
       }
       update.serviceURL = this.getUpdateURL(this._forced);
       update.channel = UpdateChannel.get();
       updates.push(update);
     }
 
     return updates;
@@ -3373,17 +3391,17 @@ Checker.prototype = {
       gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
 
       if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
         Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
 
       if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
         Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
 
-      // Tell the Update Service about the updates
+      // Tell the callback about the updates
       this._callback.onCheckComplete(event.target, updates, updates.length);
     }
     catch (e) {
       LOG("Checker:onLoad - there was a problem checking for updates. " +
           "Exception: " + e);
       var request = event.target;
       var status = this._getChannelStatus(request);
       LOG("Checker:onLoad - request.status: " + status);
--- a/toolkit/mozapps/update/nsUpdateTimerManager.js
+++ b/toolkit/mozapps/update/nsUpdateTimerManager.js
@@ -77,18 +77,18 @@ TimerManager.prototype = {
    * The set of registered timers.
    */
   _timers: { },
 
   /**
    * See nsIObserver.idl
    */
   observe: function TM_observe(aSubject, aTopic, aData) {
-    // Prevent setting the timer interval to a value of less than 60 seconds.
-    var minInterval = 60000;
+    // Prevent setting the timer interval to a value of less than 30 seconds.
+    var minInterval = 30000;
     // Prevent setting the first timer interval to a value of less than 10
     // seconds.
     var minFirstInterval = 10000;
     switch (aTopic) {
     case "utm-test-init":
       // Enforce a minimum timer interval of 500 ms for tests and fall through
       // to profile-after-change to initialize the timer.
       minInterval = 500;
--- a/toolkit/themes/linux/mozapps/update/updates.css
+++ b/toolkit/themes/linux/mozapps/update/updates.css
@@ -41,16 +41,31 @@ wizardpage {
   margin-top: 0 !important;
 }
 
 #licenseContent, #incompatibleListbox {
   -moz-margin-start: 6px;
   -moz-margin-end: 6px;
 }
 
+.inline-link {
+  color: -moz-nativehyperlinktext;
+  text-decoration: none;
+}
+
+.inline-link:hover {
+  text-decoration: underline;
+}
+
+/* Unsupported Page */
+#unsupportedLabel, #unsupportedLinkLabel {
+  -moz-margin-start: 0;
+  -moz-padding-start: 0;
+}
+
 /* Update Found Basic Page */
 #updateName, #updateFinishedName {
   font-weight: bold;
   font-size: larger;
 }
 
 /* License Page */
 #licenseContent {
--- a/toolkit/themes/osx/mozapps/update/updates.css
+++ b/toolkit/themes/osx/mozapps/update/updates.css
@@ -66,16 +66,31 @@ wizardpage {
   margin-bottom: 6px;
 }
 
 #licenseContent, #incompatibleListbox {
   -moz-margin-start: 6px;
   -moz-margin-end: 6px;
 }
 
+.inline-link {
+  color: -moz-nativehyperlinktext;
+  text-decoration: none;
+}
+
+.inline-link:hover {
+  text-decoration: underline;
+}
+
+/* Unsupported Page */
+#unsupportedLabel, #unsupportedLinkLabel {
+  -moz-margin-start: 0;
+  -moz-padding-start: 0;
+}
+
 /* Update Found Basic Page */
 #updateName, #updateFinishedName {
   font-weight: bold;
   font-size: larger;
 }
 
 /* License Page */
 #licenseContent {
--- a/toolkit/themes/windows/mozapps/update/updates.css
+++ b/toolkit/themes/windows/mozapps/update/updates.css
@@ -41,16 +41,31 @@ wizardpage {
   margin-top: 0 !important;
 }
 
 #licenseContent, #incompatibleListbox {
   -moz-margin-start: 6px;
   -moz-margin-end: 6px;
 }
 
+.inline-link {
+  color: -moz-nativehyperlinktext;
+  text-decoration: none;
+}
+
+.inline-link:hover {
+  text-decoration: underline;
+}
+
+/* Unsupported Page */
+#unsupportedLabel, #unsupportedLinkLabel {
+  -moz-margin-start: 0;
+  -moz-padding-start: 0;
+}
+
 /* Update Found Basic Page */
 #updateName, #updateFinishedName {
   font-weight: bold;
   font-size: larger;
 }
 
 /* License Page */
 #licenseContent {