Bug 726444 - Implement the Downloads Panel. r=mak ui-r=limi
Includes: Bug 697679 - By Javi Rueda <leofigueres@yahoo.com>.
@ -349,6 +349,12 @@ pref("browser.download.manager.quitBehavior", 0);
|
||||
pref("browser.download.manager.scanWhenDone", true);
|
||||
pref("browser.download.manager.resumeOnWakeDelay", 10000);
|
||||
|
||||
// This allows disabling the Downloads Panel in favor of the old interface.
|
||||
pref("browser.download.useToolkitUI", false);
|
||||
|
||||
// This controls retention behavior in the Downloads Panel only.
|
||||
pref("browser.download.panel.removeFinishedDownloads", false);
|
||||
|
||||
// search engines URL
|
||||
pref("browser.search.searchEnginesURL", "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
|
||||
|
||||
|
@ -1391,6 +1391,8 @@ function BrowserStartup() {
|
||||
|
||||
gPrivateBrowsingUI.init();
|
||||
|
||||
DownloadsButton.initializePlaceholder();
|
||||
|
||||
retrieveToolbarIconsizesFromTheme();
|
||||
|
||||
gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
|
||||
@ -1640,6 +1642,11 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
||||
#endif
|
||||
}, 10000);
|
||||
|
||||
// The object handling the downloads indicator is also initialized here in the
|
||||
// delayed startup function, but the actual indicator element is not loaded
|
||||
// unless there are downloads to be displayed.
|
||||
DownloadsButton.initializeIndicator();
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
updateEditUIVisibility();
|
||||
let placesContext = document.getElementById("placesContext");
|
||||
@ -3722,6 +3729,7 @@ function BrowserCustomizeToolbar()
|
||||
|
||||
PlacesToolbarHelper.customizeStart();
|
||||
BookmarksMenuButton.customizeStart();
|
||||
DownloadsButton.customizeStart();
|
||||
|
||||
TabsInTitlebar.allowedBy("customizing-toolbars", false);
|
||||
|
||||
@ -3788,6 +3796,7 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) {
|
||||
|
||||
PlacesToolbarHelper.customizeDone();
|
||||
BookmarksMenuButton.customizeDone();
|
||||
DownloadsButton.customizeDone();
|
||||
|
||||
// The url bar splitter state is dependent on whether stop/reload
|
||||
// and the location bar are combined, so we need this ordering
|
||||
|
@ -855,6 +855,9 @@
|
||||
label="&printButton.label;" command="cmd_print"
|
||||
tooltiptext="&printButton.tooltip;"/>
|
||||
|
||||
<!-- This is a placeholder for the Downloads Indicator. It is visible
|
||||
only during the customization of the toolbar or in the palette, and
|
||||
is replaced when customization is done. -->
|
||||
<toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
observes="Tools:Downloads"
|
||||
ondrop="DownloadsButtonDNDObserver.onDrop(event)"
|
||||
|
@ -41,5 +41,7 @@
|
||||
<script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
|
||||
|
@ -62,6 +62,7 @@ PARALLEL_DIRS = \
|
||||
about \
|
||||
certerror \
|
||||
dirprovider \
|
||||
downloads \
|
||||
feeds \
|
||||
places \
|
||||
preferences \
|
||||
|
18
browser/components/downloads/Makefile.in
Normal file
@ -0,0 +1,18 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = src
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += test
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
60
browser/components/downloads/content/download.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?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/.
|
||||
|
||||
<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
|
||||
|
||||
<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">
|
||||
<resources>
|
||||
<stylesheet src="chrome://browser/skin/downloads/downloads.css"/>
|
||||
</resources>
|
||||
<content orient="horizontal">
|
||||
<xul:hbox class="downloadInfo"
|
||||
align="center"
|
||||
flex="1"
|
||||
onclick="DownloadsView.onDownloadClick(event);">
|
||||
<xul:vbox pack="center">
|
||||
<xul:image class="downloadTypeIcon"
|
||||
validate="always"
|
||||
xbl:inherits="src=image"/>
|
||||
<xul:image class="downloadTypeIcon blockedIcon"/>
|
||||
</xul:vbox>
|
||||
<xul:vbox pack="center"
|
||||
flex="1">
|
||||
<xul:description class="downloadTarget"
|
||||
crop="center"
|
||||
xbl:inherits="value=target,tooltiptext=target"/>
|
||||
<xul:progressmeter anonid="progressmeter"
|
||||
class="downloadProgress"
|
||||
min="0"
|
||||
max="100"
|
||||
xbl:inherits="mode=progressmode,value=progress"/>
|
||||
<xul:description class="downloadDetails"
|
||||
crop="end"
|
||||
xbl:inherits="value=status,tooltiptext=statusTip"/>
|
||||
</xul:vbox>
|
||||
</xul:hbox>
|
||||
<xul:hbox class="downloadButtonContainer"
|
||||
align="center">
|
||||
<xul:button class="downloadButton downloadCancel"
|
||||
tooltiptext="&cmd.cancel.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
|
||||
<xul:button class="downloadButton downloadRetry"
|
||||
tooltiptext="&cmd.retry.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
|
||||
<xul:button class="downloadButton downloadShow"
|
||||
tooltiptext="&cmd.show.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
</binding>
|
||||
</bindings>
|
90
browser/components/downloads/content/downloads.css
Normal file
@ -0,0 +1,90 @@
|
||||
/* 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/. */
|
||||
|
||||
/*** Download items ***/
|
||||
|
||||
richlistitem[type="download"] {
|
||||
-moz-binding: url('chrome://browser/content/downloads/download.xml#download');
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:not([selected]) button {
|
||||
/* Only focus buttons in the selected item. */
|
||||
-moz-user-focus: none;
|
||||
}
|
||||
|
||||
/*** Visibility of controls inside download items ***/
|
||||
|
||||
.download-state:-moz-any( [state="6"], /* Blocked (parental) */
|
||||
[state="8"], /* Blocked (dirty) */
|
||||
[state="9"]) /* Blocked (policy) */
|
||||
.downloadTypeIcon:not(.blockedIcon),
|
||||
|
||||
.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
|
||||
[state="8"], /* Blocked (dirty) */
|
||||
[state="9"]) /* Blocked (policy) */)
|
||||
.downloadTypeIcon.blockedIcon,
|
||||
|
||||
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
|
||||
[state="5"], /* Starting (queued) */
|
||||
[state="0"], /* Downloading */
|
||||
[state="4"], /* Paused */
|
||||
[state="7"]) /* Scanning */)
|
||||
.downloadProgress,
|
||||
|
||||
.download-state:not( [state="0"] /* Downloading */)
|
||||
.downloadPauseMenuItem,
|
||||
|
||||
.download-state:not( [state="4"] /* Paused */)
|
||||
.downloadResumeMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
|
||||
[state="5"], /* Starting (queued) */
|
||||
[state="0"], /* Downloading */
|
||||
[state="4"]) /* Paused */)
|
||||
.downloadCancel,
|
||||
|
||||
.download-state:not(:-moz-any([state="2"], /* Failed */
|
||||
[state="4"]) /* Paused */)
|
||||
.downloadCancelMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="1"], /* Finished */
|
||||
[state="3"], /* Canceled */
|
||||
[state="6"], /* Blocked (parental) */
|
||||
[state="8"], /* Blocked (dirty) */
|
||||
[state="9"]) /* Blocked (policy) */)
|
||||
.downloadRemoveFromListMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="2"], /* Failed */
|
||||
[state="3"]) /* Canceled */)
|
||||
.downloadRetry,
|
||||
|
||||
.download-state:not( [state="1"] /* Finished */)
|
||||
.downloadShow,
|
||||
|
||||
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
|
||||
[state="5"], /* Starting (queued) */
|
||||
[state="0"], /* Downloading */
|
||||
[state="4"]) /* Paused */)
|
||||
.downloadShowMenuItem,
|
||||
|
||||
.download-state[state="7"] .downloadCommandsSeparator
|
||||
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*** Visibility of controls inside the downloads indicator ***/
|
||||
|
||||
#downloads-indicator:-moz-any([progress],
|
||||
[counter],
|
||||
[paused]) #downloads-indicator-icon,
|
||||
|
||||
#downloads-indicator:not(:-moz-any([progress],
|
||||
[counter],
|
||||
[paused]))
|
||||
#downloads-indicator-progress-area
|
||||
|
||||
{
|
||||
visibility: hidden;
|
||||
}
|
1201
browser/components/downloads/content/downloads.js
Normal file
114
browser/components/downloads/content/downloadsOverlay.xul
Normal file
@ -0,0 +1,114 @@
|
||||
<?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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
|
||||
|
||||
<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="downloadsOverlay">
|
||||
|
||||
<commandset>
|
||||
<command id="downloadsCmd_doDefault"
|
||||
oncommand="goDoCommand('downloadsCmd_doDefault')"/>
|
||||
<command id="downloadsCmd_pauseResume"
|
||||
oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
|
||||
<command id="downloadsCmd_cancel"
|
||||
oncommand="goDoCommand('downloadsCmd_cancel')"/>
|
||||
<command id="downloadsCmd_open"
|
||||
oncommand="goDoCommand('downloadsCmd_open')"/>
|
||||
<command id="downloadsCmd_show"
|
||||
oncommand="goDoCommand('downloadsCmd_show')"/>
|
||||
<command id="downloadsCmd_retry"
|
||||
oncommand="goDoCommand('downloadsCmd_retry')"/>
|
||||
<command id="downloadsCmd_openReferrer"
|
||||
oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
|
||||
<command id="downloadsCmd_copyLocation"
|
||||
oncommand="goDoCommand('downloadsCmd_copyLocation')"/>
|
||||
<command id="downloadsCmd_clearList"
|
||||
oncommand="goDoCommand('downloadsCmd_clearList')"/>
|
||||
</commandset>
|
||||
|
||||
<popupset>
|
||||
<!-- The panel has level="top" to ensure that it is never hidden by the
|
||||
taskbar on Windows. See bug 672365. For accessibility to screen
|
||||
readers, we use a label on the panel instead of the anchor because the
|
||||
panel can also be displayed without an anchor. -->
|
||||
<panel id="downloadsPanel"
|
||||
aria-label="&downloads.title;"
|
||||
role="group"
|
||||
type="arrow"
|
||||
orient="vertical"
|
||||
level="top"
|
||||
onpopupshown="DownloadsPanel.onPopupShown(event);"
|
||||
onpopuphidden="DownloadsPanel.onPopupHidden(event);">
|
||||
<!-- The following popup menu should be a child of the panel element,
|
||||
otherwise flickering may occur when the cursor is moved over the area
|
||||
of a disabled menu item that overlaps the panel. See bug 492960. -->
|
||||
<menupopup id="downloadsContextMenu"
|
||||
class="download-state">
|
||||
<menuitem command="downloadsCmd_pauseResume"
|
||||
class="downloadPauseMenuItem"
|
||||
label="&cmd.pause.label;"
|
||||
accesskey="&cmd.pause.accesskey;"/>
|
||||
<menuitem command="downloadsCmd_pauseResume"
|
||||
class="downloadResumeMenuItem"
|
||||
label="&cmd.resume.label;"
|
||||
accesskey="&cmd.resume.accesskey;"/>
|
||||
<menuitem command="downloadsCmd_cancel"
|
||||
class="downloadCancelMenuItem"
|
||||
label="&cmd.cancel.label;"
|
||||
accesskey="&cmd.cancel.accesskey;"/>
|
||||
<menuitem command="cmd_delete"
|
||||
class="downloadRemoveFromListMenuItem"
|
||||
label="&cmd.removeFromList.label;"
|
||||
accesskey="&cmd.removeFromList.accesskey;"/>
|
||||
<menuitem command="downloadsCmd_show"
|
||||
class="downloadShowMenuItem"
|
||||
#ifdef XP_MACOSX
|
||||
label="&cmd.showMac.label;"
|
||||
accesskey="&cmd.showMac.accesskey;"
|
||||
#else
|
||||
label="&cmd.show.label;"
|
||||
accesskey="&cmd.show.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
|
||||
<menuseparator class="downloadCommandsSeparator"/>
|
||||
|
||||
<menuitem command="downloadsCmd_openReferrer"
|
||||
label="&cmd.goToDownloadPage.label;"
|
||||
accesskey="&cmd.goToDownloadPage.accesskey;"/>
|
||||
<menuitem command="downloadsCmd_copyLocation"
|
||||
label="&cmd.copyDownloadLink.label;"
|
||||
accesskey="&cmd.copyDownloadLink.accesskey;"/>
|
||||
|
||||
<menuseparator/>
|
||||
|
||||
<menuitem command="downloadsCmd_clearList"
|
||||
label="&cmd.clearList.label;"
|
||||
accesskey="&cmd.clearList.accesskey;"/>
|
||||
</menupopup>
|
||||
|
||||
<richlistbox id="downloadsListBox"
|
||||
class="plain"
|
||||
flex="1"
|
||||
context="downloadsContextMenu"
|
||||
onkeypress="DownloadsView.onDownloadKeyPress(event);"
|
||||
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
|
||||
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
|
||||
|
||||
<button id="downloadsHistory"
|
||||
class="plain"
|
||||
label="&downloadshistory.label;"
|
||||
accesskey="&downloadshistory.accesskey;"
|
||||
oncommand="DownloadsPanel.showDownloadsHistory();"/>
|
||||
</panel>
|
||||
</popupset>
|
||||
</overlay>
|
591
browser/components/downloads/content/indicator.js
Normal file
@ -0,0 +1,591 @@
|
||||
/* -*- Mode: C++; 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/. */
|
||||
|
||||
/**
|
||||
* Handles the indicator that displays the progress of ongoing downloads, which
|
||||
* is also used as the anchor for the downloads panel.
|
||||
*
|
||||
* This module includes the following constructors and global objects:
|
||||
*
|
||||
* DownloadsButton
|
||||
* Main entry point for the downloads indicator. Depending on how the toolbars
|
||||
* have been customized, this object determines if we should show a fully
|
||||
* functional indicator, a placeholder used during customization and in the
|
||||
* customization palette, or a neutral view as a temporary anchor for the
|
||||
* downloads panel.
|
||||
*
|
||||
* DownloadsIndicatorView
|
||||
* Builds and updates the actual downloads status widget, responding to changes
|
||||
* in the global status data, or provides a neutral view if the indicator is
|
||||
* removed from the toolbars and only used as a temporary anchor. In addition,
|
||||
* handles the user interaction events raised by the widget.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsButton
|
||||
|
||||
/**
|
||||
* Main entry point for the downloads indicator. Depending on how the toolbars
|
||||
* have been customized, this object determines if we should show a fully
|
||||
* functional indicator, a placeholder used during customization and in the
|
||||
* customization palette, or a neutral view as a temporary anchor for the
|
||||
* downloads panel.
|
||||
*/
|
||||
const DownloadsButton = {
|
||||
/**
|
||||
* Location of the indicator overlay.
|
||||
*/
|
||||
get kIndicatorOverlay()
|
||||
"chrome://browser/content/downloads/indicatorOverlay.xul",
|
||||
|
||||
/**
|
||||
* Returns a reference to the downloads button position placeholder, or null
|
||||
* if not available because it has been removed from the toolbars.
|
||||
*/
|
||||
get _placeholder()
|
||||
{
|
||||
return document.getElementById("downloads-button");
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called synchronously at window initialization. It only
|
||||
* sets the visibility of user interface elements to avoid flickering.
|
||||
*
|
||||
* NOTE: To keep startup time to a minimum, this function should not perform
|
||||
* any expensive operations or input/output, and should not cause the
|
||||
* Download Manager service to start.
|
||||
*/
|
||||
initializePlaceholder: function DB_initializePlaceholder()
|
||||
{
|
||||
// Exit now if the feature is disabled. To improve startup time, we don't
|
||||
// load the DownloadsCommon module yet, but check the preference directly.
|
||||
if (gPrefService.getBoolPref("browser.download.useToolkitUI")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must hide the placeholder used for toolbar customization, unless it
|
||||
// has been removed from the toolbars and is now located in the palette.
|
||||
let placeholder = this._placeholder;
|
||||
if (placeholder) {
|
||||
placeholder.collapsed = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called asynchronously just after window initialization.
|
||||
*
|
||||
* NOTE: This function should limit the input/output it performs to improve
|
||||
* startup time, and in particular should not cause the Download Manager
|
||||
* service to start.
|
||||
*/
|
||||
initializeIndicator: function DB_initializeIndicator()
|
||||
{
|
||||
this._update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates whether toolbar customization is in progress.
|
||||
*/
|
||||
_customizing: false,
|
||||
|
||||
/**
|
||||
* This function is called when toolbar customization starts.
|
||||
*
|
||||
* During customization, we never show the actual download progress indication
|
||||
* or the event notifications, but we show a neutral placeholder. The neutral
|
||||
* placeholder is an ordinary button defined in the browser window that can be
|
||||
* moved freely between the toolbars and the customization palette.
|
||||
*/
|
||||
customizeStart: function DB_customizeStart()
|
||||
{
|
||||
// Hide the indicator and prevent it to be displayed as a temporary anchor
|
||||
// during customization, even if requested using the getAnchor method.
|
||||
this._customizing = true;
|
||||
this._anchorRequested = false;
|
||||
|
||||
let indicator = DownloadsIndicatorView.indicator;
|
||||
if (indicator) {
|
||||
indicator.collapsed = true;
|
||||
}
|
||||
|
||||
let placeholder = this._placeholder;
|
||||
if (placeholder) {
|
||||
placeholder.collapsed = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called when toolbar customization ends.
|
||||
*/
|
||||
customizeDone: function DB_customizeDone()
|
||||
{
|
||||
this._customizing = false;
|
||||
this._update();
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called during initialization or when toolbar customization
|
||||
* ends. It determines if we should enable or disable the object that keeps
|
||||
* the indicator updated, and ensures that the placeholder is hidden unless it
|
||||
* has been moved to the customization palette.
|
||||
*
|
||||
* NOTE: This function is also called on startup, thus it should limit the
|
||||
* input/output it performs, and in particular should not cause the
|
||||
* Download Manager service to start.
|
||||
*/
|
||||
_update: function DB_update() {
|
||||
this._updatePositionInternal();
|
||||
|
||||
let placeholder = this._placeholder;
|
||||
if (!DownloadsCommon.useToolkitUI) {
|
||||
DownloadsIndicatorView.ensureInitialized();
|
||||
if (placeholder) {
|
||||
placeholder.collapsed = true;
|
||||
}
|
||||
} else {
|
||||
DownloadsIndicatorView.ensureTerminated();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the position where the indicator should appear, and moves its
|
||||
* associated element to the new position. This does not happen if the
|
||||
* indicator is currently being used as the anchor for the panel, to ensure
|
||||
* that the panel doesn't flicker because we move the DOM element to which
|
||||
* it's anchored.
|
||||
*/
|
||||
updatePosition: function DB_updatePosition()
|
||||
{
|
||||
if (!this._anchorRequested) {
|
||||
this._updatePositionInternal();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the position where the indicator should appear, and moves its
|
||||
* associated element to the new position.
|
||||
*
|
||||
* @return Anchor element, or null if the indicator is not visible.
|
||||
*/
|
||||
_updatePositionInternal: function DB_updatePositionInternal()
|
||||
{
|
||||
let indicator = DownloadsIndicatorView.indicator;
|
||||
if (!indicator) {
|
||||
// Exit now if the indicator overlay isn't loaded yet.
|
||||
return null;
|
||||
}
|
||||
|
||||
let placeholder = this._placeholder;
|
||||
|
||||
// Firstly, determine if we should always hide the indicator.
|
||||
if (!placeholder && !this._anchorRequested &&
|
||||
!DownloadsIndicatorView.hasDownloads) {
|
||||
indicator.collapsed = true;
|
||||
return null;
|
||||
}
|
||||
indicator.collapsed = false;
|
||||
|
||||
indicator.open = this._anchorRequested;
|
||||
|
||||
// Determine if we should display the indicator in a known position.
|
||||
if (placeholder) {
|
||||
placeholder.parentNode.insertBefore(indicator, placeholder);
|
||||
// Determine if the placeholder is located on a visible toolbar.
|
||||
if (isElementVisible(placeholder.parentNode)) {
|
||||
return DownloadsIndicatorView.indicatorAnchor;
|
||||
}
|
||||
}
|
||||
|
||||
// If not customized, the indicator is normally in the navigation bar.
|
||||
// Always place it in the default position, unless we need an anchor.
|
||||
if (!this._anchorRequested) {
|
||||
this._navBar.appendChild(indicator);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Show the indicator temporarily in the navigation bar, if visible.
|
||||
if (isElementVisible(this._navBar)) {
|
||||
this._navBar.appendChild(indicator);
|
||||
return DownloadsIndicatorView.indicatorAnchor;
|
||||
}
|
||||
|
||||
// Show the indicator temporarily in the tab bar, if visible.
|
||||
if (!this._tabsToolbar.collapsed) {
|
||||
this._tabsToolbar.appendChild(indicator);
|
||||
return DownloadsIndicatorView.indicatorAnchor;
|
||||
}
|
||||
|
||||
// The temporary anchor cannot be shown.
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates whether we should try and show the indicator temporarily as an
|
||||
* anchor for the panel, even if the indicator would be hidden by default.
|
||||
*/
|
||||
_anchorRequested: false,
|
||||
|
||||
/**
|
||||
* Ensures that there is an anchor available for the panel.
|
||||
*
|
||||
* @param aCallback
|
||||
* Called when the anchor is available, passing the element where the
|
||||
* panel should be anchored, or null if an anchor is not available (for
|
||||
* example because both the tab bar and the navigation bar are hidden).
|
||||
*/
|
||||
getAnchor: function DB_getAnchor(aCallback)
|
||||
{
|
||||
// Do not allow anchoring the panel to the element while customizing.
|
||||
if (this._customizing) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
function DB_GA_callback() {
|
||||
this._anchorRequested = true;
|
||||
aCallback(this._updatePositionInternal());
|
||||
}
|
||||
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay,
|
||||
DB_GA_callback.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows the temporary anchor to be hidden.
|
||||
*/
|
||||
releaseAnchor: function DB_releaseAnchor()
|
||||
{
|
||||
this._anchorRequested = false;
|
||||
this._updatePositionInternal();
|
||||
},
|
||||
|
||||
get _tabsToolbar()
|
||||
{
|
||||
delete this._tabsToolbar;
|
||||
return this._tabsToolbar = document.getElementById("TabsToolbar");
|
||||
},
|
||||
|
||||
get _navBar()
|
||||
{
|
||||
delete this._navBar;
|
||||
return this._navBar = document.getElementById("nav-bar");
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsIndicatorView
|
||||
|
||||
/**
|
||||
* Builds and updates the actual downloads status widget, responding to changes
|
||||
* in the global status data, or provides a neutral view if the indicator is
|
||||
* removed from the toolbars and only used as a temporary anchor. In addition,
|
||||
* handles the user interaction events raised by the widget.
|
||||
*/
|
||||
const DownloadsIndicatorView = {
|
||||
/**
|
||||
* True when the view is connected with the underlying downloads data.
|
||||
*/
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* True when the user interface elements required to display the indicator
|
||||
* have finished loading in the browser window, and can be referenced.
|
||||
*/
|
||||
_operational: false,
|
||||
|
||||
/**
|
||||
* Prepares the downloads indicator to be displayed.
|
||||
*/
|
||||
ensureInitialized: function DIV_ensureInitialized()
|
||||
{
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
window.addEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.indicatorData.addView(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Frees the internal resources related to the indicator.
|
||||
*/
|
||||
ensureTerminated: function DIV_ensureTerminated()
|
||||
{
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = false;
|
||||
|
||||
window.removeEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.indicatorData.removeView(this);
|
||||
|
||||
// Reset the view properties, so that a neutral indicator is displayed if we
|
||||
// are visible only temporarily as an anchor.
|
||||
this.counter = "";
|
||||
this.percentComplete = 0;
|
||||
this.paused = false;
|
||||
this.attention = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the user interface elements required to display the indicator
|
||||
* are loaded, then invokes the given callback.
|
||||
*/
|
||||
_ensureOperational: function DIV_ensureOperational(aCallback)
|
||||
{
|
||||
if (this._operational) {
|
||||
aCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
function DIV_EO_callback() {
|
||||
this._operational = true;
|
||||
|
||||
// If the view is initialized, we need to update the elements now that
|
||||
// they are finally available in the document.
|
||||
if (this._initialized) {
|
||||
DownloadsCommon.indicatorData.refreshView(this);
|
||||
}
|
||||
|
||||
aCallback();
|
||||
}
|
||||
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(
|
||||
DownloadsButton.kIndicatorOverlay,
|
||||
DIV_EO_callback.bind(this));
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Direct control functions
|
||||
|
||||
/**
|
||||
* Set while we are waiting for a notification to fade out.
|
||||
*/
|
||||
_notificationTimeout: null,
|
||||
|
||||
/**
|
||||
* If the status indicator is visible in its assigned position, shows for a
|
||||
* brief time a visual notification of a relevant event, like a new download.
|
||||
*/
|
||||
showEventNotification: function DIV_showEventNotification()
|
||||
{
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
function DIV_SEN_callback() {
|
||||
if (this._notificationTimeout) {
|
||||
clearTimeout(this._notificationTimeout);
|
||||
}
|
||||
|
||||
let indicator = this.indicator;
|
||||
indicator.setAttribute("notification", "true");
|
||||
this._notificationTimeout = setTimeout(
|
||||
function () indicator.removeAttribute("notification"), 1000);
|
||||
}
|
||||
|
||||
this._ensureOperational(DIV_SEN_callback.bind(this));
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Callback functions from DownloadsIndicatorData
|
||||
|
||||
/**
|
||||
* Indicates whether the indicator should be shown because there are some
|
||||
* downloads to be displayed.
|
||||
*/
|
||||
set hasDownloads(aValue)
|
||||
{
|
||||
if (this._hasDownloads != aValue) {
|
||||
this._hasDownloads = aValue;
|
||||
|
||||
// If there is at least one download, ensure that the view elements are
|
||||
// loaded before determining the position of the downloads button.
|
||||
if (aValue) {
|
||||
this._ensureOperational(function() DownloadsButton.updatePosition());
|
||||
} else {
|
||||
DownloadsButton.updatePosition();
|
||||
}
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
get hasDownloads()
|
||||
{
|
||||
return this._hasDownloads;
|
||||
},
|
||||
_hasDownloads: false,
|
||||
|
||||
/**
|
||||
* Status text displayed in the indicator. If this is set to an empty value,
|
||||
* then the small downloads icon is displayed instead of the text.
|
||||
*/
|
||||
set counter(aValue)
|
||||
{
|
||||
if (!this._operational) {
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
if (this._counter !== aValue) {
|
||||
this._counter = aValue;
|
||||
if (this._counter)
|
||||
this.indicator.setAttribute("counter", "true");
|
||||
else
|
||||
this.indicator.removeAttribute("counter");
|
||||
// We have to set the attribute instead of using the property because the
|
||||
// XBL binding isn't applied if the element is invisible for any reason.
|
||||
this._indicatorCounter.setAttribute("value", aValue);
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
_counter: null,
|
||||
|
||||
/**
|
||||
* Progress indication to display, from 0 to 100, or -1 if unknown. The
|
||||
* progress bar is hidden if the current progress is unknown and no status
|
||||
* text is set in the "counter" property.
|
||||
*/
|
||||
set percentComplete(aValue)
|
||||
{
|
||||
if (!this._operational) {
|
||||
return this._percentComplete;
|
||||
}
|
||||
|
||||
if (this._percentComplete !== aValue) {
|
||||
this._percentComplete = aValue;
|
||||
if (this._percentComplete >= 0)
|
||||
this.indicator.setAttribute("progress", "true");
|
||||
else
|
||||
this.indicator.removeAttribute("progress");
|
||||
// We have to set the attribute instead of using the property because the
|
||||
// XBL binding isn't applied if the element is invisible for any reason.
|
||||
this._indicatorProgress.setAttribute("value", Math.max(aValue, 0));
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
_percentComplete: null,
|
||||
|
||||
/**
|
||||
* Indicates whether the progress won't advance because of a paused state.
|
||||
* Setting this property forces a paused progress bar to be displayed, even if
|
||||
* the current progress information is unavailable.
|
||||
*/
|
||||
set paused(aValue)
|
||||
{
|
||||
if (!this._operational) {
|
||||
return this._paused;
|
||||
}
|
||||
|
||||
if (this._paused != aValue) {
|
||||
this._paused = aValue;
|
||||
if (this._paused) {
|
||||
this.indicator.setAttribute("paused", "true")
|
||||
} else {
|
||||
this.indicator.removeAttribute("paused");
|
||||
}
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
_paused: false,
|
||||
|
||||
/**
|
||||
* Set when the indicator should draw user attention to itself.
|
||||
*/
|
||||
set attention(aValue)
|
||||
{
|
||||
if (!this._operational) {
|
||||
return this._attention;
|
||||
}
|
||||
|
||||
if (this._attention != aValue) {
|
||||
this._attention = aValue;
|
||||
if (aValue) {
|
||||
this.indicator.setAttribute("attention", "true")
|
||||
} else {
|
||||
this.indicator.removeAttribute("attention");
|
||||
}
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
_attention: false,
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// User interface event functions
|
||||
|
||||
onWindowUnload: function DIV_onWindowUnload()
|
||||
{
|
||||
// This function is registered as an event listener, we can't use "this".
|
||||
DownloadsIndicatorView.ensureTerminated();
|
||||
},
|
||||
|
||||
onCommand: function DIV_onCommand(aEvent)
|
||||
{
|
||||
if (DownloadsCommon.useToolkitUI) {
|
||||
// The panel won't suppress attention for us, we need to clear now.
|
||||
DownloadsCommon.indicatorData.attention = false;
|
||||
}
|
||||
|
||||
BrowserDownloadsUI();
|
||||
|
||||
aEvent.stopPropagation();
|
||||
},
|
||||
|
||||
onDragOver: function DIV_onDragOver(aEvent)
|
||||
{
|
||||
browserDragAndDrop.dragOver(aEvent);
|
||||
},
|
||||
|
||||
onDragExit: function () { },
|
||||
|
||||
onDrop: function DIV_onDrop(aEvent)
|
||||
{
|
||||
let name = {};
|
||||
let url = browserDragAndDrop.drop(aEvent, name);
|
||||
if (url) {
|
||||
saveURL(url, name.value, null, true, true);
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a reference to the main indicator element, or null if the element
|
||||
* is not present in the browser window yet.
|
||||
*/
|
||||
get indicator()
|
||||
{
|
||||
let indicator = document.getElementById("downloads-indicator");
|
||||
if (!indicator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Once the element is loaded, it will never be unloaded.
|
||||
delete this.indicator;
|
||||
return this.indicator = indicator;
|
||||
},
|
||||
|
||||
get indicatorAnchor()
|
||||
{
|
||||
delete this.indicatorAnchor;
|
||||
return this.indicatorAnchor =
|
||||
document.getElementById("downloads-indicator-anchor");
|
||||
},
|
||||
|
||||
get _indicatorCounter()
|
||||
{
|
||||
delete this._indicatorCounter;
|
||||
return this._indicatorCounter =
|
||||
document.getElementById("downloads-indicator-counter");
|
||||
},
|
||||
|
||||
get _indicatorProgress()
|
||||
{
|
||||
delete this._indicatorProgress;
|
||||
return this._indicatorProgress =
|
||||
document.getElementById("downloads-indicator-progress");
|
||||
}
|
||||
};
|
51
browser/components/downloads/content/indicatorOverlay.xul
Normal file
@ -0,0 +1,51 @@
|
||||
<?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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
|
||||
|
||||
<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="indicatorOverlay">
|
||||
|
||||
<popupset>
|
||||
<!-- The downloads indicator is placed in its final toolbar location
|
||||
programmatically, and can be shown temporarily even when its
|
||||
placeholder is removed from the toolbars. Its initial location within
|
||||
the document must not be a toolbar or the toolbar palette, otherwise the
|
||||
toolbar handling code could remove it from the document. -->
|
||||
<toolbarbutton id="downloads-indicator"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
tooltiptext="&indicator.tooltiptext;"
|
||||
collapsed="true"
|
||||
oncommand="DownloadsIndicatorView.onCommand(event);"
|
||||
ondrop="DownloadsIndicatorView.onDrop(event);"
|
||||
ondragover="DownloadsIndicatorView.onDragOver(event);"
|
||||
ondragenter="DownloadsIndicatorView.onDragOver(event);"
|
||||
ondragleave="DownloadsIndicatorView.onDragLeave(event);">
|
||||
<!-- The panel's anchor area is smaller than the outer button, but must
|
||||
always be visible and must not move or resize when the indicator
|
||||
state changes, otherwise the panel could change its position or lose
|
||||
its arrow unexpectedly. -->
|
||||
<stack id="downloads-indicator-anchor"
|
||||
class="toolbarbutton-icon">
|
||||
<vbox id="downloads-indicator-progress-area"
|
||||
pack="center">
|
||||
<description id="downloads-indicator-counter"/>
|
||||
<progressmeter id="downloads-indicator-progress"
|
||||
class="plain"
|
||||
min="0"
|
||||
max="100"/>
|
||||
</vbox>
|
||||
<vbox id="downloads-indicator-icon"/>
|
||||
<vbox id="downloads-indicator-notification"/>
|
||||
</stack>
|
||||
</toolbarbutton>
|
||||
</popupset>
|
||||
</overlay>
|
7
browser/components/downloads/jar.mn
Normal file
@ -0,0 +1,7 @@
|
||||
browser.jar:
|
||||
* content/browser/downloads/download.xml (content/download.xml)
|
||||
content/browser/downloads/downloads.css (content/downloads.css)
|
||||
content/browser/downloads/downloads.js (content/downloads.js)
|
||||
* content/browser/downloads/downloadsOverlay.xul (content/downloadsOverlay.xul)
|
||||
content/browser/downloads/indicator.js (content/indicator.js)
|
||||
* content/browser/downloads/indicatorOverlay.xul (content/indicatorOverlay.xul)
|
@ -0,0 +1,4 @@
|
||||
component {49507fe5-2cee-4824-b6a3-e999150ce9b8} DownloadsStartup.js
|
||||
contract @mozilla.org/browser/downloadsstartup;1 {49507fe5-2cee-4824-b6a3-e999150ce9b8}
|
||||
category app-startup DownloadsStartup service,@mozilla.org/browser/downloadsstartup;1
|
||||
component {4d99321e-d156-455b-81f7-e7aa2308134f} DownloadsUI.js
|
1230
browser/components/downloads/src/DownloadsCommon.jsm
Normal file
242
browser/components/downloads/src/DownloadsStartup.js
Normal file
@ -0,0 +1,242 @@
|
||||
/* -*- Mode: C++; 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/. */
|
||||
|
||||
/**
|
||||
* This component listens to notifications for startup, shutdown and session
|
||||
* restore, controlling which downloads should be loaded from the database.
|
||||
*
|
||||
* To avoid affecting startup performance, this component monitors the current
|
||||
* session restore state, but defers the actual downloads data manipulation
|
||||
* until the Download Manager service is loaded.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
"@mozilla.org/browser/sessionstartup;1",
|
||||
"nsISessionStartup");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsingService",
|
||||
"@mozilla.org/privatebrowsing;1",
|
||||
"nsIPrivateBrowsingService");
|
||||
|
||||
const kObservedTopics = [
|
||||
"sessionstore-windows-restored",
|
||||
"sessionstore-browser-state-restored",
|
||||
"download-manager-initialized",
|
||||
"download-manager-change-retention",
|
||||
"private-browsing-transition-complete",
|
||||
"browser-lastwindow-close-granted",
|
||||
"quit-application",
|
||||
"profile-change-teardown",
|
||||
];
|
||||
|
||||
/**
|
||||
* CID of our implementation of nsIDownloadManagerUI.
|
||||
*/
|
||||
const kDownloadsUICid = Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}");
|
||||
|
||||
/**
|
||||
* Contract ID of the service implementing nsIDownloadManagerUI.
|
||||
*/
|
||||
const kDownloadsUIContractId = "@mozilla.org/download-manager-ui;1";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsStartup
|
||||
|
||||
function DownloadsStartup() { }
|
||||
|
||||
DownloadsStartup.prototype = {
|
||||
classID: Components.ID("{49507fe5-2cee-4824-b6a3-e999150ce9b8}"),
|
||||
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsStartup),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIObserver
|
||||
|
||||
observe: function DS_observe(aSubject, aTopic, aData)
|
||||
{
|
||||
switch (aTopic) {
|
||||
case "app-startup":
|
||||
kObservedTopics.forEach(
|
||||
function (topic) Services.obs.addObserver(this, topic, true),
|
||||
this);
|
||||
|
||||
// Override Toolkit's nsIDownloadManagerUI implementation with our own.
|
||||
// This must be done at application startup and not in the manifest to
|
||||
// ensure that our implementation overrides the original one.
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(kDownloadsUICid, "",
|
||||
kDownloadsUIContractId, null);
|
||||
break;
|
||||
|
||||
case "sessionstore-windows-restored":
|
||||
case "sessionstore-browser-state-restored":
|
||||
// Unless there is no saved session, there is a chance that we are
|
||||
// starting up after a restart or a crash. We should check the disk
|
||||
// database to see if there are completed downloads to recover and show
|
||||
// in the panel, in addition to in-progress downloads.
|
||||
if (gSessionStartup.sessionType != Ci.nsISessionStartup.NO_SESSION) {
|
||||
this._recoverAllDownloads = true;
|
||||
}
|
||||
this._ensureDataLoaded();
|
||||
break;
|
||||
|
||||
case "download-manager-initialized":
|
||||
// Don't initialize the JavaScript data and user interface layer if we
|
||||
// are initializing the Download Manager service during shutdown.
|
||||
if (this._shuttingDown) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Start receiving events for active and new downloads before we return
|
||||
// from this observer function. We can't defer the execution of this
|
||||
// step, to ensure that we don't lose events raised in the meantime.
|
||||
DownloadsCommon.data.initializeDataLink(
|
||||
aSubject.QueryInterface(Ci.nsIDownloadManager));
|
||||
|
||||
this._downloadsServiceInitialized = true;
|
||||
|
||||
// Since this notification is generated during the getService call and
|
||||
// we need to get the Download Manager service ourselves, we must post
|
||||
// the handler on the event queue to be executed later.
|
||||
Services.tm.mainThread.dispatch(this._ensureDataLoaded.bind(this),
|
||||
Ci.nsIThread.DISPATCH_NORMAL);
|
||||
break;
|
||||
|
||||
case "download-manager-change-retention":
|
||||
// When the panel interface is enabled, we use a different preference to
|
||||
// determine whether downloads should be removed from view as soon as
|
||||
// they are finished. We do this to allow proper migration to the new
|
||||
// feature when using the same profile on multiple versions of the
|
||||
// product (bug 697678).
|
||||
if (!DownloadsCommon.useToolkitUI) {
|
||||
let removeFinishedDownloads = Services.prefs.getBoolPref(
|
||||
"browser.download.panel.removeFinishedDownloads");
|
||||
aSubject.QueryInterface(Ci.nsISupportsPRInt32)
|
||||
.data = removeFinishedDownloads ? 0 : 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case "private-browsing-transition-complete":
|
||||
// Ensure that persistent data is reloaded only when the database
|
||||
// connection is available again.
|
||||
this._ensureDataLoaded();
|
||||
break;
|
||||
|
||||
case "browser-lastwindow-close-granted":
|
||||
// When using the panel interface, downloads that are already completed
|
||||
// should be removed when the last full browser window is closed. This
|
||||
// event is invoked only if the application is not shutting down yet.
|
||||
// If the Download Manager service is not initialized, we don't want to
|
||||
// initialize it just to clean up completed downloads, because they can
|
||||
// be present only in case there was a browser crash or restart.
|
||||
if (this._downloadsServiceInitialized &&
|
||||
!DownloadsCommon.useToolkitUI) {
|
||||
Services.downloads.cleanUp();
|
||||
}
|
||||
break;
|
||||
|
||||
case "quit-application":
|
||||
// When the application is shutting down, we must free all resources in
|
||||
// addition to cleaning up completed downloads. If the Download Manager
|
||||
// service is not initialized, we don't want to initialize it just to
|
||||
// clean up completed downloads, because they can be present only in
|
||||
// case there was a browser crash or restart.
|
||||
this._shuttingDown = true;
|
||||
if (!this._downloadsServiceInitialized) {
|
||||
break;
|
||||
}
|
||||
|
||||
DownloadsCommon.data.terminateDataLink();
|
||||
|
||||
// When using the panel interface, downloads that are already completed
|
||||
// should be removed when quitting the application.
|
||||
if (!DownloadsCommon.useToolkitUI && aData != "restart") {
|
||||
this._cleanupOnShutdown = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "profile-change-teardown":
|
||||
// If we need to clean up, we must do it synchronously after all the
|
||||
// "quit-application" listeners are invoked, so that the Download
|
||||
// Manager service has a chance to pause or cancel in-progress downloads
|
||||
// before we remove completed downloads from the list. Note that, since
|
||||
// "quit-application" was invoked, we've already exited Private Browsing
|
||||
// Mode, thus we are always working on the disk database.
|
||||
if (this._cleanupOnShutdown) {
|
||||
Services.downloads.cleanUp();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Private
|
||||
|
||||
/**
|
||||
* Indicates whether we should load all downloads from the previous session,
|
||||
* including completed items as well as active downloads.
|
||||
*/
|
||||
_recoverAllDownloads: false,
|
||||
|
||||
/**
|
||||
* Indicates whether the Download Manager service has been initialized. This
|
||||
* flag is required because we want to avoid accessing the service immediately
|
||||
* at browser startup. The service will start when the user first requests a
|
||||
* download, or some time after browser startup.
|
||||
*/
|
||||
_downloadsServiceInitialized: false,
|
||||
|
||||
/**
|
||||
* True while we are processing the "quit-application" event, and later.
|
||||
*/
|
||||
_shuttingDown: false,
|
||||
|
||||
/**
|
||||
* True during shutdown if we need to remove completed downloads.
|
||||
*/
|
||||
_cleanupOnShutdown: false,
|
||||
|
||||
/**
|
||||
* Ensures that persistent download data is reloaded at the appropriate time.
|
||||
*/
|
||||
_ensureDataLoaded: function DS_ensureDataLoaded()
|
||||
{
|
||||
if (!this._downloadsServiceInitialized ||
|
||||
gPrivateBrowsingService.privateBrowsingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the previous session has been already restored, then we ensure that
|
||||
// all the downloads are loaded. Otherwise, we only ensure that the active
|
||||
// downloads from the previous session are loaded.
|
||||
DownloadsCommon.data.ensurePersistentDataLoaded(!this._recoverAllDownloads);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Module
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]);
|
125
browser/components/downloads/src/DownloadsUI.js
Normal file
@ -0,0 +1,125 @@
|
||||
/* -*- Mode: C++; 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/. */
|
||||
|
||||
/**
|
||||
* This component implements the nsIDownloadManagerUI interface and opens the
|
||||
* downloads panel in the most recent browser window when requested.
|
||||
*
|
||||
* If a specific preference is set, this component transparently forwards all
|
||||
* calls to the original implementation in Toolkit, that shows the window UI.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue",
|
||||
"@mozilla.org/browser/browserglue;1",
|
||||
"nsIBrowserGlue");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsUI
|
||||
|
||||
function DownloadsUI()
|
||||
{
|
||||
XPCOMUtils.defineLazyGetter(this, "_toolkitUI", function () {
|
||||
// Create Toolkit's nsIDownloadManagerUI implementation.
|
||||
return Components.classesByID["{7dfdf0d1-aff6-4a34-bad1-d0fe74601642}"]
|
||||
.getService(Ci.nsIDownloadManagerUI);
|
||||
});
|
||||
}
|
||||
|
||||
DownloadsUI.prototype = {
|
||||
classID: Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}"),
|
||||
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsUI),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsISupports
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIDownloadManagerUI
|
||||
|
||||
show: function DUI_show(aWindowContext, aID, aReason)
|
||||
{
|
||||
if (DownloadsCommon.useToolkitUI) {
|
||||
this._toolkitUI.show(aWindowContext, aID, aReason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aReason) {
|
||||
aReason = Ci.nsIDownloadManagerUI.REASON_USER_INTERACTED;
|
||||
}
|
||||
|
||||
if (aReason == Ci.nsIDownloadManagerUI.REASON_NEW_DOWNLOAD) {
|
||||
// New download notifications are already handled by the panel service.
|
||||
// We don't handle them here because we don't want them to depend on the
|
||||
// "browser.download.manager.showWhenStarting" and
|
||||
// "browser.download.manager.focusWhenStarting" preferences.
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the panel in the most recent browser window, if present.
|
||||
let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
|
||||
if (browserWin) {
|
||||
browserWin.focus();
|
||||
browserWin.DownloadsPanel.showPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
// If no browser window is visible and the user requested to show the
|
||||
// current downloads, try and open a new window. We'll open the panel when
|
||||
// delayed loading is finished.
|
||||
Services.obs.addObserver(function DUIO_observe(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(DUIO_observe, aTopic);
|
||||
aSubject.DownloadsPanel.showPanel();
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
|
||||
// We must really build an empty arguments list for the new window.
|
||||
let windowFirstArg = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
let windowArgs = Cc["@mozilla.org/supports-array;1"]
|
||||
.createInstance(Ci.nsISupportsArray);
|
||||
windowArgs.AppendElement(windowFirstArg);
|
||||
Services.ww.openWindow(null, "chrome://browser/content/browser.xul",
|
||||
null, "chrome,dialog=no,all", windowArgs);
|
||||
},
|
||||
|
||||
get visible()
|
||||
{
|
||||
if (DownloadsCommon.useToolkitUI) {
|
||||
return this._toolkitUI.visible;
|
||||
}
|
||||
|
||||
let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
|
||||
return browserWin ? browserWin.DownloadsPanel.isPanelShowing : false;
|
||||
},
|
||||
|
||||
getAttention: function DUI_getAttention()
|
||||
{
|
||||
if (DownloadsCommon.useToolkitUI) {
|
||||
this._toolkitUI.getAttention();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Module
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsUI]);
|
22
browser/components/downloads/src/Makefile.in
Normal file
@ -0,0 +1,22 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
BrowserDownloads.manifest \
|
||||
DownloadsStartup.js \
|
||||
DownloadsUI.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_JS_MODULES = \
|
||||
DownloadsCommon.jsm \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
17
browser/components/downloads/test/Makefile.in
Normal file
@ -0,0 +1,17 @@
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
relativesrcdir = browser/components/downloads/test
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
DIRS = browser
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
20
browser/components/downloads/test/browser/Makefile.in
Normal file
@ -0,0 +1,20 @@
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
DEPTH = ../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
relativesrcdir = browser/components/downloads/test/browser
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_basic_functionality.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
@ -0,0 +1,53 @@
|
||||
/* -*- 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/ */
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel can display items in the right order and
|
||||
* contains the expected data.
|
||||
*/
|
||||
function gen_test()
|
||||
{
|
||||
// Display one of each download state.
|
||||
const DownloadData = [
|
||||
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
|
||||
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
|
||||
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
|
||||
{ endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
|
||||
{ endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
|
||||
{ endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
|
||||
{ endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
|
||||
{ endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
|
||||
{ endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
|
||||
{ endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
|
||||
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
|
||||
];
|
||||
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
|
||||
// Populate the downloads database with the data required by this test.
|
||||
for (let yy in gen_addDownloadRows(DownloadData)) yield;
|
||||
|
||||
// Open the user interface and wait for data to be fully loaded.
|
||||
for (let yy in gen_openPanel()) yield;
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let richlistbox = document.getElementById("downloadsListBox");
|
||||
is(richlistbox.children.length, DownloadData.length,
|
||||
"There is the correct number of richlistitems");
|
||||
for (let i = 0; i < richlistbox.children.length; i++) {
|
||||
let element = richlistbox.children[i];
|
||||
let dataItem = new DownloadsViewItemController(element).dataItem;
|
||||
is(dataItem.target, DownloadData[i].name, "Download names match up");
|
||||
is(dataItem.state, DownloadData[i].state, "Download states match up");
|
||||
is(dataItem.file, DownloadData[i].target, "Download targets match up");
|
||||
is(dataItem.uri, DownloadData[i].source, "Download sources match up");
|
||||
}
|
||||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
}
|
||||
}
|
229
browser/components/downloads/test/browser/head.js
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*- 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/ */
|
||||
|
||||
/**
|
||||
* Provides infrastructure for automated download components tests.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
const nsIDM = Ci.nsIDownloadManager;
|
||||
|
||||
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
|
||||
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
registerCleanupFunction(function () {
|
||||
gTestTargetFile.remove(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* This objects contains a property for each column in the downloads table.
|
||||
*/
|
||||
let gDownloadRowTemplate = {
|
||||
name: "test-download.txt",
|
||||
source: "http://www.example.com/test-download.txt",
|
||||
target: NetUtil.newURI(gTestTargetFile).spec,
|
||||
startTime: 1180493839859230,
|
||||
endTime: 1180493839859234,
|
||||
state: nsIDM.DOWNLOAD_FINISHED,
|
||||
currBytes: 0,
|
||||
maxBytes: -1,
|
||||
preferredAction: 0,
|
||||
autoResume: 0
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Infrastructure
|
||||
|
||||
// All test are run through the test runner.
|
||||
function test()
|
||||
{
|
||||
testRunner.runTest(this.gen_test);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a browser-chrome test defined through a generator function.
|
||||
*
|
||||
* This object is a singleton, initialized automatically when this script is
|
||||
* included. Every browser-chrome test file includes a new copy of this object.
|
||||
*/
|
||||
var testRunner = {
|
||||
_testIterator: null,
|
||||
_lastEventResult: undefined,
|
||||
_testRunning: false,
|
||||
_eventRaised: false,
|
||||
|
||||
// --- Main test runner ---
|
||||
|
||||
/**
|
||||
* Runs the test described by the provided generator function asynchronously.
|
||||
*
|
||||
* Calling yield in the generator will cause it to wait until continueTest is
|
||||
* called. The parameter provided to continueTest will be the return value of
|
||||
* the yield operator.
|
||||
*
|
||||
* @param aGenerator
|
||||
* Test generator function. The function will be called with no
|
||||
* arguments to retrieve its iterator.
|
||||
*/
|
||||
runTest: function TR_runTest(aGenerator) {
|
||||
waitForExplicitFinish();
|
||||
testRunner._testIterator = aGenerator();
|
||||
testRunner.continueTest();
|
||||
},
|
||||
|
||||
/**
|
||||
* Continues the currently running test.
|
||||
*
|
||||
* @param aEventResult
|
||||
* This will be the return value of the yield operator in the test.
|
||||
*/
|
||||
continueTest: function TR_continueTest(aEventResult) {
|
||||
// Store the last event result, or set it to undefined.
|
||||
testRunner._lastEventResult = aEventResult;
|
||||
|
||||
// Never reenter the main loop, but notify that the event has been raised.
|
||||
if (testRunner._testRunning) {
|
||||
testRunner._eventRaised = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter the main iteration loop.
|
||||
testRunner._testRunning = true;
|
||||
try {
|
||||
do {
|
||||
// Call the iterator, but don't leave the loop if the expected event is
|
||||
// raised during the execution of the generator.
|
||||
testRunner._eventRaised = false;
|
||||
testRunner._testIterator.send(testRunner._lastEventResult);
|
||||
} while (testRunner._eventRaised);
|
||||
}
|
||||
catch (e) {
|
||||
// This block catches exceptions raised by the generator, including the
|
||||
// normal StopIteration exception. Unexpected exceptions are reported as
|
||||
// test failures.
|
||||
if (!(e instanceof StopIteration))
|
||||
ok(false, e);
|
||||
// In any case, stop the tests in this file.
|
||||
finish();
|
||||
}
|
||||
|
||||
// Wait for the next event or finish.
|
||||
testRunner._testRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Asynchronous generator-based support subroutines
|
||||
|
||||
//
|
||||
// The following functions are all generators that can be used inside the main
|
||||
// test generator to perform specific tasks asynchronously. To invoke these
|
||||
// subroutines correctly, an iteration syntax should be used:
|
||||
//
|
||||
// for (let yy in gen_example("Parameter")) yield;
|
||||
//
|
||||
|
||||
function gen_resetState()
|
||||
{
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
"DELETE FROM moz_downloads");
|
||||
try {
|
||||
statement.executeAsync({
|
||||
handleResult: function(aResultSet) { },
|
||||
handleError: function(aError)
|
||||
{
|
||||
Cu.reportError(aError);
|
||||
},
|
||||
handleCompletion: function(aReason)
|
||||
{
|
||||
testRunner.continueTest();
|
||||
}
|
||||
});
|
||||
yield;
|
||||
} finally {
|
||||
statement.finalize();
|
||||
}
|
||||
|
||||
// Ensure that the panel is closed and data is unloaded.
|
||||
DownloadsCommon.data.clear();
|
||||
DownloadsCommon.data._loadState = DownloadsCommon.data.kLoadNone;
|
||||
|
||||
// Wait for focus on the main window.
|
||||
waitForFocus(testRunner.continueTest);
|
||||
yield;
|
||||
}
|
||||
|
||||
function gen_addDownloadRows(aDataRows)
|
||||
{
|
||||
let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
|
||||
let parameterNames = Object.keys(gDownloadRowTemplate)
|
||||
.map(function(n) ":" + n)
|
||||
.join(", ");
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
"INSERT INTO moz_downloads (" + columnNames +
|
||||
") VALUES(" + parameterNames + ")");
|
||||
try {
|
||||
// Execute the statement for each of the provided downloads in reverse.
|
||||
for (let i = aDataRows.length - 1; i >= 0; i--) {
|
||||
let dataRow = aDataRows[i];
|
||||
|
||||
// Populate insert parameters from the provided data.
|
||||
for (let columnName in gDownloadRowTemplate) {
|
||||
if (!(columnName in dataRow)) {
|
||||
// Update the provided row object with data from the global template,
|
||||
// for columns whose value is not provided explicitly.
|
||||
dataRow[columnName] = gDownloadRowTemplate[columnName];
|
||||
}
|
||||
statement.params[columnName] = dataRow[columnName];
|
||||
}
|
||||
|
||||
// Run the statement asynchronously and wait.
|
||||
statement.executeAsync({
|
||||
handleResult: function(aResultSet) { },
|
||||
handleError: function(aError)
|
||||
{
|
||||
Cu.reportError(aError);
|
||||
},
|
||||
handleCompletion: function(aReason)
|
||||
{
|
||||
testRunner.continueTest();
|
||||
}
|
||||
});
|
||||
yield;
|
||||
|
||||
// At each iteration, ensure that the end time in the global template is
|
||||
// distinct, as this column is used to sort each download in its category.
|
||||
gDownloadRowTemplate.endTime++;
|
||||
}
|
||||
} finally {
|
||||
statement.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
function gen_openPanel(aData)
|
||||
{
|
||||
// Hook to wait until the test data has been loaded.
|
||||
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
|
||||
DownloadsPanel.onViewLoadCompleted = function () {
|
||||
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
|
||||
originalOnViewLoadCompleted.apply(this);
|
||||
testRunner.continueTest();
|
||||
};
|
||||
|
||||
// Start loading all the downloads from the database asynchronously.
|
||||
DownloadsCommon.data.ensurePersistentDataLoaded(false);
|
||||
|
||||
// Wait for focus on the main window.
|
||||
waitForFocus(testRunner.continueTest);
|
||||
yield;
|
||||
|
||||
// Open the downloads panel, waiting until loading is completed.
|
||||
DownloadsPanel.showPanel();
|
||||
yield;
|
||||
}
|
18
browser/components/downloads/test/unit/head.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- 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/ */
|
||||
|
||||
/**
|
||||
* Provides infrastructure for automated download components tests.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource:///modules/DownloadsCommon.jsm");
|
@ -0,0 +1,37 @@
|
||||
/* -*- 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 for the functions located directly in the "DownloadsCommon" object.
|
||||
*/
|
||||
|
||||
function testFormatTimeLeft(aSeconds, aExpectedValue, aExpectedUnitString)
|
||||
{
|
||||
let expected = "";
|
||||
if (aExpectedValue) {
|
||||
// Format the expected result based on the current language.
|
||||
expected = DownloadsCommon.strings[aExpectedUnitString](aExpectedValue);
|
||||
}
|
||||
do_check_eq(DownloadsCommon.formatTimeLeft(aSeconds), expected);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
testFormatTimeLeft( 0, "", "");
|
||||
testFormatTimeLeft( 1, "1", "shortTimeLeftSeconds");
|
||||
testFormatTimeLeft( 29, "29", "shortTimeLeftSeconds");
|
||||
testFormatTimeLeft( 30, "30", "shortTimeLeftSeconds");
|
||||
testFormatTimeLeft( 31, "1", "shortTimeLeftMinutes");
|
||||
testFormatTimeLeft( 60, "1", "shortTimeLeftMinutes");
|
||||
testFormatTimeLeft( 89, "1", "shortTimeLeftMinutes");
|
||||
testFormatTimeLeft( 90, "2", "shortTimeLeftMinutes");
|
||||
testFormatTimeLeft( 91, "2", "shortTimeLeftMinutes");
|
||||
testFormatTimeLeft( 3600, "1", "shortTimeLeftHours");
|
||||
testFormatTimeLeft( 86400, "24", "shortTimeLeftHours");
|
||||
testFormatTimeLeft( 169200, "47", "shortTimeLeftHours");
|
||||
testFormatTimeLeft( 172800, "2", "shortTimeLeftDays");
|
||||
testFormatTimeLeft(8553600, "99", "shortTimeLeftDays");
|
||||
testFormatTimeLeft(8640000, "99", "shortTimeLeftDays");
|
||||
}
|
5
browser/components/downloads/test/unit/xpcshell.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = head.js
|
||||
tail =
|
||||
|
||||
[test_DownloadsCommon.js]
|
@ -38,6 +38,10 @@
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
|
||||
var gMainPane = {
|
||||
_pane: null,
|
||||
|
||||
@ -55,12 +59,25 @@ var gMainPane = {
|
||||
this.updateBrowserStartupLastSession();
|
||||
this.startupPagePrefChanged();
|
||||
|
||||
this.setupDownloadsWindowOptions();
|
||||
|
||||
// Notify observers that the UI is now ready
|
||||
Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService)
|
||||
.notifyObservers(window, "main-pane-loaded", null);
|
||||
},
|
||||
|
||||
setupDownloadsWindowOptions: function ()
|
||||
{
|
||||
var showWhenDownloading = document.getElementById("showWhenDownloading");
|
||||
var closeWhenDone = document.getElementById("closeWhenDone");
|
||||
|
||||
// These radio-buttons should not be visible if we have enabled the Downloads Panel.
|
||||
let shouldHide = !DownloadsCommon.useToolkitUI;
|
||||
showWhenDownloading.hidden = shouldHide;
|
||||
closeWhenDone.hidden = shouldHide;
|
||||
},
|
||||
|
||||
// HOME PAGE
|
||||
|
||||
/*
|
||||
|
@ -308,6 +308,9 @@
|
||||
@BINPATH@/components/nsBrowserGlue.js
|
||||
@BINPATH@/components/nsSetDefaultBrowser.manifest
|
||||
@BINPATH@/components/nsSetDefaultBrowser.js
|
||||
@BINPATH@/components/BrowserDownloads.manifest
|
||||
@BINPATH@/components/DownloadsStartup.js
|
||||
@BINPATH@/components/DownloadsUI.js
|
||||
@BINPATH@/components/BrowserPlaces.manifest
|
||||
@BINPATH@/components/BrowserPageThumbs.manifest
|
||||
@BINPATH@/components/nsPrivateBrowsingService.manifest
|
||||
|
41
browser/locales/en-US/chrome/browser/downloads/downloads.dtd
Normal file
@ -0,0 +1,41 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<!-- LOCALIZATION NOTE (indicator.tooltiptext):
|
||||
Tooltip for the indicator that displays the progress of ongoing downloads.
|
||||
-->
|
||||
<!ENTITY indicator.tooltiptext "Downloads">
|
||||
|
||||
<!-- LOCALIZATION NOTE (downloads.title):
|
||||
Used by screen readers to describe the Downloads Panel.
|
||||
-->
|
||||
<!ENTITY downloads.title "Downloads">
|
||||
|
||||
<!ENTITY cmd.pause.label "Pause">
|
||||
<!ENTITY cmd.pause.accesskey "P">
|
||||
<!ENTITY cmd.resume.label "Resume">
|
||||
<!ENTITY cmd.resume.accesskey "R">
|
||||
<!ENTITY cmd.cancel.label "Cancel">
|
||||
<!ENTITY cmd.cancel.accesskey "C">
|
||||
<!-- 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.retry.label "Retry">
|
||||
<!ENTITY cmd.goToDownloadPage.label "Go To Download Page">
|
||||
<!ENTITY cmd.goToDownloadPage.accesskey "G">
|
||||
<!ENTITY cmd.copyDownloadLink.label "Copy Download Link">
|
||||
<!ENTITY cmd.copyDownloadLink.accesskey "L">
|
||||
<!ENTITY cmd.removeFromList.label "Remove From List">
|
||||
<!ENTITY cmd.removeFromList.accesskey "e">
|
||||
<!ENTITY cmd.clearList.label "Clear List">
|
||||
<!ENTITY cmd.clearList.accesskey "a">
|
||||
|
||||
<!ENTITY downloadshistory.label "Show All Downloads">
|
||||
<!ENTITY downloadshistory.accesskey "S">
|
@ -0,0 +1,72 @@
|
||||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE (stateStarting):
|
||||
# Indicates that the download is starting.
|
||||
stateStarting=Starting…
|
||||
# LOCALIZATION NOTE (stateScanning):
|
||||
# Indicates that an external program is scanning the download for viruses.
|
||||
stateScanning=Scanning for viruses…
|
||||
# LOCALIZATION NOTE (stateFailed):
|
||||
# Indicates that the download failed because of an error.
|
||||
stateFailed=Failed
|
||||
# LOCALIZATION NOTE (statePaused):
|
||||
# Indicates that the download was paused by the user.
|
||||
statePaused=Paused
|
||||
# LOCALIZATION NOTE (stateCanceled):
|
||||
# Indicates that the download was canceled by the user.
|
||||
stateCanceled=Canceled
|
||||
# LOCALIZATION NOTE (stateBlockedParentalControls):
|
||||
# Indicates that the download was blocked by the Parental Controls feature of
|
||||
# Windows. "Parental Controls" should be consistently named and capitalized
|
||||
# with the display of this feature in Windows. The following article can
|
||||
# provide a reference for the translation of "Parental Controls" in various
|
||||
# languages:
|
||||
# http://windows.microsoft.com/en-US/windows-vista/Set-up-Parental-Controls
|
||||
stateBlockedParentalControls=Blocked by Parental Controls
|
||||
# LOCALIZATION NOTE (stateBlockedPolicy):
|
||||
# Indicates that the download was blocked on Windows because of the "Launching
|
||||
# applications and unsafe files" setting of the "security zone" associated with
|
||||
# the target site. "Security zone" should be consistently named and capitalized
|
||||
# with the display of this feature in Windows. The following article can
|
||||
# provide a reference for the translation of "security zone" in various
|
||||
# languages:
|
||||
# http://support.microsoft.com/kb/174360
|
||||
stateBlockedPolicy=Blocked by your security zone policy
|
||||
# LOCALIZATION NOTE (stateDirty):
|
||||
# Indicates that the download was blocked after scanning.
|
||||
stateDirty=Blocked: May contain a virus or spyware
|
||||
|
||||
# LOCALIZATION NOTE (sizeWithUnits):
|
||||
# %1$S is replaced with the size number, and %2$S with the measurement unit.
|
||||
sizeWithUnits=%1$S %2$S
|
||||
sizeUnknown=Unknown size
|
||||
|
||||
# LOCALIZATION NOTE (shortTimeLeftSeconds, shortTimeLeftMinutes,
|
||||
# shortTimeLeftHours, shortTimeLeftDays):
|
||||
# These values are displayed in the downloads indicator in the main browser
|
||||
# window, where space is available for three characters maximum. %1$S is
|
||||
# replaced with the time left for the given measurement unit. Even for days,
|
||||
# the value is never longer than two digits.
|
||||
shortTimeLeftSeconds=%1$Ss
|
||||
shortTimeLeftMinutes=%1$Sm
|
||||
shortTimeLeftHours=%1$Sh
|
||||
shortTimeLeftDays=%1$Sd
|
||||
|
||||
# LOCALIZATION NOTE (statusSeparator, statusSeparatorBeforeNumber):
|
||||
# These strings define templates for the separation of different elements in the
|
||||
# status line of a download item. As a separator, by default we use the Unicode
|
||||
# character U+2014 'EM DASH' (long dash). Examples of status lines include
|
||||
# "Canceled - 222.net", "1.1 MB - website2.com", or "Paused - 1.1 MB". Note
|
||||
# that we use a wider space after the separator when it is followed by a number,
|
||||
# just to avoid visually confusing it with with a minus sign with some fonts.
|
||||
# If you use a different separator, this might not be necessary. However, there
|
||||
# is usually no need to change the separator or the order of the substitutions,
|
||||
# even for right-to-left languages, unless the defaults are not suitable.
|
||||
statusSeparator=%1$S \u2014 %2$S
|
||||
statusSeparatorBeforeNumber=%1$S \u2014 %2$S
|
||||
|
||||
fileExecutableSecurityWarning="%S" is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch "%S"?
|
||||
fileExecutableSecurityWarningTitle=Open Executable File?
|
||||
fileExecutableSecurityWarningDontAsk=Don't ask me this again
|
@ -50,6 +50,8 @@
|
||||
locale/browser/tabbrowser.properties (%chrome/browser/tabbrowser.properties)
|
||||
locale/browser/tabview.properties (%chrome/browser/tabview.properties)
|
||||
locale/browser/taskbar.properties (%chrome/browser/taskbar.properties)
|
||||
locale/browser/downloads/downloads.dtd (%chrome/browser/downloads/downloads.dtd)
|
||||
locale/browser/downloads/downloads.properties (%chrome/browser/downloads/downloads.properties)
|
||||
locale/browser/places/places.dtd (%chrome/browser/places/places.dtd)
|
||||
locale/browser/places/places.properties (%chrome/browser/places/places.properties)
|
||||
locale/browser/places/editBookmarkOverlay.dtd (%chrome/browser/places/editBookmarkOverlay.dtd)
|
||||
|
@ -48,6 +48,8 @@ browser/components/about/Makefile
|
||||
browser/components/build/Makefile
|
||||
browser/components/certerror/Makefile
|
||||
browser/components/dirprovider/Makefile
|
||||
browser/components/downloads/Makefile
|
||||
browser/components/downloads/src/Makefile
|
||||
browser/components/feeds/Makefile
|
||||
browser/components/feeds/public/Makefile
|
||||
browser/components/feeds/src/Makefile
|
||||
@ -131,6 +133,8 @@ if [ "$ENABLE_TESTS" ]; then
|
||||
browser/base/content/test/newtab/Makefile
|
||||
browser/components/certerror/test/Makefile
|
||||
browser/components/dirprovider/tests/Makefile
|
||||
browser/components/downloads/test/Makefile
|
||||
browser/components/downloads/test/browser/Makefile
|
||||
browser/components/preferences/tests/Makefile
|
||||
browser/components/search/test/Makefile
|
||||
browser/components/sessionstore/test/Makefile
|
||||
|
BIN
browser/themes/gnomestripe/downloads/buttons.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
browser/themes/gnomestripe/downloads/download-glow.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
browser/themes/gnomestripe/downloads/download-notification.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
279
browser/themes/gnomestripe/downloads/downloads.css
Normal file
@ -0,0 +1,279 @@
|
||||
/* 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/. */
|
||||
|
||||
/*** Panel and outer controls ***/
|
||||
|
||||
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#downloadsListBox {
|
||||
width: 60ch;
|
||||
background: transparent;
|
||||
padding: 4px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsListBox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#downloadsHistory {
|
||||
background: inherit;
|
||||
color: -moz-nativehyperlinktext;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsHistory {
|
||||
border-top: 1px solid ThreeDShadow;
|
||||
background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
|
||||
}
|
||||
|
||||
#downloadsHistory > .button-box {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
/*** List items ***/
|
||||
|
||||
richlistitem[type="download"] {
|
||||
height: 6em;
|
||||
margin: 0;
|
||||
border-top: 1px solid hsla(0,0%,100%,.2);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.15);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:first-child {
|
||||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:last-child {
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
#downloadsListBox:-moz-focusring > richlistitem[type="download"][selected] {
|
||||
outline: 1px #999 dotted;
|
||||
outline-offset: -1px;
|
||||
-moz-outline-radius: 3px;
|
||||
}
|
||||
|
||||
.downloadInfo {
|
||||
padding: 8px;
|
||||
-moz-padding-end: 0;
|
||||
}
|
||||
|
||||
.downloadTypeIcon {
|
||||
-moz-margin-end: 8px;
|
||||
/* Prevent flickering when changing states. */
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.blockedIcon {
|
||||
list-style-image: url("chrome://global/skin/icons/Error.png");
|
||||
}
|
||||
|
||||
.downloadTarget {
|
||||
margin-bottom: 7px;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadDetails {
|
||||
margin-top: 1px;
|
||||
opacity: 0.6;
|
||||
font-size: 90%;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin: 6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 5px;
|
||||
list-style-image: url("chrome://browser/skin/downloads/buttons.png");
|
||||
}
|
||||
|
||||
.downloadButton > .button-box {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*** Highlighted list items ***/
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo {
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
-moz-padding-end: 8px;
|
||||
}
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo:hover {
|
||||
border-radius: 3px;
|
||||
border-top: 1px solid hsla(0,0%,100%,.3);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.2);
|
||||
background-color: Highlight;
|
||||
background-image: -moz-linear-gradient(hsla(0,0%,100%,.1), hsla(0,0%,100%,0));
|
||||
color: HighlightText;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*** Button icons ***/
|
||||
|
||||
.downloadButton.downloadCancel {
|
||||
-moz-image-region: rect(0px, 14px, 14px, 0px);
|
||||
}
|
||||
.downloadButton.downloadCancel:hover {
|
||||
-moz-image-region: rect(0px, 28px, 14px, 14px);
|
||||
}
|
||||
.downloadButton.downloadCancel:active {
|
||||
-moz-image-region: rect(0px, 42px, 14px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadShow {
|
||||
-moz-image-region: rect(14px, 14px, 28px, 0px);
|
||||
}
|
||||
.downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(14px, 28px, 28px, 14px);
|
||||
}
|
||||
.downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(14px, 42px, 28px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadRetry {
|
||||
-moz-image-region: rect(28px, 14px, 42px, 0px);
|
||||
}
|
||||
.downloadButton.downloadRetry:hover {
|
||||
-moz-image-region: rect(28px, 28px, 42px, 14px);
|
||||
}
|
||||
.downloadButton.downloadRetry:active {
|
||||
-moz-image-region: rect(28px, 42px, 42px, 28px);
|
||||
}
|
||||
|
||||
/*** Status and progress indicator ***/
|
||||
|
||||
#downloads-indicator {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
#downloads-indicator-anchor {
|
||||
min-width: 18px;
|
||||
min-height: 18px;
|
||||
/* Makes the outermost stack element positioned, so that its contents are
|
||||
rendered over the main browser window in the Z order. This is required by
|
||||
the animated event notification. */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*** Main indicator icon ***/
|
||||
|
||||
#downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
|
||||
0, 16, 16, 0) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator-icon:-moz-lwtheme-brighttext {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"),
|
||||
0, 16, 16, 0) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
16, 32, 32, 16) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
|
||||
0, 16, 16, 0) center no-repeat;
|
||||
background-size: 12px;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
16, 32, 32, 16);
|
||||
}
|
||||
|
||||
/*** Event notification ***/
|
||||
|
||||
#downloads-indicator-notification {
|
||||
opacity: 0;
|
||||
background: url("chrome://browser/skin/downloads/download-notification.png")
|
||||
center no-repeat;
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationRight {
|
||||
from { opacity: 0; -moz-transform: translate(-128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationLeft {
|
||||
from { opacity: 0; -moz-transform: translate(128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
#downloads-indicator[notification] > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationRight;
|
||||
-moz-animation-duration: 1s;
|
||||
}
|
||||
|
||||
#downloads-indicator[notification]:-moz-locale-dir(rtl) > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationLeft;
|
||||
}
|
||||
|
||||
/*** Progress bar and text ***/
|
||||
|
||||
#downloads-indicator-counter {
|
||||
height: 12px;
|
||||
margin: 0;
|
||||
color: hsl(0,0%,30%);
|
||||
text-shadow: 0 1px 0 hsla(0,0%,100%,.5);
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#downloads-indicator-progress {
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.4);
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-bar {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#41a0ff, #2090ff);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.6) hsla(0,0%,0%,.2) hsla(0,0%,0%,.2);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-remainder {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#9a9a9a, #a1a1a1);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.3) hsla(0,0%,0%,.2);
|
||||
-moz-border-start: none;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-bar {
|
||||
background-image: -moz-linear-gradient(#a0a000, #909000);
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-remainder {
|
||||
background-image: -moz-linear-gradient(#9a9a00, #a1a100);
|
||||
}
|
@ -35,6 +35,10 @@ browser.jar:
|
||||
skin/classic/browser/Toolbar.png
|
||||
skin/classic/browser/Toolbar-small.png
|
||||
skin/classic/browser/urlbar-arrow.png
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/browser/downloads/download-notification.png (downloads/download-notification.png)
|
||||
skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
|
||||
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
|
||||
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
|
||||
skin/classic/browser/feeds/videoFeedIcon.png (feeds/feedIcon.png)
|
||||
|
BIN
browser/themes/pinstripe/downloads/buttons.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
browser/themes/pinstripe/downloads/download-glow.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
browser/themes/pinstripe/downloads/download-notification.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
300
browser/themes/pinstripe/downloads/downloads.css
Normal file
@ -0,0 +1,300 @@
|
||||
/* 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/. */
|
||||
|
||||
/*** Panel and outer controls ***/
|
||||
|
||||
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#downloadsListBox {
|
||||
width: 60ch;
|
||||
background: transparent;
|
||||
padding: 4px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsListBox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#downloadsHistory {
|
||||
background: inherit;
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
color: hsl(210,100%,75%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsHistory {
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsHistory {
|
||||
border-top: 1px solid hsla(0,0%,100%,.1);
|
||||
background-color: hsla(0,0%,7%,.3);
|
||||
box-shadow: 0 1px 1px hsla(0,0%,0%,.25) inset;
|
||||
}
|
||||
|
||||
#downloadsHistory > .button-box {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#downloadsHistory:-moz-focusring > .button-box {
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsHistory:-moz-focusring > .button-box {
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
/*** List items ***/
|
||||
|
||||
richlistitem[type="download"] {
|
||||
height: 7em;
|
||||
margin: 0;
|
||||
border-top: 1px solid hsla(0,0%,100%,.07);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.2);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:first-child {
|
||||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:last-child {
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
#downloadsListBox:-moz-focusring > richlistitem[type="download"][selected] {
|
||||
outline: 1px #999 dotted;
|
||||
outline-offset: -1px;
|
||||
-moz-outline-radius: 3px;
|
||||
}
|
||||
|
||||
.downloadInfo {
|
||||
color: white;
|
||||
padding: 8px;
|
||||
-moz-padding-end: 0;
|
||||
}
|
||||
|
||||
.downloadTypeIcon {
|
||||
-moz-margin-end: 8px;
|
||||
/* Prevent flickering when changing states. */
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.blockedIcon {
|
||||
list-style-image: url("chrome://global/skin/icons/Error.png");
|
||||
}
|
||||
|
||||
.downloadTarget {
|
||||
margin-bottom: 6px;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadDetails {
|
||||
opacity: 0.7;
|
||||
font-size: 95%;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin: 6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 5px;
|
||||
list-style-image: url("chrome://browser/skin/downloads/buttons.png");
|
||||
}
|
||||
|
||||
.downloadButton > .button-box {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*** Highlighted list items ***/
|
||||
|
||||
richlistitem[type="download"][state="1"] .downloadInfo {
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
-moz-padding-end: 8px;
|
||||
}
|
||||
|
||||
richlistitem[type="download"][state="1"] .downloadInfo:hover {
|
||||
border-radius: 3px;
|
||||
border-top: 1px solid hsla(0,0%,100%,.2);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.4);
|
||||
background-color: Highlight;
|
||||
background-image: -moz-linear-gradient(hsl(210,100%,50%), hsl(210,96%,41%));
|
||||
color: HighlightText;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*** Button icons ***/
|
||||
|
||||
.downloadButton.downloadCancel {
|
||||
-moz-image-region: rect(0px, 14px, 14px, 0px);
|
||||
}
|
||||
.downloadButton.downloadCancel:hover {
|
||||
-moz-image-region: rect(0px, 28px, 14px, 14px);
|
||||
}
|
||||
.downloadButton.downloadCancel:active {
|
||||
-moz-image-region: rect(0px, 42px, 14px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadShow {
|
||||
-moz-image-region: rect(14px, 14px, 28px, 0px);
|
||||
}
|
||||
.downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(14px, 28px, 28px, 14px);
|
||||
}
|
||||
.downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(14px, 42px, 28px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadRetry {
|
||||
-moz-image-region: rect(28px, 14px, 42px, 0px);
|
||||
}
|
||||
.downloadButton.downloadRetry:hover {
|
||||
-moz-image-region: rect(28px, 28px, 42px, 14px);
|
||||
}
|
||||
.downloadButton.downloadRetry:active {
|
||||
-moz-image-region: rect(28px, 42px, 42px, 28px);
|
||||
}
|
||||
|
||||
/*** Status and progress indicator ***/
|
||||
|
||||
#downloads-indicator {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
#downloads-indicator-anchor {
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
/* Makes the outermost stack element positioned, so that its contents are
|
||||
rendered over the main browser window in the Z order. This is required by
|
||||
the animated event notification. */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*** Main indicator icon ***/
|
||||
|
||||
#downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
|
||||
0, 140, 20, 120) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator-icon:-moz-lwtheme-brighttext {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"),
|
||||
0, 140, 20, 120) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator[attention]
|
||||
#downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
14, 34, 34, 14) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter])
|
||||
#downloads-indicator-counter {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
|
||||
0, 140, 20, 120) center no-repeat;
|
||||
background-size: 12px;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter])[attention]
|
||||
#downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
14, 34, 34, 14);
|
||||
}
|
||||
|
||||
/*** Event notification ***/
|
||||
|
||||
#downloads-indicator-notification {
|
||||
opacity: 0;
|
||||
background: url("chrome://browser/skin/downloads/download-notification.png")
|
||||
center no-repeat;
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationRight {
|
||||
from { opacity: 0; -moz-transform: translate(-128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationLeft {
|
||||
from { opacity: 0; -moz-transform: translate(128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
#downloads-indicator[notification] > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationRight;
|
||||
-moz-animation-duration: 1s;
|
||||
}
|
||||
|
||||
#downloads-indicator[notification]:-moz-locale-dir(rtl) > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationLeft;
|
||||
}
|
||||
|
||||
/*** Progress bar and text ***/
|
||||
|
||||
#downloads-indicator-counter {
|
||||
height: 12px;
|
||||
margin: 0;
|
||||
color: hsl(0,0%,30%);
|
||||
text-shadow: 0 1px 0 hsla(0,0%,100%,.5);
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#downloads-indicator-progress {
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.4);
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-bar {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#41a0ff, #2090ff);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.6) hsla(0,0%,0%,.2) hsla(0,0%,0%,.2);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-remainder {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#9a9a9a, #a1a1a1);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.3) hsla(0,0%,0%,.2);
|
||||
-moz-border-start: none;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-bar {
|
||||
background-image: -moz-linear-gradient(#a0a000, #909000);
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-remainder {
|
||||
background-image: -moz-linear-gradient(#9a9a00, #a1a100);
|
||||
}
|
@ -45,6 +45,10 @@ browser.jar:
|
||||
skin/classic/browser/urlbar-history-dropmarker.png
|
||||
skin/classic/browser/urlbar-arrow.png
|
||||
skin/classic/browser/urlbar-popup-blocked.png
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/browser/downloads/download-notification.png (downloads/download-notification.png)
|
||||
skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
|
||||
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
|
||||
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
|
||||
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
|
||||
|
BIN
browser/themes/winstripe/downloads/buttons-aero.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
browser/themes/winstripe/downloads/buttons.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
browser/themes/winstripe/downloads/download-glow-aero.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
browser/themes/winstripe/downloads/download-glow.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
browser/themes/winstripe/downloads/download-notification.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
49
browser/themes/winstripe/downloads/downloads-aero.css
Normal file
@ -0,0 +1,49 @@
|
||||
%define WINSTRIPE_AERO
|
||||
%include downloads.css
|
||||
%undef WINSTRIPE_AERO
|
||||
|
||||
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent > .panel-inner-arrowcontent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
#downloadsPanel[hasdownloads] > #downloadsHistory {
|
||||
background-color: #f1f5fb;
|
||||
}
|
||||
|
||||
richlistitem[type="download"] {
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid hsl(213,40%,90%);
|
||||
}
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo {
|
||||
border: 1px solid transparent;
|
||||
-moz-padding-end: 8px;
|
||||
}
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo:hover {
|
||||
border: 1px solid hsl(213,45%,65%);
|
||||
box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.3) inset;
|
||||
background-image: -moz-linear-gradient(hsl(212,86%,92%), hsl(212,91%,86%));
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-windows-compositor) {
|
||||
#toolbar-menubar #downloads-indicator-icon:not(:-moz-lwtheme),
|
||||
#TabsToolbar[tabsontop=true] #downloads-indicator-icon:not(:-moz-lwtheme),
|
||||
#navigator-toolbox[tabsontop=false] > #nav-bar #downloads-indicator-icon:not(:-moz-lwtheme),
|
||||
#nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator-icon:not(:-moz-lwtheme) {
|
||||
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
|
||||
}
|
||||
|
||||
#toolbar-menubar #downloads-indicator-counter:not(:-moz-lwtheme),
|
||||
#TabsToolbar[tabsontop=true] #downloads-indicator-counter:not(:-moz-lwtheme),
|
||||
#navigator-toolbox[tabsontop=false] > #nav-bar #downloads-indicator-counter:not(:-moz-lwtheme),
|
||||
#nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator-counter:not(:-moz-lwtheme) {
|
||||
color: white;
|
||||
text-shadow: 0 0 1px rgba(0,0,0,.7),
|
||||
0 1px 1.5px rgba(0,0,0,.5);
|
||||
}
|
||||
}
|
310
browser/themes/winstripe/downloads/downloads.css
Normal file
@ -0,0 +1,310 @@
|
||||
/* 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/. */
|
||||
|
||||
/*** Panel and outer controls ***/
|
||||
|
||||
%ifndef WINSTRIPE_AERO
|
||||
#downloadsHistory,
|
||||
#downloadsHistory:-moz-focusring > .button-box {
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsHistory,
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsHistory:-moz-focusring > .button-box {
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
/* Avoid that the arrow overlaps the selection on first item */
|
||||
padding-top: 5px;
|
||||
}
|
||||
%endif
|
||||
|
||||
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#downloadsListBox {
|
||||
width: 60ch;
|
||||
background-color: transparent;
|
||||
padding: 4px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#downloadsHistory {
|
||||
background: inherit;
|
||||
color: -moz-nativehyperlinktext;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsHistory > .button-box {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
#downloadsPanel[hasdownloads] > #downloadsHistory {
|
||||
background-color: hsla(216,45%,88%,.98);
|
||||
box-shadow: 0px 1px 2px rgb(204,214,234) inset;
|
||||
}
|
||||
}
|
||||
|
||||
/*** List items ***/
|
||||
|
||||
richlistitem[type="download"] {
|
||||
height: 7em;
|
||||
margin: 0;
|
||||
border-top: 1px solid hsla(0,0%,100%,.3);
|
||||
border-bottom: 1px solid hsla(220,18%,51%,.25);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
richlistitem[type="download"]:first-child {
|
||||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
richlistitem[type="download"]:last-child {
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
#downloadsListBox:-moz-focusring > richlistitem[type="download"][selected] {
|
||||
outline: 1px #999 dotted;
|
||||
outline-offset: -1px;
|
||||
-moz-outline-radius: 3px;
|
||||
}
|
||||
|
||||
.downloadInfo {
|
||||
padding: 8px;
|
||||
-moz-padding-end: 0;
|
||||
}
|
||||
|
||||
.downloadTypeIcon {
|
||||
-moz-margin-end: 8px;
|
||||
/* Prevent flickering when changing states. */
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.blockedIcon {
|
||||
list-style-image: url("chrome://global/skin/icons/Error.png");
|
||||
}
|
||||
|
||||
.downloadTarget {
|
||||
margin-bottom: 6px;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadDetails {
|
||||
opacity: 0.6;
|
||||
font-size: 90%;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin: 6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 5px;
|
||||
list-style-image: url("chrome://browser/skin/downloads/buttons.png");
|
||||
}
|
||||
|
||||
.downloadButton > .button-box {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*** Highlighted list items ***/
|
||||
|
||||
%ifdef WINSTRIPE_AERO
|
||||
@media not all and (-moz-windows-default-theme) {
|
||||
%endif
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo {
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
-moz-padding-end: 8px;
|
||||
}
|
||||
|
||||
%ifdef WINSTRIPE_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
richlistitem[type="download"][state="1"] > .downloadInfo:hover {
|
||||
border-radius: 3px;
|
||||
border-top: 1px solid hsla(0,0%,100%,.2);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.2);
|
||||
background-color: Highlight;
|
||||
color: HighlightText;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*** Button icons ***/
|
||||
|
||||
.downloadButton.downloadCancel {
|
||||
-moz-image-region: rect(0px, 14px, 14px, 0px);
|
||||
}
|
||||
.downloadButton.downloadCancel:hover {
|
||||
-moz-image-region: rect(0px, 28px, 14px, 14px);
|
||||
}
|
||||
.downloadButton.downloadCancel:active {
|
||||
-moz-image-region: rect(0px, 42px, 14px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadShow {
|
||||
-moz-image-region: rect(14px, 14px, 28px, 0px);
|
||||
}
|
||||
.downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(14px, 28px, 28px, 14px);
|
||||
}
|
||||
.downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(14px, 42px, 28px, 28px);
|
||||
}
|
||||
|
||||
.downloadButton.downloadRetry {
|
||||
-moz-image-region: rect(28px, 14px, 42px, 0px);
|
||||
}
|
||||
.downloadButton.downloadRetry:hover {
|
||||
-moz-image-region: rect(28px, 28px, 42px, 14px);
|
||||
}
|
||||
.downloadButton.downloadRetry:active {
|
||||
-moz-image-region: rect(28px, 42px, 42px, 28px);
|
||||
}
|
||||
|
||||
/*** Status and progress indicator ***/
|
||||
|
||||
#downloads-indicator {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
#downloads-indicator-anchor {
|
||||
min-width: 18px;
|
||||
min-height: 18px;
|
||||
/* Makes the outermost stack element positioned, so that its contents are
|
||||
rendered over the main browser window in the Z order. This is required by
|
||||
the animated event notification. */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*** Main indicator icon ***/
|
||||
|
||||
#downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
|
||||
0, 108, 18, 90) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator-icon:-moz-lwtheme-brighttext {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"),
|
||||
0, 108, 18, 90) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
15, 33, 33, 15) center no-repeat;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
|
||||
0, 108, 18, 90) center no-repeat;
|
||||
background-size: 12px;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
|
||||
16, 32, 32, 16);
|
||||
}
|
||||
|
||||
/*** Event notification ***/
|
||||
|
||||
#downloads-indicator-notification {
|
||||
opacity: 0;
|
||||
background: url("chrome://browser/skin/downloads/download-notification.png")
|
||||
center no-repeat;
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationRight {
|
||||
from { opacity: 0; -moz-transform: translate(-128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
@-moz-keyframes downloadsIndicatorNotificationLeft {
|
||||
from { opacity: 0; -moz-transform: translate(128px, 128px) scale(8); }
|
||||
20% { opacity: .85; -moz-animation-timing-function: ease-out; }
|
||||
to { opacity: 0; -moz-transform: translate(0) scale(1); }
|
||||
}
|
||||
|
||||
#downloads-indicator[notification] > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationRight;
|
||||
-moz-animation-duration: 1s;
|
||||
}
|
||||
|
||||
#downloads-indicator[notification]:-moz-locale-dir(rtl) > #downloads-indicator-anchor > #downloads-indicator-notification {
|
||||
-moz-animation-name: downloadsIndicatorNotificationLeft;
|
||||
}
|
||||
|
||||
/*** Progress bar and text ***/
|
||||
|
||||
#downloads-indicator-counter {
|
||||
height: 12px;
|
||||
margin: 0;
|
||||
color: hsl(0,0%,30%);
|
||||
text-shadow: hsla(0,0%,100%,.5) 0 1px;
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#downloads-indicator-counter:-moz-lwtheme-brighttext {
|
||||
color: white;
|
||||
text-shadow: 0 0 1px rgba(0,0,0,.7),
|
||||
0 1px 1.5px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
#downloads-indicator-progress {
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.4);
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-bar {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#92DDA0, #6FC483 49%, #5EB272 51%, #80CE91);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.6) hsla(0,0%,0%,.2) hsla(0,0%,0%,.2);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
#downloads-indicator-progress > .progress-remainder {
|
||||
-moz-appearance: none;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-image: -moz-linear-gradient(#9a9a9a, #a1a1a1);
|
||||
border: 1px solid;
|
||||
border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.3) hsla(0,0%,0%,.2);
|
||||
-moz-border-start: none;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-bar {
|
||||
background-image: -moz-linear-gradient(#DDDD00, #C4C400 49%, #B2B200 51%, #CECE00);
|
||||
}
|
||||
|
||||
#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-remainder {
|
||||
background-image: -moz-linear-gradient(#9a9a00, #a1a100);
|
||||
}
|
@ -50,6 +50,10 @@ browser.jar:
|
||||
skin/classic/browser/urlbar-arrow.png
|
||||
skin/classic/browser/urlbar-popup-blocked.png
|
||||
skin/classic/browser/urlbar-history-dropmarker.png
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/browser/downloads/download-notification.png (downloads/download-notification.png)
|
||||
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
|
||||
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
|
||||
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
|
||||
skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png)
|
||||
@ -224,6 +228,10 @@ browser.jar:
|
||||
skin/classic/aero/browser/urlbar-arrow.png
|
||||
skin/classic/aero/browser/urlbar-popup-blocked.png
|
||||
skin/classic/aero/browser/urlbar-history-dropmarker.png
|
||||
skin/classic/aero/browser/downloads/buttons.png (downloads/buttons-aero.png)
|
||||
skin/classic/aero/browser/downloads/download-glow.png (downloads/download-glow-aero.png)
|
||||
skin/classic/aero/browser/downloads/download-notification.png (downloads/download-notification.png)
|
||||
* skin/classic/aero/browser/downloads/downloads.css (downloads/downloads-aero.css)
|
||||
skin/classic/aero/browser/feeds/feedIcon.png (feeds/feedIcon-aero.png)
|
||||
skin/classic/aero/browser/feeds/feedIcon16.png (feeds/feedIcon16-aero.png)
|
||||
skin/classic/aero/browser/feeds/audioFeedIcon.png (feeds/feedIcon-aero.png)
|
||||
|
@ -74,6 +74,7 @@ skip-if = os == "android"
|
||||
# Bug 676978: tests hang on Android
|
||||
skip-if = os == "android"
|
||||
[include:browser/components/dirprovider/tests/unit/xpcshell.ini]
|
||||
[include:browser/components/downloads/test/unit/xpcshell.ini]
|
||||
[include:browser/components/feeds/test/unit/xpcshell.ini]
|
||||
[include:browser/components/migration/tests/unit/xpcshell.ini]
|
||||
[include:browser/components/places/tests/unit/xpcshell.ini]
|
||||
|
@ -326,7 +326,7 @@ nsDownloadManager::CloseDB()
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
rv = mUpdateDownloadStatement->Finalize();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
rv = mDBConn->Close();
|
||||
rv = mDBConn->AsyncClose(nsnull);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
@ -899,6 +899,11 @@ nsDownloadManager::Init()
|
||||
nsCOMPtr<nsINavHistoryService> history =
|
||||
do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
|
||||
|
||||
(void)mObserverService->NotifyObservers(
|
||||
static_cast<nsIDownloadManager *>(this),
|
||||
"download-manager-initialized",
|
||||
nsnull);
|
||||
|
||||
// The following AddObserver calls must be the last lines in this function,
|
||||
// because otherwise, this function may fail (and thus, this object would be not
|
||||
// completely initialized), but the observerservice would still keep a reference
|
||||
@ -933,6 +938,19 @@ nsDownloadManager::GetRetentionBehavior()
|
||||
rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
// Allow the Downloads Panel to change the retention behavior. We do this to
|
||||
// allow proper migration to the new feature when using the same profile on
|
||||
// multiple versions of the product (bug 697678). Implementation note: in
|
||||
// order to allow observers to change the retention value, we have to pass an
|
||||
// object in the aSubject parameter, we cannot use aData for that.
|
||||
nsCOMPtr<nsISupportsPRInt32> retentionBehavior =
|
||||
do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
|
||||
retentionBehavior->SetData(val);
|
||||
(void)mObserverService->NotifyObservers(retentionBehavior,
|
||||
"download-manager-change-retention",
|
||||
nsnull);
|
||||
retentionBehavior->GetData(&val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -1841,6 +1859,12 @@ nsDownloadManager::SwitchDatabaseTypeTo(enum nsDownloadManager::DatabaseType aTy
|
||||
rv = RestoreDatabaseState();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Notify that the database type changed before resuming current downloads
|
||||
(void)mObserverService->NotifyObservers(
|
||||
static_cast<nsIDownloadManager *>(this),
|
||||
"download-manager-database-type-changed",
|
||||
nsnull);
|
||||
|
||||
rv = RestoreActiveDownloads();
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
|
||||
|
||||
|
@ -106,6 +106,13 @@ function test()
|
||||
if (win)
|
||||
win.close();
|
||||
|
||||
// Ensure that the download manager callbacks and nsIDownloadManagerUI always
|
||||
// use the window UI instead of the panel in the browser's window.
|
||||
Services.prefs.setBoolPref("browser.download.useToolkitUI", true);
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.download.useToolkitUI");
|
||||
});
|
||||
|
||||
// OK, now that all the data is in, let's pull up the UI
|
||||
Cc["@mozilla.org/download-manager-ui;1"].
|
||||
getService(Ci.nsIDownloadManagerUI).show();
|
||||
|
@ -68,6 +68,7 @@ let initTable = [
|
||||
["console", "@mozilla.org/consoleservice;1", "nsIConsoleService"],
|
||||
["contentPrefs", "@mozilla.org/content-pref/service;1", "nsIContentPrefService"],
|
||||
["cookies", "@mozilla.org/cookiemanager;1", "nsICookieManager2"],
|
||||
["downloads", "@mozilla.org/download-manager;1", "nsIDownloadManager"],
|
||||
["droppedLinkHandler", "@mozilla.org/content/dropped-link-handler;1", "nsIDroppedLinkHandler"],
|
||||
["eTLD", "@mozilla.org/network/effective-tld-service;1", "nsIEffectiveTLDService"],
|
||||
["io", "@mozilla.org/network/io-service;1", "nsIIOService2"],
|
||||
|
@ -37,6 +37,7 @@ let dm = Cc["@mozilla.org/download-manager;1"]
|
||||
let os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
let checkDestination = false,
|
||||
@ -114,6 +115,10 @@ let downloadListener = {
|
||||
};
|
||||
|
||||
function init() {
|
||||
// Ensure that the download manager callbacks always use the window UI instead
|
||||
// of the panel in the browser's window.
|
||||
Services.prefs.setBoolPref("browser.download.useToolkitUI", true);
|
||||
|
||||
ww.registerNotification(windowObserver);
|
||||
PlacesUtils.annotations.addObserver(annoObserver, false);
|
||||
dm.addListener(downloadListener);
|
||||
@ -162,6 +167,8 @@ function endTest() {
|
||||
.getMostRecentWindow("Download:Manager")
|
||||
.close();
|
||||
|
||||
Services.prefs.clearUserPref("browser.download.useToolkitUI");
|
||||
|
||||
waitForClearHistory(SimpleTest.finish);
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,8 @@ const kTaskbarID = "@mozilla.org/windows-taskbar;1";
|
||||
const DOWNLOAD_MANAGER_URL = "chrome://mozapps/content/downloads/downloads.xul";
|
||||
const DLMGR_UI_DONE = "download-manager-ui-done";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let DownloadTaskbarProgress, TaskbarService, observerService, wwatch, chromeWindow;
|
||||
let gGen = null;
|
||||
|
||||
@ -86,6 +88,9 @@ function continueTest() {
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
// Ensure that the download manager callbacks always use the window UI instead
|
||||
// of the panel in the browser's window.
|
||||
Services.prefs.setBoolPref("browser.download.useToolkitUI", true);
|
||||
|
||||
let tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
|
||||
@ -144,6 +149,8 @@ function doTest() {
|
||||
// original window still exists
|
||||
checkActiveTaskbar(false, window);
|
||||
|
||||
Services.prefs.clearUserPref("browser.download.useToolkitUI");
|
||||
|
||||
browserWindow.close();
|
||||
SimpleTest.finish();
|
||||
yield;
|
||||
|
@ -55,6 +55,8 @@
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function test()
|
||||
{
|
||||
var dmui = getDMUI();
|
||||
@ -69,9 +71,9 @@ function test()
|
||||
// Empty any old downloads
|
||||
dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
|
||||
|
||||
let setClose = function(aVal) Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch).
|
||||
setBoolPref("browser.download.manager.closeWhenDone", aVal);
|
||||
function setClose(aVal) {
|
||||
Services.prefs.setBoolPref("browser.download.manager.closeWhenDone", aVal);
|
||||
}
|
||||
|
||||
// Close the UI if necessary
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
@ -79,6 +81,10 @@ function test()
|
||||
let win = wm.getMostRecentWindow("Download:Manager");
|
||||
if (win) win.close();
|
||||
|
||||
// Ensure that the download manager callbacks always use the window UI instead
|
||||
// of the panel in the browser's window.
|
||||
Services.prefs.setBoolPref("browser.download.useToolkitUI", true);
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
const DLMGR_UI_DONE = "download-manager-ui-done";
|
||||
@ -101,11 +107,9 @@ function test()
|
||||
setClose(false);
|
||||
os.removeObserver(testObs, DLMGR_UI_DONE);
|
||||
|
||||
try {
|
||||
Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch).
|
||||
clearUserPref("browser.download.manager.closeWhenDone");
|
||||
} catch (err) { }
|
||||
Services.prefs.clearUserPref("browser.download.manager.closeWhenDone");
|
||||
Services.prefs.clearUserPref("browser.download.useToolkitUI");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|