зеркало из https://github.com/mozilla/gecko-dev.git
Bug 896927 - Handle the executable warning prompt. r=enn
This commit is contained in:
Родитель
52608ac5e3
Коммит
73620d69d7
|
@ -53,6 +53,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
|||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||
"resource://gre/modules/DownloadUIHelper.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||
"resource://gre/modules/DownloadUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
|
@ -68,27 +70,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger",
|
|||
|
||||
const nsIDM = Ci.nsIDownloadManager;
|
||||
|
||||
const kDownloadsStringBundleUrl =
|
||||
"chrome://browser/locale/downloads/downloads.properties";
|
||||
|
||||
const kPrefBdmScanWhenDone = "browser.download.manager.scanWhenDone";
|
||||
const kPrefBdmAlertOnExeOpen = "browser.download.manager.alertOnEXEOpen";
|
||||
|
||||
const kDownloadsStringsRequiringFormatting = {
|
||||
sizeWithUnits: true,
|
||||
shortTimeLeftSeconds: true,
|
||||
shortTimeLeftMinutes: true,
|
||||
shortTimeLeftHours: true,
|
||||
shortTimeLeftDays: true,
|
||||
statusSeparator: true,
|
||||
statusSeparatorBeforeNumber: true,
|
||||
fileExecutableSecurityWarning: true
|
||||
};
|
||||
|
||||
const kDownloadsStringsRequiringPluralForm = {
|
||||
otherDownloads2: true
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DownloadsLocalFileCtor", function () {
|
||||
return Components.Constructor("@mozilla.org/file/local;1",
|
||||
"nsILocalFile", "initWithPath");
|
||||
|
@ -163,41 +147,6 @@ this.DownloadsCommon = {
|
|||
}
|
||||
this.error.apply(this, aMessageArgs);
|
||||
},
|
||||
/**
|
||||
* Returns an object whose keys are the string names from the downloads string
|
||||
* bundle, and whose values are either the translated strings or functions
|
||||
* returning formatted strings.
|
||||
*/
|
||||
get strings()
|
||||
{
|
||||
let strings = {};
|
||||
let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
|
||||
let enumerator = sb.getSimpleEnumeration();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
|
||||
let stringName = string.key;
|
||||
if (stringName in kDownloadsStringsRequiringFormatting) {
|
||||
strings[stringName] = function () {
|
||||
// Convert "arguments" to a real array before calling into XPCOM.
|
||||
return sb.formatStringFromName(stringName,
|
||||
Array.slice(arguments, 0),
|
||||
arguments.length);
|
||||
};
|
||||
} else if (stringName in kDownloadsStringsRequiringPluralForm) {
|
||||
strings[stringName] = function (aCount) {
|
||||
// Convert "arguments" to a real array before calling into XPCOM.
|
||||
let formattedString = sb.formatStringFromName(stringName,
|
||||
Array.slice(arguments, 0),
|
||||
arguments.length);
|
||||
return PluralForm.get(aCount, formattedString);
|
||||
};
|
||||
} else {
|
||||
strings[stringName] = string.value;
|
||||
}
|
||||
}
|
||||
delete this.strings;
|
||||
return this.strings = strings;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates a very short string representing the given time left.
|
||||
|
@ -480,44 +429,22 @@ this.DownloadsCommon = {
|
|||
if (!(aOwnerWindow instanceof Ci.nsIDOMWindow))
|
||||
throw new Error("aOwnerWindow must be a dom-window object");
|
||||
|
||||
// Confirm opening executable files if required.
|
||||
let promiseShouldLaunch;
|
||||
if (aFile.isExecutable()) {
|
||||
let showAlert = true;
|
||||
try {
|
||||
showAlert = Services.prefs.getBoolPref(kPrefBdmAlertOnExeOpen);
|
||||
} catch (ex) { }
|
||||
|
||||
// On Vista and above, we rely on native security prompting for
|
||||
// downloaded content unless it's disabled.
|
||||
if (DownloadsCommon.isWinVistaOrHigher) {
|
||||
try {
|
||||
if (Services.prefs.getBoolPref(kPrefBdmScanWhenDone)) {
|
||||
showAlert = false;
|
||||
}
|
||||
} catch (ex) { }
|
||||
// We get a prompter for the provided window here, even though anchoring
|
||||
// to the most recently active window should work as well.
|
||||
promiseShouldLaunch =
|
||||
DownloadUIHelper.getPrompter(aOwnerWindow)
|
||||
.confirmLaunchExecutable(aFile.path);
|
||||
} else {
|
||||
promiseShouldLaunch = Promise.resolve(true);
|
||||
}
|
||||
|
||||
if (showAlert) {
|
||||
let name = aFile.leafName;
|
||||
let message =
|
||||
DownloadsCommon.strings.fileExecutableSecurityWarning(name, name);
|
||||
let title =
|
||||
DownloadsCommon.strings.fileExecutableSecurityWarningTitle;
|
||||
let dontAsk =
|
||||
DownloadsCommon.strings.fileExecutableSecurityWarningDontAsk;
|
||||
|
||||
let checkbox = { value: false };
|
||||
let open = Services.prompt.confirmCheck(aOwnerWindow, title, message,
|
||||
dontAsk, checkbox);
|
||||
if (!open) {
|
||||
promiseShouldLaunch.then(shouldLaunch => {
|
||||
if (!shouldLaunch) {
|
||||
return;
|
||||
}
|
||||
|
||||
Services.prefs.setBoolPref(kPrefBdmAlertOnExeOpen,
|
||||
!checkbox.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually open the file.
|
||||
try {
|
||||
if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
|
||||
|
@ -539,6 +466,7 @@ this.DownloadsCommon = {
|
|||
.getService(Ci.nsIExternalProtocolService)
|
||||
.loadUrl(NetUtil.newURI(aFile));
|
||||
}
|
||||
}).then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -574,6 +502,15 @@ this.DownloadsCommon = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object whose keys are the string names from the downloads string
|
||||
* bundle, and whose values are either the translated strings or functions
|
||||
* returning formatted strings.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(DownloadsCommon, "strings", function () {
|
||||
return DownloadUIHelper.strings;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns true if we are executing on Windows Vista or a later version.
|
||||
*/
|
||||
|
|
|
@ -29,6 +29,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore",
|
|||
"resource://gre/modules/DownloadStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport",
|
||||
"resource://gre/modules/DownloadImport.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||
"resource://gre/modules/DownloadUIHelper.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
|
@ -61,6 +63,7 @@ XPCOMUtils.defineLazyGetter(this, "gParentalControlsService", function() {
|
|||
return null;
|
||||
});
|
||||
|
||||
// This will be replaced by "DownloadUIHelper.strings" (see bug 905123).
|
||||
XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
|
||||
return Services.strings.
|
||||
createBundle("chrome://mozapps/locale/downloads/downloads.properties");
|
||||
|
@ -414,6 +417,24 @@ this.DownloadIntegration = {
|
|||
let deferred = Task.spawn(function DI_launchDownload_task() {
|
||||
let file = new FileUtils.File(aDownload.target.path);
|
||||
|
||||
// Ask for confirmation if the file is executable. We do this here,
|
||||
// instead of letting the caller handle the prompt separately in the user
|
||||
// interface layer, for two reasons. The first is because of its security
|
||||
// nature, so that add-ons cannot forget to do this check. The second is
|
||||
// that the system-level security prompt, if enabled, would be displayed
|
||||
// at launch time in any case.
|
||||
if (file.isExecutable() && !this.dontOpenFileAndFolder) {
|
||||
// We don't anchor the prompt to a specific window intentionally, not
|
||||
// only because this is the same behavior as the system-level prompt,
|
||||
// but also because the most recently active window is the right choice
|
||||
// in basically all cases.
|
||||
let shouldLaunch = yield DownloadUIHelper.getPrompter()
|
||||
.confirmLaunchExecutable(file.path);
|
||||
if (!shouldLaunch) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In case of a double extension, like ".tar.gz", we only
|
||||
// consider the last one, because the MIME service cannot
|
||||
// handle multiple extensions.
|
||||
|
@ -560,7 +581,11 @@ this.DownloadIntegration = {
|
|||
*/
|
||||
_createDownloadsDirectory: function DI_createDownloadsDirectory(aName) {
|
||||
let directory = this._getDirectory(aName);
|
||||
directory.append(gStringBundle.GetStringFromName("downloadsFolder"));
|
||||
|
||||
// We read the name of the directory from the list of translated strings
|
||||
// that is kept by the UI helper module, even if this string is not strictly
|
||||
// displayed in the user interface.
|
||||
directory.append(DownloadUIHelper.strings.downloadsFolder);
|
||||
|
||||
// Create the Downloads folder and ignore if it already exists.
|
||||
return OS.File.makeDir(directory.path, { ignoreExisting: true }).
|
||||
|
|
|
@ -24,6 +24,31 @@ const Cr = Components.results;
|
|||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
const kStringBundleUrl =
|
||||
"chrome://browser/locale/downloads/downloads.properties";
|
||||
|
||||
const kStringsRequiringFormatting = {
|
||||
sizeWithUnits: true,
|
||||
shortTimeLeftSeconds: true,
|
||||
shortTimeLeftMinutes: true,
|
||||
shortTimeLeftHours: true,
|
||||
shortTimeLeftDays: true,
|
||||
statusSeparator: true,
|
||||
statusSeparatorBeforeNumber: true,
|
||||
fileExecutableSecurityWarning: true,
|
||||
};
|
||||
|
||||
const kStringsRequiringPluralForm = {
|
||||
otherDownloads2: true,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadUIHelper
|
||||
|
||||
|
@ -31,4 +56,123 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
* Provides functions to handle status and messages in the user interface.
|
||||
*/
|
||||
this.DownloadUIHelper = {
|
||||
/**
|
||||
* Returns an object that can be used to display prompts related to downloads.
|
||||
*
|
||||
* The prompts may be either anchored to a specified window, or anchored to
|
||||
* the most recently active window, for example if the prompt is displayed in
|
||||
* response to global notifications that are not associated with any window.
|
||||
*
|
||||
* @param aParent
|
||||
* If specified, should reference the nsIDOMWindow to which the prompts
|
||||
* should be attached. If omitted, the prompts will be attached to the
|
||||
* most recently active window.
|
||||
*
|
||||
* @return A DownloadPrompter object.
|
||||
*/
|
||||
getPrompter: function (aParent)
|
||||
{
|
||||
return new DownloadPrompter(aParent || null);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object whose keys are the string names from the downloads string
|
||||
* bundle, and whose values are either the translated strings or functions
|
||||
* returning formatted strings.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(DownloadUIHelper, "strings", function () {
|
||||
let strings = {};
|
||||
let sb = Services.strings.createBundle(kStringBundleUrl);
|
||||
let enumerator = sb.getSimpleEnumeration();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
|
||||
let stringName = string.key;
|
||||
if (stringName in kStringsRequiringFormatting) {
|
||||
strings[stringName] = function () {
|
||||
// Convert "arguments" to a real array before calling into XPCOM.
|
||||
return sb.formatStringFromName(stringName,
|
||||
Array.slice(arguments, 0),
|
||||
arguments.length);
|
||||
};
|
||||
} else if (stringName in kStringsRequiringPluralForm) {
|
||||
strings[stringName] = function (aCount) {
|
||||
// Convert "arguments" to a real array before calling into XPCOM.
|
||||
let formattedString = sb.formatStringFromName(stringName,
|
||||
Array.slice(arguments, 0),
|
||||
arguments.length);
|
||||
return PluralForm.get(aCount, formattedString);
|
||||
};
|
||||
} else {
|
||||
strings[stringName] = string.value;
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadPrompter
|
||||
|
||||
/**
|
||||
* Allows displaying prompts related to downloads.
|
||||
*
|
||||
* @param aParent
|
||||
* The nsIDOMWindow to which prompts should be attached, or null to
|
||||
* attach prompts to the most recently active window.
|
||||
*/
|
||||
function DownloadPrompter(aParent)
|
||||
{
|
||||
this._prompter = Services.ww.getNewPrompter(aParent);
|
||||
}
|
||||
|
||||
DownloadPrompter.prototype = {
|
||||
/**
|
||||
* nsIPrompt instance for displaying messages.
|
||||
*/
|
||||
_prompter: null,
|
||||
|
||||
/**
|
||||
* Displays a warning message box that informs that the specified file is
|
||||
* executable, and asks whether the user wants to launch it. The user is
|
||||
* given the option of disabling future instances of this warning.
|
||||
*
|
||||
* @param aPath
|
||||
* String containing the full path to the file to be opened.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves Boolean indicating whether the launch operation can continue.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
confirmLaunchExecutable: function (aPath)
|
||||
{
|
||||
const kPrefAlertOnEXEOpen = "browser.download.manager.alertOnEXEOpen";
|
||||
|
||||
try {
|
||||
try {
|
||||
if (!Services.prefs.getBoolPref(kPrefAlertOnEXEOpen)) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
} catch (ex) {
|
||||
// If the preference does not exist, continue with the prompt.
|
||||
}
|
||||
|
||||
let leafName = OS.Path.basename(aPath);
|
||||
|
||||
let s = DownloadUIHelper.strings;
|
||||
let checkState = { value: false };
|
||||
let shouldLaunch = this._prompter.confirmCheck(
|
||||
s.fileExecutableSecurityWarningTitle,
|
||||
s.fileExecutableSecurityWarning(leafName, leafName),
|
||||
s.fileExecutableSecurityWarningDontAsk,
|
||||
checkState);
|
||||
|
||||
if (shouldLaunch) {
|
||||
Services.prefs.setBoolPref(kPrefAlertOnEXEOpen, !checkState.value);
|
||||
}
|
||||
|
||||
return Promise.resolve(shouldLaunch);
|
||||
} catch (ex) {
|
||||
return Promise.reject(ex);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче