Bug 737368, part 3: Implement an update prompter (v0) that simply restarts Gecko when an update is downloaded. Not what we want in the log term. r=fabrice
authorChris Jones <jones.chris.g@gmail.com>
Wed, 21 Mar 2012 15:50:53 -0700
changeset 93287 faf8114e0700d189ca0802f10cc5aeadc6aa03b8
parent 93286 5ac6a3d9139485f8c4124c71ae0d1be344cd30f6
child 93288 41f1b58186b4adcf8034a934580a2a588a999b0e
push idunknown
push userunknown
push dateunknown
reviewersfabrice
bugs737368
milestone14.0a1
Bug 737368, part 3: Implement an update prompter (v0) that simply restarts Gecko when an update is downloaded. Not what we want in the log term. r=fabrice
b2g/chrome/content/shell.js
b2g/components/B2GComponents.manifest
b2g/components/Makefile.in
b2g/components/UpdatePrompt.js
b2g/installer/package-manifest.in
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -105,17 +105,17 @@ var shell = {
         return url;
     }
     return null;
   },
 
   start: function shell_init() {
     let homeURL = this.homeURL;
     if (!homeURL) {
-      let msg = 'Fatal error during startup: [No homescreen found]';
+      let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       return alert(msg);
     }
 
     ['keydown', 'keypress', 'keyup'].forEach((function listenKey(type) {
       window.addEventListener(type, this, false, true);
       window.addEventListener(type, this, true, true);
     }).bind(this));
 
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -9,12 +9,18 @@ category JavaScript-navigator-property m
 # AlertsService.js
 component {5dce03b2-8faa-4b6e-9242-6ddb0411750c} AlertsService.js
 contract @mozilla.org/alerts-service;1 {5dce03b2-8faa-4b6e-9242-6ddb0411750c}
 
 # ContentPermissionPrompt.js
 component {8c719f03-afe0-4aac-91ff-6c215895d467} ContentPermissionPrompt.js
 contract @mozilla.org/content-permission/prompt;1 {8c719f03-afe0-4aac-91ff-6c215895d467}
 
+#ifdef MOZ_UPDATER
+# UpdatePrompt.js
+component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
+contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
+#endif
+
 # MozKeyboard.js
 component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
 contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
 category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
--- a/b2g/components/Makefile.in
+++ b/b2g/components/Makefile.in
@@ -51,9 +51,13 @@ XPIDLSRCS = \
 EXTRA_PP_COMPONENTS = \
         B2GComponents.manifest \
         CameraContent.js \
         AlertsService.js \
         ContentPermissionPrompt.js \
         MozKeyboard.js \
         $(NULL)
 
+ifdef MOZ_UPDATER
+EXTRA_PP_COMPONENTS += UpdatePrompt.js
+endif
+
 include $(topsrcdir)/config/rules.mk
copy from mobile/android/components/UpdatePrompt.js
copy to b2g/components/UpdatePrompt.js
--- a/mobile/android/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -1,349 +1,65 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Update Prompt.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mark Finkle <mfinkle@mozilla.com>
- *   Alex Pakhotin <alexp@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: Java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-const UPDATE_NOTIFICATION_NAME = "update-app";
-const UPDATE_NOTIFICATION_ICON = "drawable://alert_download_progress";
-
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-
-XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
-  return Services.strings.createBundle("chrome://mozapps/locale/update/updates.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function aus_gBrandBundle() {
-  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function aus_gBrowserBundle() {
-  return Services.strings.createBundle("chrome://browser/locale/browser.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "AddonManager", function() {
-  Cu.import("resource://gre/modules/AddonManager.jsm");
-  return AddonManager;
-});
-
-XPCOMUtils.defineLazyGetter(this, "LocaleRepository", function() {
-  Cu.import("resource://gre/modules/LocaleRepository.jsm");
-  return LocaleRepository;
-});
-
-function getPref(func, preference, defaultValue) {
-  try {
-    return Services.prefs[func](preference);
-  } catch (e) {}
-  return defaultValue;
-}
-
-function sendMessageToJava(aMsg) {
-  let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(aMsg));
-  return JSON.parse(data);
-}
-
-// -----------------------------------------------------------------------
-// Update Prompt
-// -----------------------------------------------------------------------
+const VERBOSE = 1;
+let log =
+  VERBOSE ?
+  function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
+  function log_noop(msg) { };
 
 function UpdatePrompt() { }
 
 UpdatePrompt.prototype = {
   classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, Ci.nsIRequestObserver, Ci.nsIProgressEventSink]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt]),
+
+  // nsIUpdatePrompt
 
-  get _enabled() {
-    return !getPref("getBoolPref", "app.update.silent", false);
-  },
+  // FIXME/bug 737601: we should have users opt-in to downloading
+  // updates when on a billed pipe.  Initially, opt-in for 3g, but
+  // that doesn't cover all cases.
+  checkForUpdates: function UP_checkForUpdates() { },
+  showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { },
 
