Bug 695178 - Download Manager. r=mfinkle,wesj

This commit is contained in:
Brian Nicholson 2012-04-04 10:12:44 -07:00
Родитель 91df92bc66
Коммит 17b4366fa5
14 изменённых файлов: 392 добавлений и 53 удалений

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

@ -517,8 +517,7 @@ abstract public class GeckoApp
loadUrlInTab("about:addons");
return true;
case R.id.downloads:
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
startActivity(intent);
loadUrlInTab("about:downloads");
return true;
case R.id.char_encoding:
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("CharEncoding:Get", null));

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

@ -178,7 +178,6 @@ FENNEC_PP_JAVA_FILES = \
FENNEC_PP_XML_FILES = \
res/layout/abouthome_content.xml \
res/menu/gecko_menu.xml \
res/menu-v11/gecko_menu.xml \
$(NULL)

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

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/reload"
android:icon="@drawable/ic_menu_reload"
android:title="@string/reload"/>
<item android:id="@+id/forward"
android:icon="@drawable/ic_menu_forward"
android:title="@string/forward"/>
<item android:id="@+id/bookmark"
android:icon="@drawable/ic_menu_bookmark_add"
android:title="@string/bookmark"/>
<item android:id="@+id/share"
android:icon="@drawable/ic_menu_share"
android:title="@string/share" />
<item android:id="@+id/save_as_pdf"
android:icon="@drawable/ic_menu_save_as_pdf"
android:title="@string/save_as_pdf" />
<item android:id="@+id/site_settings"
android:title="@string/site_settings_title" />
<item android:id="@+id/addons"
android:title="@string/addons"/>
<item android:id="@+id/downloads"
android:title="@string/downloads"/>
<item android:id="@+id/char_encoding"
android:visible="false"
android:title="@string/char_encoding"/>
<item android:id="@+id/settings"
android:title="@string/settings" />
#ifdef MOZ_PROFILING
<item android:id="@+id/toggle_profiling"
android:title="@string/toggle_profiling" />
#endif
<item android:id="@+id/quit"
android:title="@string/quit"
android:orderInCategory="10" />
</menu>

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

@ -28,8 +28,7 @@
android:title="@string/addons"/>
<item android:id="@+id/downloads"
android:title="@string/downloads"
android:visible="false"/>
android:title="@string/downloads"/>
<item android:id="@+id/char_encoding"
android:visible="false"

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

