richard pushed to branch mullvad-browser-115.4.0esr-13.5-1 at The Tor Project / Applications / Mullvad Browser

Commits:

2 changed files:

Changes:

  • browser/app/profile/001-base-profile.js
    ... ... @@ -87,6 +87,9 @@ pref("browser.sessionstore.resume_from_crash", false);
    87 87
     // Also not needed in PBM at the moment.
    
    88 88
     pref("browser.pagethumbnails.capturing_disabled", true);
    
    89 89
     
    
    90
    +// Empty clipboard content from private windows on exit (tor-browser#42154)
    
    91
    +pref("browser.privatebrowsing.preserveClipboard", false);
    
    92
    +
    
    90 93
     // Enable HTTPS-Only mode (tor-browser#19850)
    
    91 94
     pref("dom.security.https_only_mode", true);
    
    92 95
     // The previous pref automatically sets this to true (see StaticPrefList.yaml),
    

  • browser/components/BrowserGlue.sys.mjs
    ... ... @@ -148,6 +148,119 @@ const PRIVATE_BROWSING_EXE_ICON_INDEX = 1;
    148 148
     const PREF_PRIVATE_BROWSING_SHORTCUT_CREATED =
    
    149 149
       "browser.privacySegmentation.createdShortcut";
    
    150 150
     
    
    151
    +// Empty clipboard content from private windows on exit
    
    152
    +// (tor-browser#42154)
    
    153
    +const ClipboardPrivacy = {
    
    154
    +  _lastClipboardHash: null,
    
    155
    +  _globalActivation: false,
    
    156
    +  _isPrivateClipboard: false,
    
    157
    +  _hasher: null,
    
    158
    +
    
    159
    +  _computeClipboardHash(win = Services.ww.activeWindow) {
    
    160
    +    const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
    
    161
    +      Ci.nsITransferable
    
    162
    +    );
    
    163
    +    trans.init(win?.docShell?.QueryInterface(Ci.nsILoadContext) || null);
    
    164
    +    ["text/x-moz-url", "text/plain"].forEach(trans.addDataFlavor);
    
    165
    +    try {
    
    166
    +      Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
    
    167
    +      const clipboardContent = {};
    
    168
    +      trans.getAnyTransferData({}, clipboardContent);
    
    169
    +      const { data } = clipboardContent.value.QueryInterface(
    
    170
    +        Ci.nsISupportsString
    
    171
    +      );
    
    172
    +      const bytes = new TextEncoder().encode(data);
    
    173
    +      const hasher = (this._hasher ||= Cc[
    
    174
    +        "@mozilla.org/security/hash;1"
    
    175
    +      ].createInstance(Ci.nsICryptoHash));
    
    176
    +      hasher.init(hasher.SHA256);
    
    177
    +      hasher.update(bytes, bytes.length);
    
    178
    +      return hasher.finish(true);
    
    179
    +    } catch (e) {}
    
    180
    +    return null;
    
    181
    +  },
    
    182
    +
    
    183
    +  startup() {
    
    184
    +    this._lastClipboardHash = this._computeClipboardHash();
    
    185
    +
    
    186
    +    // Here we track changes in active window / application,
    
    187
    +    // by filtering focus events and window closures.
    
    188
    +    const handleActivation = (win, activation) => {
    
    189
    +      if (activation) {
    
    190
    +        if (!this._globalActivation) {
    
    191
    +          // focus changed within this window, bail out.
    
    192
    +          return;
    
    193
    +        }
    
    194
    +        this._globalActivation = false;
    
    195
    +      } else if (!Services.focus.activeWindow) {
    
    196
    +        // focus is leaving this window:
    
    197
    +        // let's track whether it remains within the browser.
    
    198
    +        lazy.setTimeout(() => {
    
    199
    +          this._globalActivation = !Services.focus.activeWindow;
    
    200
    +        }, 100);
    
    201
    +      }
    
    202
    +      const clipboardHash = this._computeClipboardHash(win);
    
    203
    +      if (clipboardHash !== this._lastClipboardHash) {
    
    204
    +        this._isPrivateClipboard =
    
    205
    +          !activation &&
    
    206
    +          (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
    
    207
    +            lazy.PrivateBrowsingUtils.isWindowPrivate(win));
    
    208
    +        this._lastClipboardHash = clipboardHash;
    
    209
    +        console.log(
    
    210
    +          `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
    
    211
    +        );
    
    212
    +      }
    
    213
    +    };
    
    214
    +    const focusListener = e =>
    
    215
    +      e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
    
    216
    +    const initWindow = win => {
    
    217
    +      for (const e of ["focusin", "focusout"]) {
    
    218
    +        win.addEventListener(e, focusListener);
    
    219
    +      }
    
    220
    +    };
    
    221
    +    for (const w of Services.ww.getWindowEnumerator()) {
    
    222
    +      initWindow(w);
    
    223
    +    }
    
    224
    +    Services.ww.registerNotification((win, event) => {
    
    225
    +      switch (event) {
    
    226
    +        case "domwindowopened":
    
    227
    +          initWindow(win);
    
    228
    +          break;
    
    229
    +        case "domwindowclosed":
    
    230
    +          handleActivation(win, false);
    
    231
    +          if (
    
    232
    +            this._isPrivateClipboard &&
    
    233
    +            lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
    
    234
    +            !(
    
    235
    +              lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
    
    236
    +              Array.from(Services.ww.getWindowEnumerator()).find(w =>
    
    237
    +                lazy.PrivateBrowsingUtils.isWindowPrivate(w)
    
    238
    +              )
    
    239
    +            )
    
    240
    +          ) {
    
    241
    +            // no more private windows, empty private content if needed
    
    242
    +            this.emptyPrivate();
    
    243
    +          }
    
    244
    +      }
    
    245
    +    });
    
    246
    +  },
    
    247
    +  emptyPrivate() {
    
    248
    +    if (
    
    249
    +      this._isPrivateClipboard &&
    
    250
    +      !Services.prefs.getBoolPref(
    
    251
    +        "browser.privatebrowsing.preserveClipboard",
    
    252
    +        false
    
    253
    +      ) &&
    
    254
    +      this._lastClipboardHash === this._computeClipboardHash()
    
    255
    +    ) {
    
    256
    +      Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
    
    257
    +      this._lastClipboardHash = null;
    
    258
    +      this._isPrivateClipboard = false;
    
    259
    +      console.log("Private clipboard emptied.");
    
    260
    +    }
    
    261
    +  },
    
    262
    +};
    
    263
    +
    
    151 264
     /**
    
    152 265
      * Fission-compatible JSProcess implementations.
    
    153 266
      * Each actor options object takes the form of a ProcessActorOptions dictionary.
    
    ... ... @@ -1619,6 +1732,8 @@ BrowserGlue.prototype = {
    1619 1732
     
    
    1620 1733
         lazy.DoHController.init();
    
    1621 1734
     
    
    1735
    +    ClipboardPrivacy.startup();
    
    1736
    +
    
    1622 1737
         this._firstWindowTelemetry(aWindow);
    
    1623 1738
         this._firstWindowLoaded();
    
    1624 1739
     
    
    ... ... @@ -1879,7 +1994,7 @@ BrowserGlue.prototype = {
    1879 1994
               lazy.UpdateListener.reset();
    
    1880 1995
             }
    
    1881 1996
           },
    
    1882
    -      () => Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard), // tor-browser#42019
    
    1997
    +      () => ClipboardPrivacy.emptyPrivate(), // tor-browser#42019
    
    1883 1998
         ];
    
    1884 1999
     
    
    1885 2000
         for (let task of tasks) {