!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is the Update Service.
16 *
17 * The Initial Developer of the Original Code is Google Inc.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Ben Goodger <ben@mozilla.org> (Original Author)
23 * Asaf Romano <mozilla.mano@sent.com>
24 * Jeff Walden <jwalden+code@mit.edu>
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
41 const nsIIncrementalDownload = Components.interfaces.nsIIncrementalDownload;
42
43 const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
44
45 const PREF_UPDATE_MANUAL_URL = "app.update.url.manual";
46 const PREF_UPDATE_NAGTIMER_RESTART = "app.update.nagTimer.restart";
47 const PREF_APP_UPDATE_LOG_BRANCH = "app.update.log.";
48 const PREF_UPDATE_TEST_LOOP = "app.update.test.loop";
49 const PREF_UPDATE_NEVER_BRANCH = "app.update.never.";
50
51 const UPDATE_TEST_LOOP_INTERVAL = 2000;
52
53 const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
54
55 const STATE_DOWNLOADING = "downloading";
56 const STATE_PENDING = "pending";
57 const STATE_APPLYING = "applying";
58 const STATE_SUCCEEDED = "succeeded";
59 const STATE_DOWNLOAD_FAILED = "download-failed";
60 const STATE_FAILED = "failed";
61
62 const SRCEVT_FOREGROUND = 1;
63 const SRCEVT_BACKGROUND = 2;
64
65 var gConsole = null;
66 var gPref = null;
67 var gLogEnabled = { };
68
69 /**
70 * Logs a string to the error console.
71 * @param string
72 * The string to write to the error console..
73 */
LOG
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
74 function LOG(module, string) {
75 if (module in gLogEnabled || "all" in gLogEnabled) {
76 dump("*** " + module + ":" + string + "\n");
77 gConsole.logStringMessage(string);
78 }
79 }
80
81 /**
82 * Gets a preference value, handling the case where there is no default.
83 * @param func
84 * The name of the preference function to call, on nsIPrefBranch
85 * @param preference
86 * The name of the preference
87 * @param defaultValue
88 * The default value to return in the event the preference has
89 * no setting
90 * @returns The value of the preference, or undefined if there was no
91 * user or default value.
92 */
getPref
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
93 function getPref(func, preference, defaultValue) {
94 try {
95 return gPref[func](preference);
96 }
97 catch (e) {
98 LOG("General", "Failed to get preference " + preference);
99 }
100 return defaultValue;
101 }
102
103 /**
104 * A set of shared data and control functions for the wizard as a whole.
105 */
106 var gUpdates = {
107 /**
108 * The nsIUpdate object being used by this window (either for downloading,
109 * notification or both).
110 */
111 update: null,
112
113 /**
114 * The updates.properties <stringbundle> element.
115 */
116 strings: null,
117
118 /**
119 * The Application brandShortName (e.g. "Firefox")
120 */
121 brandName: null,
122
123 /**
124 * The <wizard> element
125 */
126 wiz: null,
127
128 /**
129 * Helper function for setButtons
130 * Resets button to original label & accesskey if string is null.
131 */
_setButton
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
132 _setButton: function(button, string) {
133 if (string) {
134 var label = this.strings.getString(string);
135 if (label.indexOf("%S") != -1)
136 label = label.replace(/%S/, this.brandName);
137 button.label = label;
138 button.setAttribute("accesskey",
139 this.strings.getString(string + ".accesskey"));
140 } else {
141 button.label = button.defaultLabel;
142 button.setAttribute("accesskey", button.defaultAccesskey);
143 }
144 },
145
146 /**
147 * Set the state for the Wizard's control buttons (labels and disabled
148 * state).
149 * @param backButtonString
150 * The property in the stringbundle containing the label
151 * to put on the Back button, or null for default.
152 * @param backButtonDisabled
153 * true if the Back button should be disabled, false otherwise
154 * @param nextButtonString
155 * The property in the stringbundle containing the label
156 * to put on the Next button, or null for default.
157 * @param nextButtonDisabled
158 * true if the Next button should be disabled, false otherwise
159 * @param finishButtonString
160 * The property in the stringbundle containing the label
161 * to put on the Finish button, or null for default.
162 * @param finishButtonDisabled
163 * true if the Finish button should be disabled, false otherwise
164 * @param cancelButtonString
165 * The property in the stringbundle containing the label
166 * to put on the Cancel button, or null for default.
167 * @param cancelButtonDisabled
168 * true if the Cancel button should be disabled, false otherwise
169 * @param hideBackAndCancelButtons
170 * true if the Cancel button should be hidden, false otherwise
171 * @param extraButton1String
172 * The property in the stringbundle containing the label to put
173 * on the first Extra button, if present, or null for default.
174 * @param extraButton1Disabled
175 * true if the first Extra button should be disabled,
176 * false otherwise
177 * @param extraButton2String
178 * The property in the stringbundle containing the label to put
179 * on the second Extra button, if present, or null for default.
180 * @param extraButton2Disabled
181 * true if the second Extra button should be disabled,
182 * false otherwise
183 */
setButtons
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
184 setButtons: function(backButtonString, backButtonDisabled,
185 nextButtonString, nextButtonDisabled,
186 finishButtonString, finishButtonDisabled,
187 cancelButtonString, cancelButtonDisabled,
188 hideBackAndCancelButtons,
189 extraButton1String, extraButton1Disabled,
190 extraButton2String, extraButton2Disabled) {
191 var bb = this.wiz.getButton("back");
192 var bn = this.wiz.getButton("next");
193 var bf = this.wiz.getButton("finish");
194 var bc = this.wiz.getButton("cancel");
195 var be1 = this.wiz.getButton("extra1");
196 var be2 = this.wiz.getButton("extra2");
197
198 this._setButton(bb, backButtonString);
199 this._setButton(bn, nextButtonString);
200 this._setButton(bf, finishButtonString);
201 this._setButton(bc, cancelButtonString);
202 this._setButton(be1, extraButton1String);
203 this._setButton(be2, extraButton2String);
204
205 // update button state using the wizard commands
206 this.wiz.canRewind = !backButtonDisabled;
207 // The Finish and Next buttons are never exposed at the same time
208 if (this.wiz.onLastPage)
209 this.wiz.canAdvance = !finishButtonDisabled;
210 else
211 this.wiz.canAdvance = !nextButtonDisabled;
212
213 bf.disabled = finishButtonDisabled;
214 bc.disabled = cancelButtonDisabled;
215 be1.disabled = extraButton1Disabled;
216 be2.disabled = extraButton2Disabled;
217
218 // Show or hide the cancel and back buttons, since the first Extra
219 // button does this job.
220 bc.hidden = hideBackAndCancelButtons;
221 bb.hidden = hideBackAndCancelButtons;
222
223 // Show or hide the extra buttons
224 be1.hidden = extraButton1String == null;
225 be2.hidden = extraButton2String == null;
226 },
227
never
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
228 never: function () {
229 // if the user hits "Never", we should not prompt them about this
230 // major version again, unless they manually do "Check for Updates..."
231 // which, will clear the "never" pref for the version presented
232 // so that if they do "Later", we will remind them later.
233 //
234 // fix for bug #359093
235 // version might one day come back from AUS as an
236 // arbitrary (and possibly non ascii) string, so we need to encode it
237 var neverPrefName = PREF_UPDATE_NEVER_BRANCH + encodeURIComponent(gUpdates.update.version);
238 gPref.setBoolPref(neverPrefName, true);
239 this.wiz.cancel();
240 },
241
later
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
242 later: function () {
243 // The user said "Later", so close the wizard
244 this.wiz.cancel();
245 },
246
247 /**
248 * A hash of |pageid| attribute to page object. Can be used to dispatch
249 * function calls to the appropriate page.
250 */
251 _pages: { },
252
253 /**
254 * Called when the user presses the "Finish" button on the wizard, dispatches
255 * the function call to the selected page.
256 */
onWizardFinish
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
257 onWizardFinish: function() {
258 var pageid = document.documentElement.currentPage.pageid;
259 if ("onWizardFinish" in this._pages[pageid])
260 this._pages[pageid].onWizardFinish();
261 },
262
263 /**
264 * Called when the user presses the "Cancel" button on the wizard, dispatches
265 * the function call to the selected page.
266 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
267 onWizardCancel: function() {
268 var pageid = document.documentElement.currentPage.pageid;
269 if ("onWizardCancel" in this._pages[pageid])
270 this._pages[pageid].onWizardCancel();
271 },
272
273 /**
274 * Called when the user presses the "Next" button on the wizard, dispatches
275 * the function call to the selected page.
276 */
onWizardNext
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
277 onWizardNext: function() {
278 var cp = document.documentElement.currentPage;
279 if (!cp)
280 return;
281 var pageid = cp.pageid;
282 if ("onWizardNext" in this._pages[pageid])
283 this._pages[pageid].onWizardNext();
284 },
285
286 /**
287 * The checking process that spawned this update UI. There are two types:
288 * SRCEVT_FOREGROUND:
289 * Some user-generated event caused this UI to appear, e.g. the Help
290 * menu item or the button in preferences. When in this mode, the UI
291 * should remain active for the duration of the download.
292 * SRCEVT_BACKGROUND:
293 * A background update check caused this UI to appear, probably because
294 * incompatibilities in Extensions or other addons were discovered and
295 * the user's consent to continue was required. When in this mode, the
296 * UI will disappear after the user's consent is obtained.
297 */
298 sourceEvent: SRCEVT_FOREGROUND,
299
300 /**
301 * The global error message - the reason the update failed. This is human
302 * readable text, used to initialize the error page.
303 */
304 errorMessage: "",
305
306 /**
307 * Helper function for onLoad
308 * Saves default button label & accesskey for use by _setButton
309 */
_cacheButtonStrings
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
310 _cacheButtonStrings: function (buttonName) {
311 var button = this.wiz.getButton(buttonName);
312 button.defaultLabel = button.label;
313 button.defaultAccesskey = button.getAttribute("accesskey");
314 },
315
316 /**
317 * Called when the wizard UI is loaded.
318 */
onLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
319 onLoad: function() {
320 this.wiz = document.documentElement;
321
322 gPref = Components.classes["@mozilla.org/preferences-service;1"]
323 .getService(Components.interfaces.nsIPrefBranch2);
324 gConsole = Components.classes["@mozilla.org/consoleservice;1"]
325 .getService(Components.interfaces.nsIConsoleService);
326 this._initLoggingPrefs();
327
328 this.strings = document.getElementById("updateStrings");
329 var brandStrings = document.getElementById("brandStrings");
330 this.brandName = brandStrings.getString("brandShortName");
331
332 var pages = gUpdates.wiz.childNodes;
333 for (var i = 0; i < pages.length; ++i) {
334 var page = pages[i];
335 if (page.localName == "wizardpage")
336 this._pages[page.pageid] = eval(page.getAttribute("object"));
337 }
338
339 // Cache the standard button labels in case we need to restore them
340 this._cacheButtonStrings("back");
341 this._cacheButtonStrings("next");
342 this._cacheButtonStrings("finish");
343 this._cacheButtonStrings("cancel");
344 this._cacheButtonStrings("extra1");
345 this._cacheButtonStrings("extra2");
346
347 // Advance to the Start page.
348 gUpdates.wiz.currentPage = this.startPage;
349 },
350
351 /**
352 * Initialize Logging preferences, formatted like so:
353 * app.update.log.<moduleName> = <true|false>
354 */
_initLoggingPrefs
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
355 _initLoggingPrefs: function() {
356 try {
357 var ps = Components.classes["@mozilla.org/preferences-service;1"]
358 .getService(Components.interfaces.nsIPrefService);
359 var logBranch = ps.getBranch(PREF_APP_UPDATE_LOG_BRANCH);
360 var modules = logBranch.getChildList("", { value: 0 });
361
362 for (var i = 0; i < modules.length; ++i) {
363 if (logBranch.prefHasUserValue(modules[i]))
364 gLogEnabled[modules[i]] = logBranch.getBoolPref(modules[i]);
365 }
366 }
367 catch (e) {
368 }
369 },
370
371 /**
372 * Return the <wizardpage> object that should be displayed first.
373 *
374 * This is determined by how we were called by the update prompt:
375 *
376 * U'Prompt Method: Arg0: Update State: Src Event: p'Failed: Result:
377 * showUpdateAvailable nsIUpdate obj -- background -- updatesfound
378 * showUpdateDownloaded nsIUpdate obj pending background -- finishedBackground
379 * showUpdateInstalled nsIUpdate obj succeeded either -- installed
380 * showUpdateError nsIUpdate obj failed either partial errorpatching
381 * showUpdateError nsIUpdate obj failed either complete errors
382 * checkForUpdates null -- foreground -- checking
383 * checkForUpdates null downloading foreground -- downloading
384 */
get_startPage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
385 get startPage() {
386 if (window.arguments) {
387 var arg0 = window.arguments[0];
388 if (arg0 instanceof Components.interfaces.nsIUpdate) {
389 // If the first argument is a nsIUpdate object, we are notifying the
390 // user that the background checking found an update that requires
391 // their permission to install, and it's ready for download.
392 this.setUpdate(arg0);
393 var p = this.update.selectedPatch;
394 if (p) {
395 var state = p.state;
396 if (state == STATE_DOWNLOADING) {
397 var patchFailed = false;
398 try {
399 patchFailed = this.update.getProperty("patchingFailed");
400 }
401 catch (e) {
402 }
403 if (patchFailed == "partial") {
404 // If the system failed to apply the partial patch, show the
405 // screen which best describes this condition, which is triggered
406 // by the |STATE_FAILED| state.
407 state = STATE_FAILED;
408 }
409 else if (patchFailed == "complete") {
410 // Otherwise, if the complete patch failed, which is far less
411 // likely, show the error text held by the update object in the
412 // generic errors page, triggered by the |STATE_DOWNLOAD_FAILED|
413 // state.
414 state = STATE_DOWNLOAD_FAILED;
415 }
416 }
417
418 // Now select the best page to start with, given the current state of
419 // the Update.
420 switch (state) {
421 case STATE_PENDING:
422 this.sourceEvent = SRCEVT_BACKGROUND;
423 return document.getElementById("finishedBackground");
424 case STATE_SUCCEEDED:
425 return document.getElementById("installed");
426 case STATE_DOWNLOADING:
427 return document.getElementById("downloading");
428 case STATE_FAILED:
429 window.getAttention();
430 return document.getElementById("errorpatching");
431 case STATE_DOWNLOAD_FAILED:
432 case STATE_APPLYING:
433 return document.getElementById("errors");
434 }
435 }
436 return document.getElementById("updatesfound");
437 }
438 }
439 else {
440 var um =
441 Components.classes["@mozilla.org/updates/update-manager;1"].
442 getService(Components.interfaces.nsIUpdateManager);
443 if (um.activeUpdate) {
444 this.setUpdate(um.activeUpdate);
445 return document.getElementById("downloading");
446 }
447 }
448 return document.getElementById("checking");
449 },
450
451 /**
452 * Sets the Update object for this wizard
453 * @param update
454 * The update object
455 */
setUpdate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
456 setUpdate: function(update) {
457 this.update = update;
458 if (this.update)
459 this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
460 },
461
462 /**
463 * Registers a timer to nag the user about something relating to update
464 * @param timerID
465 * The ID of the timer to register, used for persistence
466 * @param timerInterval
467 * The interval of the timer
468 * @param methodName
469 * The method to call on the Update Prompter when the timer fires
470 */
registerNagTimer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
471 registerNagTimer: function(timerID, timerInterval, methodName) {
472 // Remind the user to restart their browser in a little bit.
473 var tm =
474 Components.classes["@mozilla.org/updates/timer-manager;1"].
475 getService(Components.interfaces.nsIUpdateTimerManager);
476
477 /**
478 * An object implementing nsITimerCallback that uses the Update Prompt
479 * component to notify the user about some event relating to app update
480 * that they should take action on.
481 * @param update
482 * The nsIUpdate object in question
483 * @param methodName
484 * The name of the method on the Update Prompter that should be
485 * called
486 * @constructor
487 */
Callback
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
488 function Callback(update, methodName) {
489 this._update = update;
490 this._methodName = methodName;
491 this._prompter =
492 Components.classes["@mozilla.org/updates/update-prompt;1"].
493 createInstance(Components.interfaces.nsIUpdatePrompt);
494 }
495 Callback.prototype = {
496 /**
497 * The Update we should nag about downloading
498 */
499 _update: null,
500
501 /**
502 * The Update prompter we can use to notify the user
503 */
504 _prompter: null,
505
506 /**
507 * The method on the update prompt that should be called
508 */
509 _methodName: "",
510
511 /**
512 * Called when the timer fires. Notifies the user about whichever event
513 * they need to be nagged about (e.g. update available, please restart,
514 * etc).
515 */
notify
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
516 notify: function(timerCallback) {
517 if (methodName in this._prompter)
518 this._prompter[methodName](null, this._update);
519 }
520 }
521 tm.registerTimer(timerID, (new Callback(gUpdates.update, methodName)),
522 timerInterval);
523 }
524 }
525
526 /**
527 * The "Checking for Updates" page. Provides feedback on the update checking
528 * process.
529 */
530 var gCheckingPage = {
531 /**
532 * The nsIUpdateChecker that is currently checking for updates. We hold onto
533 * this so we can cancel the update check if the user closes the window.
534 */
535 _checker: null,
536
537 /**
538 * Starts the update check when the page is shown.
539 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
540 onPageShow: function() {
541 gUpdates.setButtons(null, true, null, true, null, true,
542 null, false, false, null,
543 false, null, false);
544 this._checker =
545 Components.classes["@mozilla.org/updates/update-checker;1"].
546 createInstance(Components.interfaces.nsIUpdateChecker);
547 this._checker.checkForUpdates(this.updateListener, true);
548 },
549
550 /**
551 * The user has closed the window, either by pressing cancel or using a Window
552 * Manager control, so stop checking for updates.
553 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
554 onWizardCancel: function() {
555 if (this._checker) {
556 const nsIUpdateChecker = Components.interfaces.nsIUpdateChecker;
557 this._checker.stopChecking(nsIUpdateChecker.CURRENT_CHECK);
558 }
559 },
560
561 /**
562 * An object implementing nsIUpdateCheckListener that is notified as the
563 * update check commences.
564 */
565 updateListener: {
566 /**
567 * See nsIUpdateCheckListener
568 */
onProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
569 onProgress: function(request, position, totalSize) {
570 var pm = document.getElementById("checkingProgress");
571 checkingProgress.setAttribute("mode", "normal");
572 checkingProgress.setAttribute("value", Math.floor(100 * (position/totalSize)));
573 },
574
575 /**
576 * See nsIUpdateCheckListener
577 */
onCheckComplete
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
578 onCheckComplete: function(request, updates, updateCount) {
579 var aus = Components.classes["@mozilla.org/updates/update-service;1"]
580 .getService(Components.interfaces.nsIApplicationUpdateService);
581 gUpdates.setUpdate(aus.selectUpdate(updates, updates.length));
582 if (!gUpdates.update) {
583 LOG("UI:CheckingPage",
584 "Could not select an appropriate update, either because there " +
585 "were none, or |selectUpdate| failed.");
586 var checking = document.getElementById("checking");
587 checking.setAttribute("next", "noupdatesfound");
588 }
589 gUpdates.wiz.canAdvance = true;
590 gUpdates.wiz.advance();
591 },
592
593 /**
594 * See nsIUpdateCheckListener
595 */
onError
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
596 onError: function(request, update) {
597 LOG("UI:CheckingPage", "UpdateCheckListener: error");
598
599 gUpdates.setUpdate(update);
600
601 gUpdates.wiz.currentPage = document.getElementById("errors");
602 },
603
604 /**
605 * See nsISupports.idl
606 */
QueryInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
607 QueryInterface: function(aIID) {
608 if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
609 !aIID.equals(Components.interfaces.nsISupports))
610 throw Components.results.NS_ERROR_NO_INTERFACE;
611 return this;
612 }
613 }
614 };
615
616 /**
617 * The "No Updates Are Available" page
618 */
619 var gNoUpdatesPage = {
620 /**
621 * Initialize
622 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
623 onPageShow: function() {
624 gUpdates.setButtons(null, true, null, true, null, false, "hideButton",
625 true, false, null, false, null, false);
626 gUpdates.wiz.getButton("finish").focus();
627 }
628 };
629
630 /**
631 * The "Updates Are Available" page. Provides the user information about the
632 * available update, extensions it might make incompatible, and a means to
633 * continue downloading and installing it.
634 */
635 var gUpdatesAvailablePage = {
636 /**
637 * An array of installed addons incompatible with this update.
638 */
639 _incompatibleItems: null,
640
641 /**
642 * The <license> binding (which we are also using for the details content)
643 */
644 _updateMoreInfoContent: null,
645
646 /**
647 * Initialize.
648 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
649 onPageShow: function() {
650 // disable the "next" button, as we don't want the user to
651 // be able to progress with the update util we show them the details
652 // which might include a warning about incompatible add-ons
653 // disable the "back" button, as well (note the call to setButtons()
654 // below does this, too). This just leaves the cancel button
655 // until we are ready
656 gUpdates.wiz.getButton("next").disabled = true;
657 gUpdates.wiz.getButton("back").disabled = true;
658
659 var updateName = gUpdates.strings.getFormattedString("updateName",
660 [gUpdates.brandName, gUpdates.update.version]);
661 if (gUpdates.update.channel == "nightly")
662 updateName = updateName + " " + gUpdates.update.buildID + " nightly";
663 var updateNameElement = document.getElementById("updateName");
664 updateNameElement.value = updateName;
665 var severity = gUpdates.update.type;
666 var updateTypeElement = document.getElementById("updateType");
667 updateTypeElement.setAttribute("severity", severity);
668
669 var intro;
670 if (severity == "major") {
671 // for major updates, use the brandName and the version for the intro
672 intro = gUpdates.strings.getFormattedString(
673 "introType_major_app_and_version",
674 [gUpdates.brandName, gUpdates.update.version]);
675
676 this._updateMoreInfoContent =
677 document.getElementById("updateMoreInfoContent");
678
679 // update_name and update_version need to be set before url
680 // so that when attempting to download the url, we can show
681 // the formatted "Download..." string
682 this._updateMoreInfoContent.update_name = gUpdates.brandName;
683 this._updateMoreInfoContent.update_version = gUpdates.update.version;
684 this._updateMoreInfoContent.url = gUpdates.update.detailsURL;
685 }
686 else {
687 // for minor updates, do not include the version
688 // just use the brandName for the intro
689 intro = gUpdates.strings.getFormattedString(
690 "introType_minor_app", [gUpdates.brandName]);
691
692 var updateMoreInfoURL = document.getElementById("updateMoreInfoURL");
693 updateMoreInfoURL.href = gUpdates.update.detailsURL;
694 }
695
696 var updateTitle = gUpdates.strings
697 .getString("updatesfound_" + severity + ".title");
698 gUpdates.wiz.currentPage.setAttribute("label", updateTitle);
699 // this is necessary to make this change to the label of the current
700 // wizard page show up
701 gUpdates.wiz._adjustWizardHeader();
702
703 while (updateTypeElement.hasChildNodes())
704 updateTypeElement.removeChild(updateTypeElement.firstChild);
705 updateTypeElement.appendChild(document.createTextNode(intro));
706
707 var em = Components.classes["@mozilla.org/extensions/manager;1"]
708 .getService(Components.interfaces.nsIExtensionManager);
709 var items = em.getIncompatibleItemList("", gUpdates.update.version,
710 gUpdates.update.platformVersion,
711 nsIUpdateItem.TYPE_ANY, false,
712 { });
713 if (items.length > 0) {
714 // There are addons that are incompatible with this update, so show the
715 // warning message.
716 var incompatibleWarning = document.getElementById("incompatibleWarning");
717 incompatibleWarning.hidden = false;
718
719 this._incompatibleItems = items;
720 }
721
722 // wait to show the additional details until after we do the check
723 // for add-on incompatibilty
724 this.onShowMoreDetails();
725
726 var licenseAccepted;
727 try {
728 licenseAccepted = gUpdates.update.getProperty("licenseAccepted");
729 }
730 catch (e) {
731 gUpdates.update.setProperty("licenseAccepted", "false");
732 licenseAccepted = false;
733 }
734
735 // only major updates show EULAs
736 if (gUpdates.update.type == "major" &&
737 gUpdates.update.licenseURL && !licenseAccepted)
738 gUpdates.wiz.currentPage.setAttribute("next", "license");
739
740 gUpdates.setButtons(null, true, "downloadButton_" + severity,
741 false, null, false,
742 null, false, true,
743 "notNowButton", false,
744 severity == "major" ? "neverButton" : null, false);
745 gUpdates.wiz.getButton("next").focus();
746 },
747
748 /**
749 * User clicked the "More Details..." button
750 */
onShowMoreDetails
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
751 onShowMoreDetails: function() {
752 var updateTypeElement = document.getElementById("updateType");
753 var moreInfoURL = document.getElementById("moreInfoURL");
754 var moreInfoContent = document.getElementById("moreInfoContent");
755
756 if (updateTypeElement.getAttribute("severity") == "major") {
757 moreInfoURL.hidden = true;
758 moreInfoContent.hidden = false;
759 document.getElementById("updateName").hidden = true;
760 document.getElementById("updateNameSep").hidden = true;
761 document.getElementById("upgradeEvangelism").hidden = true;
762 document.getElementById("upgradeEvangelismSep").hidden = true;
763
764 // clear the "never" pref for this version. this is to handle the
765 // scenario where the user clicked "never" for a major update
766 // and then at a later point, did "Check for Updates..."
767 // and then hit "Later". If we don't clear the "never" pref
768 // "Later" will never happen.
769 //
770 // fix for bug #359093
771 // version might one day come back from AUS as an
772 // arbitrary (and possibly non ascii) string, so we need to encode it
773 var neverPrefName = PREF_UPDATE_NEVER_BRANCH + encodeURIComponent(gUpdates.update.version);
774 gPref.setBoolPref(neverPrefName, false);
775 }
776 else {
777 moreInfoURL.hidden = false;
778 moreInfoContent.hidden = true;
779 }
780
781 // in order to prevent showing the blank xul:browser (<license> binding)
782 // delay setting the selected index of the detailsDeck until after
783 // we've set everything up. see bug #352400 for more details.
784 var detailsDeck = document.getElementById("detailsDeck");
785 detailsDeck.selectedIndex = 1;
786 },
787
788 /**
789 * When the user cancels the wizard or they decline the license
790 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
791 onWizardCancel: function() {
792 try {
793 // If the _updateMoreInfoContent was downloading, stop it.
794 if (this._updateMoreInfoContent)
795 this._updateMoreInfoContent.stopDownloading();
796 }
797 catch (ex) {
798 dump("XXX _updateMoreInfoContent.stopDownloading() failed: " + ex + "\n");
799 }
800 },
801
802 /**
803 * Show a list of extensions made incompatible by this update.
804 */
showIncompatibleItems
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
805 showIncompatibleItems: function() {
806 openDialog("chrome://mozapps/content/update/incompatible.xul", "",
807 "dialog,centerscreen,modal,titlebar", this._incompatibleItems);
808 }
809 };
810
811 /**
812 * The page which shows the user a license associated with an update. The
813 * user must agree to the terms of the license before continuing to install
814 * the update.
815 */
816 var gLicensePage = {
817 /**
818 * The <license> element
819 */
820 _licenseContent: null,
821
822 /**
823 * Initialize
824 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
825 onPageShow: function() {
826 this._licenseContent = document.getElementById("licenseContent");
827
828 // the license radiogroup is disabled until the EULA is downloaded
829 document.getElementById("acceptDeclineLicense").disabled = true;
830
831 gUpdates.setButtons(null, true, null, true, null, true, null,
832 false, false, null, false, null, false);
833
834 this._licenseContent.addEventListener("load", this.onLicenseLoad, false);
835 // update_name and update_version need to be set before url
836 // so that when attempting to download the url, we can show
837 // the formatted "Download..." string
838 this._licenseContent.update_name = gUpdates.brandName;
839 this._licenseContent.update_version = gUpdates.update.version;
840 this._licenseContent.url = gUpdates.update.licenseURL;
841 },
842
843 /**
844 * When the license document has loaded
845 */
onLicenseLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
846 onLicenseLoad: function() {
847 // on the load event, disable or enable the radiogroup based on
848 // the state of the licenseContent. note, you may get multiple
849 // onLoad events
850 document.getElementById("acceptDeclineLicense").disabled =
851 (gLicensePage._licenseContent.getAttribute("state") == "error");
852 },
853
854 /**
855 * When the user changes the state of the accept / decline radio group
856 */
onAcceptDeclineRadio
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
857 onAcceptDeclineRadio: function() {
858 var selectedIndex = document.getElementById("acceptDeclineLicense")
859 .selectedIndex;
860 // 0 == Accept, 1 == Decline
861 var licenseAccepted = (selectedIndex == 0);
862 gUpdates.wiz.getButton("next").disabled = !licenseAccepted;
863 gUpdates.wiz.canAdvance = licenseAccepted;
864 },
865
866 /**
867 * When the user accepts the license by hitting "Next"
868 */
onWizardNext
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
869 onWizardNext: function() {
870 try {
871 gUpdates.update.setProperty("licenseAccepted", "true");
872 var um =
873 Components.classes["@mozilla.org/updates/update-manager;1"].
874 getService(Components.interfaces.nsIUpdateManager);
875 um.saveUpdates();
876 }
877 catch (ex) {
878 dump("XXX ex " + ex + "\n");
879 }
880 },
881
882 /**
883 * When the user cancels the wizard or they decline the license
884 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
885 onWizardCancel: function() {
886 try {
887 // If the license was downloading, stop it.
888 if (this._licenseContent)
889 this._licenseContent.stopDownloading();
890 }
891 catch (ex) {
892 dump("XXX _licenseContent.stopDownloading() failed: " + ex + "\n");
893 }
894 }
895 };
896
897 /**
898 * Formats status messages for a download operation based on the progress
899 * of the download.
900 * @constructor
901 */
DownloadStatusFormatter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
902 function DownloadStatusFormatter() {
903 this._startTime = Math.floor((new Date()).getTime() / 1000);
904 this._elapsed = 0;
905
906 var us = gUpdates.strings;
907 this._statusFormat = us.getString("statusFormat");
908
909 this._progressFormat = us.getString("progressFormat");
910 this._progressFormatKBMB = us.getString("progressFormatKBMB");
911 this._progressFormatKBKB = us.getString("progressFormatKBKB");
912 this._progressFormatMBMB = us.getString("progressFormatMBMB");
913 this._progressFormatUnknownMB = us.getString("progressFormatUnknownMB");
914 this._progressFormatUnknownKB = us.getString("progressFormatUnknownKB");
915
916 this._rateFormat = us.getString("rateFormat");
917 this._rateFormatKBSec = us.getString("rateFormatKBSec");
918 this._rateFormatMBSec = us.getString("rateFormatMBSec");
919
920 this._timeFormat = us.getString("timeFormat");
921 this._longTimeFormat = us.getString("longTimeFormat");
922 this._shortTimeFormat = us.getString("shortTimeFormat");
923
924 this._remain = us.getString("remain");
925 this._unknownFilesize = us.getString("unknownFilesize");
926 }
927 DownloadStatusFormatter.prototype = {
928 /**
929 * Time when the download started (in seconds since epoch)
930 */
931 _startTime: 0,
932
933 /**
934 * Time elapsed since the start of the download operation (in seconds)
935 */
936 _elapsed: -1,
937
938 /**
939 * Transfer rate of the download
940 */
941 _rate: 0,
942
943 /**
944 * Transfer rate of the download, formatted as text
945 */
946 _rateFormatted: "",
947
948 /**
949 * Transfer rate, formatted into text container
950 */
951 _rateFormattedContainer: "",
952
953 /**
954 * Number of Kilobytes downloaded so far in the form:
955 * 376KB of 9.3MB
956 */
957 progress: "",
958
959 /**
960 * Format a human-readable status message based on the current download
961 * progress.
962 * @param currSize
963 * The current number of bytes transferred
964 * @param finalSize
965 * The total number of bytes to be transferred
966 * @returns A human readable status message, e.g.
967 * "3.4 of 4.7MB; 01:15 remain"
968 */
formatStatus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
969 formatStatus: function(currSize, finalSize) {
970 var now = Math.floor((new Date()).getTime() / 1000);
971
972 // 1) Determine the Download Progress in Kilobytes
973 var total = parseInt(finalSize/1024 + 0.5);
974 this.progress = this._formatKBytes(parseInt(currSize/1024 + 0.5), total);
975
976 var progress = this._replaceInsert(this._progressFormat, 1, this.progress);
977 var rateFormatted = "";
978
979 // 2) Determine the Transfer Rate
980 var oldElapsed = this._elapsed;
981 this._elapsed = now - this._startTime;
982 if (oldElapsed != this._elapsed) {
983 this._rate = this._elapsed ? Math.floor((currSize / 1024) / this._elapsed) : 0;
984 var isKB = true;
985 if (parseInt(this._rate / 1024) > 0) {
986 this._rate = (this._rate / 1024).toFixed(1);
987 isKB = false;
988 }
989 if (this._rate > 100)
990 this._rate = Math.round(this._rate);
991
992 if (this._rate) {
993 var format = isKB ? this._rateFormatKBSec : this._rateFormatMBSec;
994 this._rateFormatted = this._replaceInsert(format, 1, this._rate);
995 this._rateFormattedContainer = this._replaceInsert(" " + this._rateFormat, 1, this._rateFormatted);
996 }
997 }
998 progress = this._replaceInsert(progress, 2, this._rateFormattedContainer);
999
1000
1001 // 3) Determine the Time Remaining
1002 var remainingTime = "";
1003 if (this._rate && (finalSize > 0)) {
1004 remainingTime = Math.floor(((finalSize - currSize) / 1024) / this._rate);
1005 remainingTime = this._formatSeconds(remainingTime);
1006 remainingTime = this._replaceInsert(this._timeFormat, 1, remainingTime)
1007 remainingTime = this._replaceInsert(remainingTime, 2, this._remain);
1008 }
1009
1010 //
1011 // [statusFormat:
1012 // [progressFormat:
1013 // [[progressFormatKBKB|
1014 // progressFormatKBMB|
1015 // progressFormatMBMB|
1016 // progressFormatUnknownKB|
1017 // progressFormatUnknownMB
1018 // ][rateFormat]]
1019 // ][timeFormat]
1020 // ]
1021 var status = this._statusFormat;
1022 status = this._replaceInsert(status, 1, progress);
1023 status = this._replaceInsert(status, 2, remainingTime);
1024 return status;
1025 },
1026
1027 /**
1028 * Inserts a string into another string at the specified index, e.g. for
1029 * the format string var foo ="#1 #2 #3", |_replaceInsert(foo, 2, "test")|
1030 * returns "#1 test #3";
1031 * @param format
1032 * The format string
1033 * @param index
1034 * The Index to insert into
1035 * @param value
1036 * The value to insert
1037 * @returns The string with the value inserted.
1038 */
_replaceInsert
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1039 _replaceInsert: function(format, index, value) {
1040 return format.replace(new RegExp("#" + index), value);
1041 },
1042
1043 /**
1044 * Formats progress in the form of kilobytes transfered vs. total to
1045 * transfer.
1046 * @param currentKB
1047 * The current amount of data transfered, in kilobytes.
1048 * @param totalKB
1049 * The total amount of data that must be transfered, in kilobytes.
1050 * @returns A string representation of the progress, formatted according to:
1051 *
1052 * KB totalKB returns
1053 * x, < 1MB y < 1MB x of y KB
1054 * x, < 1MB y >= 1MB x KB of y MB
1055 * x, >= 1MB y >= 1MB x of y MB
1056 */
_formatKBytes
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1057 _formatKBytes: function(currentKB, totalKB) {
1058 var progressHasMB = parseInt(currentKB / 1024) > 0;
1059 var totalHasMB = parseInt(totalKB / 1024) > 0;
1060
1061 var format = "";
1062 if (!progressHasMB && !totalHasMB) {
1063 if (!totalKB) {
1064 format = this._progressFormatUnknownKB;
1065 format = this._replaceInsert(format, 1, currentKB);
1066 } else {
1067 format = this._progressFormatKBKB;
1068 format = this._replaceInsert(format, 1, currentKB);
1069 format = this._replaceInsert(format, 2, totalKB);
1070 }
1071 }
1072 else if (progressHasMB && totalHasMB) {
1073 format = this._progressFormatMBMB;
1074 format = this._replaceInsert(format, 1, (currentKB / 1024).toFixed(1));
1075 format = this._replaceInsert(format, 2, (totalKB / 1024).toFixed(1));
1076 }
1077 else if (totalHasMB && !progressHasMB) {
1078 format = this._progressFormatKBMB;
1079 format = this._replaceInsert(format, 1, currentKB);
1080 format = this._replaceInsert(format, 2, (totalKB / 1024).toFixed(1));
1081 }
1082 else if (progressHasMB && !totalHasMB) {
1083 format = this._progressFormatUnknownMB;
1084 format = this._replaceInsert(format, 1, (currentKB / 1024).toFixed(1));
1085 }
1086 return format;
1087 },
1088
1089 /**
1090 * Formats a time in seconds into something human readable.
1091 * @param seconds
1092 * The time to format
1093 * @returns A human readable string representing the date.
1094 */
_formatSeconds
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1095 _formatSeconds: function(seconds) {
1096 // Determine number of hours/minutes/seconds
1097 var hours = (seconds - (seconds % 3600)) / 3600;
1098 seconds -= hours * 3600;
1099 var minutes = (seconds - (seconds % 60)) / 60;
1100 seconds -= minutes * 60;
1101
1102 // Pad single digit values
1103 if (hours < 10)
1104 hours = "0" + hours;
1105 if (minutes < 10)
1106 minutes = "0" + minutes;
1107 if (seconds < 10)
1108 seconds = "0" + seconds;
1109
1110 // Insert hours, minutes, and seconds into result string.
1111 var result = parseInt(hours) ? this._longTimeFormat : this._shortTimeFormat;
1112 result = this._replaceInsert(result, 1, hours);
1113 result = this._replaceInsert(result, 2, minutes);
1114 result = this._replaceInsert(result, 3, seconds);
1115
1116 return result;
1117 }
1118 };
1119
1120 /**
1121 * The "Update is Downloading" page - provides feedback for the download
1122 * process plus a pause/resume UI
1123 */
1124 var gDownloadingPage = {
1125 /**
1126 * DOM Elements
1127 */
1128 _downloadName : null,
1129 _downloadStatus : null,
1130 _downloadProgress : null,
1131 _downloadThrobber : null,
1132 _pauseButton : null,
1133
1134 /**
1135 * Label cache to hold the 'Connecting' string
1136 */
1137 _label_downloadStatus : null,
1138
1139 /**
1140 * An instance of the status formatter object
1141 */
1142 _statusFormatter : null,
get_statusFormatter
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1143 get statusFormatter() {
1144 if (!this._statusFormatter)
1145 this._statusFormatter = new DownloadStatusFormatter();
1146 return this._statusFormatter;
1147 },
1148
1149 /**
1150 * Initialize
1151 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1152 onPageShow: function() {
1153 this._downloadName = document.getElementById("downloadName");
1154 this._downloadStatus = document.getElementById("downloadStatus");
1155 this._downloadProgress = document.getElementById("downloadProgress");
1156 this._downloadThrobber = document.getElementById("downloadThrobber");
1157 this._pauseButton = document.getElementById("pauseButton");
1158 this._label_downloadStatus = this._downloadStatus.textContent;
1159
1160 var updates =
1161 Components.classes["@mozilla.org/updates/update-service;1"].
1162 getService(Components.interfaces.nsIApplicationUpdateService);
1163
1164 var um =
1165 Components.classes["@mozilla.org/updates/update-manager;1"].
1166 getService(Components.interfaces.nsIUpdateManager);
1167 var activeUpdate = um.activeUpdate;
1168 if (activeUpdate)
1169 gUpdates.setUpdate(activeUpdate);
1170
1171 if (!gUpdates.update) {
1172 LOG("UI:DownloadingPage", "onPageShow: no valid update to download?!");
1173 return;
1174 }
1175
1176 try {
1177 // Say that this was a foreground download, not a background download,
1178 // since the user cared enough to look in on this process.
1179 gUpdates.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
1180 gUpdates.update.setProperty("foregroundDownload", "true");
1181
1182 // Pause any active background download and restart it as a foreground
1183 // download.
1184 updates.pauseDownload();
1185 var state = updates.downloadUpdate(gUpdates.update, false);
1186 if (state == "failed") {
1187 // We've tried as hard as we could to download a valid update -
1188 // we fell back from a partial patch to a complete patch and even
1189 // then we couldn't validate. Show a validation error with instructions
1190 // on how to manually update.
1191 this.showVerificationError();
1192 }
1193 else {
1194 // Add this UI as a listener for active downloads
1195 updates.addDownloadListener(this);
1196 }
1197
1198 if (activeUpdate)
1199 this._setUIState(!updates.isDownloading);
1200
1201 var link = document.getElementById("detailsLink");
1202 link.href = gUpdates.update.detailsURL;
1203 }
1204 catch(ex) {
1205 LOG("UI:DownloadingPage", "onPageShow: " + ex);
1206 }
1207
1208 gUpdates.setButtons(null, true, null, true, null, true, "hideButton",
1209 false, false, null, false, null, false);
1210 },
1211
1212 /**
1213 * Updates the text status message
1214 */
_setStatus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1215 _setStatus: function(status) {
1216 // Don't bother setting the same text more than once. This can happen
1217 // due to the asynchronous behavior of the downloader.
1218 if (this._downloadStatus.textContent == status)
1219 return;
1220 while (this._downloadStatus.hasChildNodes())
1221 this._downloadStatus.removeChild(this._downloadStatus.firstChild);
1222 this._downloadStatus.appendChild(document.createTextNode(status));
1223 },
1224
1225 /**
1226 * Whether or not we are currently paused
1227 */
1228 _paused : false,
1229
1230 /**
1231 * Adjust UI to suit a certain state of paused-ness
1232 * @param paused
1233 * Whether or not the download is paused
1234 */
_setUIState
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1235 _setUIState: function(paused) {
1236 var u = gUpdates.update;
1237 if (paused) {
1238 if (this._downloadThrobber.hasAttribute("state"))
1239 this._downloadThrobber.removeAttribute("state");
1240 if (this._downloadProgress.mode != "normal")
1241 this._downloadProgress.mode = "normal";
1242 this._downloadName.value = gUpdates.strings.getFormattedString(
1243 "pausedName", [u.name]);
1244 this._pauseButton.label = gUpdates.strings.getString("pauseButtonResume");
1245 var p = u.selectedPatch.QueryInterface(Components.interfaces.nsIPropertyBag);
1246 var status = p.getProperty("status");
1247 if (status)
1248 this._setStatus(status);
1249 }
1250 else {
1251 if (!(this._downloadThrobber.hasAttribute("state") &&
1252 (this._downloadThrobber.getAttribute("state") == "loading")))
1253 this._downloadThrobber.setAttribute("state", "loading");
1254 if (this._downloadProgress.mode != "undetermined")
1255 this._downloadProgress.mode = "undetermined";
1256 this._downloadName.value = gUpdates.strings.getFormattedString(
1257 "downloadingPrefix", [u.name]);
1258 this._pauseButton.label = gUpdates.strings.getString("pauseButtonPause");
1259 this._setStatus(this._label_downloadStatus);
1260 }
1261 },
1262
1263 /**
1264 * When the user clicks the Pause/Resume button
1265 */
onPause
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1266 onPause: function() {
1267 var updates =
1268 Components.classes["@mozilla.org/updates/update-service;1"].
1269 getService(Components.interfaces.nsIApplicationUpdateService);
1270 if (this._paused)
1271 updates.downloadUpdate(gUpdates.update, false);
1272 else {
1273 var patch = gUpdates.update.selectedPatch;
1274 patch.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
1275 patch.setProperty("status",
1276 gUpdates.strings.getFormattedString("pausedStatus",
1277 [this.statusFormatter.progress]));
1278 updates.pauseDownload();
1279 }
1280 this._paused = !this._paused;
1281
1282 // Update the UI
1283 this._setUIState(this._paused);
1284 },
1285
1286 /**
1287 * When the user closes the Wizard UI (including by clicking the Hide button)
1288 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1289 onWizardCancel: function() {
1290 // Remove ourself as a download listener so that we don't continue to be
1291 // fed progress and state notifications after the UI we're updating has
1292 // gone away.
1293 var updates =
1294 Components.classes["@mozilla.org/updates/update-service;1"].
1295 getService(Components.interfaces.nsIApplicationUpdateService);
1296 updates.removeDownloadListener(this);
1297
1298 var um =
1299 Components.classes["@mozilla.org/updates/update-manager;1"]
1300 .getService(Components.interfaces.nsIUpdateManager);
1301 um.activeUpdate = gUpdates.update;
1302
1303 // If the download was paused by the user, ask the user if they want to
1304 // have the update resume in the background.
1305 var downloadInBackground = true;
1306 if (this._paused) {
1307 var title = gUpdates.strings.getString("resumePausedAfterCloseTitle");
1308 var message = gUpdates.strings.getFormattedString(
1309 "resumePausedAfterCloseMessage", [gUpdates.brandName]);
1310 var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
1311 .getService(Components.interfaces.nsIPromptService);
1312 var flags = ps.STD_YES_NO_BUTTONS;
1313 // focus the software update wizard before prompting.
1314 // this will raise the software update wizard if it is minimized
1315 // making it more obvious what the prompt is for and will
1316 // solve the problem of windows "obscuring" the prompt.
1317 // see bug #350299 for more details
1318 window.focus();
1319 var rv = ps.confirmEx(window, title, message, flags, null, null, null, null, { });
1320 if (rv == 1) {
1321 downloadInBackground = false;
1322 }
1323 }
1324 if (downloadInBackground) {
1325 // Continue download in the background at full speed.
1326 LOG("UI:DownloadingPage", "onWizardCancel: continuing download in background at full speed");
1327 updates.downloadUpdate(gUpdates.update, false);
1328 }
1329 },
1330
1331 /**
1332 * When the data transfer begins
1333 * @param request
1334 * The nsIRequest object for the transfer
1335 * @param context
1336 * Additional data
1337 */
onStartRequest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1338 onStartRequest: function(request, context) {
1339 request.QueryInterface(nsIIncrementalDownload);
1340 LOG("UI:DownloadingPage", "onStartRequest: " + request.URI.spec);
1341
1342 // This !paused test is necessary because onStartRequest may fire after
1343 // the download was paused (for those speedy clickers...)
1344 if (this._paused)
1345 return;
1346
1347 if (!(this._downloadThrobber.hasAttribute("state") &&
1348 (this._downloadThrobber.getAttribute("state") == "loading")))
1349 this._downloadThrobber.setAttribute("state", "loading");
1350 if (this._downloadProgress.mode != "undetermined")
1351 this._downloadProgress.mode = "undetermined";
1352 this._setStatus(this._label_downloadStatus);
1353 },
1354
1355 /**
1356 * When new data has been downloaded
1357 * @param request
1358 * The nsIRequest object for the transfer
1359 * @param context
1360 * Additional data
1361 * @param progress
1362 * The current number of bytes transferred
1363 * @param maxProgress
1364 * The total number of bytes that must be transferred
1365 */
onProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1366 onProgress: function(request, context, progress, maxProgress) {
1367 request.QueryInterface(nsIIncrementalDownload);
1368 LOG("UI:DownloadingPage.onProgress", " " + request.URI.spec + ", " + progress +
1369 "/" + maxProgress);
1370
1371 var name = gUpdates.strings.getFormattedString("downloadingPrefix", [gUpdates.update.name]);
1372 var status = this.statusFormatter.formatStatus(progress, maxProgress);
1373 var progress = Math.round(100 * (progress/maxProgress));
1374
1375 var p = gUpdates.update.selectedPatch;
1376 p.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
1377 p.setProperty("progress", progress);
1378 p.setProperty("status", status);
1379
1380 // This !paused test is necessary because onProgress may fire after
1381 // the download was paused (for those speedy clickers...)
1382 if (this._paused)
1383 return;
1384
1385 if (!(this._downloadThrobber.hasAttribute("state") &&
1386 (this._downloadThrobber.getAttribute("state") == "loading")))
1387 this._downloadThrobber.setAttribute("state", "loading");
1388 if (this._downloadProgress.mode != "normal")
1389 this._downloadProgress.mode = "normal";
1390 this._downloadProgress.value = progress;
1391 this._pauseButton.disabled = false;
1392 this._downloadName.value = name;
1393 this._setStatus(status);
1394 },
1395
1396 /**
1397 * When we have new status text
1398 * @param request
1399 * The nsIRequest object for the transfer
1400 * @param context
1401 * Additional data
1402 * @param status
1403 * A status code
1404 * @param statusText
1405 * Human readable version of |status|
1406 */
onStatus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1407 onStatus: function(request, context, status, statusText) {
1408 request.QueryInterface(nsIIncrementalDownload);
1409 LOG("UI:DownloadingPage", "onStatus: " + request.URI.spec + " status = " +
1410 status + ", text = " + statusText);
1411 this._setStatus(statusText);
1412 },
1413
1414 /**
1415 * When data transfer ceases
1416 * @param request
1417 * The nsIRequest object for the transfer
1418 * @param context
1419 * Additional data
1420 * @param status
1421 * Status code containing the reason for the cessation.
1422 */
onStopRequest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1423 onStopRequest: function(request, context, status) {
1424 request.QueryInterface(nsIIncrementalDownload);
1425 LOG("UI:DownloadingPage", "onStopRequest: " + request.URI.spec +
1426 ", status = " + status);
1427
1428 if (this._downloadThrobber.hasAttribute("state"))
1429 this._downloadThrobber.removeAttribute("state");
1430 if (this._downloadProgress.mode != "normal")
1431 this._downloadProgress.mode = "normal";
1432
1433 var u = gUpdates.update;
1434 const NS_BINDING_ABORTED = 0x804b0002;
1435 switch (status) {
1436 case Components.results.NS_ERROR_UNEXPECTED:
1437 if (u.selectedPatch.state == STATE_DOWNLOAD_FAILED &&
1438 u.isCompleteUpdate) {
1439 // Verification error of complete patch, informational text is held in
1440 // the update object.
1441 gUpdates.wiz.currentPage = document.getElementById("errors");
1442 }
1443 else {
1444 // Verification failed for a partial patch, complete patch is now
1445 // downloading so return early and do NOT remove the download listener!
1446
1447 // Reset the progress meter to "undertermined" mode so that we don't
1448 // show old progress for the new download of the "complete" patch.
1449 this._downloadProgress.mode = "undetermined";
1450 this._pauseButton.disabled = true;
1451
1452 var verificationFailed = document.getElementById("verificationFailed");
1453 verificationFailed.hidden = false;
1454
1455 this._statusFormatter = null;
1456 return;
1457 }
1458 break;
1459 case NS_BINDING_ABORTED:
1460 LOG("UI:DownloadingPage", "onStopRequest: Pausing Download");
1461 // Return early, do not remove UI listener since the user may resume
1462 // downloading again.
1463 return;
1464 case Components.results.NS_OK:
1465 LOG("UI:DownloadingPage", "onStopRequest: Patch Verification Succeeded");
1466 gUpdates.wiz.canAdvance = true;
1467 gUpdates.wiz.advance();
1468 break;
1469 default:
1470 LOG("UI:DownloadingPage", "onStopRequest: Transfer failed");
1471 // Some kind of transfer error, die.
1472 gUpdates.wiz.currentPage = document.getElementById("errors");
1473 break;
1474 }
1475
1476 var updates =
1477 Components.classes["@mozilla.org/updates/update-service;1"].
1478 getService(Components.interfaces.nsIApplicationUpdateService);
1479 updates.removeDownloadListener(this);
1480 },
1481
1482 /**
1483 * See nsISupports.idl
1484 */
QueryInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1485 QueryInterface: function(iid) {
1486 if (!iid.equals(Components.interfaces.nsIRequestObserver) &&
1487 !iid.equals(Components.interfaces.nsIProgressEventSink) &&
1488 !iid.equals(Components.interfaces.nsISupports))
1489 throw Components.results.NS_ERROR_NO_INTERFACE;
1490 return this;
1491 }
1492 };
1493
1494 /**
1495 * The "There was an error during the update" page.
1496 */
1497 var gErrorsPage = {
1498 /**
1499 * Initialize
1500 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1501 onPageShow: function() {
1502 gUpdates.setButtons(null, true, null, true, null, false, "hideButton",
1503 true, false, null, false, null, false);
1504 gUpdates.wiz.getButton("finish").focus();
1505
1506 var errorReason = document.getElementById("errorReason");
1507 errorReason.value = gUpdates.update.statusText;
1508 var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
1509 .getService(Components.interfaces.nsIURLFormatter);
1510 var manualURL = formatter.formatURLPref(PREF_UPDATE_MANUAL_URL);
1511 var errorLinkLabel = document.getElementById("errorLinkLabel");
1512 errorLinkLabel.value = manualURL;
1513 errorLinkLabel.href = manualURL;
1514 },
1515
1516 /**
1517 * Initialize, for the "Error Applying Patch" case.
1518 */
onPageShowPatching
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1519 onPageShowPatching: function() {
1520 gUpdates.wiz.getButton("back").disabled = true;
1521 gUpdates.wiz.getButton("cancel").disabled = true;
1522 gUpdates.wiz.getButton("next").focus();
1523 },
1524
1525 /**
1526 * Finish button clicked.
1527 */
onWizardFinish
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1528 onWizardFinish: function() {
1529 // XXXjwalden COMPLETE AND TOTAL HACK!!!
1530 //
1531 // The problem the following code is working around is this: the update
1532 // service's API for responding to updates is poor. Essentially, all
1533 // the information we can get is that we've started to request a download or
1534 // that the download we've started has finished, with minimal details about
1535 // how it finished which aren't described in the API (and which internally
1536 // are not entirely useful, either -- mostly unuseful nsresults). How
1537 // do you signal the difference between "this download failed" and "all
1538 // downloads failed", particularly if you aim for API compatibility? The
1539 // code in nsApplicationUpdateService only determines the difference *after*
1540 // the current request is stopped, and since the subsequent second call to
1541 // downloadUpdate doesn't start/stop a request, the download listener is
1542 // never notified and whatever code was relying on it just fails without
1543 // notification. The consequence of this is that it's impossible to
1544 // properly remove the download listener.
1545 //
1546 // The code before this patch tried to do the exit after all downloads
1547 // failed but was missing a QueryInterface to work; with it, making sure
1548 // that the download listener is removed in all cases, including in the case
1549 // where the last onStopRequest corresponds to *a* failed download instead
1550 // of to *all* failed downloads, simply means that we have to try to remove
1551 // that listener in the error page spawned by the update service. If there
1552 // were time and API compat weren't a problem, we'd add an onFinish handler
1553 // or something which could signal exactly what happened and not overload
1554 // onStopRequest, but there isn't, so we can't.
1555 //
1556 var updates =
1557 Components.classes["@mozilla.org/updates/update-service;1"].
1558 getService(Components.interfaces.nsIApplicationUpdateService);
1559 updates.removeDownloadListener(gDownloadingPage);
1560 }
1561 };
1562
1563 /**
1564 * The "Update has been downloaded" page. Shows information about what
1565 * was downloaded.
1566 */
1567 var gFinishedPage = {
1568 /**
1569 * Called to initialize the Wizard Page.
1570 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1571 onPageShow: function() {
1572 gUpdates.setButtons(null, true, null, true, "restartButton", false,
1573 "notNowButton", false, false, null, false, null, false);
1574 //XXX bug 426021
anon:1575:15
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1575 setTimeout(function () {
1576 gUpdates.wiz.getButton("finish").focus();
1577 }, 0);
1578 },
1579
1580 /**
1581 * Called to initialize the Wizard Page. (Background Source Event)
1582 */
onPageShowBackground
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1583 onPageShowBackground: function() {
1584 var finishedBackground = document.getElementById("finishedBackground");
1585 finishedBackground.setAttribute("label", gUpdates.strings.getFormattedString(
1586 "updateReadyToInstallHeader", [gUpdates.update.name]));
1587 // XXXben - wizard should give us a way to set the page header.
1588 gUpdates.wiz._adjustWizardHeader();
1589 var updateFinishedName = document.getElementById("updateFinishedName");
1590 updateFinishedName.value = gUpdates.update.name;
1591
1592 var link = document.getElementById("finishedBackgroundLink");
1593 link.href = gUpdates.update.detailsURL;
1594
1595 this.onPageShow();
1596
1597 if (getPref("getBoolPref", PREF_UPDATE_TEST_LOOP, false)) {
anon:1598:17
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1598 setTimeout(function () {
1599 gUpdates.wiz.getButton("finish").click();
1600 }, UPDATE_TEST_LOOP_INTERVAL);
1601 }
1602 },
1603
1604 /**
1605 * Called when the wizard finishes, i.e. the "Restart Now" button is
1606 * clicked.
1607 */
onWizardFinish
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1608 onWizardFinish: function() {
1609 // Do the restart
1610 LOG("UI:FinishedPage" , "onWizardFinish: Restarting Application...");
1611
1612 // disable the "finish" (Restart) and "cancel" (Later) buttons
1613 // because the Software Update wizard is still up at the point,
1614 // and will remain up until we return and we close the
1615 // window with a |window.close()| in wizard.xml
1616 // (it was the firing the "wizardfinish" event that got us here.)
1617 // This prevents the user from switching back
1618 // to the Software Update dialog and clicking "Restart" or "Later"
1619 // when dealing with the "confirm close" prompts.
1620 // See bug #350299 for more details.
1621 gUpdates.wiz.getButton("finish").disabled = true;
1622 gUpdates.wiz.getButton("cancel").disabled = true;
1623
1624 // This process is *extremely* broken. There should be some nice
1625 // integrated system for determining whether or not windows are allowed
1626 // to close or not, and what happens when that happens. We need to
1627 // jump through all these hoops (duplicated from globalOverlay.js) to
1628 // ensure that various window types (browser, downloads, etc) all
1629 // allow the app to shut down.
1630 // bsmedberg?
1631
1632 // Notify all windows that an application quit has been requested.
1633 var os = Components.classes["@mozilla.org/observer-service;1"]
1634 .getService(Components.interfaces.nsIObserverService);
1635 var cancelQuit =
1636 Components.classes["@mozilla.org/supports-PRBool;1"].
1637 createInstance(Components.interfaces.nsISupportsPRBool);
1638 os.notifyObservers(cancelQuit, "quit-application-requested", "restart");
1639
1640 // Something aborted the quit process.
1641 if (cancelQuit.data)
1642 return;
1643
1644 var appStartup =
1645 Components.classes["@mozilla.org/toolkit/app-startup;1"].
1646 getService(Components.interfaces.nsIAppStartup);
1647 appStartup.quit(appStartup.eAttemptQuit | appStartup.eRestart);
1648 },
1649
1650 /**
1651 * Called when the wizard is canceled, i.e. when the "Not Now" button is
1652 * clicked.
1653 */
onWizardCancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1654 onWizardCancel: function() {
1655 var interval = getPref("getIntPref", PREF_UPDATE_NAGTIMER_RESTART, 86400);
1656 gUpdates.registerNagTimer("restart-nag-timer", interval,
1657 "showUpdateComplete");
1658 }
1659 };
1660
1661 /**
1662 * The "Update was Installed Successfully" page.
1663 */
1664 var gInstalledPage = {
1665 /**
1666 * Initialize
1667 */
onPageShow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1668 onPageShow: function() {
1669 var ai =
1670 Components.classes["@mozilla.org/xre/app-info;1"].
1671 getService(Components.interfaces.nsIXULAppInfo);
1672
1673 var branding = document.getElementById("brandStrings");
1674 try {
1675 var url = branding.getFormattedString("whatsNewURL", [ai.version]);
1676 var whatsnewLink = document.getElementById("whatsnewLink");
1677 whatsnewLink.href = url;
1678 whatsnewLink.hidden = false;
1679 }
1680 catch (e) {
1681 }
1682
1683 gUpdates.setButtons(null, true, null, true, null, false, "hideButton",
1684 true, false, null, false, null, false);
1685 gUpdates.wiz.getButton("finish").focus();
1686 }
1687 };
1688
1689 /**
1690 * Called as the application shuts down due to being quit from the File->Quit
1691 * menu item.
1692 * XXXben this API is broken.
1693 */
tryToClose
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1694 function tryToClose() {
1695 var cp = gUpdates.wiz.currentPage;
1696 if (cp.pageid != "finished" && cp.pageid != "finishedBackground")
1697 gUpdates.onWizardCancel();
1698 return true;
1699 }
1700
1701 /**
1702 * Callback for the Update Prompt to set the current page if an Update Wizard
1703 * window is already found to be open.
1704 * @param pageid
1705 * The ID of the page to switch to
1706 */
setCurrentPage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1707 function setCurrentPage(pageid) {
1708 gUpdates.wiz.currentPage = document.getElementById(pageid);
1709 }