ma1 pushed to branch tor-browser-115.4.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits: 2b8d06d6 by hackademix at 2023-11-06T23:14:33+01:00 fixup! Bug 42019: Empty browser's clipboard on browser shutdown
Bug 42154: empty clipboard content from private windows on exit.
- - - - -
2 changed files:
- browser/app/profile/001-base-profile.js - browser/components/BrowserGlue.sys.mjs
Changes:
===================================== browser/app/profile/001-base-profile.js ===================================== @@ -87,6 +87,9 @@ pref("browser.sessionstore.resume_from_crash", false); // Also not needed in PBM at the moment. pref("browser.pagethumbnails.capturing_disabled", true);
+// Empty clipboard content from private windows on exit (tor-browser#42154) +pref("browser.privatebrowsing.preserveClipboard", false); + // Enable HTTPS-Only mode (tor-browser#19850) pref("dom.security.https_only_mode", true); // The previous pref automatically sets this to true (see StaticPrefList.yaml),
===================================== browser/components/BrowserGlue.sys.mjs ===================================== @@ -150,6 +150,119 @@ const PRIVATE_BROWSING_EXE_ICON_INDEX = 1; const PREF_PRIVATE_BROWSING_SHORTCUT_CREATED = "browser.privacySegmentation.createdShortcut";
+// Empty clipboard content from private windows on exit +// (tor-browser#42154) +const ClipboardPrivacy = { + _lastClipboardHash: null, + _globalActivation: false, + _isPrivateClipboard: false, + _hasher: null, + + _computeClipboardHash(win = Services.ww.activeWindow) { + const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(win?.docShell?.QueryInterface(Ci.nsILoadContext) || null); + ["text/x-moz-url", "text/plain"].forEach(trans.addDataFlavor); + try { + Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); + const clipboardContent = {}; + trans.getAnyTransferData({}, clipboardContent); + const { data } = clipboardContent.value.QueryInterface( + Ci.nsISupportsString + ); + const bytes = new TextEncoder().encode(data); + const hasher = (this._hasher ||= Cc[ + "@mozilla.org/security/hash;1" + ].createInstance(Ci.nsICryptoHash)); + hasher.init(hasher.SHA256); + hasher.update(bytes, bytes.length); + return hasher.finish(true); + } catch (e) {} + return null; + }, + + startup() { + this._lastClipboardHash = this._computeClipboardHash(); + + // Here we track changes in active window / application, + // by filtering focus events and window closures. + const handleActivation = (win, activation) => { + if (activation) { + if (!this._globalActivation) { + // focus changed within this window, bail out. + return; + } + this._globalActivation = false; + } else if (!Services.focus.activeWindow) { + // focus is leaving this window: + // let's track whether it remains within the browser. + lazy.setTimeout(() => { + this._globalActivation = !Services.focus.activeWindow; + }, 100); + } + const clipboardHash = this._computeClipboardHash(win); + if (clipboardHash !== this._lastClipboardHash) { + this._isPrivateClipboard = + !activation && + (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || + lazy.PrivateBrowsingUtils.isWindowPrivate(win)); + this._lastClipboardHash = clipboardHash; + console.log( + `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.` + ); + } + }; + const focusListener = e => + e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin"); + const initWindow = win => { + for (const e of ["focusin", "focusout"]) { + win.addEventListener(e, focusListener); + } + }; + for (const w of Services.ww.getWindowEnumerator()) { + initWindow(w); + } + Services.ww.registerNotification((win, event) => { + switch (event) { + case "domwindowopened": + initWindow(win); + break; + case "domwindowclosed": + handleActivation(win, false); + if ( + this._isPrivateClipboard && + lazy.PrivateBrowsingUtils.isWindowPrivate(win) && + !( + lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || + Array.from(Services.ww.getWindowEnumerator()).find(w => + lazy.PrivateBrowsingUtils.isWindowPrivate(w) + ) + ) + ) { + // no more private windows, empty private content if needed + this.emptyPrivate(); + } + } + }); + }, + emptyPrivate() { + if ( + this._isPrivateClipboard && + !Services.prefs.getBoolPref( + "browser.privatebrowsing.preserveClipboard", + false + ) && + this._lastClipboardHash === this._computeClipboardHash() + ) { + Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); + this._lastClipboardHash = null; + this._isPrivateClipboard = false; + console.log("Private clipboard emptied."); + } + }, +}; + /** * Fission-compatible JSProcess implementations. * Each actor options object takes the form of a ProcessActorOptions dictionary. @@ -1753,6 +1866,8 @@ BrowserGlue.prototype = {
lazy.TorProviderBuilder.firstWindowLoaded();
+ ClipboardPrivacy.startup(); + this._firstWindowTelemetry(aWindow); this._firstWindowLoaded();
@@ -2013,8 +2128,8 @@ BrowserGlue.prototype = { lazy.UpdateListener.reset(); } }, - () => Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard), // tor-browser#42019 () => lazy.OnionAliasStore.uninit(), + () => ClipboardPrivacy.emptyPrivate(), // tor-browser#42019 ];
for (let task of tasks) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/2b8d06d6...