Bug 1087233 - Create about:downloads to migrate to Downloads.jsm. r=mconley

This commit is contained in:
Hiroyuki Ikezoe 2015-02-20 18:20:35 -08:00
Родитель e5d6d6f6d9
Коммит 5392ec3a79
18 изменённых файлов: 882 добавлений и 5 удалений

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

@ -508,9 +508,10 @@ function openIMAccountWizard()
function openSavedFilesWnd()
{
Components.classes['@mozilla.org/download-manager-ui;1']
.getService(Components.interfaces.nsIDownloadManagerUI)
.show(window);
let tabmail = document.getElementById("tabmail");
tabmail.openTab("chromeTab",
{ chromePage: "about:downloads",
clickHandler: "specialTabs.aboutClickHandler(event);" });
}
function SetBusyCursor(window, enable)

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

@ -0,0 +1,73 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* 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/. */
/**
* This component enables the JavaScript API for downloads at startup. This
* will eventually be removed when nsIDownloadManager will not be available
* anymore (bug 851471).
*/
"use strict";
////////////////////////////////////////////////////////////////////////////////
//// Globals
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
/**
* CID and Contract ID of the JavaScript implementation of nsITransfer.
*/
const kTransferCid = Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}");
const kTransferContractId = "@mozilla.org/transfer;1";
////////////////////////////////////////////////////////////////////////////////
//// DownloadsStartup
function DownloadsStartup() { }
DownloadsStartup.prototype = {
classID: Components.ID("{a93f0d6f-02a3-4486-a662-8f49b8c1de48}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsStartup),
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
//////////////////////////////////////////////////////////////////////////////
//// nsIObserver
observe: function DS_observe(aSubject, aTopic, aData)
{
if (aTopic != "profile-after-change") {
Cu.reportError("Unexpected observer notification.");
return;
}
// Override Toolkit's nsITransfer implementation with the one from the
// JavaScript API for downloads.
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
.registerFactory(kTransferCid, "",
kTransferContractId, null);
// To preserve download list across sessions.
DownloadIntegration.shouldPersistDownload = function(aDownload) {
return true;
};
},
};
////////////////////////////////////////////////////////////////////////////////
//// Module
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]);

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

@ -24,6 +24,8 @@ AboutRedirector.prototype = {
flags: Ci.nsIAboutModule.ALLOW_SCRIPT},
"preferences": {url: "chrome://messenger/content/preferences/aboutPreferences.xul",
flags: Ci.nsIAboutModule.ALLOW_SCRIPT},
"downloads": {url: "chrome://messenger/content/downloads/aboutDownloads.xul",
flags: Ci.nsIAboutModule.ALLOW_SCRIPT},
},
/**

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

@ -0,0 +1,26 @@
/* 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/. */
richlistitem.download {
-moz-binding: url('chrome://messenger/content/downloads/download.xml#download');
}
#msgDownloadsRichListBox {
/** The default listbox appearance comes with an unwanted margin. **/
margin: 0;
display: -moz-box;
}
#msgDownloadsRichListBox > richlistitem.download {
height: 5em;
padding: 5px 8px;
}
#msgDownloadsListEmptyDescription {
display: none;
}
#msgDownloadsRichListBox:empty + #msgDownloadsListEmptyDescription {
display: -moz-box;
}

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

