Bug 852599 - Block downloads when they are disallowed globally by the parental control service. r=paolo

This commit is contained in:
Raymond Lee 2013-06-29 03:49:31 +08:00
parent 09d5621e94
commit 688f174edb
7 changed files with 134 additions and 7 deletions

View File

@ -54,6 +54,8 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
@ -269,6 +271,14 @@ Download.prototype = {
yield this._promiseCanceled;
}
// Disallow download if parental controls service restricts it.
if (yield DownloadIntegration.shouldBlockForParentalControls(this)) {
let error = new DownloadError(Cr.NS_ERROR_FAILURE, "Download blocked.");
error.becauseBlocked = true;
error.becauseBlockedByParentalControls = true;
throw error;
}
try {
// Execute the actual download through the saver object.
yield this.saver.execute(DS_setProgressBytes.bind(this));
@ -541,6 +551,18 @@ DownloadError.prototype = {
* Indicates an error occurred while writing to the local target.
*/
becauseTargetFailed: false,
/**
* Indicates the download failed because it was blocked. If the reason for
* blocking is known, the corresponding property will be also set.
*/
becauseBlocked: false,
/**
* Indicates the download was blocked because downloads are globally
* disallowed by the Parental Controls or Family Safety features on Windows.
*/
becauseBlockedByParentalControls: false,
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -30,7 +30,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore",
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm")
"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");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
@ -38,6 +40,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
XPCOMUtils.defineLazyServiceGetter(this, "env",
"@mozilla.org/process/environment;1",
"nsIEnvironment");
XPCOMUtils.defineLazyGetter(this, "gParentalControlsService", function() {
if ("@mozilla.org/parental-controls-service;1" in Cc) {
return Cc["@mozilla.org/parental-controls-service;1"]
.createInstance(Ci.nsIParentalControlsService);
}
return null;
});
XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
return Services.strings.
createBundle("chrome://mozapps/locale/downloads/downloads.properties");
@ -54,6 +64,8 @@ this.DownloadIntegration = {
// For testing only
testMode: false,
dontLoad: false,
dontCheckParentalControls: false,
shouldBlockInTest: false,
/**
* Main DownloadStore object for loading and saving the list of persistent
@ -182,7 +194,6 @@ this.DownloadIntegration = {
directory = Services.prefs.getComplexValue("browser.download.dir",
Ci.nsIFile);
yield OS.File.makeDir(directory.path, { ignoreExisting: true });
} catch(ex) {
// Either the preference isn't set or the directory cannot be created.
directory = yield this.getSystemDownloadsDirectory();
@ -221,6 +232,34 @@ this.DownloadIntegration = {
}.bind(this));
},
/**
* Checks to determine whether to block downloads for parental controls.
*
* aParam aDownload
* The download object.
*
* @return {Promise}
* @resolves The boolean indicates to block downloads or not.
*/
shouldBlockForParentalControls: function DI_shouldBlockForParentalControls(aDownload) {
if (this.dontCheckParentalControls) {
return Promise.resolve(this.shouldBlockInTest);
}
let isEnabled = gParentalControlsService &&
gParentalControlsService.parentalControlsEnabled;
let shouldBlock = isEnabled &&
gParentalControlsService.blockFileDownloadsEnabled;
// Log the event if required by parental controls settings.
if (isEnabled && gParentalControlsService.loggingEnabled) {
gParentalControlsService.log(gParentalControlsService.ePCLog_FileDownload,
shouldBlock, aDownload.source.uri, null);
}
return Promise.resolve(shouldBlock);
},
/**
* Determines whether it's a Windows Metro app.
*/

View File

@ -83,6 +83,10 @@ DownloadLegacyTransfer.prototype = {
onStateChange: function DLT_onStateChange(aWebProgress, aRequest, aStateFlags,
aStatus)
{
if (!Components.isSuccessCode(aStatus)) {
this._componentFailed = true;
}
// Detect when the last file has been received, or the download failed.
if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
@ -113,6 +117,8 @@ DownloadLegacyTransfer.prototype = {
// change, but if no network request actually started, it is possible that
// we only receive a status change with an error status code.
if (!Components.isSuccessCode(aStatus)) {
this._componentFailed = true;
// Wait for the associated Download object to be available.
this._deferDownload.promise.then(function DLT_OSC_onDownload(aDownload) {
aDownload.saver.onTransferFinished(aRequest, aStatus);
@ -160,12 +166,18 @@ DownloadLegacyTransfer.prototype = {
saver: { type: "legacy" },
}).then(function DLT_I_onDownload(aDownload) {
// Now that the saver is available, hook up the cancellation handler.
aDownload.saver.deferCanceled.promise
.then(function () aCancelable.cancel(Cr.NS_ERROR_ABORT))
.then(null, Cu.reportError);
aDownload.saver.deferCanceled.promise.then(() => {
// Only cancel if the object executing the download is still running.
if (!this._componentFailed) {
aCancelable.cancel(Cr.NS_ERROR_ABORT);
}
}).then(null, Cu.reportError);
// Start the download before allowing it to be controlled.
aDownload.start();
aDownload.start().then(null, function () {
// In case the operation failed, ensure we stop downloading data.
aDownload.saver.deferCanceled.resolve();
});
// Start processing all the other events received through nsITransfer.
this._deferDownload.resolve(aDownload);
@ -189,6 +201,13 @@ DownloadLegacyTransfer.prototype = {
* object associated with this nsITransfer instance, when it is available.
*/
_deferDownload: null,
/**
* Indicates that the component that executes the download has notified a
* failure condition. In this case, we should never use the component methods
* that cancel the download.
*/
_componentFailed: false,
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -451,4 +451,6 @@ add_task(function test_common_initialize()
// Disable integration with the host application requiring profile access.
DownloadIntegration.dontLoad = true;
// Disable the parental controls checking.
DownloadIntegration.dontCheckParentalControls = true;
});

View File

@ -873,3 +873,25 @@ add_task(function test_download_cancel_midway_restart_with_content_encoding()
yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT);
});
/**
* Download with parental controls enabled.
*/
add_task(function test_download_blocked_parental_controls()
{
function cleanup() {
DownloadIntegration.shouldBlockInTest = false;
}
do_register_cleanup(cleanup);
DownloadIntegration.shouldBlockInTest = true;
let download = yield promiseSimpleDownload();
try {
yield download.start();
do_throw("The download should have blocked.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
do_check_true(ex.becauseBlockedByParentalControls);
}
cleanup();
});

View File

@ -125,7 +125,6 @@ add_task(function test_getUserDownloadsDirectory()
cleanup();
});
/**
* Tests that the getTemporaryDownloadsDirectory returns a valid nsFile
* download directory object.

View File

@ -353,3 +353,27 @@ add_task(function test_download_public_and_private()
cleanup();
});
/**
* Download with parental controls enabled.
*/
add_task(function test_download_blocked_parental_controls()
{
function cleanup() {
DownloadIntegration.shouldBlockInTest = false;
}
do_register_cleanup(cleanup);
DownloadIntegration.shouldBlockInTest = true;
let download = yield promiseStartLegacyDownload();
try {
yield download.start();
do_throw("The download should have blocked.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
do_check_true(ex.becauseBlockedByParentalControls);
}
do_check_false(download.target.file.exists());
cleanup();
});