merge m-c to fx-team; a=merge

--HG--
extra : amend_source : d5fc0736172ed22bc792e03fe69d0f67531b6273
This commit is contained in:
Tim Taubert 2014-09-29 02:16:43 -07:00
commit 6ea4a0f904
69 changed files with 980 additions and 157 deletions

View File

@ -526,6 +526,7 @@ pref("privacy.clearOnShutdown.cache", true);
pref("privacy.clearOnShutdown.sessions", true);
pref("privacy.clearOnShutdown.offlineApps", false);
pref("privacy.clearOnShutdown.siteSettings", false);
pref("privacy.clearOnShutdown.openWindows", false);
pref("privacy.cpd.history", true);
pref("privacy.cpd.formdata", true);
@ -536,6 +537,7 @@ pref("privacy.cpd.cache", true);
pref("privacy.cpd.sessions", true);
pref("privacy.cpd.offlineApps", false);
pref("privacy.cpd.siteSettings", false);
pref("privacy.cpd.openWindows", false);
// What default should we use for the time span in the sanitizer:
// 0 - Clear everything
@ -543,11 +545,15 @@ pref("privacy.cpd.siteSettings", false);
// 2 - Last 2 Hours
// 3 - Last 4 Hours
// 4 - Today
// 5 - Last 5 minutes
// 6 - Last 24 hours
pref("privacy.sanitize.timeSpan", 1);
pref("privacy.sanitize.sanitizeOnShutdown", false);
pref("privacy.sanitize.migrateFx3Prefs", false);
pref("privacy.panicButton.enabled", true);
pref("network.proxy.share_proxy_settings", false); // use the same proxy settings for all protocols
// simple gestures support

View File

@ -1297,6 +1297,8 @@ var gBrowserInit = {
.getBoolPref("privacy.trackingprotection.enabled");
Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED")
.add(tpEnabled);
PanicButtonNotifier.init();
});
this.delayedStartupFinished = true;
@ -7412,3 +7414,35 @@ let ToolbarIconColor = {
}
}
}
let PanicButtonNotifier = {
init: function() {
this._initialized = true;
if (window.PanicButtonNotifierShouldNotify) {
delete window.PanicButtonNotifierShouldNotify;
this.notify();
}
},
notify: function() {
if (!this._initialized) {
window.PanicButtonNotifierShouldNotify = true;
return;
}
// Display notification panel here...
try {
let popup = document.getElementById("panic-button-success-notification");
popup.hidden = false;
let widget = CustomizableUI.getWidget("panic-button").forWindow(window);
let anchor = widget.anchor;
anchor = document.getAnonymousElementByAttribute(anchor, "class", "toolbarbutton-icon");
popup.openPopup(anchor, popup.getAttribute("position"));
} catch (ex) {
Cu.reportError(ex);
}
},
close: function() {
let popup = document.getElementById("panic-button-success-notification");
popup.hidePopup();
},
};

View File

@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm");
Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm");
Cu.import("resource:///modules/DirectoryLinksProvider.jsm");
Cu.import("resource://gre/modules/NewTabUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Rect",

View File

@ -4,6 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
@ -50,14 +51,34 @@ Sanitizer.prototype = {
* Returns a promise which is resolved if no errors occurred. If an error
* occurs, a message is reported to the console and all other items are still
* cleared before the promise is finally rejected.
*
* If the consumer specifies the (optional) array parameter, only those
* items get cleared (irrespective of the preference settings)
*/
sanitize: function ()
sanitize: function (aItemsToClear)
{
var deferred = Promise.defer();
var psvc = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var branch = psvc.getBranch(this.prefDomain);
var seenError = false;
if (Array.isArray(aItemsToClear)) {
var itemsToClear = [...aItemsToClear];
} else {
let branch = Services.prefs.getBranch(this.prefDomain);
itemsToClear = Object.keys(this.items).filter(itemName => branch.getBoolPref(itemName));
}
// Ensure open windows get cleared first, if they're in our list, so that they don't stick
// around in the recently closed windows list, and so we can cancel the whole thing
// if the user selects to keep a window open from a beforeunload prompt.
let openWindowsIndex = itemsToClear.indexOf("openWindows");
if (openWindowsIndex != -1) {
itemsToClear.splice(openWindowsIndex, 1);
let item = this.items.openWindows;
if (!item.clear()) {
// When cancelled, reject the deferred and return the promise:
deferred.reject();
return deferred.promise;
}
}
// Cache the range of times to clear
if (this.ignoreTimespan)
@ -65,16 +86,16 @@ Sanitizer.prototype = {
else
range = this.range || Sanitizer.getClearRange();
let itemCount = Object.keys(this.items).length;
let itemCount = Object.keys(itemsToClear).length;
let onItemComplete = function() {
if (!--itemCount) {
seenError ? deferred.reject() : deferred.resolve();
}
};
for (var itemName in this.items) {
for (let itemName of itemsToClear) {
let item = this.items[itemName];
item.range = range;
if ("clear" in item && branch.getBoolPref(itemName)) {
if ("clear" in item) {
let clearCallback = (itemName, aCanClear) => {
// Some of these clear() may raise exceptions (see bug #265028)
// to sanitize as much as possible, we catch and store them,
@ -401,7 +422,89 @@ Sanitizer.prototype = {
{
return true;
}
}
},
openWindows: {
privateStateForNewWindow: "non-private",
_canCloseWindow: function(aWindow) {
// Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
if (!aWindow.gMultiProcessBrowser) {
// Cargo-culted out of browser.js' WindowIsClosing because we don't care
// about TabView or the regular 'warn me before closing windows with N tabs'
// stuff here, and more importantly, we want to set aCallerClosesWindow to true
// when calling into permitUnload:
for (let browser of aWindow.gBrowser.browsers) {
let ds = browser.docShell;
// 'true' here means we will be closing the window soon, so please don't dispatch
// another onbeforeunload event when we do so. If unload is *not* permitted somewhere,
// we will reset the flag that this triggers everywhere so that we don't interfere
// with the browser after all:
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
return false;
}
}
}
return true;
},
_resetAllWindowClosures: function(aWindowList) {
for (let win of aWindowList) {
win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
}
},
clear: function()
{
// NB: this closes all *browser* windows, not other windows like the library, about window,
// browser console, etc.
// Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
// dialogs
let existingWindow = Services.appShell.hiddenDOMWindow;
let startDate = existingWindow.performance.now();
// First check if all these windows are OK with being closed:
let windowEnumerator = Services.wm.getEnumerator("navigator:browser");
let windowList = [];
while (windowEnumerator.hasMoreElements()) {
let someWin = windowEnumerator.getNext();
windowList.push(someWin);
// If someone says "no" to a beforeunload prompt, we abort here:
if (!this._canCloseWindow(someWin)) {
this._resetAllWindowClosures(windowList);
return false;
}
// ...however, beforeunload prompts spin the event loop, and so the code here won't get
// hit until the prompt has been dismissed. If more than 1 minute has elapsed since we
// started prompting, stop, because the user might not even remember initiating the
// 'forget', and the timespans will be all wrong by now anyway:
if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
this._resetAllWindowClosures(windowList);
return false;
}
}
// If/once we get here, we should actually be able to close all windows.
// First create a new window. We do this first so that on non-mac, we don't
// accidentally close the app by closing all the windows.
let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler);
let defaultArgs = handler.defaultArgs;
let features = "chrome,all,dialog=no," + this.privateStateForNewWindow;
let newWindow = existingWindow.openDialog("chrome://browser/content/", "_blank",
features, defaultArgs);
// Then close all those windows we checked:
while (windowList.length) {
windowList.pop().close();
}
newWindow.focus();
return true;
},
get canClear()
{
return true;
}
},
}
};
@ -419,6 +522,8 @@ Sanitizer.TIMESPAN_HOUR = 1;
Sanitizer.TIMESPAN_2HOURS = 2;
Sanitizer.TIMESPAN_4HOURS = 3;
Sanitizer.TIMESPAN_TODAY = 4;
Sanitizer.TIMESPAN_5MIN = 5;
Sanitizer.TIMESPAN_24HOURS = 6;
// Return a 2 element array representing the start and end times,
// in the uSec-since-epoch format that PRTime likes. If we should
@ -433,8 +538,11 @@ Sanitizer.getClearRange = function (ts) {
// PRTime is microseconds while JS time is milliseconds
var endDate = Date.now() * 1000;
switch (ts) {
case Sanitizer.TIMESPAN_5MIN :
var startDate = endDate - 300000000; // 5*60*1000000
break;
case Sanitizer.TIMESPAN_HOUR :
var startDate = endDate - 3600000000; // 1*60*60*1000000
startDate = endDate - 3600000000; // 1*60*60*1000000
break;
case Sanitizer.TIMESPAN_2HOURS :
startDate = endDate - 7200000000; // 2*60*60*1000000
@ -449,6 +557,9 @@ Sanitizer.getClearRange = function (ts) {
d.setSeconds(0);
startDate = d.valueOf() * 1000; // convert to epoch usec
break;
case Sanitizer.TIMESPAN_24HOURS :
startDate = endDate - 86400000000; // 24*60*60*1000000
break;
default:
throw "Invalid time span for clear private data: " + ts;
}

View File

@ -57,7 +57,7 @@ const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
*/
function test() {
waitForExplicitFinish();
let DirectoryLinksProvider = Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm", {}).DirectoryLinksProvider;
let DirectoryLinksProvider = Cu.import("resource:///modules/DirectoryLinksProvider.jsm", {}).DirectoryLinksProvider;
let NewTabUtils = Cu.import("resource://gre/modules/NewTabUtils.jsm", {}).NewTabUtils;
let Promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;

View File

@ -9,7 +9,7 @@ Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm", tmp);
Cu.import("resource:///modules/DirectoryLinksProvider.jsm", tmp);
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://browser/content/sanitize.js", tmp);

View File

@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
"resource://gre/modules/ShortcutUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
"resource://gre/modules/CharsetMenu.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
@ -947,6 +949,98 @@ if (Services.metro && Services.metro.supported) {
#endif
#endif
if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
CustomizableWidgets.push({
id: "panic-button",
type: "view",
viewId: "PanelUI-panicView",
_sanitizer: null,
_ensureSanitizer: function() {
if (!this.sanitizer) {
let scope = {};
Services.scriptloader.loadSubScript("chrome://browser/content/sanitize.js",
scope);
this._Sanitizer = scope.Sanitizer;
this._sanitizer = new scope.Sanitizer();
this._sanitizer.ignoreTimespan = false;
}
},
_getSanitizeRange: function(aDocument) {
let group = aDocument.getElementById("PanelUI-panic-timeSpan");
return this._Sanitizer.getClearRange(+group.value);
},
forgetButtonCalled: function(aEvent) {
let doc = aEvent.target.ownerDocument;
this._ensureSanitizer();
this._sanitizer.range = this._getSanitizeRange(doc);
let group = doc.getElementById("PanelUI-panic-timeSpan");
group.selectedItem = doc.getElementById("PanelUI-panic-5min");
let itemsToClear = [
"cookies", "history", "openWindows", "formdata", "sessions", "cache", "downloads"
];
let newWindowPrivateState = PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) ?
"private" : "non-private";
this._sanitizer.items.openWindows.privateStateForNewWindow = newWindowPrivateState;
let promise = this._sanitizer.sanitize(itemsToClear);
promise.then(function() {
let otherWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (otherWindow.closed) {
Cu.reportError("Got a closed window!");
}
if (otherWindow.PanicButtonNotifier) {
otherWindow.PanicButtonNotifier.notify();
} else {
otherWindow.PanicButtonNotifierShouldNotify = true;
}
});
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "command":
this.forgetButtonCalled(aEvent);
break;
case "popupshowing":
let popup = aEvent.target;
if (popup.id == "customizationui-widget-panel" &&
popup.querySelector("#PanelUI-panicView")) {
popup.ownerDocument.removeEventListener("popupshowing", this);
this._updateHeights(popup, true);
}
break;
}
},
// Workaround bug 451997 by hardcoding heights for (potentially) wrapped items:
_updateHeights: function(aContainer, aSetHeights) {
// Make sure we don't get stuck not finding anything because of the XBL binding between
// the popup and the radio/label elements:
let view = aContainer.ownerDocument.getElementById("PanelUI-panicView");
let variableHeightItems = view.querySelectorAll("radio, label");
let win = aContainer.ownerDocument.defaultView;
for (let item of variableHeightItems) {
if (aSetHeights) {
item.style.height = win.getComputedStyle(item, null).getPropertyValue("height");
} else {
item.style.removeProperty("height");
}
}
},
onViewShowing: function(aEvent) {
let view = aEvent.target;
let forgetButton = view.querySelector("#PanelUI-panic-view-button");
forgetButton.addEventListener("command", this);
// When the popup starts showing, fix the label and radio heights
// if we're in a standalone view (can't tell from here) - see updateHeights.
view.ownerDocument.addEventListener("popupshowing", this);
},
onViewHiding: function(aEvent) {
let view = aEvent.target;
let forgetButton = view.querySelector("#PanelUI-panic-view-button");
forgetButton.removeEventListener("command", this);
this._updateHeights(view, false);
},
});
}
#ifdef E10S_TESTING_ONLY
/**
* The e10s button's purpose is to lower the barrier of entry

View File

@ -179,6 +179,40 @@
</vbox>
</panelview>
<panelview id="PanelUI-panicView" flex="1">
<vbox class="panel-subview-body">
<hbox id="PanelUI-panic-timeframe">
<image id="PanelUI-panic-timeframe-icon" alt=""/>
<vbox flex="1">
<hbox id="PanelUI-panic-header">
<image id="PanelUI-panic-timeframe-icon-small" alt=""/>
<description value="&panicButton.view.mainTimeframeDesc;" id="PanelUI-panic-mainDesc"/>
</hbox>
<radiogroup id="PanelUI-panic-timeSpan" aria-labelledby="PanelUI-panic-mainDesc">
<radio id="PanelUI-panic-5min" label="&panicButton.view.5min;" selected="true"
value="5" class="subviewradio"/>
<radio id="PanelUI-panic-2hr" label="&panicButton.view.2hr;"
value="2" class="subviewradio"/>
<radio id="PanelUI-panic-day" label="&panicButton.view.day;"
value="6" class="subviewradio"/>
</radiogroup>
</vbox>
</hbox>
<vbox id="PanelUI-panic-explanations">
<label id="PanelUI-panic-actionlist-main-label" value="&panicButton.view.mainActionDesc;"/>
<label id="PanelUI-panic-actionlist-cookies" class="PanelUI-panic-actionlist">&panicButton.view.deleteCookies;</label>
<label id="PanelUI-panic-actionlist-history" class="PanelUI-panic-actionlist">&panicButton.view.deleteHistory;</label>
<label id="PanelUI-panic-actionlist-windows" class="PanelUI-panic-actionlist">&panicButton.view.deleteTabsAndWindows;</label>
<label id="PanelUI-panic-actionlist-newwindow" class="PanelUI-panic-actionlist">&panicButton.view.openNewWindow;</label>
<label id="PanelUI-panic-warning" value="&panicButton.view.undoWarning;"/>
</vbox>
<button id="PanelUI-panic-view-button"
label="&panicButton.view.forgetButton;"/>
</vbox>
</panelview>
</panelmultiview>
<!-- These menupopups are located here to prevent flickering,
see bug 492960 comment 20. -->
@ -247,3 +281,21 @@
</vbox>
</hbox>
</panel>
<panel id="panic-button-success-notification"
type="arrow"
position="bottomcenter topright"
hidden="true"
role="alert"
orient="vertical">
<hbox id="panic-button-success-header">
<image id="panic-button-success-icon" alt=""/>
<vbox>
<description>&panicButton.thankyou.msg1;</description>
<description>&panicButton.thankyou.msg2;</description>
</vbox>
</hbox>
<button label="&panicButton.thankyou.buttonlabel;"
id="panic-button-success-closebutton"
oncommand="PanicButtonNotifier.close()"/>
</panel>

View File

@ -23,7 +23,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentClick",
"resource:///modules/ContentClick.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider",
"resource://gre/modules/DirectoryLinksProvider.jsm");
"resource:///modules/DirectoryLinksProvider.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");

View File

@ -0,0 +1,66 @@
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["RunState"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm", this);
const STATE_STOPPED = 0;
const STATE_RUNNING = 1;
const STATE_QUITTING = 2;
// We're initially stopped.
let state = STATE_STOPPED;
function observer(subj, topic) {
Services.obs.removeObserver(observer, topic);
state = STATE_QUITTING;
}
// Listen for when the application is quitting.
Services.obs.addObserver(observer, "quit-application-granted", false);
/**
* This module keeps track of SessionStore's current run state. We will
* always start out at STATE_STOPPED. After the sessionw as read from disk and
* the initial browser window has loaded we switch to STATE_RUNNING. On the
* first notice that a browser shutdown was granted we switch to STATE_QUITTING.
*/
this.RunState = Object.freeze({
// If we're stopped then SessionStore hasn't been initialized yet. As soon
// as the session is read from disk and the initial browser window has loaded
// the run state will change to STATE_RUNNING.
get isStopped() {
return state == STATE_STOPPED;
},
// STATE_RUNNING is our default mode of operation that we'll spend most of
// the time in. After the session was read from disk and the first browser
// window has loaded we remain running until the browser quits.
get isRunning() {
return state == STATE_RUNNING;
},
// We will enter STATE_QUITTING as soon as we receive notice that a browser
// shutdown was granted. SessionStore will use this information to prevent
// us from collecting partial information while the browser is shutting down
// as well as to allow a last single write to disk and block all writes after
// that.
get isQuitting() {
return state == STATE_QUITTING;
},
// Switch the run state to STATE_RUNNING. This must be called after the
// session was read from, the initial browser window has loaded and we're
// now ready to restore session data.
setRunning() {
if (this.isStopped) {
state = STATE_RUNNING;
}
}
});

View File

@ -38,6 +38,8 @@ Cu.import("resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RunState",
"resource:///modules/sessionstore/RunState.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
@ -267,7 +269,7 @@ let SessionFileInternal = {
}
let isFinalWrite = false;
if (Services.startup.shuttingDown) {
if (RunState.isQuitting) {
// If shutdown has started, we will want to stop receiving
// write instructions.
isFinalWrite = this._isClosed = true;

View File

@ -11,10 +11,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const STATE_STOPPED = 0;
const STATE_RUNNING = 1;
const STATE_QUITTING = -1;
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
@ -31,8 +27,7 @@ const MAX_CONCURRENT_TAB_RESTORES = 3;
// global notifications observed
const OBSERVING = [
"browser-window-before-show", "domwindowclosed",
"quit-application-requested", "quit-application-granted",
"browser-lastwindow-close-granted",
"quit-application-requested", "browser-lastwindow-close-granted",
"quit-application", "browser:purge-session-history",
"browser:purge-domain-data",
"gather-telemetry",
@ -112,6 +107,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "GlobalState",
"resource:///modules/sessionstore/GlobalState.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
"resource:///modules/sessionstore/PrivacyFilter.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RunState",
"resource:///modules/sessionstore/RunState.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
"resource:///modules/devtools/scratchpad-manager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
@ -288,9 +285,6 @@ let SessionStoreInternal = {
Ci.nsISupportsWeakReference
]),
// set default load state
_loadState: STATE_STOPPED,
_globalState: new GlobalState(),
// During the initial restore and setBrowserState calls tracks the number of
@ -472,7 +466,7 @@ let SessionStoreInternal = {
// at this point, we've as good as resumed the session, so we can
// clear the resume_session_once flag, if it's set
if (this._loadState != STATE_QUITTING &&
if (!RunState.isQuitting &&
this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
@ -531,9 +525,6 @@ let SessionStoreInternal = {
case "quit-application-requested":
this.onQuitApplicationRequested();
break;
case "quit-application-granted":
this.onQuitApplicationGranted();
break;
case "browser-lastwindow-close-granted":
this.onLastWindowCloseGranted();
break;
@ -739,7 +730,7 @@ let SessionStoreInternal = {
return;
// ignore windows opened while shutting down
if (this._loadState == STATE_QUITTING)
if (RunState.isQuitting)
return;
// Assign the window a unique identifier we can use to reference
@ -764,8 +755,8 @@ let SessionStoreInternal = {
this._windows[aWindow.__SSi].isPopup = true;
// perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
this._loadState = STATE_RUNNING;
if (RunState.isStopped) {
RunState.setRunning();
SessionSaver.updateLastSaveTime();
// restore a crashed session resp. resume the last session if requested
@ -1008,7 +999,7 @@ let SessionStoreInternal = {
let winData = this._windows[aWindow.__SSi];
// Collect window data only when *not* closed during shutdown.
if (this._loadState == STATE_RUNNING) {
if (RunState.isRunning) {
// Flush all data queued in the content script before the window is gone.
TabState.flushWindow(aWindow);
@ -1100,14 +1091,6 @@ let SessionStoreInternal = {
DirtyWindows.clear();
},
/**
* On quit application granted
*/
onQuitApplicationGranted: function ssi_onQuitApplicationGranted() {
// freeze the data at what we've got (ignoring closing windows)
this._loadState = STATE_QUITTING;
},
/**
* On last browser window close
*/
@ -1141,7 +1124,6 @@ let SessionStoreInternal = {
LastSession.clear();
}
this._loadState = STATE_QUITTING; // just to be sure
this._uninit();
},
@ -1153,7 +1135,7 @@ let SessionStoreInternal = {
// If the browser is shutting down, simply return after clearing the
// session data on disk as this notification fires after the
// quit-application notification so the browser is about to exit.
if (this._loadState == STATE_QUITTING)
if (RunState.isQuitting)
return;
LastSession.clear();
let openWindows = {};
@ -1179,7 +1161,7 @@ let SessionStoreInternal = {
var win = this._getMostRecentBrowserWindow();
if (win) {
win.setTimeout(() => SessionSaver.run(), 0);
} else if (this._loadState == STATE_RUNNING) {
} else if (RunState.isRunning) {
SessionSaver.run();
}
@ -1237,7 +1219,7 @@ let SessionStoreInternal = {
}
}
if (this._loadState == STATE_RUNNING) {
if (RunState.isRunning) {
SessionSaver.run();
}
@ -1368,7 +1350,7 @@ let SessionStoreInternal = {
* Window reference
*/
onTabSelect: function ssi_onTabSelect(aWindow) {
if (this._loadState == STATE_RUNNING) {
if (RunState.isRunning) {
this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
let tab = aWindow.gBrowser.selectedTab;
@ -2033,7 +2015,7 @@ let SessionStoreInternal = {
var activeWindow = this._getMostRecentBrowserWindow();
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
if (this._loadState == STATE_RUNNING) {
if (RunState.isRunning) {
// update the data for all windows with activities since the last save operation
this._forEachBrowserWindow(function(aWindow) {
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
@ -2090,7 +2072,7 @@ let SessionStoreInternal = {
//XXXzpao We should do this for _restoreLastWindow == true, but that has
// its own check for popups. c.f. bug 597619
if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0 &&
this._loadState == STATE_QUITTING) {
RunState.isQuitting) {
// prepend the last non-popup browser window, so that if the user loads more tabs
// at startup we don't accidentally add them to a popup window
do {
@ -2155,7 +2137,7 @@ let SessionStoreInternal = {
if (!this._isWindowLoaded(aWindow))
return this._statesToRestore[aWindow.__SS_restoreID];
if (this._loadState == STATE_RUNNING) {
if (RunState.isRunning) {
this._collectWindowData(aWindow);
}
@ -2637,7 +2619,7 @@ let SessionStoreInternal = {
*/
restoreNextTab: function ssi_restoreNextTab() {
// If we call in here while quitting, we don't actually want to do anything
if (this._loadState == STATE_QUITTING)
if (RunState.isQuitting)
return;
// Don't exceed the maximum number of concurrent tab restores.

View File

@ -31,6 +31,7 @@ EXTRA_JS_MODULES.sessionstore = [
'PrivacyFilter.jsm',
'PrivacyLevel.jsm',
'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
'RunState.jsm',
'SessionCookies.jsm',
'SessionFile.jsm',
'SessionHistory.jsm',

View File

@ -2260,6 +2260,8 @@ this.Experiments.PreviousExperimentProvider = function (experiments) {
}
this.Experiments.PreviousExperimentProvider.prototype = Object.freeze({
get name() "PreviousExperimentProvider",
startup: function () {
this._log.trace("startup()");
Services.obs.addObserver(this, EXPERIMENTS_CHANGED_TOPIC, false);

View File

@ -773,3 +773,28 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY switchToMetroCmd2.label "Relaunch in &brandShortName; for Windows 8 Touch">
<!-- LOCALIZATION NOTE: (panicButton.view.mainTimeframeDesc, panicButton.view.5min, panicButton.view.2hr, panicButton.view.day):
The .mainTimeframeDesc string combined with any of the 3 others is meant to form a complete sentence, e.g. "Forget the last: Five minutes".
Please ensure that this remains the case in the translation. -->
<!ENTITY panicButton.view.mainTimeframeDesc "Forget the last:">
<!ENTITY panicButton.view.5min "Five minutes">
<!ENTITY panicButton.view.2hr "Two hours">
<!ENTITY panicButton.view.day "24 hours">
<!-- LOCALIZATION NOTE: (panicButton.view.mainLabel, panicButton.view.deleteCookies, panicButton.view.deleteHistory, panicButton.view.deleteTabsAndWindows, panicButton.view.openNewWindow):
The .mainActionDesc string combined with any of the 4 others is meant to form a complete sentence, e.g. "Proceeding will: Delete Recent Cookies".
Note also that the deleteCookies, deleteHistory and deleteTabsAndWindows strings include <html:strong> tags for emphasis on the words "Cookies", "History", "Tabs" and "Windows".
The translation should do the same. -->
<!ENTITY panicButton.view.mainActionDesc "Proceeding will:">
<!ENTITY panicButton.view.deleteCookies "Delete Recent <html:strong>Cookies</html:strong>">
<!ENTITY panicButton.view.deleteHistory "Delete Recent <html:strong>History</html:strong>">
<!ENTITY panicButton.view.deleteTabsAndWindows "Close all <html:strong>Tabs</html:strong> and <html:strong>Windows</html:strong>">
<!ENTITY panicButton.view.openNewWindow "Open a new clean Window">
<!ENTITY panicButton.view.undoWarning "This action cannot be undone.">
<!ENTITY panicButton.view.forgetButton "Forget!">
<!ENTITY panicButton.thankyou.msg1 "Your recent history is cleared.">
<!ENTITY panicButton.thankyou.msg2 "Safe browsing!">
<!ENTITY panicButton.thankyou.buttonlabel "Thanks!">

View File

@ -99,3 +99,6 @@ quit-button.tooltiptext.mac = Quit %1$S (%2$S)
loop-call-button.label = Invite someone to talk
loop-call-button.tooltiptext = Invite someone to talk
panic-button.label = Forget
panic-button.tooltiptext = Forget about some browsing history

View File

@ -103,6 +103,10 @@ XPCOMUtils.defineLazyGetter(this, "PALETTE_ITEMS", function() {
result.push("characterencoding-button");
}
if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
result.push("panic-button");
}
return result;
});

View File

@ -6,7 +6,10 @@
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/unit/social/xpcshell.ini']
XPCSHELL_TESTS_MANIFESTS += [
'test/unit/social/xpcshell.ini',
'test/xpcshell/xpcshell.ini',
]
EXTRA_JS_MODULES += [
'BrowserNewTabPreloader.jsm',
@ -17,6 +20,7 @@ EXTRA_JS_MODULES += [
'ContentSearch.jsm',
'ContentWebRTC.jsm',
'CustomizationTabPreloader.jsm',
'DirectoryLinksProvider.jsm',
'E10SUtils.jsm',
'Feeds.jsm',
'FormSubmitObserver.jsm',

View File

@ -9,11 +9,11 @@
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu, Constructor: CC } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm");
Cu.import("resource:///modules/DirectoryLinksProvider.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Http.jsm");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/osfile.jsm")
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

View File

@ -0,0 +1,6 @@
[DEFAULT]
head =
tail =
firefox-appdir = browser
[test_DirectoryLinksProvider.js]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -121,6 +121,9 @@ browser.jar:
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/panic-panel/header.png (../shared/panic-panel/header.png)
skin/classic/browser/panic-panel/header-small.png (../shared/panic-panel/header-small.png)
skin/classic/browser/panic-panel/icons.png (../shared/panic-panel/icons.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -1115,6 +1115,23 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(36px, 1368px, 72px, 1332px);
}
#panic-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 1404px, 36px, 1368px);
}
#panic-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(36px, 1404px, 72px, 1368px);
}
#panic-button[cui-areatype="toolbar"][open] {
-moz-image-region: rect(72px, 1404px, 108px, 1368px);
}
#panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
width: 18px;
@ -1264,6 +1281,15 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(0px, 1728px, 64px, 1664px);
}
#panic-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #panic-button {
-moz-image-region: rect(0, 1792px, 64px, 1728px);
}
#panic-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
-moz-image-region: rect(64px, 1792px, 128px, 1728px);
}
toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 1664px, 64px, 1600px);
}

View File

@ -70,6 +70,30 @@
background-image: url("chrome://global/skin/menu/shared-menu-check@2x.png");
}
#panic-button-success-icon,
#PanelUI-panic-timeframe-icon {
list-style-image: url(chrome://browser/skin/panic-panel/header@2x.png);
}
#PanelUI-panic-timeframe-icon-small {
list-style-image: url(chrome://browser/skin/panic-panel/header-small@2x.png);
}
#PanelUI-panic-actionlist-cookies {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons@2x.png), 0, 32, 32, 0);
}
#PanelUI-panic-actionlist-history {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons@2x.png), 0, 64, 32, 32);
}
#PanelUI-panic-actionlist-windows {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons@2x.png), 0, 96, 32, 64);
}
#PanelUI-panic-actionlist-newwindow {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons@2x.png), 0, 128, 32, 96);
}
}
.panelUI-grid .toolbarbutton-1 {

View File

@ -202,6 +202,12 @@ browser.jar:
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/monitor.png
skin/classic/browser/monitor_16-10.png
skin/classic/browser/panic-panel/header.png (../shared/panic-panel/header.png)
skin/classic/browser/panic-panel/header@2x.png (../shared/panic-panel/header@2x.png)
skin/classic/browser/panic-panel/header-small.png (../shared/panic-panel/header-small.png)
skin/classic/browser/panic-panel/header-small@2x.png (../shared/panic-panel/header-small@2x.png)
skin/classic/browser/panic-panel/icons.png (../shared/panic-panel/icons.png)
skin/classic/browser/panic-panel/icons@2x.png (../shared/panic-panel/icons@2x.png)
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
* skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -2,7 +2,7 @@
% Note that zoom-reset-button is a bit different since it doesn't use an image and thus has the image with display: none.
%define nestedButtons #zoom-out-button, #zoom-reset-button, #zoom-in-button, #cut-button, #copy-button, #paste-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button, #panic-button
%ifdef XP_MACOSX
% Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen

View File

@ -1107,3 +1107,187 @@ toolbaritem[overflowedItem=true],
menuitem[checked="true"].subviewbutton > .menu-iconic-left {
visibility: hidden;
}
#PanelUI-panicView.cui-widget-panelview {
min-width: 280px;
}
#PanelUI-panic-timeframe {
padding: 15px;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
#panic-button-success-icon,
#PanelUI-panic-timeframe-icon,
#PanelUI-panic-timeframe-icon-small {
background-color: transparent;
-moz-margin-end: 10px;
}
#panic-button-success-icon,
#PanelUI-panic-timeframe-icon {
list-style-image: url(chrome://browser/skin/panic-panel/header.png);
max-height: 48px;
width: 48px;
}
#PanelUI-panic-timeframe-icon-small {
list-style-image: url(chrome://browser/skin/panic-panel/header-small.png);
max-height: 32px;
width: 32px;
}
/* current attribute is only set when in use as a subview instead of a main view */
#PanelUI-panicView[current] #PanelUI-panic-timeframe-icon {
display: none;
}
#PanelUI-panicView.cui-widget-panelview #PanelUI-panic-timeframe-icon-small {
display: none;
}
#panic-button-success-header,
#PanelUI-panic-header {
-moz-box-align: center;
margin-bottom: 5px;
}
#PanelUI-panicView.cui-widget-panelview #PanelUI-panic-header {
margin-bottom: 0;
}
#PanelUI-panic-timeframe-icon-small:-moz-locale-dir(rtl),
#PanelUI-panic-timeframe-icon:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
.subviewradio {
-moz-binding: url(chrome://global/content/bindings/radio.xml#radio);
-moz-appearance: none;
padding: 1px;
margin: 0 0 2px;
background-color: hsla(210,4%,10%,0);
border-radius: 2px;
border-width: 1px;
border-style: solid;
border-color: hsla(210,4%,10%,0);
}
.subviewradio@buttonStateHover@ {
background-color: hsla(210,4%,10%,.08);
border-color: hsla(210,4%,10%,.11);
}
.subviewradio[selected],
.subviewradio[selected]:hover,
.subviewradio@buttonStateActive@ {
background-color: hsla(210,4%,10%,.12);
border-color: hsla(210,4%,10%,.14);
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
}
.subviewradio > .radio-check {
-moz-appearance: none;
width: 16px;
height: 16px;
border: 1px solid #e7e7e7;
border-radius: 50%;
margin: 1px 5px;
background-color: #f1f1f1;
}
.subviewradio > .radio-check[selected] {
background-color: #fff;
border: 4px solid #177ee6;
}
#PanelUI-panic-explanations {
padding: 10px 10px 0;
}
#PanelUI-panic-actionlist-main-label {
color: GrayText;
font-size: 0.9em;
}
.PanelUI-panic-actionlist {
-moz-padding-start: 20px;
padding-top: 2px;
padding-bottom: 2px;
background-size: 16px 16px;
background-repeat: no-repeat;
background-color: transparent;
background-position: center left;
}
.PanelUI-panic-actionlist:-moz-locale-dir(rtl) {
background-position: center right;
}
#PanelUI-panic-actionlist-cookies {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 16, 16, 0);
}
#PanelUI-panic-actionlist-history {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 32, 16, 16);
}
#PanelUI-panic-actionlist-windows {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 48, 16, 32);
}
#PanelUI-panic-actionlist-newwindow {
background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 64, 16, 48);
}
#PanelUI-panic-warning {
color: #C11F14;
text-align: center;
width: 100%;
margin-top: 20px;
}
#PanelUI-panic-view-button {
-moz-appearance: none;
background-color: #d92316;
color: white;
margin: 5px 15px 11px;
border: 1px solid #c92014;
border-radius: 3px;
padding: 10px;
}
#PanelUI-panic-view-button:hover {
background-color: #bf1f13;
border-color: #b81d12;
}
#PanelUI-panic-view-button:hover:active {
background-color: #99180f;
border-color: #91170f;
}
#PanelUI-panic-view-button > .toolbarbutton-text {
text-align: center;
text-shadow: none;
}
#panic-button-success-closebutton {
background-color: #e5e5e5;
color: black;
margin: 5px 0 0;
border: 1px solid #ccc;
border-radius: 3px;
padding: 10px;
-moz-appearance: none;
}
#panic-button-success-closebutton:hover {
background-color: #dedede;
border-color: #bbb;
}
#panic-button-success-closebutton:hover:active {
background-color: #d0d0d0;
border-color: #aaa;
}

View File

@ -1,4 +1,5 @@
/* Menu panel and palette styles */
/* Note that this file isn't used for HiDPI on OS X. */
toolbaritem[sdkstylewidget="true"] > toolbarbutton,
:-moz-any(@primaryToolbarButtons@)[cui-areatype="menu-panel"],
@ -146,6 +147,15 @@ toolbarpaletteitem[place="palette"] > #sidebar-button {
-moz-image-region: rect(32px, 864px, 64px, 832px);
}
#panic-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #panic-button {
-moz-image-region: rect(0, 896px, 32px, 864px);
}
#panic-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
-moz-image-region: rect(32px, 896px, 64px, 864px);
}
toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 832px, 32px, 800px);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -168,6 +168,29 @@ toolbar[brighttext] #sync-button[status="active"]:hover:active:not([disabled="tr
-moz-image-region: rect(0, 684px, 18px, 666px);
}
#panic-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 702px, 18px, 684px);
}
%ifdef XP_MACOSX
#panic-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(18px, 702px, 36px, 684px);
}
#panic-button[cui-areatype="toolbar"][open] {
-moz-image-region: rect(36px, 702px, 54px, 684px);
}
%else
#panic-button[cui-areatype="toolbar"][open] {
-moz-image-region: rect(18px, 702px, 36px, 684px);
}
%endif
#panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
#loop-call-button > .toolbarbutton-badge-container {
list-style-image: url(chrome://browser/skin/loop/toolbar.png);
-moz-image-region: rect(0, 18px, 18px, 0);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -145,6 +145,9 @@ browser.jar:
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/panic-panel/header.png (../shared/panic-panel/header.png)
skin/classic/browser/panic-panel/header-small.png (../shared/panic-panel/header-small.png)
skin/classic/browser/panic-panel/icons.png (../shared/panic-panel/icons.png)
skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/bookmark.png (places/bookmark.png)
@ -569,6 +572,9 @@ browser.jar:
skin/classic/aero/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/aero/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/aero/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/aero/browser/panic-panel/header.png (../shared/panic-panel/header.png)
skin/classic/aero/browser/panic-panel/header-small.png (../shared/panic-panel/header-small.png)
skin/classic/aero/browser/panic-panel/icons.png (../shared/panic-panel/icons.png)
* skin/classic/aero/browser/places/places.css (places/places-aero.css)
* skin/classic/aero/browser/places/organizer.css (places/organizer-aero.css)
skin/classic/aero/browser/places/bookmark.png (places/bookmark-aero.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -195,6 +195,7 @@ public final class NotificationHelper implements GeckoEventListener {
notificationIntent.setData(dataUri);
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
notificationIntent.putExtra(COOKIE_ATTR, message.optString(COOKIE_ATTR));
notificationIntent.setClass(mContext, GeckoAppShell.getGeckoInterface().getActivity().getClass());
return notificationIntent;
}

View File

@ -65,8 +65,18 @@ let WebappRT = {
// If the app is in debug mode, configure and enable the remote debugger.
Messaging.sendRequestForResult({ type: "NativeApp:IsDebuggable" }).then((response) => {
if (response.isDebuggable) {
this._enableRemoteDebugger(aUrl);
let that = this;
let name = this._getAppName(aUrl);
if (response.isDebuggable) {
Notifications.create({
title: Strings.browser.formatStringFromName("remoteStartNotificationTitle", [name], 1),
message: Strings.browser.GetStringFromName("remoteStartNotificationMessage"),
icon: "drawable://warning_doorhanger",
onClick: function(aId, aCookie) {
that._enableRemoteDebugger(aUrl);
},
});
}
});
@ -139,6 +149,18 @@ let WebappRT = {
}
},
_getAppName: function(aUrl) {
let name = Strings.browser.GetStringFromName("remoteNotificationGenericName");
let app = DOMApplicationRegistry.getAppByManifestURL(aUrl);
if (app) {
name = app.name;
}
return name;
},
_enableRemoteDebugger: function(aUrl) {
// Skip the connection prompt in favor of notifying the user below.
Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
@ -158,13 +180,7 @@ let WebappRT = {
// Notify the user that we enabled the debugger and which port it's using
// so they can use the DevTools Connect… dialog to connect the client to it.
DOMApplicationRegistry.registryReady.then(() => {
let name;
let app = DOMApplicationRegistry.getAppByManifestURL(aUrl);
if (app) {
name = app.name;
} else {
name = Strings.browser.GetStringFromName("remoteNotificationGenericName");
}
let name = this._getAppName(aUrl);
Notifications.create({
title: Strings.browser.formatStringFromName("remoteNotificationTitle", [name], 1),

View File

@ -322,6 +322,10 @@ remoteNotificationGenericName=App
# LOCALIZATION NOTE (remoteNotificationMessage): %S is the port on which
# the remote debugger server is listening.
remoteNotificationMessage=Listening on port %S
# LOCALIZATION NOTE (remoteStartNotificationTitle): %S is the name of the app.
remoteStartNotificationTitle=Activate debugging for %S
# LOCALIZATION NOTE (remoteStartNotificationMessage):
remoteStartNotificationMessage=Touch to activate remote debugger
# Helper apps
helperapps.open=Open

View File

@ -20,7 +20,6 @@ EXTRA_JS_MODULES += [
'DeferredTask.jsm',
'Deprecated.jsm',
'Dict.jsm',
'DirectoryLinksProvider.jsm',
'FileUtils.jsm',
'Finder.jsm',
'Geometry.jsm',

View File

@ -10,7 +10,6 @@ support-files =
[test_BinarySearch.js]
[test_DeferredTask.js]
[test_dict.js]
[test_DirectoryLinksProvider.js]
[test_FileUtils.js]
[test_GMPInstallManager.js]
# GMPInstallManager is not shipped on Android

View File

@ -122,6 +122,8 @@ let logger = Log.repository.getLogger(LOGGER_ID);
const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
const UNNAMED_PROVIDER = "<unnamed-provider>";
/**
* Preference listener which listens for a change in the
* "extensions.logging.enabled" preference and changes the logging level of the
@ -464,6 +466,8 @@ var gUpdateEnabled = true;
var gAutoUpdateDefault = true;
var gHotfixID = null;
var gShutdownBarrier = null;
var gRepoShutdownState = "";
var gShutdownInProgress = false;
/**
* This is the real manager, kept here rather than in AddonManager to keep its
@ -475,6 +479,7 @@ var AddonManagerInternal = {
addonListeners: [],
typeListeners: [],
providers: [],
providerShutdowns: new Map(),
types: {},
startupChanges: {},
// Store telemetry details per addon provider
@ -613,6 +618,33 @@ var AddonManagerInternal = {
}
},
/**
* Start up a provider, and register its shutdown hook if it has one
*/
_startProvider(aProvider, aAppChanged, aOldAppVersion, aOldPlatformVersion) {
if (!gStarted)
throw Components.Exception("AddonManager is not initialized",
Cr.NS_ERROR_NOT_INITIALIZED);
callProvider(aProvider, "startup", null, aAppChanged, aOldAppVersion, aOldPlatformVersion);
if ('shutdown' in aProvider) {
let name = aProvider.name || "Provider";
let AMProviderShutdown = () => {
return new Promise((resolve, reject) => {
logger.debug("Calling shutdown blocker for " + name);
resolve(aProvider.shutdown());
})
.catch(err => {
logger.warn("Failure during shutdown of " + name, err);
AddonManagerPrivate.recordException("AMI", "Async shutdown of " + name, err);
});
};
logger.debug("Registering shutdown blocker for " + name);
this.providerShutdowns.set(aProvider, AMProviderShutdown);
AddonManager.shutdown.addBlocker(name, AMProviderShutdown);
}
},
/**
* Initializes the AddonManager, loading any known providers and initializing
* them.
@ -742,15 +774,17 @@ var AddonManagerInternal = {
}
// Register our shutdown handler with the AsyncShutdown manager
gShutdownBarrier = new AsyncShutdown.Barrier("AddonManager: Waiting for clients to shut down.");
AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
this.shutdownManager.bind(this));
gShutdownBarrier = new AsyncShutdown.Barrier("AddonManager: Waiting for providers to shut down.");
AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down.",
this.shutdownManager.bind(this),
{fetchState: this.shutdownState.bind(this)});
// Once we start calling providers we must allow all normal methods to work.
gStarted = true;
this.callProviders("startup", appChanged, oldAppVersion,
oldPlatformVersion);
for (let provider of this.providers) {
this._startProvider(provider, appChanged, oldAppVersion, oldPlatformVersion);
}
// If this is a new profile just pretend that there were no changes
if (appChanged === undefined) {
@ -813,8 +847,9 @@ var AddonManagerInternal = {
}
// If we're registering after startup call this provider's startup.
if (gStarted)
callProvider(aProvider, "startup");
if (gStarted) {
this._startProvider(aProvider);
}
},
/**
@ -822,6 +857,9 @@ var AddonManagerInternal = {
*
* @param aProvider
* The provider to unregister
* @return Whatever the provider's 'shutdown' method returns (if anything).
* For providers that have async shutdown methods returning Promises,
* the caller should wait for that Promise to resolve.
*/
unregisterProvider: function AMI_unregisterProvider(aProvider) {
if (!aProvider || typeof aProvider != "object")
@ -851,9 +889,19 @@ var AddonManagerInternal = {
}
}
// If we're unregistering after startup call this provider's shutdown.
if (gStarted)
callProvider(aProvider, "shutdown");
// If we're unregistering after startup but before shutting down,
// remove the blocker for this provider's shutdown and call it.
// If we're already shutting down, just let gShutdownBarrier call it to avoid races.
if (gStarted && !gShutdownInProgress) {
logger.debug("Unregistering shutdown blocker for " + (aProvider.name || "Provider"));
let shutter = this.providerShutdowns.get(aProvider);
if (shutter) {
this.providerShutdowns.delete(aProvider);
gShutdownBarrier.client.removeBlocker(shutter);
return shutter();
}
}
return undefined;
},
/**
@ -884,46 +932,21 @@ var AddonManagerInternal = {
},
/**
* Calls a method on all registered providers, if the provider implements
* the method. The called method is expected to return a promise, and
* callProvidersAsync returns a promise that resolves when every provider
* method has either resolved or rejected. Rejection reasons are logged
* but otherwise ignored. Return values are ignored. Any parameters after the
* method parameter are passed to the provider's method.
*
* @param aMethod
* The method name to call
* @see callProvider
* Report the current state of asynchronous shutdown
*/
callProvidersAsync: function AMI_callProviders(aMethod, ...aArgs) {
if (!aMethod || typeof aMethod != "string")
throw Components.Exception("aMethod must be a non-empty string",
Cr.NS_ERROR_INVALID_ARG);
let allProviders = [];
let providers = this.providers.slice(0);
for (let provider of providers) {
try {
if (aMethod in provider) {
// Resolve a new promise with the result of the method, to handle both
// methods that return values (or nothing) and methods that return promises.
let providerResult = provider[aMethod].apply(provider, aArgs);
let nextPromise = Promise.resolve(providerResult);
// Log and swallow the errors from methods that do return promises.
nextPromise = nextPromise.then(
null,
e => logger.error("Exception calling provider " + aMethod, e));
allProviders.push(nextPromise);
}
}
catch (e) {
logger.error("Exception calling provider " + aMethod, e);
}
shutdownState() {
let state = [];
if (gShutdownBarrier) {
state.push({
name: gShutdownBarrier.client.name,
state: gShutdownBarrier.state
});
}
// Because we use promise.then to catch and log all errors above, Promise.all()
// will never exit early because of a rejection.
return Promise.all(allProviders);
state.push({
name: "AddonRepository: async shutdown",
state: gRepoShutdownState
});
return state;
},
/**
@ -932,8 +955,10 @@ var AddonManagerInternal = {
* @return Promise{null} that resolves when all providers and dependent modules
* have finished shutting down
*/
shutdownManager: function() {
shutdownManager: Task.async(function* () {
logger.debug("shutdown");
gRepoShutdownState = "pending";
gShutdownInProgress = true;
// Clean up listeners
Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
@ -942,37 +967,47 @@ var AddonManagerInternal = {
Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
// Only shut down providers if they've been started. Shut down
// AddonRepository after providers (if any).
let shuttingDown = null;
let savedError = null;
// Only shut down providers if they've been started.
if (gStarted) {
shuttingDown = gShutdownBarrier.wait()
.then(null, err => logger.error("Failure during wait for shutdown barrier", err))
.then(() => this.callProvidersAsync("shutdown"))
.then(null,
err => logger.error("Failure during async provider shutdown", err))
.then(() => AddonRepository.shutdown());
}
else {
shuttingDown = AddonRepository.shutdown();
try {
yield gShutdownBarrier.wait();
}
catch(err) {
savedError = err;
logger.error("Failure during wait for shutdown barrier", err);
AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonRepository", err);
}
}
shuttingDown.then(val => logger.debug("Async provider shutdown done"),
err => logger.error("Failure during AddonRepository shutdown", err))
.then(() => {
this.managerListeners.splice(0, this.managerListeners.length);
this.installListeners.splice(0, this.installListeners.length);
this.addonListeners.splice(0, this.addonListeners.length);
this.typeListeners.splice(0, this.typeListeners.length);
for (let type in this.startupChanges)
delete this.startupChanges[type];
gStarted = false;
gStartupComplete = false;
gShutdownBarrier = null;
});
// Shut down AddonRepository after providers (if any).
try {
gRepoShutdownState = "in progress";
yield AddonRepository.shutdown();
gRepoShutdownState = "done";
}
catch(err) {
savedError = err;
logger.error("Failure during AddonRepository shutdown", err);
AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonRepository", err);
}
return shuttingDown;
},
logger.debug("Async provider shutdown done");
this.managerListeners.splice(0, this.managerListeners.length);
this.installListeners.splice(0, this.installListeners.length);
this.addonListeners.splice(0, this.addonListeners.length);
this.typeListeners.splice(0, this.typeListeners.length);
this.providerShutdowns.clear();
for (let type in this.startupChanges)
delete this.startupChanges[type];
gStarted = false;
gStartupComplete = false;
gShutdownBarrier = null;
gShutdownInProgress = false;
if (savedError) {
throw savedError;
}
}),
/**
* Notified when a preference we're interested in has changed.

View File

@ -70,6 +70,8 @@ var _themeIDBeingEnabled = null;
var _themeIDBeingDisabled = null;
this.LightweightThemeManager = {
get name() "LightweightThemeManager",
get usedThemes () {
try {
return JSON.parse(_prefs.getComplexValue("usedThemes",

View File

@ -245,6 +245,8 @@ let OpenH264Wrapper = {
};
let OpenH264Provider = {
get name() "OpenH264Provider",
startup: function() {
configureLogging();
this._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.OpenH264Provider",

View File

@ -48,6 +48,8 @@ function getIDHashForString(aStr) {
}
var PluginProvider = {
get name() "PluginProvider",
// A dictionary mapping IDs to names and descriptions
plugins: null,

View File

@ -1787,6 +1787,8 @@ this.XPIStates = {
};
this.XPIProvider = {
get name() "XPIProvider",
// An array of known install locations
installLocations: null,
// A dictionary of known install locations by name

View File

@ -26,6 +26,7 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/Promise.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
Services.prefs.setBoolPref("toolkit.osfile.log", true);
@ -37,31 +38,18 @@ let AddonManagerInternal = AMscope.AddonManagerInternal;
// down AddonManager from the test
let MockAsyncShutdown = {
hook: null,
status: null,
profileBeforeChange: {
addBlocker: function(aName, aBlocker) {
addBlocker: function(aName, aBlocker, aOptions) {
do_print("Mock profileBeforeChange blocker for '" + aName + "'");
MockAsyncShutdown.hook = aBlocker;
MockAsyncShutdown.status = aOptions.fetchState;
}
},
Barrier: function (name) {
this.name = name;
this.client.addBlocker = (name, blocker) => {
do_print("Mock Barrier blocker for '" + name + "' for barrier '" + this.name + "'");
this.blockers.push({name: name, blocker: blocker});
};
},
// We can use the real Barrier
Barrier: AsyncShutdown.Barrier
};
MockAsyncShutdown.Barrier.prototype = Object.freeze({
blockers: [],
client: {},
wait: Task.async(function* () {
for (let b of this.blockers) {
yield b.blocker();
}
}),
});
AMscope.AsyncShutdown = MockAsyncShutdown;
var gInternalManager = null;

View File

@ -0,0 +1,98 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Verify that we report shutdown status for Addon Manager providers
// and AddonRepository correctly.
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
// Make a mock AddonRepository that just lets us hang shutdown.
// Needs two promises - one to let us know that AM has called shutdown,
// and one for us to let AM know that shutdown is done.
function mockAddonProvider(aName) {
let mockProvider = {
donePromise: null,
doneResolve: null,
doneReject: null,
shutdownPromise: null,
shutdownResolve: null,
get name() aName,
shutdown() {
this.shutdownResolve();
return this.donePromise;
},
};
mockProvider.donePromise = new Promise((resolve, reject) => {
mockProvider.doneResolve = resolve;
mockProvider.doneResject = reject;
});
mockProvider.shutdownPromise = new Promise((resolve, reject) => {
mockProvider.shutdownResolve = resolve;
});
return mockProvider;
};
function run_test() {
run_next_test();
}
// Helper to find a particular shutdown blocker's status in the JSON blob
function findInStatus(aStatus, aName) {
for (let {name, state} of aStatus.state) {
if (name == aName) {
return state;
}
}
return null;
}
/*
* Make sure we report correctly when an add-on provider or AddonRepository block shutdown
*/
add_task(function* blockRepoShutdown() {
// Reach into the AddonManager scope and inject our mock AddonRepository
let realAddonRepo = AMscope.AddonRepository;
// the mock provider behaves enough like AddonRepository for the purpose of this test
let mockRepo = mockAddonProvider("Mock repo");
AMscope.AddonRepository = mockRepo;
let mockProvider = mockAddonProvider("Mock provider");
startupManager();
AddonManagerPrivate.registerProvider(mockProvider);
// Start shutting the manager down
let managerDown = promiseShutdownManager();
// Wait for manager to call provider shutdown.
yield mockProvider.shutdownPromise;
// check AsyncShutdown state
let status = MockAsyncShutdown.status();
equal(findInStatus(status[0], "Mock provider"), "(none)");
equal(status[1].name, "AddonRepository: async shutdown");
equal(status[1].state, "pending");
// let the provider finish
mockProvider.doneResolve();
// Wait for manager to call repo shutdown and start waiting for it
yield mockRepo.shutdownPromise;
// Check the shutdown state
status = MockAsyncShutdown.status();
do_print(JSON.stringify(status));
equal(status[0].name, "AddonManager: Waiting for providers to shut down.");
equal(status[0].state, "Complete");
equal(status[1].name, "AddonRepository: async shutdown");
equal(status[1].state, "in progress");
// Now finish our shutdown, and wait for the manager to wrap up
mockRepo.doneResolve();
yield managerDown;
// Check the shutdown state again
status = MockAsyncShutdown.status();
equal(status[0].name, "AddonRepository: async shutdown");
equal(status[0].state, "done");
});

View File

@ -14,6 +14,7 @@ support-files =
[test_metadata_update.js]
[test_openh264.js]
run-if = appname == "firefox"
[test_provider_shutdown.js]
[test_shutdown.js]
[test_XPIcancel.js]
[test_XPIStates.js]