Skip to content
Adds private tabs, restartless extension for Firefox (20.0+) and SeaMonkey (2.17+)
JavaScript HTML Batchfile Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
locale update Aug 10, 2017 Info about WebExtensions port Nov 6, 2017
bootstrap.js Remove delay from updateTabsInTitlebar() to modify UI asap: Nov 13, 2018 Update changelog: Nov 14, 2018
chrome.manifest Trim spaces Jul 26, 2017
content.js Optimize: load content.jsm only into remote tabs Nov 12, 2016
log.js Fix madness with this.__defineGetter__() calls in Firefox 60.0a1 Jan 26, 2018
make.bat Update make.bat: Jun 27, 2014 Sync with make.bat Jun 28, 2014
patcher.jsm Tweak comment about sandbox.window Jan 25, 2017
privacy-24.png Add own icons for toolbar button: Mar 21, 2013
privacy-32.png Add 32×32 px icon for Australis Dec 17, 2013
protocol-process.js Clarify logic around debug logs from private protocol Aug 19, 2016
protocol.jsm Simplify code for debug logs from content process Nov 9, 2018
protocolRedirect.html Simplify URI validation Nov 9, 2018

Works only in Gecko 20.0 and higher because used API doesn't exist in older versions!

WebExtensions and compatibility with Firefox 57+

Issue #254 + Private Tab WE repository

