| ... | ... | @@ -94,8 +94,9 @@ class WindowSpamProtection { | 
| 94 | 94 |     * Add a blocked download to the spamList or increment the count of an
 | 
| 95 | 95 |     * existing blocked download, then notify listeners about this.
 | 
| 96 | 96 |     * @param {String} url
 | 
|  | 97 | +   * @param {DownloadSpamEnabler} enabler
 | 
| 97 | 98 |     */
 | 
| 98 |  | -  addDownloadSpam(url) {
 | 
|  | 99 | +  addDownloadSpam(url, enabler) {
 | 
| 99 | 100 |      this._blocking = true;
 | 
| 100 | 101 |      // Start listening on registered downloads views, if any exist.
 | 
| 101 | 102 |      this._maybeAddViews();
 | 
| ... | ... | @@ -109,7 +110,7 @@ class WindowSpamProtection { | 
| 109 | 110 |      }
 | 
| 110 | 111 |      // Otherwise, create a new DownloadSpam object for the URL, add it to the
 | 
| 111 | 112 |      // spamList, and open the downloads panel.
 | 
| 112 |  | -    let downloadSpam = new DownloadSpam(url);
 | 
|  | 113 | +    let downloadSpam = new DownloadSpam(url, enabler);
 | 
| 113 | 114 |      this.spamList.add(downloadSpam);
 | 
| 114 | 115 |      this._downloadSpamForUrl.set(url, downloadSpam);
 | 
| 115 | 116 |      this._notifyDownloadSpamAdded(downloadSpam);
 | 
| ... | ... | @@ -193,6 +194,39 @@ class WindowSpamProtection { | 
| 193 | 194 |    }
 | 
| 194 | 195 |  }
 | 
| 195 | 196 |  
 | 
|  | 197 | +/**
 | 
|  | 198 | + * Helper to grant a certain principal permission for automatic downloads
 | 
|  | 199 | + * and to clear its download spam messages from the UI
 | 
|  | 200 | + */
 | 
|  | 201 | +class DownloadSpamEnabler{
 | 
|  | 202 | +  /**
 | 
|  | 203 | +   * Constructs a DownloadSpamEnabler object
 | 
|  | 204 | +   * @param {nsIPrincipal} principal
 | 
|  | 205 | +   * @param {DownloadSpamProtection} downloadSpamProtection
 | 
|  | 206 | +   */
 | 
|  | 207 | +  constructor(principal, downloadSpamProtection) {
 | 
|  | 208 | +    this.principal = principal;
 | 
|  | 209 | +    this.downloadSpamProtection = downloadSpamProtection;
 | 
|  | 210 | +  }
 | 
|  | 211 | +  /**
 | 
|  | 212 | +   * Allows a DownloadSpam item
 | 
|  | 213 | +   * @param {DownloadSpam} downloadSpam
 | 
|  | 214 | +   */
 | 
|  | 215 | +  allow(downloadSpam) {
 | 
|  | 216 | +    const pm = Services.perms;
 | 
|  | 217 | +    pm.addFromPrincipal(
 | 
|  | 218 | +      this.principal,
 | 
|  | 219 | +      "automatic-download",
 | 
|  | 220 | +      pm.ALLOW_ACTION,
 | 
|  | 221 | +      pm.EXPIRE_SESSION
 | 
|  | 222 | +    );
 | 
|  | 223 | +    downloadSpam.hasBlockedData = downloadSpam.hasPartialData = false;
 | 
|  | 224 | +    const {url} = downloadSpam.source;
 | 
|  | 225 | +    for (let window of lazy.BrowserWindowTracker.orderedWindows) {
 | 
|  | 226 | +      this.downloadSpamProtection.removeDownloadSpamForWindow(url, window);
 | 
|  | 227 | +    }
 | 
|  | 228 | +  }
 | 
|  | 229 | +}
 | 
| 196 | 230 |  /**
 | 
| 197 | 231 |   * Responsible for detecting events related to downloads spam and notifying the
 | 
| 198 | 232 |   * relevant window's WindowSpamProtection object. This is a singleton object,
 | 
| ... | ... | @@ -210,9 +244,11 @@ export class DownloadSpamProtection { | 
| 210 | 244 |     * download was blocked. This is invoked when a download is blocked by
 | 
| 211 | 245 |     * nsExternalAppHandler::IsDownloadSpam
 | 
| 212 | 246 |     * @param {String} url
 | 
| 213 |  | -   * @param {Window} window
 | 
|  | 247 | +   * @param {nsILoadInfo} loadInfo
 | 
| 214 | 248 |     */
 | 
| 215 |  | -  update(url, window) {
 | 
|  | 249 | +  update(url, loadInfo) {
 | 
|  | 250 | +    loadInfo = loadInfo.QueryInterface(Ci.nsILoadInfo);
 | 
|  | 251 | +    const window = loadInfo.browsingContext.topChromeWindow;
 | 
| 216 | 252 |      if (window == null) {
 | 
| 217 | 253 |        lazy.DownloadsCommon.log(
 | 
| 218 | 254 |          "Download spam blocked in a non-chrome window. URL: ",
 | 
| ... | ... | @@ -226,7 +262,7 @@ export class DownloadSpamProtection { | 
| 226 | 262 |      let wsp =
 | 
| 227 | 263 |        this._forWindowMap.get(window) ?? new WindowSpamProtection(window);
 | 
| 228 | 264 |      this._forWindowMap.set(window, wsp);
 | 
| 229 |  | -    wsp.addDownloadSpam(url);
 | 
|  | 265 | +    wsp.addDownloadSpam(url, new DownloadSpamEnabler(loadInfo.triggeringPrincipal, this));
 | 
| 230 | 266 |    }
 | 
| 231 | 267 |  
 | 
| 232 | 268 |    /**
 | 
| ... | ... | @@ -285,8 +321,9 @@ export class DownloadSpamProtection { | 
| 285 | 321 |   * @extends Download
 | 
| 286 | 322 |   */
 | 
| 287 | 323 |  class DownloadSpam extends Download {
 | 
| 288 |  | -  constructor(url) {
 | 
|  | 324 | +  constructor(url, downloadSpamEnabler) {
 | 
| 289 | 325 |      super();
 | 
|  | 326 | +    this._downloadSpamEnabler = downloadSpamEnabler;
 | 
| 290 | 327 |      this.hasBlockedData = true;
 | 
| 291 | 328 |      this.stopped = true;
 | 
| 292 | 329 |      this.error = new DownloadError({
 | 
| ... | ... | @@ -297,4 +334,13 @@ class DownloadSpam extends Download { | 
| 297 | 334 |      this.source = { url };
 | 
| 298 | 335 |      this.blockedDownloadsCount = 1;
 | 
| 299 | 336 |    }
 | 
|  | 337 | +
 | 
|  | 338 | +  /**
 | 
|  | 339 | +   * Allows the principal which triggered this download to perform automatic downloads
 | 
|  | 340 | +   * and clears the UI from messages reporting this download spam
 | 
|  | 341 | +   */
 | 
|  | 342 | +  allow() {
 | 
|  | 343 | +    this._downloadSpamEnabler.allow(this);
 | 
|  | 344 | +    this._notifyChange();
 | 
|  | 345 | +  }
 | 
| 300 | 346 |  } |