Bug 899013 - Interface for customizing the DownloadIntegration module. r=mak

MozReview-Commit-ID: HWmcxXlLdLx

--HG--
extra : rebase_source : ba6b6a751710e7a45cc94baf6449d470c86cbc9f
This commit is contained in:
Paolo Amadini 2016-04-18 14:21:55 +01:00
Родитель 1d329e0789
Коммит 5c5d54e3a3
6 изменённых файлов: 598 добавлений и 556 удалений

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

@ -58,10 +58,9 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Integration.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
@ -90,6 +89,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "gPrintSettingsService",
"@mozilla.org/gfx/printsettings-service;1",
Ci.nsIPrintSettingsService);
Integration.downloads.defineModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
const BackgroundFileSaverStreamListener = Components.Constructor(
"@mozilla.org/network/background-file-saver;1?mode=streamlistener",
"nsIBackgroundFileSaver");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -22,13 +22,12 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Integration.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/DownloadCore.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadCombinedList",
"resource://gre/modules/DownloadList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadList",
"resource://gre/modules/DownloadList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadSummary",
@ -40,6 +39,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
Integration.downloads.defineModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
////////////////////////////////////////////////////////////////////////////////
//// Downloads

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

@ -130,6 +130,45 @@ var promiseVerifyTarget = Task.async(function* (downloadTarget,
do_check_eq(downloadTarget.size, expectedContents.length);
});
/**
* Waits for an attempt to launch a file, and returns the nsIMIMEInfo used for
* the launch, or null if the file was launched with the default handler.
*/
function waitForFileLaunched() {
return new Promise(resolve => {
let waitFn = base => ({
launchFile(file, mimeInfo) {
Integration.downloads.unregister(waitFn);
if (!mimeInfo ||
mimeInfo.preferredAction == Ci.nsIMIMEInfo.useSystemDefault) {
resolve(null);
} else {
resolve(mimeInfo);
}
return Promise.resolve();
},
});
Integration.downloads.register(waitFn);
});
}
/**
* Waits for an attempt to show the directory where a file is located, and
* returns the path of the file.
*/
function waitForDirectoryShown() {
return new Promise(resolve => {
let waitFn = base => ({
showContainingDirectory(path) {
Integration.downloads.unregister(waitFn);
resolve(path);
return Promise.resolve();
},
});
Integration.downloads.register(waitFn);
});
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
@ -1575,11 +1614,15 @@ add_task(function* test_cancel_midway_restart_with_content_encoding()
*/
add_task(function* test_blocked_parental_controls()
{
let blockFn = base => ({
shouldBlockForParentalControls: () => Promise.resolve(true),
});
Integration.downloads.register(blockFn);
function cleanup() {
DownloadIntegration.shouldBlockInTest = false;
Integration.downloads.unregister(blockFn);
}
do_register_cleanup(cleanup);
DownloadIntegration.shouldBlockInTest = true;
let download;
try {
@ -1645,11 +1688,15 @@ add_task(function* test_blocked_parental_controls_httpstatus450()
*/
add_task(function* test_blocked_runtime_permissions()
{
let blockFn = base => ({
shouldBlockForRuntimePermissions: () => Promise.resolve(true),
});
Integration.downloads.register(blockFn);
function cleanup() {
DownloadIntegration.shouldBlockInTestForRuntimePermissions = false;
Integration.downloads.unregister(blockFn);
}
do_register_cleanup(cleanup);
DownloadIntegration.shouldBlockInTestForRuntimePermissions = true;
let download;
try {
@ -1709,23 +1756,24 @@ add_task(function* test_getSha256Hash()
* @rejects JavaScript exception.
*/
var promiseBlockedDownload = Task.async(function* (options) {
let blockFn = base => ({
shouldBlockForReputationCheck: () => Promise.resolve({
shouldBlock: true,
verdict: Downloads.Error.BLOCK_VERDICT_UNCOMMON,
}),
shouldKeepBlockedData: () => Promise.resolve(options.keepBlockedData),
});
Integration.downloads.register(blockFn);
function cleanup() {
DownloadIntegration.shouldBlockInTestForApplicationReputation = false;
DownloadIntegration.verdictInTestForApplicationReputation = "";
DownloadIntegration.shouldKeepBlockedDataInTest = false;
Integration.downloads.unregister(blockFn);
}
do_register_cleanup(cleanup);
let {keepPartialData, keepBlockedData} = options;
DownloadIntegration.shouldBlockInTestForApplicationReputation = true;
DownloadIntegration.verdictInTestForApplicationReputation =
Downloads.Error.BLOCK_VERDICT_UNCOMMON;
DownloadIntegration.shouldKeepBlockedDataInTest = keepBlockedData;
let download;
try {
if (keepPartialData) {
if (options.keepPartialData) {
download = yield promiseStartDownload_tryToKeepPartialData();
continueResponses();
} else if (gUseLegacySaver) {
@ -1944,8 +1992,6 @@ add_task(function* test_blocked_applicationReputation_unblock()
* download.showContainingDirectory() action
*/
add_task(function* test_showContainingDirectory() {
DownloadIntegration._deferTestShowDir = Promise.defer();
let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
let download = yield Downloads.createDownload({
@ -1953,8 +1999,11 @@ add_task(function* test_showContainingDirectory() {
target: ""
});
let promiseDirectoryShown = waitForDirectoryShown();
yield download.showContainingDirectory();
let path = yield promiseDirectoryShown;
try {
yield download.showContainingDirectory();
new FileUtils.File(path);
do_throw("Should have failed because of an invalid path.");
} catch (ex) {
if (!(ex instanceof Components.Exception)) {
@ -1972,11 +2021,9 @@ add_task(function* test_showContainingDirectory() {
target: targetPath
});
DownloadIntegration._deferTestShowDir = Promise.defer();
promiseDirectoryShown = waitForDirectoryShown();
download.showContainingDirectory();
let result = yield DownloadIntegration._deferTestShowDir.promise;
do_check_eq(result, "success");
yield promiseDirectoryShown;
});
/**
@ -2019,9 +2066,9 @@ add_task(function* test_launch() {
do_check_true(download.launchWhenSucceeded);
DownloadIntegration._deferTestOpenFile = Promise.defer();
let promiseFileLaunched = waitForFileLaunched();
download.launch();
let result = yield DownloadIntegration._deferTestOpenFile.promise;
let result = yield promiseFileLaunched;
// Verify that the results match the test case.
if (!launcherPath) {
@ -2047,11 +2094,23 @@ add_task(function* test_launcherPath_invalid() {
launcherPath: " "
});
DownloadIntegration._deferTestOpenFile = Promise.defer();
let promiseDownloadLaunched = new Promise(resolve => {
let waitFn = base => ({
__proto__: base,
launchDownload() {
Integration.downloads.unregister(waitFn);
let superPromise = super.launchDownload(...arguments);
resolve(superPromise);
return superPromise;
},
});
Integration.downloads.register(waitFn);
});
yield download.start();
try {
download.launch();
result = yield DownloadIntegration._deferTestOpenFile.promise;
yield promiseDownloadLaunched;
do_throw("Can't launch file with invalid custom launcher")
} catch (ex) {
if (!(ex instanceof Components.Exception)) {
@ -2074,7 +2133,7 @@ add_task(function* test_launchWhenSucceeded() {
// Test both with and without setting a custom application.
for (let launcherPath of [null, customLauncher.path]) {
DownloadIntegration._deferTestOpenFile = Promise.defer();
let promiseFileLaunched = waitForFileLaunched();
if (!gUseLegacySaver) {
let download = yield Downloads.createDownload({
@ -2092,7 +2151,7 @@ add_task(function* test_launchWhenSucceeded() {
yield promiseDownloadStopped(download);
}
let result = yield DownloadIntegration._deferTestOpenFile.promise;
let result = yield promiseFileLaunched;
// Verify that the results match the test case.
if (!launcherPath) {
@ -2161,17 +2220,29 @@ add_task(function* test_platform_integration()
}
Services.obs.addObserver(observer, "download-watcher-notify", false);
Services.prefs.setBoolPref("device.storage.enabled", true);
let downloadDoneCalled = false;
let monitorFn = base => ({
__proto__: base,
downloadDone() {
return super.downloadDone(...arguments).then(() => {
downloadDoneCalled = true;
});
},
});
Integration.downloads.register(monitorFn);
DownloadIntegration.allowDirectories = true;
function cleanup() {
for (let file of downloadFiles) {
file.remove(true);
}
Services.obs.removeObserver(observer, "download-watcher-notify");
Services.prefs.setBoolPref("device.storage.enabled", oldDeviceStorageEnabled);
Integration.downloads.unregister(monitorFn);
DownloadIntegration.allowDirectories = false;
}
do_register_cleanup(cleanup);
for (let isPrivate of [false, true]) {
DownloadIntegration.downloadDoneCalled = false;
downloadDoneCalled = false;
// Some platform specific operations only operate on files outside the
// temporary directory or in the Downloads directory (such as setting
@ -2199,7 +2270,7 @@ add_task(function* test_platform_integration()
// Wait for the whenSucceeded promise to be resolved first.
// downloadDone should be called before the whenSucceeded promise is resolved.
yield download.whenSucceeded().then(function () {
do_check_true(DownloadIntegration.downloadDoneCalled);
do_check_true(downloadDoneCalled);
do_check_true(downloadWatcherNotified);
});
@ -2208,6 +2279,8 @@ add_task(function* test_platform_integration()
yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
}
cleanup();
});
/**

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

@ -17,12 +17,11 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/Integration.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
"resource://gre/modules/DownloadPaths.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
@ -50,6 +49,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "gExternalHelperAppService",
"@mozilla.org/uriloader/external-helper-app-service;1",
Ci.nsIExternalHelperAppService);
Integration.downloads.defineModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
const ServerSocket = Components.Constructor(
"@mozilla.org/network/server-socket;1",
"nsIServerSocket",
@ -787,23 +789,39 @@ add_task(function test_common_initialize()
"Blocked by Windows Parental Controls");
});
// Disable integration with the host application requiring profile access.
DownloadIntegration.dontLoadList = true;
DownloadIntegration.dontLoadObservers = true;
// Disable the parental controls checking.
DownloadIntegration.dontCheckParentalControls = true;
// Disable application reputation checks.
DownloadIntegration.dontCheckApplicationReputation = true;
// Disable the calls to the OS to launch files and open containing folders
DownloadIntegration.dontOpenFileAndFolder = true;
DownloadIntegration._deferTestOpenFile = Promise.defer();
DownloadIntegration._deferTestShowDir = Promise.defer();
// Disable checking runtime permissions.
DownloadIntegration.dontCheckRuntimePermissions = true;
// Avoid leaking uncaught promise errors
DownloadIntegration._deferTestOpenFile.promise.then(null, () => undefined);
DownloadIntegration._deferTestShowDir.promise.then(null, () => undefined);
// During unit tests, most of the functions that require profile access or
// operating system features will be disabled. Individual tests may override
// them again to check for specific behaviors.
Integration.downloads.register(base => ({
__proto__: base,
loadPublicDownloadListFromStore: () => Promise.resolve(),
shouldKeepBlockedData: () => Promise.resolve(false),
shouldBlockForParentalControls: () => Promise.resolve(false),
shouldBlockForRuntimePermissions: () => Promise.resolve(false),
shouldBlockForReputationCheck: () => Promise.resolve({
shouldBlock: false,
verdict: "",
}),
confirmLaunchExecutable: () => Promise.resolve(),
launchFile: () => Promise.resolve(),
showContainingDirectory: () => Promise.resolve(),
// This flag allows re-enabling the default observers during their tests.
allowObservers: false,
addListObservers() {
return this.allowObservers ? super.addListObservers(...arguments)
: Promise.resolve();
},
// This flag allows re-enabling the download directory logic for its tests.
_allowDirectories: false,
set allowDirectories(value) {
this._allowDirectories = value;
// We have to invalidate the previously computed directory path.
this._downloadsDirectory = null;
},
_getDirectory(name) {
return super._getDirectory(this._allowDirectories ? name : "TmpD");
},
}));
// Get a reference to nsIComponentRegistrar, and ensure that is is freed
// before the XPCOM shutdown.

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

@ -10,20 +10,6 @@
////////////////////////////////////////////////////////////////////////////////
//// Globals
/**
* Enable test mode for the _confirmCancelDownloads method to return
* the number of downloads instead of showing the prompt to cancel or not.
*/
function enableObserversTestMode() {
DownloadIntegration.testMode = true;
DownloadIntegration.dontLoadObservers = false;
function cleanup() {
DownloadIntegration.testMode = false;
DownloadIntegration.dontLoadObservers = true;
}
do_register_cleanup(cleanup);
}
/**
* Notifies the prompt observers and verify the expected downloads count.
*
@ -39,46 +25,53 @@ function notifyPromptObservers(aIsPrivate, aExpectedCount, aExpectedPBCount) {
createInstance(Ci.nsISupportsPRBool);
// Notify quit application requested observer.
DownloadIntegration.testPromptDownloads = -1;
DownloadIntegration._testPromptDownloads = -1;
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
do_check_eq(DownloadIntegration.testPromptDownloads, aExpectedCount);
do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedCount);
// Notify offline requested observer.
DownloadIntegration.testPromptDownloads = -1;
DownloadIntegration._testPromptDownloads = -1;
Services.obs.notifyObservers(cancelQuit, "offline-requested", null);
do_check_eq(DownloadIntegration.testPromptDownloads, aExpectedCount);
do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedCount);
if (aIsPrivate) {
// Notify last private browsing requested observer.
DownloadIntegration.testPromptDownloads = -1;
DownloadIntegration._testPromptDownloads = -1;
Services.obs.notifyObservers(cancelQuit, "last-pb-context-exiting", null);
do_check_eq(DownloadIntegration.testPromptDownloads, aExpectedPBCount);
do_check_eq(DownloadIntegration._testPromptDownloads, aExpectedPBCount);
}
delete DownloadIntegration._testPromptDownloads;
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
/**
* Allows re-enabling the real download directory logic during one test.
*/
function allowDirectoriesInTest() {
DownloadIntegration.allowDirectories = true;
function cleanup() {
DownloadIntegration.allowDirectories = false;
}
do_register_cleanup(cleanup);
return cleanup;
}
XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
return Services.strings.
createBundle("chrome://mozapps/locale/downloads/downloads.properties");
});
/**
* Tests that the getSystemDownloadsDirectory returns a valid download
* directory string path.
* Tests that getSystemDownloadsDirectory returns an existing directory or
* creates a new directory depending on the platform. Instead of the real
* directory, this test is executed in the temporary directory so we can safely
* delete the created folder to check whether it is created again.
*/
add_task(function* test_getSystemDownloadsDirectory()
add_task(function* test_getSystemDownloadsDirectory_exists_or_creates()
{
// Enable test mode for the getSystemDownloadsDirectory method to return
// temp directory instead so we can check whether the desired directory
// is created or not.
DownloadIntegration.testMode = true;
function cleanup() {
DownloadIntegration.testMode = false;
}
do_register_cleanup(cleanup);
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
let downloadDir;
@ -107,11 +100,22 @@ add_task(function* test_getSystemDownloadsDirectory()
do_check_true(info.isDir);
yield OS.File.removeEmptyDir(targetPath);
}
});
let downloadDirBefore = yield DownloadIntegration.getSystemDownloadsDirectory();
/**
* Tests that the real directory returned by getSystemDownloadsDirectory is not
* the one that is used during unit tests. Since this is the actual downloads
* directory of the operating system, we don't try to delete it afterwards.
*/
add_task(function* test_getSystemDownloadsDirectory_real()
{
let fakeDownloadDir = yield DownloadIntegration.getSystemDownloadsDirectory();
let cleanup = allowDirectoriesInTest();
let realDownloadDir = yield DownloadIntegration.getSystemDownloadsDirectory();
cleanup();
let downloadDirAfter = yield DownloadIntegration.getSystemDownloadsDirectory();
do_check_neq(downloadDirBefore, downloadDirAfter);
do_check_neq(fakeDownloadDir, realDownloadDir);
});
/**
@ -120,13 +124,15 @@ add_task(function* test_getSystemDownloadsDirectory()
*/
add_task(function* test_getPreferredDownloadsDirectory()
{
let cleanupDirectories = allowDirectoriesInTest();
let folderListPrefName = "browser.download.folderList";
let dirPrefName = "browser.download.dir";
function cleanup() {
function cleanupPrefs() {
Services.prefs.clearUserPref(folderListPrefName);
Services.prefs.clearUserPref(dirPrefName);
}
do_register_cleanup(cleanup);
do_register_cleanup(cleanupPrefs);
// Should return the system downloads directory.
Services.prefs.setIntPref(folderListPrefName, 1);
@ -174,7 +180,8 @@ add_task(function* test_getPreferredDownloadsDirectory()
downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_eq(downloadDir, systemDir);
cleanup();
cleanupPrefs();
cleanupDirectories();
});
/**
@ -183,6 +190,8 @@ add_task(function* test_getPreferredDownloadsDirectory()
*/
add_task(function* test_getTemporaryDownloadsDirectory()
{
let cleanup = allowDirectoriesInTest();
let downloadDir = yield DownloadIntegration.getTemporaryDownloadsDirectory();
do_check_neq(downloadDir, "");
@ -193,19 +202,33 @@ add_task(function* test_getTemporaryDownloadsDirectory()
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
do_check_eq(downloadDir, tempDir.path);
}
cleanup();
});
////////////////////////////////////////////////////////////////////////////////
//// Tests DownloadObserver
/**
* Re-enables the default observers for the following tests.
*
* This takes effect the first time a DownloadList object is created, and lasts
* until this test file has completed.
*/
add_task(function* test_observers_setup()
{
DownloadIntegration.allowObservers = true;
do_register_cleanup(function () {
DownloadIntegration.allowObservers = false;
});
});
/**
* Tests notifications prompts when observers are notified if there are public
* and private active downloads.
*/
add_task(function* test_notifications()
{
enableObserversTestMode();
for (let isPrivate of [false, true]) {
mustInterruptResponses();
@ -244,8 +267,6 @@ add_task(function* test_notifications()
*/
add_task(function* test_no_notifications()
{
enableObserversTestMode();
for (let isPrivate of [false, true]) {
let list = yield promiseNewList(isPrivate);
let download1 = yield promiseNewDownload(httpUrl("interruptible.txt"));
@ -274,7 +295,6 @@ add_task(function* test_no_notifications()
*/
add_task(function* test_mix_notifications()
{
enableObserversTestMode();
mustInterruptResponses();
let publicList = yield promiseNewList();
@ -306,8 +326,6 @@ add_task(function* test_mix_notifications()
*/
add_task(function* test_suspend_resume()
{
enableObserversTestMode();
// The default wake delay is 10 seconds, so set the wake delay to be much
// faster for these tests.
Services.prefs.setIntPref("browser.download.manager.resumeOnWakeDelay", 5);
@ -387,7 +405,6 @@ add_task(function* test_suspend_resume()
*/
add_task(function* test_exit_private_browsing()
{
enableObserversTestMode();
mustInterruptResponses();
let privateList = yield promiseNewList(true);
@ -406,11 +423,12 @@ add_task(function* test_exit_private_browsing()
do_check_eq((yield privateList.getAll()).length, 2);
// Simulate exiting the private browsing.
DownloadIntegration._deferTestClearPrivateList = Promise.defer();
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
let result = yield DownloadIntegration._deferTestClearPrivateList.promise;
yield new Promise(resolve => {
DownloadIntegration._testResolveClearPrivateList = resolve;
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
});
delete DownloadIntegration._testResolveClearPrivateList;
do_check_eq(result, "success");
do_check_eq((yield privateList.getAll()).length, 0);
continueResponses();