Known issues:

  • We just inherit private state from selected tab and tries preserve private state of dropped link-like things, this is simple to implement, but may confuse a bit…
  • If you use "New Private Tab" + "New Tab" buttons after tabs toolbar, you need to manually remove "New Private Tab" button before disabling or uninstalling Private Tab. Or you can remove "New Tab" button, press OK in Customize Toolbar dialog and then place "New Tab" directly after tabs.
  • Can't open new private tab, if installed Scriptify-based extension: please use Greasemonkey or Scriptish instead (#110).
  • In Firefox 52+ private:… URI may be opened only in new tab due to fix for bug 1318388 (#251).


You can use .tabbrowser-tab[privateTab-isPrivate] (private tab), #main-window[privateTab-selectedTabIsPrivate] (selected tab is private) and #main-window[privateTab-isPrivate] (built-in private window) selectors in styles for userChrome.css/Stylish.
Example styles:


See extensions.privateTab.* preferences in about:config page, some descriptions can be found in defaults/preferences/prefs.js.

  • extensions.privateTab.sendRefererHeader
    0 – don't send HTTP referer when URI is opened in new private tab
    1 – send only if private tab opened from another private tab
    2 – always send (as Firefox itself do for “Open Link in New Private Window”)

  • extensions.privateTab.usePrivateWindowStyle
    true – use style of private window, if current tab is private
    false – only show private state of window (regardless of current tab private state)

Keyboard shortcuts:

You can modify keyboard shortcuts through about:config page, see notes about extensions.privateTab.key.* preferences in defaults/preferences/prefs.js.


Try new clean Firefox profile to ensure that there are no conflicts with another extensions or some specific configuration.

Debug logs:

Options in about:config:

  • extensions.privateTab.debug – enable debug logs
  • extensions.privateTab.debug.verbose – additionally enable detailed debug logs (not needed in most cases)

Then use Browser Console (formerly Error Console, Ctrl+Shift+J) to see messages like [Private Tab] ….

API for other extensions:


You can listen for following events:

event.type event.detail Description
PrivateTab:PrivateChanged tab 1 – private tab
0 – not private
Changed private state of the tab
event.explicitOriginalTarget – initial tab in case of toggling using duplication (Firefox 51+, Private Tab
PrivateTab:OpenInNewTab tab 1 – may be opened as child tab Link was opened in new private tab
PrivateTab:OpenNewTab tab 1 – opened using middle-click
(or left-click with any modifier)
Opened new (empty) private tab
API functions:

boolean privateTab.isTabPrivate(in DOMNode tab)
void privateTab.isTabPrivateAsync(in DOMNode tab, in function callback[, in object context])
boolean privateTab.toggleTabPrivate(in DOMNode tab[, in boolean isPrivate])
DOMNode privateTab.duplicateTabAndTogglePrivate(in DOMNode tab[, in boolean isPrivate])
DOMNode privateTab.replaceTabAndTogglePrivate(in DOMNode tab[, in boolean isPrivate])
void privateTab.readyToOpenTab(in boolean isPrivate)
void privateTab.readyToOpenTabs(in boolean isPrivate)
boolean privateTab.hasClosedTabs
void privateTab.forgetClosedTabs()
boolean privateTab.tabLabelIsEmpty(in string tabLabel[, in boolean isEmpty])


Investigates that the tab are private (true) or not (false), example:

// Close all (visible) private tabs:
Array.slice(gBrowser.visibleTabs || gBrowser.tabs).forEach(function(tab) {

The same as privateTab.isTabPrivate, but get real state of nsILoadContext.usePrivateBrowsing in multi-process mode (Electrolysis aka e10s), Private Tab 0.2.1+.
Not needed in most cases!

// Asynchronously get privacy state of given tab.
// In most cases should be replaced with synchronous privateTab.isTabPrivate()
privateTab.isTabPrivateAsync(gBrowser.selectedTab, function(isPrivate) {
	alert("Selected tab is " + (isPrivate ? "private" : "not private"));

Changes tab private state:
Toggle: privateTab.toggleTabPrivate(tab)
Make private: privateTab.toggleTabPrivate(tab, true)
Make not private: privateTab.toggleTabPrivate(tab, false)

// Make all (visible) tabs private:
	gBrowser.visibleTabs || gBrowser.tabs,
	function(tab) {
			privateTab.toggleTabPrivate(tab, true);

Duplicate already opened tab in private or non-private tab.

// Duplicate selected tab and toggle private state of duplicated tab
var tab = gBrowser.selectedTab;
var pos = "_tPos" in tab
	? tab._tPos
	:, tab); // SeaMonkey
var dupTab = gBrowser.selectedTab = privateTab.duplicateTabAndTogglePrivate(tab);
gBrowser.moveTabTo(dupTab, pos + 1); // Place right after initial tab

Changes tab private state using tab duplication (for Firefox 52+):

// Duplicate selected tab, toggle private state of duplicated tab and then close initial tab

Allows to open private or not private tab (independent of any inheritance mechanism), example:

// Open in private tab:
// Open in not private tab:

Allows to open many private or not private tabs (independent of any inheritance mechanism), example:

// Open in private tabs:
// ...

Only for extensions.privateTab.rememberClosedPrivateTabs = true, Private Tab
Return true, if there is at least one private tab in undo close list, example:

	alert("We have at least one closed private tabs");
	alert("We don't have closed private tabs");

Only for extensions.privateTab.rememberClosedPrivateTabs = true, Private Tab
Forget about all closed private tabs in window, example:

// Forget about all closed private tabs in window

Mark tab label as empty (or non-empty), example:

// Mark tab label/URI as empty:
if("tabLabelIsEmpty" in privateTab) // Private Tab
	privateTab.tabLabelIsEmpty("chrome://speeddial/content/speeddial.xul", true);
// Check state:
var isEmpty = privateTab.tabLabelIsEmpty("chrome://speeddial/content/speeddial.xul");
// Restore state (e.g. for restartless extensions):
privateTab.tabLabelIsEmpty("chrome://speeddial/content/speeddial.xul", false);

Note: used global storage for labels (not per-window)! So, it's enough to call this function only once.

Backward compatibility:

Check for Private Tab installed (and enabled):

if("privateTab" in window) {
	// Do something with "privateTab" object
Code examples:

Open link in private tab using FireGestures extension:

// Remember the active tab's position
var tab = gBrowser.selectedTab;
var pos = "_tPos" in tab
	? tab._tPos
	:, tab); // SeaMonkey
// Get the DOM node at the starting point of gesture
var srcNode = FireGestures.sourceNode;
// Get the link URL inside the node
var linkURL = FireGestures.getLinkURL(srcNode);
if (!linkURL)
    throw "Not on a link";
// Check the URL is safe
FireGestures.checkURL(linkURL, srcNode.ownerDocument);
// Open link in new private tab
var newPrivateTab = gBrowser.addTab(linkURL);
gBrowser.moveTabTo(newPrivateTab, pos + 1); // Place right after initial tab
You can’t perform that action at this time.