1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 import { actionTypes as at } from "resource://activity-stream/common/Actions.sys.mjs";
9 ChromeUtils.defineESModuleGetters(lazy, {
10 DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
11 DownloadsViewUI: "resource:///modules/DownloadsViewUI.sys.mjs",
12 FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
13 NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
16 const DOWNLOAD_CHANGED_DELAY_TIME = 1000; // time in ms to delay timer for downloads changed events
18 export class DownloadsManager {
20 this._downloadData = null;
22 this._downloadItems = new Map();
23 this._downloadTimer = null;
26 setTimeout(callback, delay) {
27 let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
28 timer.initWithCallback(callback, delay, Ci.nsITimer.TYPE_ONE_SHOT);
32 formatDownload(download) {
33 let referrer = download.source.referrerInfo?.originalReferrer?.spec || null;
35 hostname: new URL(download.source.url).hostname,
36 url: download.source.url,
37 path: download.target.path,
38 title: lazy.DownloadsViewUI.getDisplayName(download),
40 lazy.DownloadsViewUI.getSizeWithUnits(download) ||
41 lazy.DownloadsCommon.strings.sizeUnknown,
43 date_added: download.endTime,
49 this._downloadData = lazy.DownloadsCommon.getData(
50 null /* null for non-private downloads */,
55 this._downloadData.addView(this);
58 onDownloadAdded(download) {
59 if (!this._downloadItems.has(download.source.url)) {
60 this._downloadItems.set(download.source.url, download);
62 // On startup, all existing downloads fire this notification, so debounce them
63 if (this._downloadTimer) {
64 this._downloadTimer.delay = DOWNLOAD_CHANGED_DELAY_TIME;
66 this._downloadTimer = this.setTimeout(() => {
67 this._downloadTimer = null;
68 this._store.dispatch({ type: at.DOWNLOAD_CHANGED });
69 }, DOWNLOAD_CHANGED_DELAY_TIME);
74 onDownloadRemoved(download) {
75 if (this._downloadItems.has(download.source.url)) {
76 this._downloadItems.delete(download.source.url);
77 this._store.dispatch({ type: at.DOWNLOAD_CHANGED });
84 numItems = this._downloadItems.size,
85 onlySucceeded = false,
94 // Only get downloads within the time threshold specified and sort by recency
95 const downloadThreshold = Date.now() - threshold;
96 let downloads = [...this._downloadItems.values()]
97 .filter(download => download.endTime > downloadThreshold)
98 .sort((download1, download2) => download1.endTime < download2.endTime);
100 for (const download of downloads) {
101 // Ignore blocked links, but allow long (data:) uris to avoid high CPU
103 download.source.url.length < 10000 &&
104 lazy.NewTabUtils.blockedLinks.isBlocked(download.source)
109 // Only include downloads where the file still exists
111 // Refresh download to ensure the 'exists' attribute is up to date
112 await download.refresh();
113 if (!download.target.exists) {
117 // Only include downloads that were completed successfully
119 if (!download.succeeded) {
123 const formattedDownloadForHighlights = this.formatDownload(download);
124 results.push(formattedDownloadForHighlights);
125 if (results.length === numItems) {
133 if (this._downloadData) {
134 this._downloadData.removeView(this);
135 this._downloadData = null;
137 if (this._downloadTimer) {
138 this._downloadTimer.cancel();
139 this._downloadTimer = null;
144 let doDownloadAction = callback => {
145 let download = this._downloadItems.get(action.data.url);
151 switch (action.type) {
152 case at.COPY_DOWNLOAD_LINK:
153 doDownloadAction(download => {
154 lazy.DownloadsCommon.copyDownloadLink(download);
157 case at.REMOVE_DOWNLOAD_FILE:
158 doDownloadAction(download => {
159 lazy.DownloadsCommon.deleteDownload(download).catch(console.error);
162 case at.SHOW_DOWNLOAD_FILE:
163 doDownloadAction(download => {
164 lazy.DownloadsCommon.showDownloadedFile(
165 new lazy.FileUtils.File(download.target.path)
169 case at.OPEN_DOWNLOAD_FILE:
170 const win = action._target.browser.ownerGlobal;
172 action.data.event && win.whereToOpenLink(action.data.event);
173 doDownloadAction(download => {
174 lazy.DownloadsCommon.openDownload(download, {
175 // Replace "current" or unknown value with "tab" as the default behavior
176 // for opening downloads when handled internally
177 openWhere: ["window", "tab", "tabshifted"].includes(openWhere)