@ -0,0 +1,325 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* 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";
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", "resource://gre/modules/DownloadUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
const DownloadsView = {
init() {
window.controllers.insertControllerAt(0, this);
this.listElement = document.getElementById("msgDownloadsRichListBox");
this.items = new Map();
Downloads.getList(Downloads.ALL)
.then(list => list.addView(this))
.then(null, Cu.reportError);
window.addEventListener("unload", aEvent => {
Downloads.getList(Downloads.ALL)
.then(list => list.removeView(this))
.then(null, Cu.reportError);
window.controllers.removeController(this);
});
},
insertOrMoveItem(aItem) {
let compare = (a, b) => {
// active downloads always before stopped downloads
if (a.stopped != b.stopped) {
return b.stopped ? -1 : 1
}
// most recent downloads first
return b.startTime - a.startTime;
};
let at = this.listElement.firstChild;
while (at && compare(aItem.download, at.download) > 0) {
at = at.nextElementSibling;
}
this.listElement.insertBefore(aItem.element, at);
},
onDownloadAdded(aDownload) {
let isPurgedFromDisk = download => {
if (!download.succeeded) {
return false;
}
let targetFile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
targetFile.initWithPath(download.target.path);
return !targetFile.exists();
}
if (isPurgedFromDisk(aDownload)) {
Downloads.getList(Downloads.ALL)
.then(list => list.remove(aDownload))
return;
}
let item = new DownloadItem(aDownload);
this.items.set(aDownload, item);
this.insertOrMoveItem(item);
},
onDownloadChanged(aDownload) {
let item = this.items.get(aDownload);
if (!item) {
Cu.reportError("No DownloadItem found for download");
return;
}
if (item.stateChanged) {
this.insertOrMoveItem(item);
}
item.onDownloadChanged();
},
onDownloadRemoved(aDownload) {
let item = this.items.get(aDownload);
if (!item) {
Cu.reportError("No DownloadItem found for download");
return;
}
this.items.delete(aDownload);
this.listElement.removeChild(item.element);
},
onDownloadContextMenu(aEvent) {
let element = this.listElement.selectedItem;
if (!element) {
return;
}
this.updateCommands();
},
clearDownloads() {
Downloads.getList(Downloads.ALL)
.then(list => list.removeFinished())
.then(null, Cu.reportError);
},
supportsCommand(aCommand) {
if (!(this.commands.some(command => command == aCommand)) &&
!(DownloadItem.prototype.supportsCommand(aCommand))) {
return false;
}
return true;
},
isCommandEnabled(aCommand) {
if (aCommand == "msgDownloadsCmd_clearDownloads") {
return true;
}
let element = this.listElement.selectedItem;
if (element) {
return element.downloadItem.isCommandEnabled(aCommand);
}
return false;
},
doCommand(aCommand) {
if (aCommand == "msgDownloadsCmd_clearDownloads") {
this.clearDownloads();
return;
}
if (this.listElement.selectedItems.length == 0) {
return;
}
for (let [, element] in Iterator(this.listElement.selectedItems)) {
element.downloadItem.doCommand(aCommand);
}
},
onEvent() { },
updateCommands() {
this.commands.forEach(goUpdateCommand);
DownloadItem.prototype.commands.forEach(goUpdateCommand);
},
commands: [
"msgDownloadsCmd_clearDownloads",
]
};
function DownloadItem(aDownload) {
this._download = aDownload;
this._updateFromDownload();
if (aDownload._unknownProperties && aDownload._unknownProperties.sender) {
this._sender = aDownload._unknownProperties.sender;
} else {
this._sender = "";
}
this._fileName = this._htmlEscape(OS.Path.basename(aDownload.target.path));
this._iconUrl = "moz-icon://" + this._fileName + "?size=32";
this._startDate = this._htmlEscape(DownloadUtils.getReadableDates(aDownload.startTime)[0]);
this._filePath = aDownload.target.path;
}
const kDownloadStatePropertyNames = [
"stopped",
"succeeded",
"canceled",
"error",
"startTime"
];
DownloadItem.prototype = {
_htmlEscape(s) {
s = s.replace(/&/g, "&");
s = s.replace(/>/g, ">");
s = s.replace(/</g, "&lt;");
s = s.replace(/"/g, "&quot;");
s = s.replace(/'/g, "&apos;");
return s;
},
_updateFromDownload() {
this._state = {};
for (let name of kDownloadStatePropertyNames) {
this._state[name] = this._download[name];
}
},
get stateChanged() {
for (let name of kDownloadStatePropertyNames) {
if (this._state[name] != this._download[name]) {
return true;
}
}
return false;
},
get download() this._download,
get element() {
if (!this._element) {
this._element = this.createElement();
}
return this._element;
},
createElement() {
let element = document.createElement("richlistitem");
element.classList.add("download");
// launch the download if double clicked
element.addEventListener("dblclick", aEvent => this.launch());
// set download as an expando property for the context menu
element.download = this.download;
element.downloadItem = this;
this.updateElement(element);
return element;
},
updateElement(aElement) {
aElement.setAttribute("image", this.iconUrl);
aElement.setAttribute("size", this.size);
aElement.setAttribute("displayName", this.fileName);
aElement.setAttribute("sender", this.sender);
aElement.setAttribute("startDate", this.startDate);
},
launch() {
if (this.download.succeeded) {
this.download.launch().then(null, Cu.reportError);
}
},
remove() {
Downloads.getList(Downloads.ALL)
.then(list => list.remove(this.download))
.then(() => this.download.finalize(true))
.then(null, Cu.reportError);
},
show() {
if (this.download.succeeded) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(this._filePath);
file.reveal();
}
},
onDownloadChanged() {
this._updateFromDownload();
this.updateElement(this.element);
},
get fileName() this._fileName,
get iconUrl() this._iconUrl,
get sender() this._sender,
get size() {
let bytes;
if (this.download.succeeded || this.download.hasProgress) {
bytes = this.download.target.size;
} else {
bytes = this.download.currentBytes;
}
return DownloadUtils.convertByteUnits(bytes).join("");
},
get startDate() this._startDate,
supportsCommand(aCommand) {
return this.commands.some(command => command == aCommand);
},
isCommandEnabled(aCommand) {
switch (aCommand) {
case "msgDownloadsCmd_open":
case "msgDownloadsCmd_show":
return this.download.succeeded;
case "msgDownloadsCmd_remove":
return true;
}
return false;
},
doCommand(aCommand) {
switch (aCommand) {
case "msgDownloadsCmd_open":
this.launch();
break;
case "msgDownloadsCmd_show":
this.show();
break;
case "msgDownloadsCmd_remove":
this.remove();
break;
}
},
commands: [
"msgDownloadsCmd_remove",
"msgDownloadsCmd_open",
"msgDownloadsCmd_show",
]
};

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

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://messenger/content/downloads/aboutDownloads.css" type="text/css"?>
<!-- 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/. -->
<!DOCTYPE window [
<!ENTITY % aboutDownloadsDTD SYSTEM "chrome://messenger/locale/aboutDownloads.dtd">
%aboutDownloadsDTD;
]>
<window id="aboutDownloads"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&aboutDownloads.title;"
onload="DownloadsView.init();">
<script type="application/javascript"
src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript"
src="chrome://messenger/content/downloads/aboutDownloads.js"/>
<stack flex="1">
<richlistbox id="msgDownloadsRichListBox"
flex="1"
seltype="multiple"
context="msgDownloadsContextMenu"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"/>
<description id="msgDownloadsListEmptyDescription"
value="&aboutDownloads.empty;"
mousethrough="always"/>
</stack>
<commandset id="msgDownloadCommands"
commandupdater="true"
events="focus,select,contextmenu">
<command id="msgDownloadsCmd_open"
oncommand="goDoCommand('msgDownloadsCmd_open')"/>
<command id="msgDownloadsCmd_show"
oncommand="goDoCommand('msgDownloadsCmd_show')"/>
<command id="msgDownloadsCmd_remove"
oncommand="goDoCommand('msgDownloadsCmd_remove')"/>
<command id="msgDownloadsCmd_clearDownloads"
oncommand="goDoCommand('msgDownloadsCmd_clearDownloads')"/>
</commandset>
<menupopup id="msgDownloadsContextMenu">
<menuitem command="msgDownloadsCmd_remove"
class="msgDownloadRemoveFromHistoryMenuItem"
label="&cmd.removeFromHistory.label;"
accesskey="&cmd.removeFromHistory.accesskey;"/>
<menuitem command="msgDownloadsCmd_open"
label="&cmd.open.label;"
accesskey="&cmd.open.accesskey;"/>
<menuitem command="msgDownloadsCmd_show"
class="msgDownloadShowMenuItem"
#ifdef XP_MACOSX
label="&cmd.showMac.label;"
accesskey="&cmd.showMac.accesskey;"
#else
label="&cmd.show.label;"
accesskey="&cmd.show.accesskey;"
#endif
/>
<menuitem command="msgDownloadsCmd_clearDownloads"
label="&cmd.clearDownloads.label;"
accesskey="&cmd.clearDownloads.accesskey;"/>
</menupopup>
</window>

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

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
<!-- vim: set ts=2 et sw=2 tw=80: -->
<!-- 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/. -->
<bindings id="downloadBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="download"
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content orient="horizontal" align="center">
<xul:image class="fileTypeIcon"
validate="always"
xbl:inherits="src=image"/>
<xul:vbox pack="center" flex="1">
<xul:description class="fileName"
crop="center"
xbl:inherits="value=displayName,tooltiptext=displayName"/>
<xul:description class="size"
xbl:inherits="value=size,tooltiptext=size"/>
<xul:description class="startDate"
crop="end"
xbl:inherits="value=startDate,tooltiptext=startDate"/>
</xul:vbox>
<xul:description class="sender"
xbl:inherits="value=sender,tooltiptext=sender"/>
</content>
</binding>
</bindings>

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

@ -0,0 +1,9 @@
# 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/.
messenger.jar:
content/messenger/downloads/download.xml (content/download.xml)
content/messenger/downloads/aboutDownloads.js (content/aboutDownloads.js)
* content/messenger/downloads/aboutDownloads.xul (content/aboutDownloads.xul)
content/messenger/downloads/aboutDownloads.css (content/aboutDownloads.css)

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

@ -0,0 +1,5 @@
# vim: set filetype=python:
# 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/.
JAR_MANIFESTS += ['jar.mn']

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

@ -4,6 +4,7 @@ component {8cc51368-6aa0-43e8-b762-bde9b9fd828c} aboutRedirector.js
contract @mozilla.org/network/protocol/about;1?what=rights {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
contract @mozilla.org/network/protocol/about;1?what=support {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
contract @mozilla.org/network/protocol/about;1?what=preferences {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
contract @mozilla.org/network/protocol/about;1?what=downloads {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
component {44346520-c5d2-44e5-a1ec-034e04d7fac4} nsMailDefaultHandler.js
contract @mozilla.org/mail/clh;1 {44346520-c5d2-44e5-a1ec-034e04d7fac4}
@ -17,3 +18,7 @@ contract @mozilla.org/uriloader/content-handler;1?type=text/plain {1c73f03a-b817
component {eb239c82-fac9-431e-98d7-11cacd0f71b8} mailGlue.js
contract @mozilla.org/mail/mailglue;1 {eb239c82-fac9-431e-98d7-11cacd0f71b8}
category app-startup MailGlue service,@mozilla.org/mail/mailglue;1
component {a93f0d6f-02a3-4486-a662-8f49b8c1de48} DownloadsStartup.js
contract @mozilla.org/mail/downloadsstartup;1 {a93f0d6f-02a3-4486-a662-8f49b8c1de48}
category profile-after-change DownloadsStartup @mozilla.org/mail/downloadsstartup;1

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

@ -9,6 +9,7 @@ DIRS += [
'compose',
'cloudfile',
'devtools',
'downloads',
'preferences',
'addrbook',
'migration',
@ -35,6 +36,7 @@ XPIDL_MODULE = 'mailcompsbase'
EXTRA_COMPONENTS += [
'aboutRedirector.js',
'DownloadsStartup.js',
'mailComponents.manifest',
'mailContentHandler.js',
'mailGlue.js',

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

@ -43,3 +43,4 @@ MAR_CHANNEL_ID=thunderbird-comm-central
# Enable generational GC on desktop.
JSGC_GENERATIONAL=1
MOZ_PROFILE_MIGRATOR=1
MOZ_JSDOWNLOADS=1

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

@ -371,11 +371,12 @@
@RESPATH@/components/storage-json.js
@RESPATH@/components/crypto-SDR.js
; download progress for jsdownloads
@RESPATH@/components/DownloadsStartup.js
; download progress
@RESPATH@/components/nsHelperAppDlg.js
@RESPATH@/components/nsHelperAppDlg.manifest
@RESPATH@/components/nsDownloadManagerUI.js
@RESPATH@/components/nsDownloadManagerUI.manifest
@RESPATH@/components/downloads.xpt
; Protocol/Content handling

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

@ -0,0 +1,21 @@
<!-- 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/. -->
<!ENTITY aboutDownloads.title "Saved Files">
<!ENTITY aboutDownloads.empty "No Saved Files">
<!-- LOCALIZATION NOTE (cmd.show.label, cmd.show.accesskey, cmd.showMac.label,
cmd.showMac.accesskey):
The show and showMac commands are never shown together, thus they can share
the same access key (though the two access keys can also be different).
-->
<!ENTITY cmd.show.label "Open Containing Folder">
<!ENTITY cmd.show.accesskey "F">
<!ENTITY cmd.showMac.label "Show In Finder">
<!ENTITY cmd.showMac.accesskey "F">
<!ENTITY cmd.open.label "Open">
<!ENTITY cmd.open.accesskey "O">
<!ENTITY cmd.removeFromHistory.label "Remove From History">
<!ENTITY cmd.removeFromHistory.accesskey "e">
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
<!ENTITY cmd.clearDownloads.accesskey "D">

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

@ -10,6 +10,7 @@
% override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://messenger/locale/downloads/settingsChange.dtd
% override chrome://global/locale/netError.dtd chrome://messenger/locale/netError.dtd
locale/@AB_CD@/messenger/aboutDialog.dtd (%chrome/messenger/aboutDialog.dtd)
locale/@AB_CD@/messenger/aboutDownloads.dtd (%chrome/messenger/aboutDownloads.dtd)
locale/@AB_CD@/messenger/aboutRights.dtd (%chrome/messenger/aboutRights.dtd)
locale/@AB_CD@/messenger/aboutRights.properties (%chrome/messenger/aboutRights.properties)
locale/@AB_CD@/messenger/aboutSupportMail.dtd (%chrome/messenger/aboutSupportMail.dtd)

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

@ -0,0 +1,296 @@
/* 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/. */
/**
* Test about:downloads.
*/
var MODULE_NAME = 'test-about-download';
var RELATIVE_ROOT = "../shared-modules";
var MODULE_REQUIRES = [ 'attachment-helpers',
'content-tab-helpers',
'dom-helpers',
'folder-display-helpers',
'prompt-helpers',
'window-helpers' ];
var mozmill = {}; Components.utils.import('resource://mozmill/modules/mozmill.js', mozmill);
var elementslib = {}; Components.utils.import('resource://mozmill/modules/elementslib.js', elementslib);
var downloads = {}; Components.utils.import("resource://gre/modules/Downloads.jsm", downloads);
var ah;
var downloadsTab;
var attachmentFileNames = [
"Attachment#1.txt",
"Attachment#2.txt",
"Attachment#3.txt",
];
const downloadsView = {
init() {
this.items = new Map();
this.removedItems = [];
},
get count() {
return this.items.size;
},
onDownloadAdded(aDownload) {
this.items.set(aDownload, aDownload.target.path);
},
onDownloadChanged(aDownload) {
},
onDownloadRemoved(aDownload) {
this.removedItems.push(aDownload.target.path);
this.items.delete(aDownload);
},
waitForFinish() {
for (let download of this.items.keys()) {
let succeededPromise = download.whenSucceeded();
yield succeededPromise;
}
}
};
function prepare_messages() {
let folder = create_folder("about:downloads");
let msgSet = make_new_sets_in_folder(folder, [
{
count: 1,
attachments: [{
filename: attachmentFileNames[0],
body: "Body"
}]
},
{
count: 1,
attachments: [{
filename: attachmentFileNames[1],
body: "Body"
}]
},
{
count: 1,
attachments: [{
filename: attachmentFileNames[2],
body: "Body"
}]
}
]);
be_in_folder(folder);
}
function prepare_downloads_view() {
let success = false;
downloads.Downloads.getList(downloads.Downloads.ALL)
.then(list => list.addView(downloadsView))
.then(() => success = true, Cu.reportError);
mc.waitFor(() => success, "Timeout waiting for attaching our download view.");
}
function setupModule(module) {
let fdh = collector.getModule("folder-display-helpers");
fdh.installInto(module);
let wh = collector.getModule('window-helpers');
wh.installInto(module);
let cth = collector.getModule("content-tab-helpers");
cth.installInto(module);
let dh = collector.getModule('dom-helpers');
dh.installInto(module);
ah = collector.getModule('attachment-helpers');
ah.installInto(module);
ah.gMockFilePickReg.register();
prepare_messages();
prepare_downloads_view();
}
function setupTest(test) {
downloadsView.init();
}
function open_about_downloads() {
let preCount = mc.tabmail.tabContainer.childNodes.length;
let newTab = mc.tabmail.openTab("chromeTab", { chromePage: "about:downloads",
clickHandler: "specialTabs.aboutClickHandler(event);" });
mc.waitFor(() => mc.tabmail.tabContainer.childNodes.length == preCount + 1,
"Timeout waiting for about:downloads tab");
wait_for_browser_load(newTab.browser, "about:downloads");
// We append new tabs at the end, so check the last one.
let expectedNewTab = mc.tabmail.tabInfo[preCount];
return expectedNewTab;
}
/**
* Test that there is no file in the list at first.
*/
function test_empty_list() {
downloadsTab = open_about_downloads();
switch_tab(downloadsTab);
let empty = content_tab_e(downloadsTab, "msgDownloadsListEmptyDescription");
assert_false(empty.hidden, "msgDownloadsListEmptyDescription is not visible");
}
function save_attachment_files() {
switch_tab(0);
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
let length = attachmentFileNames.length;
for (let i = 0; i < length; i++) {
let file = profileDir.clone();
file.append(attachmentFileNames[i]);
select_click_row(i);
gMockFilePicker.returnFiles = [ file ];
mc.click(mc.eid("attachmentSaveAllSingle",
{"class": "toolbarbutton-menubutton-button"}));
}
}
/**
* Test that all downloaded files are showed up in the list.
*/
function test_save_attachment_files_in_list() {
save_attachment_files();
mc.tabmail.switchToTab(downloadsTab);
let list = content_tab_e(downloadsTab, "msgDownloadsRichListBox");
let length = attachmentFileNames.length;
mc.waitFor(() => downloadsView.count == length,
"Timeout waiting for saving three attachment files.");
assert_equals(length, list.childNodes.length);
assert_equals(downloadsView.count, list.childNodes.length);
let actualNames = [];
let child = list.firstChild;
while (child) {
actualNames.push(child.getAttribute("displayName"));
child = child.nextSibling;
}
actualNames.sort();
for (let i = 0; i < length; i++) {
assert_equals(attachmentFileNames[i], actualNames[i]);
}
}
/**
* Test that 'remove' in context menu removes surely the target file from
* the list.
*/
function test_remove_file() {
test_save_attachment_files_in_list();
let list = content_tab_e(downloadsTab, "msgDownloadsRichListBox");
let firstElement = list.firstChild;
let removingFileName = firstElement.getAttribute("displayName");
// select first element
mc.click(new elementslib.Elem(firstElement));
mc.rightClick(new elementslib.Elem(firstElement));
let contextMenu = content_tab_e(downloadsTab, "msgDownloadsContextMenu");
wait_for_popup_to_open(contextMenu);
mc.click_menus_in_sequence(contextMenu, [
{ command: "msgDownloadsCmd_remove" }
]);
mc.waitFor(() => downloadsView.count == 2,
"Timeout waiting for removing a saved attachment file.");
child = list.firstChild;
while (child) {
assert_not_equals(removingFileName, child.getAttribute("displayName"));
child = child.nextSibling;
}
}
/**
* Test that removing multiple files surely removes the files.
*/
function test_remove_multiple_files() {
test_save_attachment_files_in_list();
let list = content_tab_e(downloadsTab, "msgDownloadsRichListBox");
let firstElement = list.firstChild;
let secondElement = firstElement.nextSibling;
let removingFileNames = [];
removingFileNames.push(firstElement.getAttribute("displayName"));
removingFileNames.push(secondElement.getAttribute("displayName"));
// select two elements
mc.click(new elementslib.Elem(firstElement));
list.selectItemRange(firstElement, secondElement);
mc.rightClick(new elementslib.Elem(firstElement));
let contextMenu = content_tab_e(downloadsTab, "msgDownloadsContextMenu");
wait_for_popup_to_open(contextMenu);
mc.click_menus_in_sequence(contextMenu, [
{ command: "msgDownloadsCmd_remove" }
]);
mc.waitFor(() => downloadsView.count == 1,
"Timeout waiting for removing two saved attachment files.");
child = list.firstChild;
while (child) {
for (let [, name] in Iterator(removingFileNames)) {
assert_not_equals(name, child.getAttribute("displayName"));
}
child = child.nextSibling;
}
}
/**
* Test that 'clearDownloads" in context menu purges all files in the list.
*/
function test_clear_all_files() {
test_save_attachment_files_in_list();
downloadsView.waitForFinish();
mc.click(content_tab_eid(downloadsTab, "msgDownloadsRichListBox"));
mc.rightClick(content_tab_eid(downloadsTab, "msgDownloadsRichListBox"));
let contextMenu = content_tab_e(downloadsTab, "msgDownloadsContextMenu");
wait_for_popup_to_open(contextMenu);
mc.click_menus_in_sequence(contextMenu, [
{ command: "msgDownloadsCmd_clearDownloads" }
]);
mc.waitFor(() => downloadsView.count == 0,
"Timeout waiting for clearing all saved attachment files.");
let empty = content_tab_e(downloadsTab, "msgDownloadsListEmptyDescription");
assert_false(empty.hidden, "msgDownloadsListEmptyDescription is not visible");
}
function teardownTest() {
downloads.Downloads.getList(downloads.Downloads.ALL)
.then(function(list) {
for (let download of downloadsView.items.keys()) {
list.remove(download);
}
})
.then(null, Cu.reportError);
mc.waitFor(() => downloadsView.count == 0,
"Timeout waiting for clearing all saved attachment files.");
let empty = content_tab_e(downloadsTab, "msgDownloadsListEmptyDescription");
mc.waitFor(() => empty.hidden == false,
"Timeout waiting for msgDownloadsListEmptyDescription is visible.");
}
function teardownModule(module) {
close_tab(downloadsTab);
ah.gMockFilePickReg.unregister();
}

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

@ -7,6 +7,7 @@ content-policy
content-tabs
cookies
crypto
downloads
folder-display
folder-pane
folder-tree-modes

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

@ -1777,6 +1777,10 @@ nsresult nsSaveMsgListener::InitializeDownload(nsIRequest * aRequest, uint32_t a
// so make an arbitrary decision based on the content length of the
// attachment -- show it if less than half of the download has completed
// Set saveToDisk explicitly to avoid launching the saved file.
// See http://hg.mozilla.org/mozilla-central/file/814a6f071472/toolkit/components/jsdownloads/src/DownloadLegacy.js#l164
mimeinfo->SetPreferredAction(nsIHandlerInfo::saveToDisk);
// When we don't allow warnings, also don't show progress, as this
// is an environment (typically filters) where we don't want
// interruption.