@ -0,0 +1,221 @@
/* 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/. */
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DownloadUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let gStrings = Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties");
let downloadTemplate =
"<li downloadID='{id}' role='button' mozDownload=''>" +
"<img class='icon' src='{icon}'/>" +
"<div class='details'>" +
"<div class='row'>" +
// This is a hack so that we can crop this label in its center
"<xul:label class='title' crop='center' value='{target}'/>" +
"<div class='date'>{date}</div>" +
"</div>" +
"<div class='size'>{size}</div>" +
"<div class='domain'>{domain}</div>" +
"</div>" +
"</li>";
XPCOMUtils.defineLazyGetter(window, "gChromeWin", function ()
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow));
let Downloads = {
init: function () {
this._list = document.getElementById("downloads-list");
this._list.addEventListener("click", function (event) {
let target = event.target;
while (target && target.nodeName != "li") {
target = target.parentNode;
}
Downloads.openDownload(target);
}, false);
let contextmenus = gChromeWin.NativeWindow.contextmenus;
Downloads.openMenuItem = contextmenus.add(gStrings.GetStringFromName("downloadAction.open"),
contextmenus.SelectorContext("li[mozDownload]"),
function (aTarget) {
Downloads.openDownload(aTarget);
}
);
Downloads.removeMenuItem = contextmenus.add(gStrings.GetStringFromName("downloadAction.remove"),
contextmenus.SelectorContext("li[mozDownload]"),
function (aTarget) {
Downloads.removeDownload(aTarget);
}
);
Services.obs.addObserver(this, "dl-failed", false);
Services.obs.addObserver(this, "dl-done", false);
Services.obs.addObserver(this, "dl-cancel", false);
this.getDownloads();
},
uninit: function () {
let contextmenus = gChromeWin.NativeWindow.contextmenus;
contextmenus.remove(this.openMenuItem);
contextmenus.remove(this.removeMenuItem);
Services.obs.removeObserver(this, "dl-failed");
Services.obs.removeObserver(this, "dl-done");
Services.obs.removeObserver(this, "dl-cancel");
},
observe: function (aSubject, aTopic, aData) {
let download = aSubject.QueryInterface(Ci.nsIDownload);
switch (aTopic) {
case "dl-failed":
case "dl-cancel":
case "dl-done":
if (!this._getElementForDownload(download.id)) {
let item = this._createItem(downloadTemplate, {
id: download.id,
target: download.displayName,
icon: "moz-icon://" + download.displayName + "?size=64",
date: DownloadUtils.getReadableDates(new Date())[0],
domain: DownloadUtils.getURIHost(download.source.spec)[0],
size: DownloadUtils.convertByteUnits(download.size).join(""),
});
this._list.insertAdjacentHTML("afterbegin", item);
break;
}
}
},
_initStatement: function dv__initStatement() {
if (this._stmt)
this._stmt.finalize();
this._stmt = this._dlmgr.DBConnection.createStatement(
"SELECT id, name, source, state, startTime, endTime, referrer, " +
"currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " +
"FROM moz_downloads " +
"WHERE NOT isActive " +
"ORDER BY endTime DESC");
},
_createItem: function (aTemplate, aValues) {
let t = aTemplate;
for (let i in aValues) {
let regEx = new RegExp("{" + i + "}", "g");
t = t.replace(regEx, aValues[i]);
}
return t;
},
_stepDownloads: function dv__stepDownloads(aNumItems) {
try {
if (!this._stmt.executeStep()) {
this._stmt.finalize();
this._stmt = null;
return;
}
// Try to get the attribute values from the statement
let attrs = {
id: this._stmt.row.id,
target: this._stmt.row.name,
icon: "moz-icon://" + this._stmt.row.name + "?size=64",
date: DownloadUtils.getReadableDates(new Date(this._stmt.row.endTime/1000))[0],
domain: DownloadUtils.getURIHost(this._stmt.row.source)[0],
size: DownloadUtils.convertByteUnits(this._stmt.row.maxBytes).join(""),
};
let item = this._createItem(downloadTemplate, attrs);
this._list.insertAdjacentHTML("beforeend", item);
} catch (e) {
// Something went wrong when stepping or getting values, so clear and quit
console.log("Error: " + e);
this._stmt.reset();
return;
}
// Add another item to the list if we should; otherwise, let the UI update
// and continue later
if (aNumItems > 1) {
this._stepDownloads(aNumItems - 1);
} else {
// Use a shorter delay for earlier downloads to display them faster
let delay = Math.min(this._list.itemCount * 10, 300);
let self = this;
this._timeoutID = setTimeout(function () { self._stepDownloads(5); }, delay);
}
},
getDownloads: function () {
this._dlmgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
this._initStatement();
clearTimeout(this._timeoutID);
this._stmt.reset();
this._stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED);
this._stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
this._stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
this._stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
this._stmt.bindInt32Parameter(4, Ci.nsIDownloadManager.DOWNLOAD_SCANNING);
// Take a quick break before we actually start building the list
let self = this;
this._timeoutID = setTimeout(function () {
self._stepDownloads(1);
}, 0);
},
_getElementForDownload: function (aKey) {
return this._list.querySelector("li[downloadID='" + aKey + "']");
},
_getDownloadForElement: function (aElement) {
let id = parseInt(aElement.getAttribute("downloadID"));
return this._dlmgr.getDownload(id);
},
openDownload: function (aItem) {
let f = null;
try {
let download = this._getDownloadForElement(aItem);
f = download.targetFile;
} catch(ex) { }
try {
if (f) f.launch();
} catch (ex) { }
},
removeDownload: function (aItem) {
let f = null;
try {
let download = this._getDownloadForElement(aItem);
f = download.targetFile;
} catch(ex) {
// even if there is no file, pretend that there is so that we can remove
// it from the list
f = { leafName: "" };
}
this._dlmgr.removeDownload(aItem.getAttribute("downloadID"));
this._list.removeChild(aItem);
try {
if (f) f.remove(false);
} catch(ex) { }
},
}

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

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/aboutDownloads.dtd" >
%downloadsDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&aboutDownloads.title;</title>
<meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=0" />
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutDownloads.css" type="text/css"/>
</head>
<body dir="&locale.dir;" onload="Downloads.init();" onunload="Downloads.uninit();">
<div class="header">
<div>&aboutDownloads.header;</div>
</div>
<ul id="downloads-list"></ul>
<span id="no-downloads-indicator">&aboutDownloads.empty;</span>
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutDownloads.js"/>
</body>
</html>

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

