Bug 284086: "Sanitize on shutdown" fails if the last closed window is not a browser window, patch by Giorgio Maone <g.maone@informaction.com>, r=mconnor

This commit is contained in:
gavin%gavinsharp.com 2005-08-23 16:38:28 +00:00
Родитель 23f130619e
Коммит e1400c9125
10 изменённых файлов: 481 добавлений и 65 удалений

Просмотреть файл

@ -166,7 +166,7 @@
'chrome,dialog=no,resizable');"/>
<command id="Tools:Extensions" oncommand="BrowserOpenExtensions('extensions');"/>
<command id="Tools:Themes" oncommand="BrowserOpenExtensions('themes');"/>
<command id="Tools:Sanitize" oncommand="gSanitizeListener.sanitize(window || null);"/>
<command id="Tools:Sanitize" oncommand="gBrowserGlue.sanitize(window || null);"/>
</commandset>
<broadcasterset id="mainBroadcasterSet">

Просмотреть файл

@ -69,6 +69,8 @@ const BROWSER_ADD_BM_FEATURES = "centerscreen,chrome,dialog,resizable,modal";
const BROWSER_ADD_BM_FEATURES = "centerscreen,chrome,dialog,resizable,dependent";
#endif
var gBrowserGlue = Components.classes["@mozilla.org/browser/browserglue;1"]
.getService(nsCI.nsIBrowserGlue);
var gRDF = null;
var gGlobalHistory = null;
var gURIFixup = null;
@ -1174,74 +1176,42 @@ function SanitizeListener()
var pbi = gPrefService.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.addObserver(this.promptDomain, this, false);
this._setSanitizeItem();
this._defaultLabel = document.getElementById("sanitizeItem")
.getAttribute("label");
this._updateSanitizeItem();
if (gPrefService.prefHasUserValue(this.didSanitizeDomain))
gPrefService.clearUserPref(this.didSanitizeDomain)
this._os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
this._os.addObserver(this, "quit-application-granted", false);
if (gPrefService.prefHasUserValue(this.didSanitizeDomain)) {
gPrefService.clearUserPref(this.didSanitizeDomain);
// We need to persist this preference change, since we want to
// check it at next app start even if the browser exits abruptly,
// but we can afford some delay before I/O, so perceived
// startup speed is not affected :)
window.setTimeout(function() { gPrefService.savePrefFile(null); }, 1000);
}
}
SanitizeListener.prototype =
{
promptDomain : "privacy.sanitize.promptOnSanitize",
shutdownDomain : "privacy.sanitize.sanitizeOnShutdown",
didSanitizeDomain : "privacy.sanitize.didShutdownSanitize",
observe: function (aSubject, aTopic, aPrefName)
{
switch (aTopic) {
case "nsPref:changed":
if (aPrefName != this.promptDomain)
return;
this._setSanitizeItem();
break;
case "quit-application-granted":
if (gPrefService.getBoolPref(this.shutdownDomain) &&
!gPrefService.prefHasUserValue(this.didSanitizeDomain)) {
this.sanitize(null);
gPrefService.setBoolPref(this.didSanitizeDomain, true);
}
break;
}
this._updateSanitizeItem();
},
shutdown: function ()
{
var pbi = gPrefService.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.removeObserver(this.promptDomain, this);
this._os.removeObserver(this, "quit-application-granted");
},
_setSanitizeItem: function ()
_updateSanitizeItem: function ()
{
var shouldPrompt = gPrefService.getBoolPref(this.promptDomain);
if (shouldPrompt) {
var sanitizeItem = document.getElementById("sanitizeItem");
var bundleBrowser = document.getElementById("bundle_browser");
var bundleBrand = document.getElementById("bundle_brand");
var brandShortName = bundleBrand.getString("brandShortName");
sanitizeItem.label = bundleBrowser.getString("sanitizeWithPromptLabel");
}
},
sanitize: function (aParentWindow)
{
var promptOnSanitize = gPrefService.getBoolPref("privacy.sanitize.promptOnSanitize");
if (promptOnSanitize) {
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
ww.openWindow(aParentWindow,
"chrome://browser/content/sanitize.xul",
"Sanitize",
"chrome,titlebar,centerscreen,modal",
null);
}
else
(new Sanitizer()).sanitize();
var label = gPrefService.getBoolPref(this.promptDomain) ?
gNavigatorBundle.getString("sanitizeWithPromptLabel") :
this._defaultLabel;
document.getElementById("sanitizeItem").setAttribute("label", label);
}
}

Просмотреть файл

@ -46,4 +46,3 @@
<script type="application/x-javascript" src="chrome://browser/content/bookmarks/bookmarksMenu.js"/>
<script type="application/x-javascript" src="chrome://global/content/viewZoomOverlay.js"/>
<script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
<script type="application/x-javascript" src="chrome://browser/content/sanitize.js"/>

Просмотреть файл

@ -19,6 +19,7 @@
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Giorgio Maone <g.maone@informaction.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
@ -36,6 +37,7 @@
function Sanitizer() {}
Sanitizer.prototype = {
// warning to the caller: this one may raise an exception (e.g. bug #265028)
clearItem: function (aItemName)
{
if (this.items[aItemName].canClear)
@ -53,26 +55,90 @@ Sanitizer.prototype = {
return aPreferenceName.substr(this._prefDomain.length);
},
/**
* Deletes privacy sensitive data in a batch, according to user preferences
*
* @returns null if everything's fine; an object in the form
* { itemName: error, ... } on (partial) failure
*/
sanitize: function ()
{
var psvc = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var branch = psvc.getBranch(this._prefDomain);
var errors = null;
for (var itemName in this.items) {
var item = this.items[itemName];
if ("clear" in item && item.canClear && branch.getBoolPref(itemName))
item.clear();
if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) {
// Some of these clear() may raise exceptions (see bug #265028)
// to sanitize as much as possible, we catch and store them,
// rather than fail fast.
// Callers should check returned errors and give user feedback
// about items that could not be sanitized
try {
item.clear();
} catch(er) {
if (!errors)
errors = {};
errors[itemName] = er;
dump("Error sanitizing " + itemName + ": " + er + "\n");
}
}
}
return errors;
},
items: {
cache: {
clear: function ()
{
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
cacheService.evictEntries(Components.interfaces.nsICache.STORE_ON_DISK);
cacheService.evictEntries(Components.interfaces.nsICache.STORE_IN_MEMORY);
const cc = Components.classes;
const ci = Components.interfaces;
var cacheService = cc["@mozilla.org/network/cache-service;1"]
.getService(ci.nsICacheService);
// Here we play dirty, trying to brutally wipe out all the cache files
// even if the disk cache device has gone away (if it is still with us,
// our removal attempt will fail because the directory is locked,
// and we fall back to the "nice" way below)
var cacheDir;
// Look at nsCacheProfilePrefObserver::ReadPrefs()
// and nsDiskCacheDevice::SetCacheParentDirectory()
// for details on how we guess the cache directory
try {
cacheDir = cc["@mozilla.org/preferences-service;1"]
.getService(ci.nsIBranch)
.getComplexValue("browser.cache.disk.parent_directory",
ci.nsILocalFile);
} catch(er) {
const dirServ = cc["@mozilla.org/file/directory_service;1"]
.getService(ci.nsIProperties);
try {
cacheDir = dirServ.get("cachePDir",ci.nsILocalFile);
} catch(er) {
cacheDir = dirServ.get("ProfLD",ci.nsILocalFile);
}
}
if (cacheDir) {
// Here we try to prevent the "phantom Cache.Trash" issue
// reported in bug #296256
cacheDir.append("Cache.Trash");
try {
cacheDir.remove(true);
} catch(er) {}
cacheDir = cacheDir.parent;
cacheDir.append("Cache");
try {
cacheDir.remove(true);
} catch(er) {}
}
// The "nice" way
cacheService.evictEntries(ci.nsICache.STORE_ON_DISK);
cacheService.evictEntries(ci.nsICache.STORE_IN_MEMORY);
},
get canClear()
@ -88,8 +154,9 @@ Sanitizer.prototype = {
.getService(Components.interfaces.nsICookieManager);
var e = cookieMgr.enumerator;
var cookies = [];
var cookie;
while (e.hasMoreElements()) {
var cookie = e.getNext().QueryInterface(Components.interfaces.nsICookie);
cookie = e.getNext().QueryInterface(Components.interfaces.nsICookie);
cookies.push(cookie);
}
@ -113,7 +180,8 @@ Sanitizer.prototype = {
globalHistory.removeAllPages();
try {
var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "browser:purge-session-history", "");
}
catch (e) { }
@ -205,3 +273,74 @@ Sanitizer.prototype = {
}
};
// "Static" members
Sanitizer.prefDomain = "privacy.sanitize.";
Sanitizer.prefPrompt = "promptOnSanitize";
Sanitizer.prefShutdown = "sanitizeOnShutdown";
Sanitizer.prefDidShutdown = "didShutdownSanitize";
Sanitizer._prefs = null;
Sanitizer.__defineGetter__("prefs", function()
{
return Sanitizer._prefs ? Sanitizer._prefs
: Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch(Sanitizer.prefDomain);
});
// Shows sanitization UI
Sanitizer.showUI = function(aParentWindow)
{
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
ww.openWindow(aParentWindow,
"chrome://browser/content/sanitize.xul",
"Sanitize",
"chrome,titlebar,centerscreen,modal",
null);
};
/**
* Deletes privacy sensitive data in a batch, optionally showing the
* sanitize UI, according to user preferences
*
* @returns null if everything's fine (no error or displayed UI, which
* should handle errors);
* an object in the form { itemName: error, ... } on (partial) failure
*/
Sanitizer.sanitize = function(aParentWindow)
{
if (Sanitizer.prefs.getBoolPref(Sanitizer.prefPrompt)) {
Sanitizer.showUI(aParentWindow);
return null;
}
return new Sanitizer().sanitize();
};
Sanitizer.onStartup = function()
{
// we check for unclean exit with pending sanitization
Sanitizer._checkAndSanitize();
};
Sanitizer.onShutdown = function()
{
// we check if sanitization is needed and perform it
Sanitizer._checkAndSanitize();
};
// this is called on startup and shutdown, to perform pending sanitizations
Sanitizer._checkAndSanitize = function()
{
const prefs = Sanitizer.prefs;
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
!prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
// this is a shutdown or a startup after an unclean exit
Sanitizer.sanitize(null) || // sanitize() returns null on full success
prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
}
};

Просмотреть файл

@ -21,6 +21,7 @@
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Giorgio Maone <g.maone@informaction.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
@ -78,11 +79,18 @@
{
var s = new Sanitizer();
var sanitizePreferences = document.getElementById("sanitizePreferences");
var preference, name;
for (var i = 0; i < sanitizePreferences.childNodes.length; ++i) {
var preference = sanitizePreferences.childNodes[i];
var name = s.getNameFromPreference(preference.name);
if (s.canClearItem(name) && preference.value)
s.clearItem(name);
preference = sanitizePreferences.childNodes[i];
if (preference.value) {
name = s.getNameFromPreference(preference.name);
try {
s.clearItem(name);
} catch(er) {
dump(er + " sanitizing " + name);
// TODO: give user feedback about partially failed sanitization
}
}
}
},

Просмотреть файл

@ -47,10 +47,12 @@ XPIDL_MODULE = browsercompsbase
XPIDLSRCS = \
nsIBrowserHandler.idl \
nsIBrowserGlue.idl \
$(NULL)
EXTRA_PP_COMPONENTS = \
nsBrowserContentHandler.js \
nsBrowserGlue.js \
$(NULL)
DIRS = \

Просмотреть файл

@ -0,0 +1,228 @@
/* ***** 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 the Firefox Browser Glue Service.
*
* The Initial Developer of the Original Code is
* Giorgio Maone
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Giorgio Maone <g.maone@informaction.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 ***** */
// Constructor
function BrowserGlue() {
this._init();
}
BrowserGlue.prototype = {
QueryInterface: function(iid)
{
xpcomCheckInterfaces(iid, kServiceIIds, Components.results.NS_ERROR_NO_INTERFACE);
return this;
}
,
// nsIObserver implementation
observe: function(subject, topic, data)
{
switch(topic) {
case "xpcom-shutdown":
this._dispose();
break;
case "profile-before-change":
this._onProfileShutdown();
break;
case "profile-after-change":
this._onProfileStartup();
break;
}
}
,
// initialization (called on application startup)
_init: function()
{
// observer registration
const osvr = Components.classes['@mozilla.org/observer-service;1']
.getService(Components.interfaces.nsIObserverService);
osvr.addObserver(this, "profile-before-change", false);
osvr.addObserver(this, "xpcom-shutdown", false);
osvr.addObserver(this, "profile-after-change", false);
},
// cleanup (called on application shutdown)
_dispose: function()
{
// observer removal
const osvr = Components.classes['@mozilla.org/observer-service;1']
.getService(Components.interfaces.nsIObserverService);
osvr.removeObserver(this, "profile-before-change");
osvr.removeObserver(this, "xpcom-shutdown");
osvr.removeObserver(this, "profile-after-change");
},
// profile startup handler (contains profile initialization routines)
_onProfileStartup: function()
{
this.Sanitizer.onStartup();
},
// profile shutdown handler (contains profile cleanup routines)
_onProfileShutdown: function()
{
his.Sanitizer.onShutdown();
},
// returns the (cached) Sanitizer constructor
get Sanitizer()
{
if(typeof(Sanitizer) != "function") { // we should dynamically load the script
Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
.loadSubScript("chrome://browser/content/sanitize.js", null);
}
return Sanitizer;
},
// ------------------------------
// public nsIBrowserGlue members
// ------------------------------
sanitize: function(aParentWindow)
{
this.Sanitizer.sanitize(aParentWindow);
}
}
// XPCOM Scaffolding code
// component defined in this file
const kServiceName = "Firefox Browser Glue Service";
const kServiceId = "{eab9012e-5f74-4cbc-b2b5-a590235513cc}";
const kServiceCtrId = "@mozilla.org/browser/browserglue;1";
const kServiceConstructor = BrowserGlue;
const kServiceCId = Components.ID(kServiceId);
// interfaces implemented by this component
const kServiceIIds = [
Components.interfaces.nsIObserver,
Components.interfaces.nsISupports,
Components.interfaces.nsISupportsWeakReference,
Components.interfaces.nsIBrowserGlue
];
// categories which this component is registered in
const kServiceCats = ["app-startup"];
// Factory object
const kServiceFactory = {
_instance: null,
createInstance: function (outer, iid)
{
if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION;
xpcomCheckInterfaces(iid, kServiceIIds,
Components.results.NS_ERROR_INVALID_ARG);
return this._instance == null ?
this._instance = new kServiceConstructor() : this._instance;
}
};
function xpcomCheckInterfaces(iid, iids, ex) {
for (var j = iids.length; j-- >0;) {
if (iid.equals(iids[j])) return true;
}
throw ex;
}
// Module
var Module = {
registered: false,
registerSelf: function(compMgr, fileSpec, location, type)
{
if (!this.registered) {
compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar)
.registerFactoryLocation(kServiceCId,
kServiceName,
kServiceCtrId,
fileSpec,
location,
type);
const catman = Components.classes['@mozilla.org/categorymanager;1']
.getService(Components.interfaces.nsICategoryManager);
var len = kServiceCats.length;
for (var j = 0; j < len; j++) {
catman.addCategoryEntry(kServiceCats[j],
kServiceCtrId, kServiceCtrId, true, true, null);
}
this.registered = true;
}
},
unregisterSelf: function(compMgr, fileSpec, location)
{
compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar)
.unregisterFactoryLocation(kServiceCId, fileSpec);
const catman = Components.classes['@mozilla.org/categorymanager;1']
.getService(Components.interfaces.nsICategoryManager);
for (var j = 0, len = kServiceCats.length; j < len; j++) {
catman.deleteCategoryEntry(kServiceCats[j], kServiceCtrId, true);
}
},
getClassObject: function(compMgr, cid, iid)
{
if(cid.equals(kServiceCId))
return kServiceFactory;
throw Components.results[
iid.equals(Components.interfaces.nsIFactory)
? "NS_ERROR_NO_INTERFACE"
: "NS_ERROR_NOT_IMPLEMENTED"
];
},
canUnload: function(compMgr)
{
return true;
}
};
// entrypoint
function NSGetModule(compMgr, fileSpec) {
return Module;
}

