mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-09 12:51:09 +02:00
485 lines
14 KiB
JavaScript
485 lines
14 KiB
JavaScript
/* -*- 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 = {
|
|
/**
|
|
* 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()
|
|
{
|
|
// 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;
|
|
DownloadsIndicatorView.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.
|
|
*
|
|
* @return Anchor element, or null if the indicator is not visible.
|
|
*/
|
|
updatePosition: function DB_updatePosition()
|
|
{
|
|
// Don't update the position of the indicator if it's currently being used
|
|
// as the anchor for the panel. This ensures that the panel doesn't flicker
|
|
// because we move the DOM element to which it's anchored.
|
|
if (!this._anchorRequested) {
|
|
return this._updatePositionInternal();
|
|
}
|
|
},
|
|
|
|
_updatePositionInternal: function DB_updatePositionInternal()
|
|
{
|
|
let indicator = DownloadsIndicatorView.indicator;
|
|
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.
|
|
*
|
|
* @return 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()
|
|
{
|
|
// Do not allow anchoring the panel to the element while customizing.
|
|
if (this._customizing) {
|
|
return null;
|
|
}
|
|
|
|
this._anchorRequested = true;
|
|
return this._updatePositionInternal();
|
|
},
|
|
|
|
/**
|
|
* 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 = {
|
|
_initialized: 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;
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//// 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;
|
|
}
|
|
|
|
if (this._notificationTimeout) {
|
|
clearTimeout(this._notificationTimeout);
|
|
}
|
|
|
|
let indicator = this.indicator;
|
|
indicator.setAttribute("notification", "true");
|
|
this._notificationTimeout = setTimeout(
|
|
function () indicator.removeAttribute("notification"), 1000);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//// 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;
|
|
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._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._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._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._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();
|
|
}
|
|
},
|
|
|
|
get indicator()
|
|
{
|
|
delete this.indicator;
|
|
return this.indicator = document.getElementById("downloads-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");
|
|
}
|
|
};
|