-  _showNotification: function UP__showNotif(aUpdate, aTitle, aText, aImageUrl, aMode) {
-    let observer = {
-      updatePrompt: this,
-      observe: function (aSubject, aTopic, aData) {
-        switch (aTopic) {
-          case "alertclickcallback":
-            this.updatePrompt._handleUpdate(aUpdate, aMode);
-            break;
-        }
-      }
-    };
+  showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
+    // FIXME/bug 737598: we should let the user request that the
+    // update be applied later, e.g. if they're in the middle of a
+    // phone call ;).
+
+    log("Update downloaded, restarting to apply it");
 
-    let notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    notifier.showAlertNotification(aImageUrl, aTitle, aText, true, "", observer, UPDATE_NOTIFICATION_NAME);
+    let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
+    // NB: on Gonk, we rely on the system process manager to restart
+    // us.  Trying to restart here would conflict with the process
+    // manager.  We should be using a runtime check to detect Gonk
+    // instead of this gross ifdef, but the ifdef works for now.
+    appStartup.quit(appStartup.eForceQuit
+#ifndef ANDROID
+                    | appStartup.eRestart
+#endif
+      );
   },
 
-  _handleUpdate: function UP__handleUpdate(aUpdate, aMode) {
-    if (aMode == "available") {
-      let window = Services.wm.getMostRecentWindow("navigator:browser");
-      let title = gUpdateBundle.GetStringFromName("updatesfound_" + aUpdate.type + ".title");
-      let brandName = gBrandBundle.GetStringFromName("brandShortName");
-
-      // Unconditionally use the "major" type here as for now it is always a new version
-      // without additional description required for a minor update message
-      let message = gUpdateBundle.formatStringFromName("intro_major", [brandName, aUpdate.displayVersion], 2);
-      let button0 = gUpdateBundle.GetStringFromName("okButton");
-      let button1 = gUpdateBundle.GetStringFromName("askLaterButton");
-      let prompt = Services.prompt;
-      let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_IS_STRING;
+  showUpdateInstalled: function UP_showUpdateInstalled() { },
 
-      let download = (prompt.confirmEx(window, title, message, flags, button0, button1, null, null, {value: false}) == 0);
-      if (download) {
-        // Start downloading the update in the background
-        let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-        if (aus.downloadUpdate(aUpdate, true) != "failed") {
-          let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [aUpdate.name], 1);
-          this._showNotification(aUpdate, title, "", UPDATE_NOTIFICATION_ICON, "download");
-
-          // Add this UI as a listener for active downloads
-          aus.addDownloadListener(this);
-        }
-      }
-    } else if(aMode == "downloaded") {
-      // Notify all windows that an application quit has been requested
-      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
-      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
-      // If nothing aborted, restart the app
-      if (cancelQuit.data == false) {
-        sendMessageToJava({
-          gecko: {
-            type: "Update:Restart"
-          }
-        });
-      }
+  showUpdateError: function UP_showUpdateError(aUpdate) {
+    if (aUpdate.state == "failed") {
+      log("Failed to download update");
     }
   },
 
-  _updateDownloadProgress: function UP__updateDownloadProgress(aProgress, aTotal) {
-    let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    if (progressListener)
-      progressListener.onProgress(UPDATE_NOTIFICATION_NAME, aProgress, aTotal);
-  },
-
-  // -------------------------
-  // nsIUpdatePrompt interface
-  // -------------------------
-
-  // Right now this is used only to check for updates in progress
-  checkForUpdates: function UP_checkForUpdates() {
-    if (!this._enabled)
-      return;
-
-    let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-    if (!aus.isDownloading)
-      return;
-
-    let updateManager = Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager);
-
-    let updateName = updateManager.activeUpdate ? updateManager.activeUpdate.name : gBrandBundle.GetStringFromName("brandShortName");
-    let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [updateName], 1);
-
-    this._showNotification(updateManager.activeUpdate, title, "", UPDATE_NOTIFICATION_ICON, "downloading");
-
-    aus.removeDownloadListener(this); // just in case it's already added
-    aus.addDownloadListener(this);
-  },
-
-  showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
-    if (!this._enabled)
-      return;
-
-    const PREF_APP_UPDATE_SKIPNOTIFICATION = "app.update.skipNotification";
-
-    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SKIPNOTIFICATION) &&
-        Services.prefs.getBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION)) {
-      Services.prefs.setBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION, false);
-
-      // Notification was already displayed and clicked, so jump to the next step:
-      // ask the user about downloading update
-      this._handleUpdate(aUpdate, "available");
-      return;
-    }
-
-    let stringsPrefix = "updateAvailable_" + aUpdate.type + ".";
-    let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
-    let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "available");
-  },
-
-  showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
-    if (!this._enabled)
-      return;
-
-    // uninstall all installed locales
-    AddonManager.getAddonsByTypes(["locale"], (function (aAddons) {
-      if (aAddons.length > 0) {
-        let listener = this.getAddonListener(aUpdate, this);
-        AddonManager.addAddonListener(listener);  
-        aAddons.forEach(function(aAddon) {
-          listener._uninstalling.push(aAddon.id);
-          aAddon.uninstall();
-        }, this);
-      } else {
-        this._showDownloadedNotification(aUpdate);
-      }
-    }).bind(this));
-  },
-
-  _showDownloadedNotification: function UP_showDlNotification(aUpdate) {
-    let stringsPrefix = "updateDownloaded_" + aUpdate.type + ".";
-    let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
-    let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "downloaded");
-  },
-
-  _uninstalling: [],
-  _installing: [],
-  _currentUpdate: null,
-
-  _reinstallLocales: function UP_reinstallLocales(aUpdate, aListener, aPending) {
-    LocaleRepository.getLocales((function(aLocales) {
-      aLocales.forEach(function(aLocale, aIndex, aArray) {
-        let index = aPending.indexOf(aLocale.addon.id);
-        if (index > -1) {
-          aListener._installing.push(aLocale.addon.id);
-          aLocale.addon.install.install();
-        }
-      }, this);
-      // store the buildid of these locales so that we can disable locales when the
-      // user updates through a non-updater channel
-      Services.prefs.setCharPref("extensions.compatability.locales.buildid", aUpdate.buildID);
-    }).bind(this), { buildID: aUpdate.buildID });
-  },
-
-  showUpdateInstalled: function UP_showUpdateInstalled() {
-    if (!this._enabled || !getPref("getBoolPref", "app.update.showInstalledUI", false))
-      return;
-
-    let title = gBrandBundle.GetStringFromName("brandShortName");
-    let text = gUpdateBundle.GetStringFromName("installSuccess");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "installed");
-  },
-
-  showUpdateError: function UP_showUpdateError(aUpdate) {
-    if (!this._enabled)
-      return;
-
-    if (aUpdate.state == "failed") {
-      var title = gBrandBundle.GetStringFromName("brandShortName");
-      let text = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
-      let imageUrl = "";
-      this._showNotification(aUpdate, title, text, imageUrl, "error");
-    }
-  },
-
-  showUpdateHistory: function UP_showUpdateHistory(aParent) {
-    // NOT IMPL
-  },
-  
-  // ----------------------------
-  // nsIRequestObserver interface
-  // ----------------------------
-  
-  // When the data transfer begins
-  onStartRequest: function(request, context) {
-    // NOT IMPL
-  },
-
-  // When the data transfer ends
-  onStopRequest: function(request, context, status) {
-    let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    if (progressListener)
-      progressListener.onCancel(UPDATE_NOTIFICATION_NAME);
-
-
-    let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-    aus.removeDownloadListener(this);
-  },
-
-  // ------------------------------
-  // nsIProgressEventSink interface
-  // ------------------------------
-  
-  // When new data has been downloaded
-  onProgress: function(request, context, progress, maxProgress) {
-    this._updateDownloadProgress(progress, maxProgress);
-  },
-
-  // When we have new status text
-  onStatus: function(request, context, status, statusText) {
-    // NOT IMPL
-  },
-
-  // -------------------------------
-  // AddonListener
-  // -------------------------------
-  getAddonListener: function(aUpdate, aUpdatePrompt) {
-    return {
-      _installing: [],
-      _uninstalling: [],
-      onInstalling: function(aAddon, aNeedsRestart) {
-        let index = this._installing.indexOf(aAddon.id);
-        if (index > -1)
-          this._installing.splice(index, 1);
-    
-        if (this._installing.length == 0) {
-          aUpdatePrompt._showDownloadedNotification(aUpdate);
-          AddonManager.removeAddonListener(this);
-        }
-      },
-    
-      onUninstalling: function(aAddon, aNeedsRestart) {
-        let pending = [];
-        let index = this._uninstalling.indexOf(aAddon.id);
-        if (index > -1) {
-          pending.push(aAddon.id);
-          this._uninstalling.splice(index, 1);
-        }
-        if (this._uninstalling.length == 0)
-          aUpdatePrompt._reinstallLocales(aUpdate, this, pending);
-      }
-    }
-  }
-
+  showUpdateHistory: function UP_showUpdateHistory(aParent) { },
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -615,9 +615,12 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/chrome/icons/
 @BINPATH@/chrome/chrome@JAREXT@
 @BINPATH@/chrome/chrome.manifest
 @BINPATH@/components/B2GComponents.manifest
 @BINPATH@/components/B2GComponents.xpt
 @BINPATH@/components/CameraContent.js
 @BINPATH@/components/AlertsService.js
 @BINPATH@/components/ContentPermissionPrompt.js
+#ifdef MOZ_UPDATER
+@BINPATH@/components/UpdatePrompt.js
+#endif
 @BINPATH@/components/MozKeyboard.js