Просмотреть файл

@ -0,0 +1,70 @@
/* ***** 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 the Firefox Browser Glue Service.
*
* The Initial Developer of the Original Code is
* Giorgio Maone
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Giorgio Maone <g.maone@informaction.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 ***** */
#include "nsISupports.idl"
interface nsIDOMWindow;
/**
* nsIBrowserGlue is a dirty and rather fluid interface to host shared utility
* methods used by browser UI code, but which are not local to a browser window.
* The component implementing this interface is meant to be a singleton
* (service) and should progressively replace some of the shared "glue" code
* scattered in browser/base/content (e.g. bits of utilOverlay.js,
* contentAreaUtils.js, globalOverlay.js, browser.js), avoiding dynamic
* inclusion and initialization of a ton of JS code for *each* window.
* Dued to its nature and origin, this interface won't probably be the most
* elegant or stable in the mozilla codebase, but its aim is rather pragmatic:
* 1) reducing the performance overhead which affects browser window load;
* 2) allow global hooks (e.g. startup and shutdown observers) which survive
* browser windows to accomplish browser-related activities, such as shutdown
* sanitization (see bug #284086)
*
*/
[scriptable, uuid(6d340848-9bc1-49a3-9073-99932bbc2a11)]
interface nsIBrowserGlue : nsISupports
{
/**
* Deletes privacy sensitive data according to user preferences
*
* @param aParentWindow an optionally null window which is the parent of the
* sanitization dialog (if it has to be shown per user preferences)
*
*/
void sanitize(in nsIDOMWindow aParentWindow);
};

Просмотреть файл

@ -166,7 +166,7 @@
<!ENTITY preferencesCmdUnix.label "Preferences">
<!ENTITY preferencesCmdUnix.accesskey "n">
<!ENTITY sanitizeCmd.label "Clear Private Data...">
<!ENTITY sanitizeCmd.label "Clear Private Data">
<!ENTITY sanitizeCmd.accesskey "P">
<!ENTITY viewMenu.label "View">

Просмотреть файл

@ -80,5 +80,5 @@
<!ENTITY clearCache.accesskey "N">
<!ENTITY sanitizeSettings.label "Settings...">
<!ENTITY sanitizeSettings.accesskey "z">
<!ENTITY sanitizeSettings.accesskey "t">
<!ENTITY sanitize.intro "The Clear Private Data tool can be used to erase your private data using a keyboard shortcut or when &brandShortName; closes.">