@ -8,6 +8,8 @@ chrome.jar:
content/aboutAddons.xhtml (content/aboutAddons.xhtml)
content/aboutAddons.js (content/aboutAddons.js)
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/aboutDownloads.xhtml (content/aboutDownloads.xhtml)
content/aboutDownloads.js (content/aboutDownloads.js)
content/aboutHome.xhtml (content/aboutHome.xhtml)
* content/aboutRights.xhtml (content/aboutRights.xhtml)
* content/aboutApps.xhtml (content/aboutApps.xhtml)

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

@ -89,6 +89,10 @@ let modules = {
apps: {
uri: "chrome://browser/content/aboutApps.xhtml",
privileged: true
},
downloads: {
uri: "chrome://browser/content/aboutDownloads.xhtml",
privileged: true
}
}

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

@ -8,6 +8,7 @@ contract @mozilla.org/network/protocol/about;1?what=rights {322ba47e-7047-4f71-a
contract @mozilla.org/network/protocol/about;1?what=certerror {322ba47e-7047-4f71-aebf-cb7d69325cd9}
contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9}
contract @mozilla.org/network/protocol/about;1?what=apps {322ba47e-7047-4f71-aebf-cb7d69325cd9}
contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9}
#ifdef MOZ_SAFE_BROWSING
contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
#endif

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

@ -0,0 +1,3 @@
<!ENTITY aboutDownloads.title "Downloads">
<!ENTITY aboutDownloads.header "Your Downloads">
<!ENTITY aboutDownloads.empty "No Downloads">

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

@ -0,0 +1,2 @@
downloadAction.open=Open
downloadAction.remove=Delete

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

@ -8,6 +8,8 @@
locale/@AB_CD@/browser/aboutApps.dtd (%chrome/aboutApps.dtd)
locale/@AB_CD@/browser/aboutApps.properties (%chrome/aboutApps.properties)
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/@AB_CD@/browser/aboutDownloads.dtd (%chrome/aboutDownloads.dtd)
locale/@AB_CD@/browser/aboutDownloads.properties (%chrome/aboutDownloads.properties)
locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties)
locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd)
locale/@AB_CD@/browser/config.properties (%chrome/config.properties)

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

@ -0,0 +1,120 @@
/* 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/. */
%filter substitution
%include defines.inc
html {
font-family: "Droid Sans",helvetica,arial,clean,sans-serif;
font-size: 18px;
background-image: url("chrome://browser/skin/images/about-bg-lightblue.png");
-moz-text-size-adjust: none;
}
body {
margin: 0;
}
li > a {
color: inherit;
text-decoration: none;
}
.header {
color: black;
padding: 10px;
-moz-padding-start: 25px;
font-size: 20px;
font-weight: bold;
border-bottom: 2px solid;
-moz-border-bottom-colors: #ff9100 #f27900;
}
.header > div {
width: 100%;
height: 45px;
display: -moz-box;
-moz-box-align: center;
}
ul {
padding: 0px;
margin: 0px;
width: 100%;
}
li {
color: black;
background-image: url("chrome://browser/skin/images/row-bg-normal.png");
border-bottom: 2px solid;
-moz-border-bottom-colors: #ffffff #bac2ac;
position: relative;
list-style-type: none;
list-style-image: none;
margin: 0px;
padding: 0px;
color: black;
height: 86px;
font-size: 18px !important;
}
li:active {
background-color: @color_background_highlight_overlay@;
background-image: none;
}
li:active .details {
background-image: none;
}
.icon {
width: 32px;
height: 32px;
position: absolute;
top: 27px;
left: 27px;
}
.details {
margin-left: 86px;
padding: 10px;
min-height: 66px;
background-image: url("chrome://browser/skin/images/row-bg-light.png");
}
.row {
display: -moz-box;
width: 100%;
}
.title {
font-size: 22px;
font-weight: bold;
overflow: hidden;
-moz-box-flex: 1;
}
.date {
color: gray;
}
.domain,
.size {
display: inline;
}
.size:after {
content: " - ";
white-space: pre;
}
#no-downloads-indicator {
display: none;
}
#downloads-list:empty + #no-downloads-indicator {
display: block;
text-align: center;
padding-top: 70px;
}

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

@ -6,6 +6,7 @@ chrome.jar:
skin/about.css (about.css)
skin/aboutAddons.css (aboutAddons.css)
skin/aboutApps.css (aboutApps.css)
* skin/aboutDownloads.css (aboutDownloads.css)
* skin/browser.css (browser.css)
* skin/content.css (content.css)
skin/config.css (config.css)