2012-04-17 04:35:09 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This component listens to notifications for startup, shutdown and session
|
|
|
|
* restore, controlling which downloads should be loaded from the database.
|
|
|
|
*
|
|
|
|
* To avoid affecting startup performance, this component monitors the current
|
|
|
|
* session restore state, but defers the actual downloads data manipulation
|
|
|
|
* until the Download Manager service is loaded.
|
|
|
|
*/
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// Globals
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
const Cr = Components.results;
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
|
|
|
"resource:///modules/DownloadsCommon.jsm");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
|
|
|
"@mozilla.org/browser/sessionstartup;1",
|
|
|
|
"nsISessionStartup");
|
|
|
|
|
|
|
|
const kObservedTopics = [
|
|
|
|
"sessionstore-windows-restored",
|
|
|
|
"sessionstore-browser-state-restored",
|
|
|
|
"download-manager-initialized",
|
|
|
|
"download-manager-change-retention",
|
2012-12-05 19:31:26 -08:00
|
|
|
"last-pb-context-exited",
|
2012-04-17 04:35:09 -07:00
|
|
|
"browser-lastwindow-close-granted",
|
|
|
|
"quit-application",
|
|
|
|
"profile-change-teardown",
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CID of our implementation of nsIDownloadManagerUI.
|
|
|
|
*/
|
|
|
|
const kDownloadsUICid = Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contract ID of the service implementing nsIDownloadManagerUI.
|
|
|
|
*/
|
|
|
|
const kDownloadsUIContractId = "@mozilla.org/download-manager-ui;1";
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// DownloadsStartup
|
|
|
|
|
|
|
|
function DownloadsStartup() { }
|
|
|
|
|
|
|
|
DownloadsStartup.prototype = {
|
|
|
|
classID: Components.ID("{49507fe5-2cee-4824-b6a3-e999150ce9b8}"),
|
|
|
|
|
|
|
|
_xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsStartup),
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// nsISupports
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
|
|
|
Ci.nsISupportsWeakReference]),
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// nsIObserver
|
|
|
|
|
|
|
|
observe: function DS_observe(aSubject, aTopic, aData)
|
|
|
|
{
|
|
|
|
switch (aTopic) {
|
|
|
|
case "app-startup":
|
|
|
|
kObservedTopics.forEach(
|
|
|
|
function (topic) Services.obs.addObserver(this, topic, true),
|
|
|
|
this);
|
|
|
|
|
|
|
|
// Override Toolkit's nsIDownloadManagerUI implementation with our own.
|
|
|
|
// This must be done at application startup and not in the manifest to
|
|
|
|
// ensure that our implementation overrides the original one.
|
|
|
|
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
|
|
|
.registerFactory(kDownloadsUICid, "",
|
|
|
|
kDownloadsUIContractId, null);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "sessionstore-windows-restored":
|
|
|
|
case "sessionstore-browser-state-restored":
|
|
|
|
// Unless there is no saved session, there is a chance that we are
|
|
|
|
// starting up after a restart or a crash. We should check the disk
|
|
|
|
// database to see if there are completed downloads to recover and show
|
|
|
|
// in the panel, in addition to in-progress downloads.
|
|
|
|
if (gSessionStartup.sessionType != Ci.nsISessionStartup.NO_SESSION) {
|
2012-10-24 07:02:29 -07:00
|
|
|
this._restoringSession = true;
|
2012-04-17 04:35:09 -07:00
|
|
|
}
|
|
|
|
this._ensureDataLoaded();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "download-manager-initialized":
|
|
|
|
// Don't initialize the JavaScript data and user interface layer if we
|
|
|
|
// are initializing the Download Manager service during shutdown.
|
|
|
|
if (this._shuttingDown) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start receiving events for active and new downloads before we return
|
|
|
|
// from this observer function. We can't defer the execution of this
|
|
|
|
// step, to ensure that we don't lose events raised in the meantime.
|
2012-12-05 19:31:26 -08:00
|
|
|
DownloadsCommon.initializeAllDataLinks(
|
|
|
|
aSubject.QueryInterface(Ci.nsIDownloadManager));
|
2012-04-17 04:35:09 -07:00
|
|
|
|
|
|
|
this._downloadsServiceInitialized = true;
|
|
|
|
|
|
|
|
// Since this notification is generated during the getService call and
|
|
|
|
// we need to get the Download Manager service ourselves, we must post
|
|
|
|
// the handler on the event queue to be executed later.
|
|
|
|
Services.tm.mainThread.dispatch(this._ensureDataLoaded.bind(this),
|
|
|
|
Ci.nsIThread.DISPATCH_NORMAL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "download-manager-change-retention":
|
2013-02-06 10:57:16 -08:00
|
|
|
// If we're using the Downloads Panel, we override the retention
|
|
|
|
// preference to always retain downloads on completion.
|
2012-04-17 04:35:09 -07:00
|
|
|
if (!DownloadsCommon.useToolkitUI) {
|
2013-02-06 10:57:16 -08:00
|
|
|
aSubject.QueryInterface(Ci.nsISupportsPRInt32).data = 2;
|
2012-04-17 04:35:09 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "browser-lastwindow-close-granted":
|
|
|
|
// When using the panel interface, downloads that are already completed
|
|
|
|
// should be removed when the last full browser window is closed. This
|
|
|
|
// event is invoked only if the application is not shutting down yet.
|
|
|
|
// If the Download Manager service is not initialized, we don't want to
|
|
|
|
// initialize it just to clean up completed downloads, because they can
|
|
|
|
// be present only in case there was a browser crash or restart.
|
|
|
|
if (this._downloadsServiceInitialized &&
|
|
|
|
!DownloadsCommon.useToolkitUI) {
|
|
|
|
Services.downloads.cleanUp();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-12-05 19:31:26 -08:00
|
|
|
case "last-pb-context-exited":
|
|
|
|
// Similar to the above notification, but for private downloads.
|
|
|
|
if (this._downloadsServiceInitialized &&
|
|
|
|
!DownloadsCommon.useToolkitUI) {
|
|
|
|
Services.downloads.cleanUpPrivate();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-04-17 04:35:09 -07:00
|
|
|
case "quit-application":
|
|
|
|
// When the application is shutting down, we must free all resources in
|
|
|
|
// addition to cleaning up completed downloads. If the Download Manager
|
|
|
|
// service is not initialized, we don't want to initialize it just to
|
|
|
|
// clean up completed downloads, because they can be present only in
|
|
|
|
// case there was a browser crash or restart.
|
|
|
|
this._shuttingDown = true;
|
|
|
|
if (!this._downloadsServiceInitialized) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-12-05 19:31:26 -08:00
|
|
|
DownloadsCommon.terminateAllDataLinks();
|
2012-04-17 04:35:09 -07:00
|
|
|
|
|
|
|
// When using the panel interface, downloads that are already completed
|
|
|
|
// should be removed when quitting the application.
|
|
|
|
if (!DownloadsCommon.useToolkitUI && aData != "restart") {
|
|
|
|
this._cleanupOnShutdown = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "profile-change-teardown":
|
|
|
|
// If we need to clean up, we must do it synchronously after all the
|
|
|
|
// "quit-application" listeners are invoked, so that the Download
|
|
|
|
// Manager service has a chance to pause or cancel in-progress downloads
|
|
|
|
// before we remove completed downloads from the list. Note that, since
|
|
|
|
// "quit-application" was invoked, we've already exited Private Browsing
|
|
|
|
// Mode, thus we are always working on the disk database.
|
|
|
|
if (this._cleanupOnShutdown) {
|
|
|
|
Services.downloads.cleanUp();
|
|
|
|
}
|
2012-10-24 07:02:29 -07:00
|
|
|
|
|
|
|
if (!DownloadsCommon.useToolkitUI) {
|
|
|
|
// If we got this far, that means that we finished our first session
|
|
|
|
// with the Downloads Panel without crashing. This means that we don't
|
|
|
|
// have to force displaying only active downloads on the next startup
|
|
|
|
// now.
|
|
|
|
this._firstSessionCompleted = true;
|
|
|
|
}
|
2012-04-17 04:35:09 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// Private
|
|
|
|
|
|
|
|
/**
|
2012-10-24 07:02:29 -07:00
|
|
|
* Indicates whether we're restoring a previous session. This is used by
|
|
|
|
* _recoverAllDownloads to determine whether or not we should load and
|
|
|
|
* display all downloads data, or restrict it to only the active downloads.
|
2012-04-17 04:35:09 -07:00
|
|
|
*/
|
2012-10-24 07:02:29 -07:00
|
|
|
_restoringSession: false,
|
2012-04-17 04:35:09 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates whether the Download Manager service has been initialized. This
|
|
|
|
* flag is required because we want to avoid accessing the service immediately
|
|
|
|
* at browser startup. The service will start when the user first requests a
|
|
|
|
* download, or some time after browser startup.
|
|
|
|
*/
|
|
|
|
_downloadsServiceInitialized: false,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True while we are processing the "quit-application" event, and later.
|
|
|
|
*/
|
|
|
|
_shuttingDown: false,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True during shutdown if we need to remove completed downloads.
|
|
|
|
*/
|
|
|
|
_cleanupOnShutdown: false,
|
|
|
|
|
2012-10-24 07:02:29 -07:00
|
|
|
/**
|
|
|
|
* True if we should display all downloads, as opposed to just active
|
|
|
|
* downloads. We decide to display all downloads if we're restoring a session,
|
|
|
|
* or if we're using the Downloads Panel anytime after the first session with
|
|
|
|
* it has completed.
|
|
|
|
*/
|
|
|
|
get _recoverAllDownloads() {
|
|
|
|
return this._restoringSession ||
|
|
|
|
(!DownloadsCommon.useToolkitUI && this._firstSessionCompleted);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if we've ever completed a session with the Downloads Panel enabled.
|
|
|
|
*/
|
|
|
|
get _firstSessionCompleted() {
|
|
|
|
return Services.prefs
|
|
|
|
.getBoolPref("browser.download.panel.firstSessionCompleted");
|
|
|
|
},
|
|
|
|
|
|
|
|
set _firstSessionCompleted(aValue) {
|
|
|
|
Services.prefs.setBoolPref("browser.download.panel.firstSessionCompleted",
|
|
|
|
aValue);
|
|
|
|
return aValue;
|
|
|
|
},
|
|
|
|
|
2012-04-17 04:35:09 -07:00
|
|
|
/**
|
|
|
|
* Ensures that persistent download data is reloaded at the appropriate time.
|
|
|
|
*/
|
|
|
|
_ensureDataLoaded: function DS_ensureDataLoaded()
|
|
|
|
{
|
2013-01-29 10:12:13 -08:00
|
|
|
if (!this._downloadsServiceInitialized) {
|
2012-04-17 04:35:09 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the previous session has been already restored, then we ensure that
|
|
|
|
// all the downloads are loaded. Otherwise, we only ensure that the active
|
|
|
|
// downloads from the previous session are loaded.
|
2012-12-05 19:31:26 -08:00
|
|
|
DownloadsCommon.ensureAllPersistentDataLoaded(!this._recoverAllDownloads);
|
2012-04-17 04:35:09 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// Module
|
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]);
|