mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 835880 - Implement the basic DownloadList object. r=enn
--HG-- extra : rebase_source : 15c2ba2132c87fdd633dc069a1e2ebef727c6392
This commit is contained in:
parent
c81286236b
commit
aac56c7c18
@ -25,6 +25,9 @@ const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadList
|
||||
|
||||
@ -32,7 +35,148 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
* Represents a collection of Download objects that can be viewed and managed by
|
||||
* the user interface, and persisted across sessions.
|
||||
*/
|
||||
function DownloadList() { }
|
||||
function DownloadList() {
|
||||
this._downloads = [];
|
||||
this._views = new Set();
|
||||
}
|
||||
|
||||
DownloadList.prototype = {
|
||||
/**
|
||||
* Array of Download objects currently in the list.
|
||||
*/
|
||||
_downloads: null,
|
||||
|
||||
/**
|
||||
* Retrieves a snapshot of the downloads that are currently in the list. The
|
||||
* returned array does not change when downloads are added or removed, though
|
||||
* the Download objects it contains are still updated in real time.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves An array of Download objects.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
getAll: function DL_getAll() {
|
||||
return Promise.resolve(Array.slice(this._downloads, 0));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new download to the end of the items list.
|
||||
*
|
||||
* @note When a download is added to the list, its "onchange" event is
|
||||
* registered by the list, thus it cannot be used to monitor the
|
||||
* download. To receive change notifications for downloads that are
|
||||
* added to the list, use the addView method to register for
|
||||
* onDownloadChanged notifications.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to add.
|
||||
*/
|
||||
add: function DL_add(aDownload) {
|
||||
this._downloads.push(aDownload);
|
||||
aDownload.onchange = this._change.bind(this, aDownload);
|
||||
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
if (view.onDownloadAdded) {
|
||||
view.onDownloadAdded(aDownload);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a download from the list. If the download was already removed,
|
||||
* this method has no effect.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to remove.
|
||||
*/
|
||||
remove: function DL_remove(aDownload) {
|
||||
let index = this._downloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
this._downloads.splice(index, 1);
|
||||
aDownload.onchange = null;
|
||||
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
if (view.onDownloadRemoved) {
|
||||
view.onDownloadRemoved(aDownload);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called when "onchange" events of downloads occur.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object that changed.
|
||||
*/
|
||||
_change: function DL_change(aDownload) {
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
if (view.onDownloadChanged) {
|
||||
view.onDownloadChanged(aDownload);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set of currently registered views.
|
||||
*/
|
||||
_views: null,
|
||||
|
||||
/**
|
||||
* Adds a view that will be notified of changes to downloads. The newly added
|
||||
* view will receive onDownloadAdded notifications for all the downloads that
|
||||
* are already in the list.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to add. The following methods may be defined:
|
||||
* {
|
||||
* onDownloadAdded: function (aDownload) {
|
||||
* // Called after aDownload is added to the end of the list.
|
||||
* },
|
||||
* onDownloadChanged: function (aDownload) {
|
||||
* // Called after the properties of aDownload change.
|
||||
* },
|
||||
* onDownloadRemoved: function (aDownload) {
|
||||
* // Called after aDownload is removed from the list.
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
addView: function DL_addView(aView)
|
||||
{
|
||||
this._views.add(aView);
|
||||
|
||||
if (aView.onDownloadAdded) {
|
||||
for (let download of this._downloads) {
|
||||
try {
|
||||
aView.onDownloadAdded(download);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a view that was previously added using addView. The removed view
|
||||
* will not receive any more notifications after this method returns.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to remove.
|
||||
*/
|
||||
removeView: function DL_removeView(aView)
|
||||
{
|
||||
this._views.delete(aView);
|
||||
},
|
||||
};
|
||||
|
@ -33,6 +33,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore",
|
||||
"resource://gre/modules/DownloadStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||
"resource://gre/modules/DownloadUIHelper.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",
|
||||
@ -125,6 +127,28 @@ this.Downloads = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the DownloadList object for downloads that were not started from
|
||||
* a private browsing window.
|
||||
*
|
||||
* Calling this function may cause the download list to be reloaded from the
|
||||
* previous session, if it wasn't loaded already.
|
||||
*
|
||||
* This method always retrieves a reference to the same download list.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The DownloadList object for public downloads.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
getPublicDownloadList: function D_getPublicDownloadList()
|
||||
{
|
||||
if (!this._publicDownloadList) {
|
||||
this._publicDownloadList = new DownloadList();
|
||||
}
|
||||
return Promise.resolve(this._publicDownloadList);
|
||||
},
|
||||
_publicDownloadList: null,
|
||||
|
||||
/**
|
||||
* Constructor for a DownloadError object. When you catch an exception during
|
||||
* a download, you can use this to verify if "ex instanceof Downloads.Error",
|
||||
|
@ -90,6 +90,25 @@ function getTempFile(aLeafName)
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Download object, using TEST_TARGET_FILE_NAME as the target.
|
||||
* The target is deleted by getTempFile when this function is called.
|
||||
*
|
||||
* @param aSourceURI
|
||||
* The nsIURI for the download source, or null to use TEST_SOURCE_URI.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The newly created Download object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
function promiseSimpleDownload(aSourceURI) {
|
||||
return Downloads.createDownload({
|
||||
source: { uri: aSourceURI || TEST_SOURCE_URI },
|
||||
target: { file: getTempFile(TEST_TARGET_FILE_NAME) },
|
||||
saver: { type: "copy" },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given file contents are equal to the given string.
|
||||
*
|
||||
|
@ -9,28 +9,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
/**
|
||||
* Creates a new Download object, using TEST_TARGET_FILE_NAME as the target.
|
||||
* The target is deleted by getTempFile when this function is called.
|
||||
*
|
||||
* @param aSourceURI
|
||||
* The nsIURI for the download source, or null to use TEST_SOURCE_URI.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The newly created Download object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
function promiseSimpleDownload(aSourceURI) {
|
||||
return Downloads.createDownload({
|
||||
source: { uri: aSourceURI || TEST_SOURCE_URI },
|
||||
target: { file: getTempFile(TEST_TARGET_FILE_NAME) },
|
||||
saver: { type: "copy" },
|
||||
});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
|
172
toolkit/components/jsdownloads/test/unit/test_DownloadList.js
Normal file
172
toolkit/components/jsdownloads/test/unit/test_DownloadList.js
Normal file
@ -0,0 +1,172 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the DownloadList object.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
/**
|
||||
* Returns a new DownloadList object.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The newly created DownloadList object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
function promiseNewDownloadList() {
|
||||
// Force the creation of a new public download list.
|
||||
Downloads._publicDownloadList = null;
|
||||
return Downloads.getPublicDownloadList();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
/**
|
||||
* Checks the testing mechanism used to build different download lists.
|
||||
*/
|
||||
add_task(function test_construction()
|
||||
{
|
||||
let downloadListOne = yield promiseNewDownloadList();
|
||||
let downloadListTwo = yield promiseNewDownloadList();
|
||||
|
||||
do_check_neq(downloadListOne, downloadListTwo);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks the methods to add and retrieve items from the list.
|
||||
*/
|
||||
add_task(function test_add_getAll()
|
||||
{
|
||||
let list = yield promiseNewDownloadList();
|
||||
|
||||
let downloadOne = yield promiseSimpleDownload();
|
||||
list.add(downloadOne);
|
||||
|
||||
let itemsOne = yield list.getAll();
|
||||
do_check_eq(itemsOne.length, 1);
|
||||
do_check_eq(itemsOne[0], downloadOne);
|
||||
|
||||
let downloadTwo = yield promiseSimpleDownload();
|
||||
list.add(downloadTwo);
|
||||
|
||||
let itemsTwo = yield list.getAll();
|
||||
do_check_eq(itemsTwo.length, 2);
|
||||
do_check_eq(itemsTwo[0], downloadOne);
|
||||
do_check_eq(itemsTwo[1], downloadTwo);
|
||||
|
||||
// The first snapshot should not have been modified.
|
||||
do_check_eq(itemsOne.length, 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks the method to remove items from the list.
|
||||
*/
|
||||
add_task(function test_remove()
|
||||
{
|
||||
let list = yield promiseNewDownloadList();
|
||||
|
||||
list.add(yield promiseSimpleDownload());
|
||||
list.add(yield promiseSimpleDownload());
|
||||
|
||||
let items = yield list.getAll();
|
||||
list.remove(items[0]);
|
||||
|
||||
// Removing an item that was never added should not raise an error.
|
||||
list.remove(yield promiseSimpleDownload());
|
||||
|
||||
items = yield list.getAll();
|
||||
do_check_eq(items.length, 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that views receive the download add and remove notifications, and that
|
||||
* adding and removing views works as expected.
|
||||
*/
|
||||
add_task(function test_notifications_add_remove()
|
||||
{
|
||||
let list = yield promiseNewDownloadList();
|
||||
|
||||
let downloadOne = yield promiseSimpleDownload();
|
||||
let downloadTwo = yield promiseSimpleDownload();
|
||||
list.add(downloadOne);
|
||||
list.add(downloadTwo);
|
||||
|
||||
// Check that we receive add notifications for existing elements.
|
||||
let addNotifications = 0;
|
||||
let viewOne = {
|
||||
onDownloadAdded: function (aDownload) {
|
||||
// The first download to be notified should be the first that was added.
|
||||
if (addNotifications == 0) {
|
||||
do_check_eq(aDownload, downloadOne);
|
||||
} else if (addNotifications == 1) {
|
||||
do_check_eq(aDownload, downloadTwo);
|
||||
}
|
||||
addNotifications++;
|
||||
},
|
||||
};
|
||||
list.addView(viewOne);
|
||||
do_check_eq(addNotifications, 2);
|
||||
|
||||
// Check that we receive add notifications for new elements.
|
||||
list.add(yield promiseSimpleDownload());
|
||||
do_check_eq(addNotifications, 3);
|
||||
|
||||
// Check that we receive remove notifications.
|
||||
let removeNotifications = 0;
|
||||
let viewTwo = {
|
||||
onDownloadRemoved: function (aDownload) {
|
||||
do_check_eq(aDownload, downloadOne);
|
||||
removeNotifications++;
|
||||
},
|
||||
};
|
||||
list.addView(viewTwo);
|
||||
list.remove(downloadOne);
|
||||
do_check_eq(removeNotifications, 1);
|
||||
|
||||
// We should not receive remove notifications after the view is removed.
|
||||
list.removeView(viewTwo);
|
||||
list.remove(downloadTwo);
|
||||
do_check_eq(removeNotifications, 1);
|
||||
|
||||
// We should not receive add notifications after the view is removed.
|
||||
list.removeView(viewOne);
|
||||
list.add(yield promiseSimpleDownload());
|
||||
do_check_eq(addNotifications, 3);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that views receive the download change notifications.
|
||||
*/
|
||||
add_task(function test_notifications_change()
|
||||
{
|
||||
let list = yield promiseNewDownloadList();
|
||||
|
||||
let downloadOne = yield promiseSimpleDownload();
|
||||
let downloadTwo = yield promiseSimpleDownload();
|
||||
list.add(downloadOne);
|
||||
list.add(downloadTwo);
|
||||
|
||||
// Check that we receive change notifications.
|
||||
let receivedOnDownloadChanged = false;
|
||||
list.addView({
|
||||
onDownloadChanged: function (aDownload) {
|
||||
do_check_eq(aDownload, downloadOne);
|
||||
receivedOnDownloadChanged = true;
|
||||
},
|
||||
});
|
||||
yield downloadOne.start();
|
||||
do_check_true(receivedOnDownloadChanged);
|
||||
|
||||
// We should not receive change notifications after a download is removed.
|
||||
receivedOnDownloadChanged = false;
|
||||
list.remove(downloadTwo);
|
||||
yield downloadTwo.start();
|
||||
do_check_false(receivedOnDownloadChanged);
|
||||
});
|
@ -14,7 +14,7 @@
|
||||
|
||||
/**
|
||||
* Tests that the createDownload function exists and can be called. More
|
||||
* detailed tests are implemented separately for the DownloadsCore module.
|
||||
* detailed tests are implemented separately for the DownloadCore module.
|
||||
*/
|
||||
add_task(function test_createDownload()
|
||||
{
|
||||
@ -46,3 +46,16 @@ add_task(function test_simpleDownload_object_arguments()
|
||||
{ file: targetFile });
|
||||
yield promiseVerifyContents(targetFile, TEST_DATA_SHORT);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that the getPublicDownloadList function returns the same list when
|
||||
* called multiple times. More detailed tests are implemented separately for
|
||||
* the DownloadList module.
|
||||
*/
|
||||
add_task(function test_getPublicDownloadList()
|
||||
{
|
||||
let downloadListOne = yield Downloads.getPublicDownloadList();
|
||||
let downloadListTwo = yield Downloads.getPublicDownloadList();
|
||||
|
||||
do_check_eq(downloadListOne, downloadListTwo);
|
||||
});
|
||||
|
@ -3,4 +3,5 @@ head = head.js
|
||||
tail = tail.js
|
||||
|
||||
[test_DownloadCore.js]
|
||||
[test_DownloadList.js]
|
||||
[test_Downloads.js]
|
||||
|
Loading…
Reference in New Issue
Block a user