11// ==UserScript==
22// @name Private Tabs
3- // @version 1.1
3+ // @version 1.2.0
44// @author aminomancer
55// @homepage https://github.com/aminomancer
66// @description An fx-autoconfig port of Private Tab by xiaoxiaoflood. Adds
2020// ==/UserScript==
2121
2222class PrivateTabManager {
23+ config = {
24+ // if you want to not record history but don't care about other data, maybe even want to keep private logins
25+ neverClearData : false ,
26+ restoreTabsOnRestart : true ,
27+ doNotClearDataUntilFxIsClosed : true ,
28+ deleteContainerOnDisable : false ,
29+ clearDataOnDisable : false ,
30+
31+ // key for toggling private mode for the active tab. ctrl + alt + T by default.
32+ toggleHotkey : "T" ,
33+
34+ // key for opening a new private tab. ctrl + alt + P by default.
35+ newTabHotkey : "P" ,
36+
37+ // modifiers for toggle hotkey. this means alt+ctrl on windows or alt+cmd on mac
38+ toggleModifiers : "alt accel" ,
39+
40+ // modifiers for new tab hotkey.
41+ newTabModifiers : "alt accel" ,
42+ } ;
43+ openTabs = new Set ( ) ;
44+ BTN_ID = "privateTab-button" ;
45+ BTN2_ID = "newPrivateTab-button" ;
2346 constructor ( ) {
2447 if ( ! _ucUtils . sharedGlobal . privateTabGlobal ) _ucUtils . sharedGlobal . privateTabGlobal = { } ;
25- let privateTabGlobal = _ucUtils . sharedGlobal . privateTabGlobal ;
26- this . config = {
27- // if you want to not record history but don't care about other data, maybe even want to keep private logins
28- neverClearData : false ,
29- restoreTabsOnRestart : true ,
30- doNotClearDataUntilFxIsClosed : true ,
31- deleteContainerOnDisable : false ,
32- clearDataOnDisable : false ,
33-
34- // key for toggling private mode for the active tab. ctrl + alt + T by default.
35- toggleHotkey : "T" ,
36-
37- // key for opening a new private tab. ctrl + alt + P by default.
38- newTabHotkey : "P" ,
39-
40- // modifiers for toggle hotkey. this means alt+ctrl on windows or alt+cmd on mac
41- toggleModifiers : "alt accel" ,
42-
43- // modifiers for new tab hotkey.
44- newTabModifiers : "alt accel" ,
45- } ;
46- // isn't strictly necessary, but due to a programmer's oversight, this seems to
47- // be the only way to prevent irritating animations when toggling private mode
48- // on/off for a tab. what's actually happening under the hood is not a simple
49- // state change, since that would be insecure. the whole point of all this is to
50- // avoid leaking information, so when we turn private mode on, we need to create
51- // a brand new tab and restore it from cached data. but the internal duplicateTab
52- // method doesn't pass the skipAnimation parameter to addTrustedTab. so we need
53- // to make our own function, which requires these unexposed modules.
54- let { SessionStoreInternal } = Cu . import ( "resource:///modules/sessionstore/SessionStore.jsm" ) ;
48+ // the internal duplicateTab method doesn't pass the skipAnimation parameter
49+ // to addTrustedTab. so we need to make our own function, which requires us
50+ // to access some private objects.
51+ let { SessionStoreInternal, TAB_CUSTOM_VALUES } = Cu . import (
52+ "resource:///modules/sessionstore/SessionStore.jsm"
53+ ) ;
5554 this . SSI = SessionStoreInternal ;
55+ this . TAB_CUSTOM_VALUES = TAB_CUSTOM_VALUES ;
5656 XPCOMUtils . defineLazyModuleGetters ( this , {
5757 TabState : "resource:///modules/sessionstore/TabState.jsm" ,
5858 TabStateFlusher : "resource:///modules/sessionstore/TabStateFlusher.jsm" ,
59+ ContextualIdentityService : "resource://gre/modules/ContextualIdentityService.jsm" ,
5960 } ) ;
6061 this . sss = Cc [ "@mozilla.org/content/style-sheet-service;1" ] . getService ( Ci . nsIStyleSheetService ) ;
61- privateTabGlobal . openTabs = new Set ( ) ;
62- if ( typeof window . gProton != "undefined" ) privateTabGlobal . gProton = window . gProton ;
63- this . menuClass = privateTabGlobal . gProton ? `` : `menuitem-iconic privatetab-icon` ;
64- this . openTabs = privateTabGlobal . openTabs ;
62+ this . menuClass = typeof window . gProton != "undefined" ? `` : `menuitem-iconic privatetab-icon` ;
6563 this . orig_getAttribute = MozElements . MozTab . prototype . getAttribute ;
66- this . BTN_ID = "privateTab-button" ;
67- this . BTN2_ID = "newPrivateTab-button" ;
6864 this . init ( ) ;
6965 if ( location . href != "chrome://browser/content/browser.xhtml" ) return this . exec ( ) ;
7066 if ( gBrowserInit . delayedStartupFinished ) this . exec ( ) ;
@@ -80,7 +76,7 @@ class PrivateTabManager {
8076 }
8177
8278 exec ( ) {
83- let privateTabGlobal = _ucUtils . sharedGlobal . privateTabGlobal ;
79+ let { privateTabGlobal } = _ucUtils . sharedGlobal ;
8480 if ( PrivateBrowsingUtils . isWindowPrivate ( window ) ) return ;
8581 let openAll = document . getElementById ( "placesContext_openBookmarkContainer:tabs" ) ;
8682 let openAllPrivate = _ucUtils . createElement ( document , "menuitem" , {
@@ -202,34 +198,14 @@ class PrivateTabManager {
202198 MozElements . MozTab . prototype . getAttribute = function ( att ) {
203199 if ( att == "usercontextid" && this . isToggling ) {
204200 delete this . isToggling ;
205- return privateTab . orig_getAttribute . call ( this , att )
201+ return privateTab . orig_getAttribute . call ( this , att ) == privateTab . container . userContextId
206202 ? 0
207203 : privateTab . container . userContextId ;
208204 } else {
209205 return privateTab . orig_getAttribute . call ( this , att ) ;
210206 }
211207 } ;
212208
213- Object . defineProperty ( customElements . get ( "tabbrowser-tabs" ) . prototype , "allTabs" , {
214- get : function allTabs ( ) {
215- let children = Array . from ( this . arrowScrollbox . children ) ;
216- while ( children . length && children [ children . length - 1 ] . tagName != "tab" ) children . pop ( ) ;
217- return children ;
218- } ,
219- } ) ;
220-
221- customElements . get ( "tabbrowser-tabs" ) . prototype . insertBefore = function ( tab , node ) {
222- if ( ! this . arrowScrollbox ) {
223- throw new Error ( "Shouldn't call this without arrowscrollbox" ) ;
224- }
225-
226- let { arrowScrollbox } = this ;
227- if ( node == null ) {
228- node = arrowScrollbox . lastChild . previousSibling . previousSibling ;
229- }
230- return arrowScrollbox . insertBefore ( tab , node ) ;
231- } ;
232-
233209 customElements . get ( "tabbrowser-tabs" ) . prototype . _updateNewTabVisibility = function ( ) {
234210 let wrap = n => ( n . parentNode . localName == "toolbarpaletteitem" ? n . parentNode : n ) ;
235211 let unwrap = n => ( n && n . localName == "toolbarpaletteitem" ? n . firstElementChild : n ) ;
@@ -292,14 +268,14 @@ class PrivateTabManager {
292268 }
293269
294270 init ( ) {
295- let privateTabGlobal = _ucUtils . sharedGlobal . privateTabGlobal ;
296- ContextualIdentityService . ensureDataReady ( ) ;
297- this . container = ContextualIdentityService . _identities . find (
271+ let { privateTabGlobal } = _ucUtils . sharedGlobal ;
272+ this . ContextualIdentityService . ensureDataReady ( ) ;
273+ this . container = this . ContextualIdentityService . _identities . find (
298274 container => container . name == "Private"
299275 ) ;
300276 if ( ! this . container ) {
301- ContextualIdentityService . create ( "Private" , "fingerprint" , "purple" ) ;
302- this . container = ContextualIdentityService . _identities . find (
277+ this . ContextualIdentityService . create ( "Private" , "fingerprint" , "purple" ) ;
278+ this . container = this . ContextualIdentityService . _identities . find (
303279 container => container . name == "Private"
304280 ) ;
305281 } else if ( ! this . config . neverClearData ) {
@@ -312,7 +288,22 @@ class PrivateTabManager {
312288 CustomizableUI . addListener ( this ) ;
313289
314290 if ( ! privateTabGlobal . privateTabsInited ) {
315- let { getBrowserWindow } = Cu . import ( "resource:///modules/PlacesUIUtils.jsm" ) ;
291+ const lazy = { } ;
292+ ChromeUtils . defineESModuleGetters ( lazy , {
293+ PlacesUtils : "resource://gre/modules/PlacesUtils.sys.mjs" ,
294+ } ) ;
295+ XPCOMUtils . defineLazyModuleGetters ( lazy , {
296+ BrowserWindowTracker : "resource:///modules/BrowserWindowTracker.jsm" ,
297+ PrivateBrowsingUtils : "resource://gre/modules/PrivateBrowsingUtils.jsm" ,
298+ } ) ;
299+ function getBrowserWindow ( aWindow ) {
300+ // Prefer the caller window if it's a browser window, otherwise use
301+ // the top browser window.
302+ return aWindow &&
303+ aWindow . document . documentElement . getAttribute ( "windowtype" ) == "navigator:browser"
304+ ? aWindow
305+ : lazy . BrowserWindowTracker . getTopWindow ( ) ;
306+ }
316307 let openTabsetString = PlacesUIUtils . openTabset . toString ( ) ;
317308 eval (
318309 `PlacesUIUtils.openTabset = ${ openTabsetString . startsWith ( "function" ) ? "" : "function " } ` +
@@ -321,14 +312,6 @@ class PrivateTabManager {
321312 "$1$2$1userContextId: aEvent.userContextId || 0,"
322313 )
323314 ) ;
324-
325- eval (
326- "PlacesUIUtils._openNodeIn = " +
327- PlacesUIUtils . _openNodeIn
328- . toString ( )
329- . replace ( / ( \s + ) ( a P r i v a t e = f a l s e ) \n / , "$1$2,$1userContextId = 0\n" )
330- . replace ( / ( \s + ) ( p r i v a t e : a P r i v a t e , ) \n / , "$1$2$1userContextId,\n" )
331- ) ;
332315 }
333316
334317 let { UUIDMap } = Cu . import ( "resource://gre/modules/Extension.jsm" ) ;
@@ -363,7 +346,7 @@ class PrivateTabManager {
363346 }
364347
365348 closeTabs ( ) {
366- ContextualIdentityService . _forEachContainerTab ( ( tab , tabbrowser ) => {
349+ this . ContextualIdentityService . _forEachContainerTab ( ( tab , tabbrowser ) => {
367350 if ( tab . userContextId == this . container . userContextId ) tabbrowser . removeTab ( tab ) ;
368351 } ) ;
369352 }
@@ -377,6 +360,8 @@ class PrivateTabManager {
377360 index,
378361 skipAnimation : true ,
379362 ...( tab == gBrowser . selectedTab ? { relatedToCurrent : true , ownerTab : tab } : { } ) ,
363+ skipLoad : true ,
364+ preferredRemoteType : aTab . linkedBrowser . remoteType ,
380365 } ;
381366 let newTab = gBrowser . addTrustedTab ( null , tabOptions ) ;
382367
@@ -394,7 +379,7 @@ class PrivateTabManager {
394379 gBrowser . setDefaultIcon ( newTab , uriObj ) ;
395380
396381 // Collect state before flushing.
397- let tabState = this . TabState . collect ( tab , SessionStore . getCustomTabValue ( tab ) ) ;
382+ let tabState = this . TabState . collect ( tab , this . TAB_CUSTOM_VALUES . get ( tab ) ) ;
398383
399384 // Flush to get the latest tab state to duplicate.
400385 let browser = tab . linkedBrowser ;
@@ -431,11 +416,11 @@ class PrivateTabManager {
431416 togglePrivate ( tab = gBrowser . selectedTab ) {
432417 tab . isToggling = true ;
433418 let shouldSelect = tab == gBrowser . selectedTab ;
434- let newTab = this . duplicateTab ( tab , {
419+ this . duplicateTab ( tab , {
435420 index : shouldSelect ? tab . _tPos + 1 : tab . _tPos ,
436421 inBackground : ! shouldSelect ,
437422 } ) ;
438- if ( shouldSelect ) if ( gURLBar . focused ) gURLBar . focus ( ) ;
423+ if ( shouldSelect && gURLBar . focused ) gURLBar . focus ( ) ;
439424 gBrowser . removeTab ( tab , { animate : false , closeWindowWithLastTab : false } ) ;
440425 }
441426
@@ -599,7 +584,7 @@ class PrivateTabManager {
599584 url : Services . io . newURI (
600585 "data:text/css;charset=UTF-8," +
601586 encodeURIComponent (
602- `.privatetab-icon, #${ this . BTN_ID } , #${ this . BTN2_ID } { list-style-image: url(chrome://browser/skin/privateBrowsing.svg) !important; fill: currentColor; -moz-context-properties: fill; } @-moz-document url('chrome://browser/content/browser.xhtml') { #private-mask[enabled="true"] { display: block !important; } #tabbrowser-tabs[hasadjacentnewprivatetabbutton]:not([overflow="true"]) ~ #${ this . BTN_ID } , #tabbrowser-tabs[overflow="true"] > #tabbrowser-arrowscrollbox > #${ this . BTN2_ID } , #tabbrowser-tabs:not([hasadjacentnewprivatetabbutton]) > #tabbrowser-arrowscrollbox > #${ this . BTN2_ID } , #TabsToolbar[customizing="true"] #${ this . BTN2_ID } { display: none; } .tabbrowser-tab[usercontextid="${ this . container . userContextId } "] .tab-label { text-decoration: underline !important; text-decoration-color: -moz-nativehyperlinktext !important; text-decoration-style: dashed !important; } .tabbrowser-tab[usercontextid="${ this . container . userContextId } "][pinned] .tab-icon-image, .tabbrowser-tab[usercontextid="${ this . container . userContextId } "][pinned] .tab-throbber { border-bottom: 1px dashed -moz-nativehyperlinktext !important; }}`
587+ `.privatetab-icon, #${ this . BTN_ID } , #${ this . BTN2_ID } { list-style-image: url(chrome://browser/skin/privateBrowsing.svg) !important; fill: currentColor; -moz-context-properties: fill; } @-moz-document url('chrome://browser/content/browser.xhtml') { #private-mask[enabled="true"] { display: block !important; } #tabbrowser-tabs[hasadjacentnewprivatetabbutton]:not([overflow="true"]) ~ #${ this . BTN_ID } , #tabbrowser-tabs[overflow="true"] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #${ this . BTN2_ID } , #tabbrowser-tabs:not([hasadjacentnewprivatetabbutton]) > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #${ this . BTN2_ID } , #TabsToolbar[customizing="true"] #${ this . BTN2_ID } { display: none; } .tabbrowser-tab[usercontextid="${ this . container . userContextId } "] .tab-label { text-decoration: underline !important; text-decoration-color: -moz-nativehyperlinktext !important; text-decoration-style: dashed !important; } .tabbrowser-tab[usercontextid="${ this . container . userContextId } "][pinned] .tab-icon-image, .tabbrowser-tab[usercontextid="${ this . container . userContextId } "][pinned] .tab-throbber { border-bottom: 1px dashed -moz-nativehyperlinktext !important; }}`
603588 )
604589 ) ,
605590 type : this . sss . USER_SHEET ,
0 commit comments