mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 23:37:33 +09:00
Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk
This commit is contained in:
parent
a951f19dfd
commit
177b28a898
1938 changed files with 0 additions and 0 deletions
1559
application/basilisk/components/downloads/DownloadsCommon.jsm
Normal file
1559
application/basilisk/components/downloads/DownloadsCommon.jsm
Normal file
File diff suppressed because it is too large
Load diff
177
application/basilisk/components/downloads/DownloadsTaskbar.jsm
Normal file
177
application/basilisk/components/downloads/DownloadsTaskbar.jsm
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Handles the download progress indicator in the taskbar.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadsTaskbar",
|
||||
];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
|
||||
if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
|
||||
return null;
|
||||
}
|
||||
let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
|
||||
.getService(Ci.nsIWinTaskbar);
|
||||
return winTaskbar.available && winTaskbar;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
|
||||
return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
|
||||
Cc["@mozilla.org/widget/macdocksupport;1"]
|
||||
.getService(Ci.nsITaskbarProgress);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsTaskbar
|
||||
|
||||
/**
|
||||
* Handles the download progress indicator in the taskbar.
|
||||
*/
|
||||
this.DownloadsTaskbar = {
|
||||
/**
|
||||
* Underlying DownloadSummary providing the aggregate download information, or
|
||||
* null if the indicator has never been initialized.
|
||||
*/
|
||||
_summary: null,
|
||||
|
||||
/**
|
||||
* nsITaskbarProgress object to which download information is dispatched.
|
||||
* This can be null if the indicator has never been initialized or if the
|
||||
* indicator is currently hidden on Windows.
|
||||
*/
|
||||
_taskbarProgress: null,
|
||||
|
||||
/**
|
||||
* This method is called after a new browser window is opened, and ensures
|
||||
* that the download progress indicator is displayed in the taskbar.
|
||||
*
|
||||
* On Windows, the indicator is attached to the first browser window that
|
||||
* calls this method. When the window is closed, the indicator is moved to
|
||||
* another browser window, if available, in no particular order. When there
|
||||
* are no browser windows visible, the indicator is hidden.
|
||||
*
|
||||
* On Mac OS X, the indicator is initialized globally when this method is
|
||||
* called for the first time. Subsequent calls have no effect.
|
||||
*
|
||||
* @param aBrowserWindow
|
||||
* nsIDOMWindow object of the newly opened browser window to which the
|
||||
* indicator may be attached.
|
||||
*/
|
||||
registerIndicator(aBrowserWindow) {
|
||||
if (!this._taskbarProgress) {
|
||||
if (gMacTaskbarProgress) {
|
||||
// On Mac OS X, we have to register the global indicator only once.
|
||||
this._taskbarProgress = gMacTaskbarProgress;
|
||||
// Free the XPCOM reference on shutdown, to prevent detecting a leak.
|
||||
Services.obs.addObserver(() => {
|
||||
this._taskbarProgress = null;
|
||||
gMacTaskbarProgress = null;
|
||||
}, "quit-application-granted", false);
|
||||
} else if (gWinTaskbar) {
|
||||
// On Windows, the indicator is currently hidden because we have no
|
||||
// previous browser window, thus we should attach the indicator now.
|
||||
this._attachIndicator(aBrowserWindow);
|
||||
} else {
|
||||
// The taskbar indicator is not available on this platform.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the DownloadSummary object will be created asynchronously.
|
||||
if (!this._summary) {
|
||||
Downloads.getSummary(Downloads.ALL).then(summary => {
|
||||
// In case the method is re-entered, we simply ignore redundant
|
||||
// invocations of the callback, instead of keeping separate state.
|
||||
if (this._summary) {
|
||||
return;
|
||||
}
|
||||
this._summary = summary;
|
||||
return this._summary.addView(this);
|
||||
}).then(null, Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On Windows, attaches the taskbar indicator to the specified browser window.
|
||||
*/
|
||||
_attachIndicator(aWindow) {
|
||||
// Activate the indicator on the specified window.
|
||||
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIXULWindow).docShell;
|
||||
this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
|
||||
|
||||
// If the DownloadSummary object has already been created, we should update
|
||||
// the state of the new indicator, otherwise it will be updated as soon as
|
||||
// the DownloadSummary view is registered.
|
||||
if (this._summary) {
|
||||
this.onSummaryChanged();
|
||||
}
|
||||
|
||||
aWindow.addEventListener("unload", () => {
|
||||
// Locate another browser window, excluding the one being closed.
|
||||
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
|
||||
if (browserWindow) {
|
||||
// Move the progress indicator to the other browser window.
|
||||
this._attachIndicator(browserWindow);
|
||||
} else {
|
||||
// The last browser window has been closed. We remove the reference to
|
||||
// the taskbar progress object so that the indicator will be registered
|
||||
// again on the next browser window that is opened.
|
||||
this._taskbarProgress = null;
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadSummary view
|
||||
|
||||
onSummaryChanged() {
|
||||
// If the last browser window has been closed, we have no indicator any more.
|
||||
if (!this._taskbarProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
|
||||
this._taskbarProgress.setProgressState(
|
||||
Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
|
||||
} else {
|
||||
// For a brief moment before completion, some download components may
|
||||
// report more transferred bytes than the total number of bytes. Thus,
|
||||
// ensure that we never break the expectations of the progress indicator.
|
||||
let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
|
||||
this._summary.progressCurrentBytes);
|
||||
this._taskbarProgress.setProgressState(
|
||||
Ci.nsITaskbarProgress.STATE_NORMAL,
|
||||
progressCurrentBytes,
|
||||
this._summary.progressTotalBytes);
|
||||
}
|
||||
},
|
||||
};
|
||||
395
application/basilisk/components/downloads/DownloadsViewUI.jsm
Normal file
395
application/basilisk/components/downloads/DownloadsViewUI.jsm
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
/* 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 module is imported by code that uses the "download.xml" binding, and
|
||||
* provides prototypes for objects that handle input and display information.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadsViewUI",
|
||||
];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||
"resource://gre/modules/DownloadUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
|
||||
this.DownloadsViewUI = {
|
||||
/**
|
||||
* Returns true if the given string is the name of a command that can be
|
||||
* handled by the Downloads user interface, including standard commands.
|
||||
*/
|
||||
isCommandName(name) {
|
||||
return name.startsWith("cmd_") || name.startsWith("downloadsCmd_");
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A download element shell is responsible for handling the commands and the
|
||||
* displayed data for a single element that uses the "download.xml" binding.
|
||||
*
|
||||
* The information to display is obtained through the associated Download object
|
||||
* from the JavaScript API for downloads, and commands are executed using a
|
||||
* combination of Download methods and DownloadsCommon.jsm helper functions.
|
||||
*
|
||||
* Specialized versions of this shell must be defined, and they are required to
|
||||
* implement the "download" property or getter. Currently these objects are the
|
||||
* HistoryDownloadElementShell and the DownloadsViewItem for the panel. The
|
||||
* history view may use a HistoryDownload object in place of a Download object.
|
||||
*/
|
||||
this.DownloadsViewUI.DownloadElementShell = function () {}
|
||||
|
||||
this.DownloadsViewUI.DownloadElementShell.prototype = {
|
||||
/**
|
||||
* The richlistitem for the download, initialized by the derived object.
|
||||
*/
|
||||
element: null,
|
||||
|
||||
/**
|
||||
* URI string for the file type icon displayed in the download element.
|
||||
*/
|
||||
get image() {
|
||||
if (!this.download.target.path) {
|
||||
// Old history downloads may not have a target path.
|
||||
return "moz-icon://.unknown?size=32";
|
||||
}
|
||||
|
||||
// When a download that was previously in progress finishes successfully, it
|
||||
// means that the target file now exists and we can extract its specific
|
||||
// icon, for example from a Windows executable. To ensure that the icon is
|
||||
// reloaded, however, we must change the URI used by the XUL image element,
|
||||
// for example by adding a query parameter. This only works if we add one of
|
||||
// the parameters explicitly supported by the nsIMozIconURI interface.
|
||||
return "moz-icon://" + this.download.target.path + "?size=32" +
|
||||
(this.download.succeeded ? "&state=normal" : "");
|
||||
},
|
||||
|
||||
/**
|
||||
* The user-facing label for the download. This is normally the leaf name of
|
||||
* the download target file. In case this is a very old history download for
|
||||
* which the target file is unknown, the download source URI is displayed.
|
||||
*/
|
||||
get displayName() {
|
||||
if (!this.download.target.path) {
|
||||
return this.download.source.url;
|
||||
}
|
||||
return OS.Path.basename(this.download.target.path);
|
||||
},
|
||||
|
||||
/**
|
||||
* The progress element for the download, or undefined in case the XBL binding
|
||||
* has not been applied yet.
|
||||
*/
|
||||
get _progressElement() {
|
||||
if (!this.__progressElement) {
|
||||
// If the element is not available now, we will try again the next time.
|
||||
this.__progressElement =
|
||||
this.element.ownerDocument.getAnonymousElementByAttribute(
|
||||
this.element, "anonid",
|
||||
"progressmeter");
|
||||
}
|
||||
return this.__progressElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* Processes a major state change in the user interface, then proceeds with
|
||||
* the normal progress update. This function is not called for every progress
|
||||
* update in order to improve performance.
|
||||
*/
|
||||
_updateState() {
|
||||
this.element.setAttribute("displayName", this.displayName);
|
||||
this.element.setAttribute("image", this.image);
|
||||
this.element.setAttribute("state",
|
||||
DownloadsCommon.stateOfDownload(this.download));
|
||||
|
||||
if (!this.download.succeeded && this.download.error &&
|
||||
this.download.error.becauseBlockedByReputationCheck) {
|
||||
this.element.setAttribute("verdict",
|
||||
this.download.error.reputationCheckVerdict);
|
||||
} else {
|
||||
this.element.removeAttribute("verdict");
|
||||
}
|
||||
|
||||
// Since state changed, reset the time left estimation.
|
||||
this.lastEstimatedSecondsLeft = Infinity;
|
||||
|
||||
this._updateProgress();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the elements that change regularly for in-progress downloads,
|
||||
* namely the progress bar and the status line.
|
||||
*/
|
||||
_updateProgress() {
|
||||
if (this.download.succeeded) {
|
||||
// We only need to add or remove this attribute for succeeded downloads.
|
||||
if (this.download.target.exists) {
|
||||
this.element.setAttribute("exists", "true");
|
||||
} else {
|
||||
this.element.removeAttribute("exists");
|
||||
}
|
||||
}
|
||||
|
||||
// When a block is confirmed, the removal of blocked data will not trigger a
|
||||
// state change for the download, so this class must be updated here.
|
||||
this.element.classList.toggle("temporary-block",
|
||||
!!this.download.hasBlockedData);
|
||||
|
||||
// The progress bar is only displayed for in-progress downloads.
|
||||
if (this.download.hasProgress) {
|
||||
this.element.setAttribute("progressmode", "normal");
|
||||
this.element.setAttribute("progress", this.download.progress);
|
||||
} else {
|
||||
this.element.setAttribute("progressmode", "undetermined");
|
||||
}
|
||||
|
||||
if (this.download.stopped && this.download.canceled &&
|
||||
this.download.hasPartialData) {
|
||||
this.element.setAttribute("progresspaused", "true");
|
||||
} else {
|
||||
this.element.removeAttribute("progresspaused");
|
||||
}
|
||||
|
||||
// Dispatch the ValueChange event for accessibility, if possible.
|
||||
if (this._progressElement) {
|
||||
let event = this.element.ownerDocument.createEvent("Events");
|
||||
event.initEvent("ValueChange", true, true);
|
||||
this._progressElement.dispatchEvent(event);
|
||||
}
|
||||
|
||||
let status = this.statusTextAndTip;
|
||||
this.element.setAttribute("status", status.text);
|
||||
this.element.setAttribute("statusTip", status.tip);
|
||||
},
|
||||
|
||||
lastEstimatedSecondsLeft: Infinity,
|
||||
|
||||
/**
|
||||
* Returns the text for the status line and the associated tooltip. These are
|
||||
* returned by a single property because they are computed together. The
|
||||
* result may be overridden by derived objects.
|
||||
*/
|
||||
get statusTextAndTip() {
|
||||
return this.rawStatusTextAndTip;
|
||||
},
|
||||
|
||||
/**
|
||||
* Derived objects may call this to get the status text.
|
||||
*/
|
||||
get rawStatusTextAndTip() {
|
||||
let s = DownloadsCommon.strings;
|
||||
|
||||
let text = "";
|
||||
let tip = "";
|
||||
|
||||
if (!this.download.stopped) {
|
||||
let totalBytes = this.download.hasProgress ? this.download.totalBytes
|
||||
: -1;
|
||||
// By default, extended status information including the individual
|
||||
// download rate is displayed in the tooltip. The history view overrides
|
||||
// the getter and displays the datails in the main area instead.
|
||||
[text] = DownloadUtils.getDownloadStatusNoRate(
|
||||
this.download.currentBytes,
|
||||
totalBytes,
|
||||
this.download.speed,
|
||||
this.lastEstimatedSecondsLeft);
|
||||
let newEstimatedSecondsLeft;
|
||||
[tip, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus(
|
||||
this.download.currentBytes,
|
||||
totalBytes,
|
||||
this.download.speed,
|
||||
this.lastEstimatedSecondsLeft);
|
||||
this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
|
||||
} else if (this.download.canceled && this.download.hasPartialData) {
|
||||
let totalBytes = this.download.hasProgress ? this.download.totalBytes
|
||||
: -1;
|
||||
let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes,
|
||||
totalBytes);
|
||||
|
||||
// We use the same XUL label to display both the state and the amount
|
||||
// transferred, for example "Paused - 1.1 MB".
|
||||
text = s.statusSeparatorBeforeNumber(s.statePaused, transfer);
|
||||
} else if (!this.download.succeeded && !this.download.canceled &&
|
||||
!this.download.error) {
|
||||
text = s.stateStarting;
|
||||
} else {
|
||||
let stateLabel;
|
||||
|
||||
if (this.download.succeeded) {
|
||||
// For completed downloads, show the file size (e.g. "1.5 MB").
|
||||
if (this.download.target.size !== undefined) {
|
||||
let [size, unit] =
|
||||
DownloadUtils.convertByteUnits(this.download.target.size);
|
||||
stateLabel = s.sizeWithUnits(size, unit);
|
||||
} else {
|
||||
// History downloads may not have a size defined.
|
||||
stateLabel = s.sizeUnknown;
|
||||
}
|
||||
} else if (this.download.canceled) {
|
||||
stateLabel = s.stateCanceled;
|
||||
} else if (this.download.error.becauseBlockedByParentalControls) {
|
||||
stateLabel = s.stateBlockedParentalControls;
|
||||
} else if (this.download.error.becauseBlockedByReputationCheck) {
|
||||
stateLabel = this.rawBlockedTitleAndDetails[0];
|
||||
} else {
|
||||
stateLabel = s.stateFailed;
|
||||
}
|
||||
|
||||
let referrer = this.download.source.referrer || this.download.source.url;
|
||||
let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer);
|
||||
|
||||
let date = new Date(this.download.endTime);
|
||||
let [displayDate, fullDate] = DownloadUtils.getReadableDates(date);
|
||||
|
||||
let firstPart = s.statusSeparator(stateLabel, displayHost);
|
||||
text = s.statusSeparator(firstPart, displayDate);
|
||||
tip = s.statusSeparator(fullHost, fullDate);
|
||||
}
|
||||
|
||||
return { text, tip: tip || text };
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns [title, [details1, details2]] for blocked downloads.
|
||||
*/
|
||||
get rawBlockedTitleAndDetails() {
|
||||
let s = DownloadsCommon.strings;
|
||||
if (!this.download.error ||
|
||||
!this.download.error.becauseBlockedByReputationCheck) {
|
||||
return [null, null];
|
||||
}
|
||||
switch (this.download.error.reputationCheckVerdict) {
|
||||
case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
|
||||
return [s.blockedUncommon2, [s.unblockTypeUncommon2, s.unblockTip2]];
|
||||
case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
|
||||
return [s.blockedPotentiallyUnwanted,
|
||||
[s.unblockTypePotentiallyUnwanted2, s.unblockTip2]];
|
||||
case Downloads.Error.BLOCK_VERDICT_MALWARE:
|
||||
return [s.blockedMalware, [s.unblockTypeMalware, s.unblockTip2]];
|
||||
}
|
||||
throw new Error("Unexpected reputationCheckVerdict: " +
|
||||
this.download.error.reputationCheckVerdict);
|
||||
// return anyway to avoid a JS strict warning.
|
||||
return [null, null];
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the appropriate unblock dialog based on the verdict, and executes the
|
||||
* action selected by the user in the dialog, which may involve unblocking,
|
||||
* opening or removing the file.
|
||||
*
|
||||
* @param window
|
||||
* The window to which the dialog should be anchored.
|
||||
* @param dialogType
|
||||
* Can be "unblock", "chooseUnblock", or "chooseOpen".
|
||||
*/
|
||||
confirmUnblock(window, dialogType) {
|
||||
DownloadsCommon.confirmUnblockDownload({
|
||||
verdict: this.download.error.reputationCheckVerdict,
|
||||
window,
|
||||
dialogType,
|
||||
}).then(action => {
|
||||
if (action == "open") {
|
||||
return this.unblockAndOpenDownload();
|
||||
} else if (action == "unblock") {
|
||||
return this.download.unblock();
|
||||
} else if (action == "confirmBlock") {
|
||||
return this.download.confirmBlock();
|
||||
}
|
||||
}).catch(Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unblocks the downloaded file and opens it.
|
||||
*
|
||||
* @return A promise that's resolved after the file has been opened.
|
||||
*/
|
||||
unblockAndOpenDownload() {
|
||||
return this.download.unblock().then(() => this.downloadsCmd_open());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the name of the default command to use for the current state of the
|
||||
* download, when there is a double click or another default interaction. If
|
||||
* there is no default command for the current state, returns an empty string.
|
||||
* The commands are implemented as functions on this object or derived ones.
|
||||
*/
|
||||
get currentDefaultCommandName() {
|
||||
switch (DownloadsCommon.stateOfDownload(this.download)) {
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED:
|
||||
return "downloadsCmd_cancel";
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
|
||||
return "downloadsCmd_retry";
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
|
||||
return "downloadsCmd_pauseResume";
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
|
||||
return "downloadsCmd_open";
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
|
||||
return "downloadsCmd_openReferrer";
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
|
||||
return "downloadsCmd_showBlockedInfo";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the specified command can be invoked on the current item.
|
||||
* The commands are implemented as functions on this object or derived ones.
|
||||
*
|
||||
* @param aCommand
|
||||
* Name of the command to check, for example "downloadsCmd_retry".
|
||||
*/
|
||||
isCommandEnabled(aCommand) {
|
||||
switch (aCommand) {
|
||||
case "downloadsCmd_retry":
|
||||
return this.download.canceled || this.download.error;
|
||||
case "downloadsCmd_pauseResume":
|
||||
return this.download.hasPartialData && !this.download.error;
|
||||
case "downloadsCmd_openReferrer":
|
||||
return !!this.download.source.referrer;
|
||||
case "downloadsCmd_confirmBlock":
|
||||
case "downloadsCmd_chooseUnblock":
|
||||
case "downloadsCmd_chooseOpen":
|
||||
case "downloadsCmd_unblock":
|
||||
case "downloadsCmd_unblockAndOpen":
|
||||
return this.download.hasBlockedData;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
downloadsCmd_cancel() {
|
||||
// This is the correct way to avoid race conditions when cancelling.
|
||||
this.download.cancel().catch(() => {});
|
||||
this.download.removePartialData().catch(Cu.reportError);
|
||||
},
|
||||
|
||||
downloadsCmd_retry() {
|
||||
// Errors when retrying are already reported as download failures.
|
||||
this.download.start().catch(() => {});
|
||||
},
|
||||
|
||||
downloadsCmd_pauseResume() {
|
||||
if (this.download.stopped) {
|
||||
this.download.start();
|
||||
} else {
|
||||
this.download.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
downloadsCmd_confirmBlock() {
|
||||
this.download.confirmBlock().catch(Cu.reportError);
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,131 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
# 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/allDownloadsViewOverlay.css"?>
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
|
||||
%downloadsDTD;
|
||||
]>
|
||||
|
||||
<!-- This overlay provides a downloads view that lists both session downloads,
|
||||
using the DownloadsView API, and history downloads, using places queries.
|
||||
The view also implements a command controller and a context menu for
|
||||
managing the downloads list. In order to use this view:
|
||||
1. Apply this overlay to your window.
|
||||
2. Insert in all the overlay entry-points, namely:
|
||||
<richlistbox id="downloadsRichListBox"/>
|
||||
<commandset id="downloadCommands"/>
|
||||
<menupopup id="downloadsContextMenu"/>
|
||||
3. Make sure your window has the editMenuOverlay overlay applied,
|
||||
because the view implements cmd_copy and cmd_delete.
|
||||
4. Make sure your window has the globalOverlay.js script loaded.
|
||||
5. To initialize the view
|
||||
let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
|
||||
// This is what the Places Library uses. It could be tweaked a bit as long as the
|
||||
// transition-type is set correctly
|
||||
view.place = "place:transition=7&sort=4";
|
||||
-->
|
||||
<overlay id="downloadsViewOverlay"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/downloads/allDownloadsViewOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/contentAreaUtils.js"/>
|
||||
|
||||
<richlistbox flex="1"
|
||||
seltype="multiple"
|
||||
id="downloadsRichListBox" context="downloadsContextMenu"
|
||||
onscroll="return this._placesView.onScroll();"
|
||||
onkeypress="return this._placesView.onKeyPress(event);"
|
||||
ondblclick="return this._placesView.onDoubleClick(event);"
|
||||
oncontextmenu="return this._placesView.onContextMenu(event);"
|
||||
ondragstart="this._placesView.onDragStart(event);"
|
||||
ondragover="this._placesView.onDragOver(event);"
|
||||
ondrop="this._placesView.onDrop(event);"
|
||||
onfocus="goUpdateDownloadCommands();"
|
||||
onselect="this._placesView.onSelect();"
|
||||
onblur="goUpdateDownloadCommands();"/>
|
||||
|
||||
<commandset id="downloadCommands"
|
||||
commandupdater="true"
|
||||
events="focus,select,contextmenu"
|
||||
oncommandupdate="goUpdateDownloadCommands();">
|
||||
<command id="downloadsCmd_pauseResume"
|
||||
oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
|
||||
<command id="downloadsCmd_cancel"
|
||||
oncommand="goDoCommand('downloadsCmd_cancel')"/>
|
||||
<command id="downloadsCmd_unblock"
|
||||
oncommand="goDoCommand('downloadsCmd_unblock')"/>
|
||||
<command id="downloadsCmd_chooseUnblock"
|
||||
oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
|
||||
<command id="downloadsCmd_chooseOpen"
|
||||
oncommand="goDoCommand('downloadsCmd_chooseOpen')"/>
|
||||
<command id="downloadsCmd_confirmBlock"
|
||||
oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
|
||||
<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_clearDownloads"
|
||||
oncommand="goDoCommand('downloadsCmd_clearDownloads')"/>
|
||||
</commandset>
|
||||
|
||||
<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="downloadsCmd_unblock"
|
||||
class="downloadUnblockMenuItem"
|
||||
label="&cmd.unblock2.label;"
|
||||
accesskey="&cmd.unblock2.accesskey;"/>
|
||||
<menuitem command="cmd_delete"
|
||||
class="downloadRemoveFromHistoryMenuItem"
|
||||
label="&cmd.removeFromHistory.label;"
|
||||
accesskey="&cmd.removeFromHistory.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="cmd_copy"
|
||||
label="&cmd.copyDownloadLink.label;"
|
||||
accesskey="&cmd.copyDownloadLink.accesskey;"/>
|
||||
|
||||
<menuseparator/>
|
||||
|
||||
<menuitem command="downloadsCmd_clearDownloads"
|
||||
label="&cmd.clearDownloads.label;"
|
||||
accesskey="&cmd.clearDownloads.accesskey;"/>
|
||||
</menupopup>
|
||||
</overlay>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/* 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/. */
|
||||
|
||||
#downloadsListEmptyDescription {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#downloadsRichListBox:empty + #downloadsListEmptyDescription {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
var ContentAreaDownloadsView = {
|
||||
init() {
|
||||
let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
|
||||
// Do not display the Places downloads in private windows
|
||||
if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
|
||||
view.place = "place:transition=7&sort=4";
|
||||
}
|
||||
// Set focus to Downloads list once it is created
|
||||
document.getElementById("downloadsRichListBox").focus();
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
# 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://global/skin/"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/downloads/contentAreaDownloadsView.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/downloads/contentAreaDownloadsView.css"?>
|
||||
|
||||
<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
|
||||
|
||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
|
||||
%downloadsDTD;
|
||||
]>
|
||||
|
||||
<window id="contentAreaDownloadsView"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&downloads.title;"
|
||||
onload="ContentAreaDownloadsView.init();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/globalOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/downloads/contentAreaDownloadsView.js"/>
|
||||
|
||||
<commandset id="editMenuCommands"/>
|
||||
|
||||
<keyset id="editMenuKeys">
|
||||
#ifdef XP_MACOSX
|
||||
<key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/>
|
||||
#endif
|
||||
</keyset>
|
||||
|
||||
<stack flex="1">
|
||||
<richlistbox id="downloadsRichListBox"/>
|
||||
<description id="downloadsListEmptyDescription"
|
||||
value="&downloadsListEmpty.label;"
|
||||
mousethrough="always"/>
|
||||
</stack>
|
||||
<commandset id="downloadCommands"/>
|
||||
<menupopup id="downloadsContextMenu"/>
|
||||
</window>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?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">
|
||||
<content orient="horizontal"
|
||||
onclick="DownloadsView.onDownloadClick(event);">
|
||||
<xul:hbox class="downloadMainArea"
|
||||
flex="1"
|
||||
align="center">
|
||||
<xul:stack>
|
||||
<xul:image class="downloadTypeIcon"
|
||||
validate="always"
|
||||
xbl:inherits="src=image"/>
|
||||
<xul:image class="downloadBlockedBadge" />
|
||||
</xul:stack>
|
||||
<xul:vbox pack="center"
|
||||
flex="1"
|
||||
class="downloadContainer"
|
||||
style="width: &downloadDetails.width;">
|
||||
<!-- We're letting localizers put a min-width in here primarily
|
||||
because of the downloads summary at the bottom of the list of
|
||||
download items. An element in the summary has the same min-width
|
||||
on a description, and we don't want the panel to change size if the
|
||||
summary isn't being displayed, so we ensure that items share the
|
||||
same minimum width.
|
||||
-->
|
||||
<xul:description class="downloadTarget"
|
||||
crop="center"
|
||||
style="min-width: &downloadsSummary.minWidth2;"
|
||||
xbl:inherits="value=displayName,tooltiptext=displayName"/>
|
||||
<xul:progressmeter anonid="progressmeter"
|
||||
class="downloadProgress"
|
||||
min="0"
|
||||
max="100"
|
||||
xbl:inherits="mode=progressmode,value=progress,paused=progresspaused"/>
|
||||
<xul:description class="downloadDetails"
|
||||
crop="end"
|
||||
xbl:inherits="value=status,tooltiptext=statusTip"/>
|
||||
</xul:vbox>
|
||||
</xul:hbox>
|
||||
<xul:toolbarseparator />
|
||||
<xul:stack class="downloadButtonArea">
|
||||
<xul:button class="downloadButton downloadCancel downloadIconCancel"
|
||||
tooltiptext="&cmd.cancel.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
|
||||
<xul:button class="downloadButton downloadRetry downloadIconRetry"
|
||||
tooltiptext="&cmd.retry.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
|
||||
<xul:button class="downloadButton downloadShow downloadIconShow"
|
||||
#ifdef XP_MACOSX
|
||||
tooltiptext="&cmd.showMac.label;"
|
||||
#else
|
||||
tooltiptext="&cmd.show.label;"
|
||||
#endif
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
|
||||
<xul:button class="downloadButton downloadConfirmBlock downloadIconCancel"
|
||||
tooltiptext="&cmd.removeFile.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_confirmBlock');"/>
|
||||
<xul:button class="downloadButton downloadChooseUnblock downloadIconShow"
|
||||
tooltiptext="&cmd.chooseUnblock.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseUnblock');"/>
|
||||
<xul:button class="downloadButton downloadChooseOpen downloadIconShow"
|
||||
tooltiptext="&cmd.chooseOpen.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseOpen');"/>
|
||||
<xul:button class="downloadButton downloadShowBlockedInfo"
|
||||
tooltiptext="&cmd.chooseUnblock.label;"
|
||||
oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_showBlockedInfo');"/>
|
||||
</xul:stack>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="download-toolbarbutton"
|
||||
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-badged">
|
||||
<content>
|
||||
<xul:stack class="toolbarbutton-badge-stack">
|
||||
<children />
|
||||
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
|
||||
<xul:label class="toolbarbutton-badge" xbl:inherits="value=badge" top="0" end="0" crop="none"/>
|
||||
</xul:stack>
|
||||
<xul:label class="toolbarbutton-text" crop="right" flex="1"
|
||||
xbl:inherits="value=label,accesskey,crop,wrap"/>
|
||||
<xul:label class="toolbarbutton-multiline-text" flex="1"
|
||||
xbl:inherits="xbl:text=label,accesskey,wrap"/>
|
||||
</content>
|
||||
</binding>
|
||||
</bindings>
|
||||
267
application/basilisk/components/downloads/content/downloads.css
Normal file
267
application/basilisk/components/downloads/content/downloads.css
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/* 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/. */
|
||||
|
||||
/*** Downloads Panel ***/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.downloadsHideDropmarker > #downloadsFooterButtonsSplitter,
|
||||
.downloadsHideDropmarker > #downloadsFooterDropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
richlistitem[type="download"].download-state[state="1"]:not([exists]) > .downloadButtonArea,
|
||||
richlistitem[type="download"].download-state[state="1"]:not([exists]) > toolbarseparator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
|
||||
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
|
||||
#downloadsFooter:not([showingsummary]) #downloadsSummary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#downloadsFooter[showingdropdown] > stack > #downloadsSummary,
|
||||
#downloadsFooter[showingsummary] > stack:hover > #downloadsSummary,
|
||||
#downloadsFooter[showingsummary]:not([showingdropdown]) > stack:not(:hover) > #downloadsFooterButtons {
|
||||
/* If we used "visibility: hidden;" then the mouseenter event of
|
||||
#downloadsHistory wouldn't be triggered immediately, and the hover styling
|
||||
of the button would not apply until the mouse is moved again.
|
||||
|
||||
"-moz-user-focus: ignore;" prevents the elements with "opacity: 0;" from
|
||||
being focused with the keyboard. */
|
||||
opacity: 0;
|
||||
-moz-user-focus: ignore;
|
||||
}
|
||||
|
||||
/*** Downloads View ***/
|
||||
|
||||
/**
|
||||
* The downloads richlistbox may list thousands of items, and it turns out
|
||||
* XBL binding attachment, and even more so detachment, is a performance hog.
|
||||
* This hack makes sure we don't apply any binding to inactive items (inactive
|
||||
* items are history downloads that haven't been in the visible area).
|
||||
* We can do this because the richlistbox implementation does not interact
|
||||
* much with the richlistitem binding. However, this may turn out to have
|
||||
* some side effects (see bug 828111 for the details).
|
||||
*
|
||||
* We might be able to do away with this workaround once bug 653881 is fixed.
|
||||
*/
|
||||
richlistitem.download {
|
||||
-moz-binding: none;
|
||||
}
|
||||
|
||||
richlistitem.download[active] {
|
||||
-moz-binding: url("chrome://browser/content/downloads/download.xml#download");
|
||||
}
|
||||
|
||||
richlistitem.download button {
|
||||
/* These buttons should never get focus, as that would "disable"
|
||||
the downloads view controller (it's only used when the richlistbox
|
||||
is focused). */
|
||||
-moz-user-focus: none;
|
||||
}
|
||||
|
||||
/*** Visibility of controls inside download items ***/
|
||||
.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
|
||||
[state="8"], /* Blocked (dirty) */
|
||||
[state="9"]) /* Blocked (policy) */)
|
||||
.downloadBlockedBadge,
|
||||
|
||||
.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,
|
||||
|
||||
/* Blocked (dirty) downloads that have not been confirmed and
|
||||
have temporary data. */
|
||||
.download-state:not( [state="8"] /* Blocked (dirty) */)
|
||||
.downloadUnblockMenuItem,
|
||||
.download-state[state="8"]:not(.temporary-block)
|
||||
.downloadUnblockMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="2"], /* Failed */
|
||||
[state="4"]) /* Paused */)
|
||||
.downloadCancelMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="1"], /* Finished */
|
||||
[state="2"], /* Failed */
|
||||
[state="3"], /* Canceled */
|
||||
[state="6"], /* Blocked (parental) */
|
||||
[state="8"], /* Blocked (dirty) */
|
||||
[state="9"]) /* Blocked (policy) */)
|
||||
.downloadRemoveFromHistoryMenuItem,
|
||||
|
||||
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
|
||||
[state="0"], /* Downloading */
|
||||
[state="1"], /* Finished */
|
||||
[state="4"], /* Paused */
|
||||
[state="5"]) /* Starting (queued) */)
|
||||
.downloadShowMenuItem,
|
||||
|
||||
.download-state[state="7"] .downloadCommandsSeparator
|
||||
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*** Visibility of download buttons ***/
|
||||
|
||||
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
|
||||
[state="5"], /* Starting (queued) */
|
||||
[state="0"], /* Downloading */
|
||||
[state="4"]) /* Paused */)
|
||||
.downloadCancel,
|
||||
|
||||
/* Blocked (dirty) downloads that have not been confirmed and
|
||||
have temporary data, for the Malware case. */
|
||||
.download-state:not( [state="8"] /* Blocked (dirty) */)
|
||||
.downloadConfirmBlock,
|
||||
.download-state[state="8"]:not(.temporary-block)
|
||||
.downloadConfirmBlock,
|
||||
.download-state[state="8"].temporary-block:not([verdict="Malware"])
|
||||
.downloadConfirmBlock,
|
||||
|
||||
/* Blocked (dirty) downloads that have not been confirmed and
|
||||
have temporary data, for the Potentially Unwanted case. */
|
||||
.download-state:not( [state="8"] /* Blocked (dirty) */)
|
||||
.downloadChooseUnblock,
|
||||
.download-state[state="8"]:not(.temporary-block)
|
||||
.downloadChooseUnblock,
|
||||
.download-state[state="8"].temporary-block:not([verdict="PotentiallyUnwanted"])
|
||||
.downloadChooseUnblock,
|
||||
|
||||
/* Blocked (dirty) downloads that have not been confirmed and
|
||||
have temporary data, for the Uncommon case. */
|
||||
.download-state:not( [state="8"] /* Blocked (dirty) */)
|
||||
.downloadChooseOpen,
|
||||
.download-state[state="8"]:not(.temporary-block)
|
||||
.downloadChooseOpen,
|
||||
.download-state[state="8"].temporary-block:not([verdict="Uncommon"])
|
||||
.downloadChooseOpen,
|
||||
|
||||
.download-state:not(:-moz-any([state="2"], /* Failed */
|
||||
[state="3"]) /* Canceled */)
|
||||
.downloadRetry,
|
||||
|
||||
.download-state:not( [state="1"] /* Finished */)
|
||||
.downloadShow,
|
||||
|
||||
.download-state:-moz-any( [state="6"], /* Blocked (parental) */
|
||||
[state="7"], /* Scanning */
|
||||
[state="9"]) /* Blocked (policy) */
|
||||
> toolbarseparator,
|
||||
|
||||
/* The "show blocked info" button is shown only in the downloads panel. */
|
||||
.downloadShowBlockedInfo
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*** Downloads panel ***/
|
||||
|
||||
#downloadsPanel[hasdownloads] #emptyDownloads,
|
||||
#downloadsPanel:not([hasdownloads]) #downloadsListBox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*** Downloads panel multiview (main view and blocked-downloads subview) ***/
|
||||
|
||||
/* Hide all the usual buttons. */
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadCancel,
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadShow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Make the panel wide enough to show the download list items without improperly
|
||||
truncating them. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack > .panel-mainview {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
/* Show the "show blocked info" button. */
|
||||
#downloadsPanel-mainView .download-state[state="8"] .downloadShowBlockedInfo {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** When the main view is showing... **/
|
||||
|
||||
/* The subview should be off to the right and not visible at all. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=main] > .panel-subviews {
|
||||
transform: translateX(101%);
|
||||
transition: transform var(--panelui-subview-transition-duration);
|
||||
}
|
||||
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=main] > .panel-subviews:-moz-locale-dir(rtl) {
|
||||
transform: translateX(-101%);
|
||||
}
|
||||
|
||||
/** When the subview is showing... **/
|
||||
|
||||
/* Hide the buttons of all downloads except the one that triggered the
|
||||
subview. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state:not([showingsubview]) .downloadButton {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* For the download that triggered the subview, move its button farther to the
|
||||
right by removing padding so that a minimum amount of the main view's right
|
||||
edge needs to be shown. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* The main view should slide to the left and its right edge should remain
|
||||
visible. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=subview] > .panel-mainview {
|
||||
transform: translateX(calc(-100% + 38px));
|
||||
transition: transform var(--panelui-subview-transition-duration);
|
||||
}
|
||||
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=subview] > .panel-mainview:-moz-locale-dir(rtl) {
|
||||
transform: translateX(calc(100% - 38px));
|
||||
}
|
||||
|
||||
/* The subview should leave the right edge of the main view uncovered. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
|
||||
/* Use a margin instead of a transform like above so that the subview's width
|
||||
isn't wider than the panel. */
|
||||
-moz-margin-start: 38px !important;
|
||||
}
|
||||
|
||||
/* Prevent keyboard interaction in the main view by preventing all elements in
|
||||
the main view from being focused... */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] > .panel-mainview #downloadsListBox,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] > .panel-mainview richlistitem,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] > .panel-mainview .downloadButton,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] > .panel-mainview .downloadsPanelFooterButton,
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] > .panel-mainview #downloadsSummary {
|
||||
-moz-user-focus: ignore;
|
||||
}
|
||||
/* ... except for the downloadShowBlockedInfo button in the blocked download.
|
||||
Selecting it with the keyboard should show the main view again. */
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadShowBlockedInfo {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
1732
application/basilisk/components/downloads/content/downloads.js
Normal file
1732
application/basilisk/components/downloads/content/downloads.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,210 @@
|
|||
<?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_unblock"
|
||||
oncommand="goDoCommand('downloadsCmd_unblock')"/>
|
||||
<command id="downloadsCmd_chooseUnblock"
|
||||
oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
|
||||
<command id="downloadsCmd_unblockAndOpen"
|
||||
oncommand="goDoCommand('downloadsCmd_unblockAndOpen')"/>
|
||||
<command id="downloadsCmd_confirmBlock"
|
||||
oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
|
||||
<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 id="mainPopupSet">
|
||||
<!-- 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"
|
||||
onpopupshown="DownloadsView.onContextPopupShown(event);"
|
||||
onpopuphidden="DownloadsView.onContextPopupHidden(event);"
|
||||
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="downloadsCmd_unblock"
|
||||
class="downloadUnblockMenuItem"
|
||||
label="&cmd.unblock2.label;"
|
||||
accesskey="&cmd.unblock2.accesskey;"/>
|
||||
<menuitem command="cmd_delete"
|
||||
class="downloadRemoveFromHistoryMenuItem"
|
||||
label="&cmd.removeFromHistory.label;"
|
||||
accesskey="&cmd.removeFromHistory.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.clearList2.label;"
|
||||
accesskey="&cmd.clearList2.accesskey;"/>
|
||||
</menupopup>
|
||||
|
||||
<panelmultiview id="downloadsPanel-multiView"
|
||||
mainViewId="downloadsPanel-mainView"
|
||||
align="stretch">
|
||||
|
||||
<panelview id="downloadsPanel-mainView"
|
||||
flex="1"
|
||||
align="stretch">
|
||||
<richlistbox id="downloadsListBox"
|
||||
context="downloadsContextMenu"
|
||||
onmouseover="DownloadsView.onDownloadMouseOver(event);"
|
||||
onmouseout="DownloadsView.onDownloadMouseOut(event);"
|
||||
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
|
||||
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
|
||||
<description id="emptyDownloads"
|
||||
mousethrough="always">
|
||||
&downloadsPanelEmpty.label;
|
||||
</description>
|
||||
<spacer flex="1"/>
|
||||
<vbox id="downloadsFooter"
|
||||
class="downloadsPanelFooter">
|
||||
<stack>
|
||||
<hbox id="downloadsSummary"
|
||||
align="center"
|
||||
orient="horizontal"
|
||||
onkeydown="DownloadsSummary.onKeyDown(event);"
|
||||
onclick="DownloadsSummary.onClick(event);">
|
||||
<image class="downloadTypeIcon" />
|
||||
<vbox pack="center"
|
||||
class="downloadContainer"
|
||||
style="width: &downloadDetails.width;">
|
||||
<description id="downloadsSummaryDescription"
|
||||
style="min-width: &downloadsSummary.minWidth2;"/>
|
||||
<progressmeter id="downloadsSummaryProgress"
|
||||
class="downloadProgress"
|
||||
min="0"
|
||||
max="100"
|
||||
mode="normal" />
|
||||
<description id="downloadsSummaryDetails"
|
||||
crop="end"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<hbox id="downloadsFooterButtons">
|
||||
<button id="downloadsHistory"
|
||||
class="downloadsPanelFooterButton"
|
||||
label="&downloadsHistory.label;"
|
||||
accesskey="&downloadsHistory.accesskey;"
|
||||
flex="1"
|
||||
oncommand="DownloadsPanel.showDownloadsHistory();"/>
|
||||
<toolbarseparator id="downloadsFooterButtonsSplitter"
|
||||
class="downloadsDropmarkerSplitter"/>
|
||||
<button id="downloadsFooterDropmarker"
|
||||
class="downloadsPanelFooterButton downloadsDropmarker"
|
||||
type="menu">
|
||||
<menupopup id="downloadSubPanel"
|
||||
onpopupshowing="DownloadsPanel.onFooterPopupShowing(event);"
|
||||
onpopuphidden="DownloadsPanel.onFooterPopupHidden(event);"
|
||||
position="after_end">
|
||||
<menuitem id="downloadsDropdownItemClearList"
|
||||
command="downloadsCmd_clearList"
|
||||
label="&cmd.clearList2.label;"/>
|
||||
<menuitem id="downloadsDropdownItemOpenDownloadsFolder"
|
||||
oncommand="DownloadsPanel.openDownloadsFolder();"
|
||||
label="&openDownloadsFolder.label;"/>
|
||||
</menupopup>
|
||||
</button>
|
||||
</hbox>
|
||||
</stack>
|
||||
</vbox>
|
||||
</panelview>
|
||||
|
||||
<panelview id="downloadsPanel-blockedSubview"
|
||||
orient="vertical"
|
||||
flex="1">
|
||||
<description id="downloadsPanel-blockedSubview-title"/>
|
||||
<description id="downloadsPanel-blockedSubview-details1"/>
|
||||
<description id="downloadsPanel-blockedSubview-details2"/>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="downloadsPanel-blockedSubview-buttons"
|
||||
class="downloadsPanelFooter"
|
||||
align="stretch">
|
||||
<button id="downloadsPanel-blockedSubview-openButton"
|
||||
class="downloadsPanelFooterButton"
|
||||
command="downloadsCmd_unblockAndOpen"
|
||||
flex="1"/>
|
||||
<toolbarseparator/>
|
||||
<button id="downloadsPanel-blockedSubview-deleteButton"
|
||||
class="downloadsPanelFooterButton"
|
||||
oncommand="DownloadsBlockedSubview.confirmBlock();"
|
||||
default="true"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
</panelview>
|
||||
|
||||
</panelmultiview>
|
||||
|
||||
</panel>
|
||||
</popupset>
|
||||
</overlay>
|
||||
606
application/basilisk/components/downloads/content/indicator.js
Normal file
606
application/basilisk/components/downloads/content/indicator.js
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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() {
|
||||
return "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 asynchronously just after window initialization.
|
||||
*
|
||||
* NOTE: This function should limit the input/output it performs to improve
|
||||
* startup time.
|
||||
*/
|
||||
initializeIndicator() {
|
||||
DownloadsIndicatorView.ensureInitialized();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// Prevent the indicator from being displayed as a temporary anchor
|
||||
// during customization, even if requested using the getAnchor method.
|
||||
this._customizing = true;
|
||||
this._anchorRequested = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called when toolbar customization ends.
|
||||
*/
|
||||
customizeDone() {
|
||||
this._customizing = false;
|
||||
DownloadsIndicatorView.afterCustomize();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
_getAnchorInternal() {
|
||||
let indicator = DownloadsIndicatorView.indicator;
|
||||
if (!indicator) {
|
||||
// Exit now if the indicator overlay isn't loaded yet, or if the button
|
||||
// is not in the document.
|
||||
return null;
|
||||
}
|
||||
|
||||
indicator.open = this._anchorRequested;
|
||||
|
||||
let widget = CustomizableUI.getWidget("downloads-button")
|
||||
.forWindow(window);
|
||||
// Determine if the indicator is located on an invisible toolbar.
|
||||
if (!isElementVisible(indicator.parentNode) && !widget.overflowed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DownloadsIndicatorView.indicatorAnchor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the indicator is, or will soon be visible in the browser
|
||||
* window.
|
||||
*
|
||||
* @param aCallback
|
||||
* Called once the indicator overlay has loaded. Gets a boolean
|
||||
* argument representing the indicator visibility.
|
||||
*/
|
||||
checkIsVisible(aCallback) {
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
|
||||
if (!this._placeholder) {
|
||||
aCallback(false);
|
||||
} else {
|
||||
let element = DownloadsIndicatorView.indicator || this._placeholder;
|
||||
aCallback(isElementVisible(element.parentNode));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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(aCallback) {
|
||||
// Do not allow anchoring the panel to the element while customizing.
|
||||
if (this._customizing) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
|
||||
this._anchorRequested = true;
|
||||
aCallback(this._getAnchorInternal());
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows the temporary anchor to be hidden.
|
||||
*/
|
||||
releaseAnchor() {
|
||||
this._anchorRequested = false;
|
||||
this._getAnchorInternal();
|
||||
},
|
||||
|
||||
get _tabsToolbar() {
|
||||
delete this._tabsToolbar;
|
||||
return this._tabsToolbar = document.getElementById("TabsToolbar");
|
||||
},
|
||||
|
||||
get _navBar() {
|
||||
delete this._navBar;
|
||||
return this._navBar = document.getElementById("nav-bar");
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(this, "DownloadsButton", {
|
||||
value: DownloadsButton,
|
||||
enumerable: true,
|
||||
writable: false
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// 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() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
window.addEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.getIndicatorData(window).addView(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Frees the internal resources related to the indicator.
|
||||
*/
|
||||
ensureTerminated() {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = false;
|
||||
|
||||
window.removeEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.getIndicatorData(window).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 = DownloadsCommon.ATTENTION_NONE;
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the user interface elements required to display the indicator
|
||||
* are loaded, then invokes the given callback.
|
||||
*/
|
||||
_ensureOperational(aCallback) {
|
||||
if (this._operational) {
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have a _placeholder, there's no chance that the overlay
|
||||
// will load correctly: bail (and don't set _operational to true!)
|
||||
if (!DownloadsButton._placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(
|
||||
DownloadsButton.kIndicatorOverlay,
|
||||
() => {
|
||||
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.getIndicatorData(window).refreshView(this);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Direct control functions
|
||||
|
||||
/**
|
||||
* Set while we are waiting for a notification to fade out.
|
||||
*/
|
||||
_notificationTimeout: null,
|
||||
|
||||
/**
|
||||
* Check if the panel containing aNode is open.
|
||||
* @param aNode
|
||||
* the node whose panel we're interested in.
|
||||
*/
|
||||
_isAncestorPanelOpen(aNode) {
|
||||
while (aNode && aNode.localName != "panel") {
|
||||
aNode = aNode.parentNode;
|
||||
}
|
||||
return aNode && aNode.state == "open";
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param aType
|
||||
* Set to "start" for new downloads, "finish" for completed downloads.
|
||||
*/
|
||||
showEventNotification(aType) {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DownloadsCommon.animateNotifications) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to show visual notification if the panel is visible.
|
||||
if (DownloadsPanel.isPanelShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let anchor = DownloadsButton._placeholder;
|
||||
let widgetGroup = CustomizableUI.getWidget("downloads-button");
|
||||
let widget = widgetGroup.forWindow(window);
|
||||
if (widget.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||
if (anchor && this._isAncestorPanelOpen(anchor)) {
|
||||
// If the containing panel is open, don't do anything, because the
|
||||
// notification would appear under the open panel. See
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=984023
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, try to use the anchor of the panel:
|
||||
anchor = widget.anchor;
|
||||
}
|
||||
if (!anchor || !isElementVisible(anchor.parentNode)) {
|
||||
// Our container isn't visible, so can't show the animation:
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._notificationTimeout) {
|
||||
clearTimeout(this._notificationTimeout);
|
||||
}
|
||||
|
||||
// The notification element is positioned to show in the same location as
|
||||
// the downloads button. It's not in the downloads button itself in order to
|
||||
// be able to anchor the notification elsewhere if required, and to ensure
|
||||
// the notification isn't clipped by overflow properties of the anchor's
|
||||
// container.
|
||||
let notifier = this.notifier;
|
||||
if (notifier.style.transform == '') {
|
||||
let anchorRect = anchor.getBoundingClientRect();
|
||||
let notifierRect = notifier.getBoundingClientRect();
|
||||
let topDiff = anchorRect.top - notifierRect.top;
|
||||
let leftDiff = anchorRect.left - notifierRect.left;
|
||||
let heightDiff = anchorRect.height - notifierRect.height;
|
||||
let widthDiff = anchorRect.width - notifierRect.width;
|
||||
let translateX = (leftDiff + .5 * widthDiff) + "px";
|
||||
let translateY = (topDiff + .5 * heightDiff) + "px";
|
||||
notifier.style.transform = "translate(" + translateX + ", " + translateY + ")";
|
||||
}
|
||||
notifier.setAttribute("notification", aType);
|
||||
this._notificationTimeout = setTimeout(() => {
|
||||
notifier.removeAttribute("notification");
|
||||
notifier.style.transform = '';
|
||||
}, 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._operational && aValue)) {
|
||||
this._hasDownloads = aValue;
|
||||
|
||||
// If there is at least one download, ensure that the view elements are
|
||||
if (aValue) {
|
||||
this._ensureOperational();
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// Check if the downloads button is in the menu panel, to determine which
|
||||
// button needs to get a badge.
|
||||
let widgetGroup = CustomizableUI.getWidget("downloads-button");
|
||||
let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
|
||||
|
||||
if (aValue == DownloadsCommon.ATTENTION_NONE) {
|
||||
this.indicator.removeAttribute("attention");
|
||||
if (inMenu) {
|
||||
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
|
||||
}
|
||||
} else {
|
||||
this.indicator.setAttribute("attention", aValue);
|
||||
if (inMenu) {
|
||||
let badgeClass = "download-" + aValue;
|
||||
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
return aValue;
|
||||
},
|
||||
_attention: DownloadsCommon.ATTENTION_NONE,
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// User interface event functions
|
||||
|
||||
onWindowUnload() {
|
||||
// This function is registered as an event listener, we can't use "this".
|
||||
DownloadsIndicatorView.ensureTerminated();
|
||||
},
|
||||
|
||||
onCommand(aEvent) {
|
||||
// If the downloads button is in the menu panel, open the Library
|
||||
let widgetGroup = CustomizableUI.getWidget("downloads-button");
|
||||
if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||
DownloadsPanel.showDownloadsHistory();
|
||||
} else {
|
||||
DownloadsPanel.showPanel();
|
||||
}
|
||||
|
||||
aEvent.stopPropagation();
|
||||
},
|
||||
|
||||
onDragOver(aEvent) {
|
||||
browserDragAndDrop.dragOver(aEvent);
|
||||
},
|
||||
|
||||
onDrop(aEvent) {
|
||||
let dt = aEvent.dataTransfer;
|
||||
// If dragged item is from our source, do not try to
|
||||
// redownload already downloaded file.
|
||||
if (dt.mozGetDataAt("application/x-moz-file", 0))
|
||||
return;
|
||||
|
||||
let links = browserDragAndDrop.dropLinks(aEvent);
|
||||
if (!links.length)
|
||||
return;
|
||||
let sourceDoc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document;
|
||||
let handled = false;
|
||||
for (let link of links) {
|
||||
if (link.url.startsWith("about:"))
|
||||
continue;
|
||||
saveURL(link.url, link.name, null, true, true, null, sourceDoc);
|
||||
handled = true;
|
||||
}
|
||||
if (handled) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
_indicator: null,
|
||||
__indicatorCounter: null,
|
||||
__indicatorProgress: null,
|
||||
|
||||
/**
|
||||
* Returns a reference to the main indicator element, or null if the element
|
||||
* is not present in the browser window yet.
|
||||
*/
|
||||
get indicator() {
|
||||
if (this._indicator) {
|
||||
return this._indicator;
|
||||
}
|
||||
|
||||
let indicator = document.getElementById("downloads-button");
|
||||
if (!indicator || indicator.getAttribute("indicator") != "true") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._indicator = indicator;
|
||||
},
|
||||
|
||||
get indicatorAnchor() {
|
||||
let widget = CustomizableUI.getWidget("downloads-button")
|
||||
.forWindow(window);
|
||||
if (widget.overflowed) {
|
||||
return widget.anchor;
|
||||
}
|
||||
return document.getElementById("downloads-indicator-anchor");
|
||||
},
|
||||
|
||||
get _indicatorCounter() {
|
||||
return this.__indicatorCounter ||
|
||||
(this.__indicatorCounter = document.getElementById("downloads-indicator-counter"));
|
||||
},
|
||||
|
||||
get _indicatorProgress() {
|
||||
return this.__indicatorProgress ||
|
||||
(this.__indicatorProgress = document.getElementById("downloads-indicator-progress"));
|
||||
},
|
||||
|
||||
get notifier() {
|
||||
return this._notifier ||
|
||||
(this._notifier = document.getElementById("downloads-notification-anchor"));
|
||||
},
|
||||
|
||||
_onCustomizedAway() {
|
||||
this._indicator = null;
|
||||
this.__indicatorCounter = null;
|
||||
this.__indicatorProgress = null;
|
||||
},
|
||||
|
||||
afterCustomize() {
|
||||
// If the cached indicator is not the one currently in the document,
|
||||
// invalidate our references
|
||||
if (this._indicator != document.getElementById("downloads-button")) {
|
||||
this._onCustomizedAway();
|
||||
this._operational = false;
|
||||
this.ensureTerminated();
|
||||
this.ensureInitialized();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Object.defineProperty(this, "DownloadsIndicatorView", {
|
||||
value: DownloadsIndicatorView,
|
||||
enumerable: true,
|
||||
writable: false
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
|
||||
<!-- vim: set ts=2 et sw=2 tw=80: -->
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<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">
|
||||
|
||||
<!-- We dynamically add the stack with the progress meter and notification icon,
|
||||
originally loaded lazily because of performance reasons, to the existing
|
||||
downloads-button. -->
|
||||
<toolbarbutton id="downloads-button" indicator="true">
|
||||
<!-- 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"
|
||||
consumeanchor="downloads-button">
|
||||
<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"/>
|
||||
</stack>
|
||||
</toolbarbutton>
|
||||
</overlay>
|
||||
16
application/basilisk/components/downloads/jar.mn
Normal file
16
application/basilisk/components/downloads/jar.mn
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# 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/.
|
||||
|
||||
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)
|
||||
* content/browser/downloads/allDownloadsViewOverlay.xul (content/allDownloadsViewOverlay.xul)
|
||||
content/browser/downloads/allDownloadsViewOverlay.js (content/allDownloadsViewOverlay.js)
|
||||
* content/browser/downloads/contentAreaDownloadsView.xul (content/contentAreaDownloadsView.xul)
|
||||
content/browser/downloads/contentAreaDownloadsView.js (content/contentAreaDownloadsView.js)
|
||||
content/browser/downloads/contentAreaDownloadsView.css (content/contentAreaDownloadsView.css)
|
||||
13
application/basilisk/components/downloads/moz.build
Normal file
13
application/basilisk/components/downloads/moz.build
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'DownloadsCommon.jsm',
|
||||
'DownloadsTaskbar.jsm',
|
||||
'DownloadsViewUI.jsm',
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue