tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DownloadsManager.sys.mjs (5795B)


      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/. */
      4 
      5 import { actionTypes as at } from "resource://newtab/common/Actions.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
     11  DownloadsCommon:
     12    "moz-src:///browser/components/downloads/DownloadsCommon.sys.mjs",
     13  DownloadsViewUI:
     14    "moz-src:///browser/components/downloads/DownloadsViewUI.sys.mjs",
     15  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
     16  NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
     17 });
     18 
     19 const DOWNLOAD_CHANGED_DELAY_TIME = 1000; // time in ms to delay timer for downloads changed events
     20 
     21 export class DownloadsManager {
     22  constructor() {
     23    this._downloadData = null;
     24    this._store = null;
     25    this._downloadItems = new Map();
     26    this._downloadTimer = null;
     27  }
     28 
     29  setTimeout(callback, delay) {
     30    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     31    timer.initWithCallback(callback, delay, Ci.nsITimer.TYPE_ONE_SHOT);
     32    return timer;
     33  }
     34 
     35  formatDownload(download) {
     36    let referrer = download.source.referrerInfo?.originalReferrer?.spec || null;
     37    return {
     38      hostname: new URL(download.source.url).hostname,
     39      url: download.source.url,
     40      path: download.target.path,
     41      title: lazy.DownloadsViewUI.getDisplayName(download),
     42      description:
     43        lazy.DownloadsViewUI.getSizeWithUnits(download) ||
     44        lazy.DownloadsCommon.strings.sizeUnknown,
     45      referrer,
     46      date_added: download.endTime,
     47    };
     48  }
     49 
     50  init(store) {
     51    this._store = store;
     52    this._downloadData = lazy.DownloadsCommon.getData(
     53      null /* null for non-private downloads */,
     54      true,
     55      false,
     56      true
     57    );
     58    this._downloadData.addView(this);
     59  }
     60 
     61  onDownloadAdded(download) {
     62    if (!this._downloadItems.has(download.source.url)) {
     63      this._downloadItems.set(download.source.url, download);
     64 
     65      // On startup, all existing downloads fire this notification, so debounce them
     66      if (this._downloadTimer) {
     67        this._downloadTimer.delay = DOWNLOAD_CHANGED_DELAY_TIME;
     68      } else {
     69        this._downloadTimer = this.setTimeout(() => {
     70          this._downloadTimer = null;
     71          this._store.dispatch({ type: at.DOWNLOAD_CHANGED });
     72        }, DOWNLOAD_CHANGED_DELAY_TIME);
     73      }
     74    }
     75  }
     76 
     77  onDownloadRemoved(download) {
     78    if (this._downloadItems.has(download.source.url)) {
     79      this._downloadItems.delete(download.source.url);
     80      this._store.dispatch({ type: at.DOWNLOAD_CHANGED });
     81    }
     82  }
     83 
     84  async getDownloads(
     85    threshold,
     86    {
     87      numItems = this._downloadItems.size,
     88      onlySucceeded = false,
     89      onlyExists = false,
     90    }
     91  ) {
     92    if (!threshold) {
     93      return [];
     94    }
     95    let results = [];
     96 
     97    // Only get downloads within the time threshold specified and sort by recency
     98    const downloadThreshold = Date.now() - threshold;
     99    let downloads = [...this._downloadItems.values()]
    100      .filter(download => download.endTime > downloadThreshold)
    101      .sort((download1, download2) => download1.endTime < download2.endTime);
    102 
    103    for (const download of downloads) {
    104      // Ignore blocked links, but allow long (data:) uris to avoid high CPU
    105      if (
    106        download.source.url.length < 10000 &&
    107        lazy.NewTabUtils.blockedLinks.isBlocked(download.source)
    108      ) {
    109        continue;
    110      }
    111 
    112      // Only include downloads where the file still exists
    113      if (onlyExists) {
    114        // Refresh download to ensure the 'exists' attribute is up to date
    115        await download.refresh();
    116        if (!download.target.exists) {
    117          continue;
    118        }
    119      }
    120      // Only include downloads that were completed successfully
    121      if (onlySucceeded) {
    122        if (!download.succeeded) {
    123          continue;
    124        }
    125      }
    126      const formattedDownloadForHighlights = this.formatDownload(download);
    127      results.push(formattedDownloadForHighlights);
    128      if (results.length === numItems) {
    129        break;
    130      }
    131    }
    132    return results;
    133  }
    134 
    135  uninit() {
    136    if (this._downloadData) {
    137      this._downloadData.removeView(this);
    138      this._downloadData = null;
    139    }
    140    if (this._downloadTimer) {
    141      this._downloadTimer.cancel();
    142      this._downloadTimer = null;
    143    }
    144  }
    145 
    146  onAction(action) {
    147    let doDownloadAction = callback => {
    148      let download = this._downloadItems.get(action.data.url);
    149      if (download) {
    150        callback(download);
    151      }
    152    };
    153 
    154    switch (action.type) {
    155      case at.COPY_DOWNLOAD_LINK:
    156        doDownloadAction(download => {
    157          lazy.DownloadsCommon.copyDownloadLink(download);
    158        });
    159        break;
    160      case at.REMOVE_DOWNLOAD_FILE:
    161        doDownloadAction(download => {
    162          lazy.DownloadsCommon.deleteDownload(download).catch(console.error);
    163        });
    164        break;
    165      case at.SHOW_DOWNLOAD_FILE:
    166        doDownloadAction(download => {
    167          lazy.DownloadsCommon.showDownloadedFile(
    168            new lazy.FileUtils.File(download.target.path)
    169          );
    170        });
    171        break;
    172      case at.OPEN_DOWNLOAD_FILE: {
    173        const openWhere = lazy.BrowserUtils.whereToOpenLink(action.data.event);
    174        doDownloadAction(download => {
    175          lazy.DownloadsCommon.openDownload(download, {
    176            // Replace "current" or unknown value with "tab" as the default behavior
    177            // for opening downloads when handled internally
    178            openWhere: ["window", "tab", "tabshifted"].includes(openWhere)
    179              ? openWhere
    180              : "tab",
    181          });
    182        });
    183        break;
    184      }
    185      case at.UNINIT:
    186        this.uninit();
    187        break;
    188    }
    189  }
    190 }