Per-window Private Browsing

From MozillaWiki
Jump to: navigation, search
Please use "Edit with form" above to edit this page.


Per-window Private Browsing
Stage Landed
Status Complete
Release target Firefox 20
Health OK
Status note Landed in Firefox 20


Product manager Sid Stamm
Directly Responsible Individual Josh Matthews
Lead engineer Josh Matthews
Security lead Dan Veditz
Privacy lead `
Localization lead `
Accessibility lead `
QA lead Ioana Budnar
UX lead `
Product marketing lead `
Operations lead `
Additional members Asa Dotzler, Ehsan Akhgari

Open issues/risks

The proposed design will break every addon that uses the existing privite browsing implementation in any way (even just attempting to play nicely with entering/existing).

Stage 1: Definition

1. Feature overview

We want to move away from a global flag model of PB being enabled to a per-window model more in line with other browsers. The implementation should support per-tab private browsing as an implementation detail, but that functionality will not be exposed by default.

2. Users & use cases

Users should be able to open up a new private browsing window without interacting with their existing browsing session in any way (especially through leakage of data from one context to the other).

3. Dependencies


4. Requirements



We do not want to expose per-tab UI for enabling/disabling private browsing.

Stage 2: Design

5. Functional specification


6. User experience design


Stage 3: Planning

7. Implementation plan has a discussion of various consumers of the existing service and the ways they will be modified.

Unresolved design issues:

  • API design for consumers that don't have access to a channel or docshell - duplicate relevant Add/RemoveFoo APIs with Add/RemovePrivateFoo? Something else?

Resolved design issues:

  • Open a new private mode window from command line: -private-window

Can't we use a temporary profile in a separate Firefox instance instead?

  • It's assumed that we want things like extensions, history, bookmarks, etc. from the user's profile to continue to exist in the private window. A separate profile won't provide these, so this is not a feasible quick and dirty solution.

8. Reviews

Security review


Privacy review


Localization review




Quality Assurance review


Operations review


Stage 4: Development

9. Implementation

Implementation is under way by volunteers and paid contributors working in their spare time. Some patches have landed on mozilla-central.

The metabug for this work is with individual bugs filed for existing consumers of the global service. Please see this bug to get a sense of the remaining items.

Stage 5: Release

10. Landing criteria


Feature details

Priority P2
Rank 999
Theme / Goal Contextual Identity
Roadmap Privacy
Secondary roadmap Firefox Desktop
Feature list Desktop
Project `
Engineering team `

Team status notes

  status notes
Products ` `
Engineering ` `
Security sec-review-complete
Privacy ` `
Localization ` `
Accessibility ` `
Quality assurance Signed off Test Plan
User experience ` `
Product marketing ` `
Operations ` `

Full Query
ID Summary Priority Status
456884 Provide a way to open a link into private browsing mode -- RESOLVED
647038 New project branch request for per-window private browsing P2 RESOLVED
722840 Add inherited private browsing attribute to docshells -- RESOLVED
722845 Necko channel infrastructure for nextGen private browsing -- RESOLVED
722849 Remove use of global PB state from cache service -- RESOLVED
722850 Manipulate cookie DB based on per-request private browsing data -- RESOLVED
722853 Determine Private Browsing state from nearest docshell -- RESOLVED
722857 DOMStorage should obtain Private Browsing information from related docshell -- RESOLVED
722859 Download Manager uses global PB service to act on downloads -- RESOLVED
722861 imgLoader uses global PB notifications to clear the image cache -- RESOLVED
722864 nsFilePicker uses global PB service to decide whether to add choices to recent documents -- RESOLVED
722868 nsExternalAppHelperService uses global PB service to decide when to remove temporary files -- RESOLVED
722872 nsClipboardPrivacyHandler uses global Private Browsing state to manipulate clipboard data -- RESOLVED
722942 nsPluginHost and nsNPAPIPluginInstance use Private Browsing global state to set plugin properties -- RESOLVED
722976 ConsoleAPIStorage uses global Private Browsing status to make decisions P2 RESOLVED
722977 Tabs engine uses global Svc.Private to make decisions based on private browsing state -- RESOLVED
722978 exceptionDialog.js makes use of the global Private Browsing service to make decisions -- RESOLVED
722979 nsStrictTransportSecurityService uses the global Private Browsing service to decide where to set permissions -- RESOLVED
722981 nsMediaCacheFlusher uses the global Private Browsing status to decide when to flush the media cache -- RESOLVED
722982 tabview/storagePolicy.js uses the global Private Browsing service to make decisions -- RESOLVED
722983 Private Browsing autostart relies on global service -- VERIFIED
722984 nsBrowserGlue uses global private browsing service to make decisions -- RESOLVED
722985 nsSessionStore makes many decisions based on global Private Browsing state -- RESOLVED
722986 WebContentConverter uses global Private Browsing state to make decisions -- RESOLVED
722987 Places UI makes decisions based on global Private Browsing state -- RESOLVED
722988 openLocationLastURL.jsm uses global Private Browsing state to make decisions -- RESOLVED
722990 NewTabUtils.jsm uses global Private Browsing state to make decisions -- RESOLVED
722993 WindowsJumpLists.jsm uses global Private Browsing state to make decisions -- VERIFIED
722994 globalOverlay.js uses global Private Browsing state to control display of a prompt -- RESOLVED
722995 DownloadLastDir.jsm uses global Private Browsing state to make decisions -- RESOLVED
722996 cookieAcceptDialog.js uses global Private Browsing state to make decisions -- RESOLVED
723001 nsFormHistory.js uses global Private Browsing state to make decisions -- VERIFIED
723002 ContentPrefService uses global Private Browsing state to make decisions -- RESOLVED
723003 nsLoginManager.js uses global Private Browsing state to make decisions -- RESOLVED
723004 nsLoginManagerPrompter.js uses global Private Browsing state to make decisions -- RESOLVED
723005 nsNavHistory uses global Private Browsing state to make decisions -- VERIFIED
723018 Toggle the private browsing attribute of all root docshells when transitioning through private browsing mode -- RESOLVED
723353 Add chrome window API for per-window private browsing -- RESOLVED
725210 Add internal observer notification for last PB docshell going away -- RESOLVED
729140 Add de-XPCOMed getter for private browsing status on nsILoadContext -- RESOLVED
729162 Add per-docshell observer list for private browsing transitions -- RESOLVED
729204 Private docshell tracking won't work with multiple content processes -- RESOLVED
729261 PB transition sets the usePrivateBrowsing attribute too eagerly -- RESOLVED
729706 Ensure worker XHRs inherit load context of page in private mode -- RESOLVED
729707 Spawn a separate instance of plugins for private windows -- RESOLVED
729865 UI for per-window private browsing on desktop -- RESOLVED
729867 There is no way to pop up confirmation dialogs when the last private browsing window is being closed -- RESOLVED
740832 [Security Review][Action Item]Per Window Private Browsing - Workers cookie context -- RESOLVED
741059 Make network favicon requests inherit private browsing context of document -- RESOLVED
748477 Switch chrome privateWindow getter to check currentTab and remove setter -- RESOLVED
748604 Modify Add-on SDK to work with per-window private browsing -- RESOLVED
748635 Make the view source window aware of whether it has been opened from a private document or not -- RESOLVED
748752 nsNPAPIPlugin check for private browsing value will not work -- RESOLVED
748802 browser-thumbnails.js uses global private browsing state instead of per-window state -- RESOLVED
748890 Make getting the load context in nsExternalHelperAppService work in multiprocess mode -- RESOLVED
749187 Ensure new tabs and windows opened in private browsing mode have docshells set accordingly -- RESOLVED
749390 Figure out which keyboard shortcut we want to use on desktop for opening a new Private Browsing window -- RESOLVED
749394 Desktop theme changes for per-window Private Browsing (Windows and OS X) -- RESOLVED
749795 Getter for NPNVprivateModeBool is broken with OOPP -- RESOLVED
758660 Panorama telemetry gatherer checks global PB state -- RESOLVED
763111 Use gBrowser.docShell for brevity in bug 748477 -- RESOLVED
769283 Support concurrent private/public auth caches -- RESOLVED
769285 Support concurrent private/public geolocation wifi access tokens -- RESOLVED
769288 Perform cleanup analogous to the relevant effects of LogoutAndTearDown when leaving private browsing mode -- RESOLVED
769298 Avoid logging script errors coming from private windows in the global error console -- RESOLVED
769460 Add a build time option for enabling per-window private browsing -- RESOLVED
769467 Add a PrivateBrowsingUtils module with a helper for determining whether a content window is in private browsing mode -- RESOLVED
773760 Cross-process clipboard setting doesn't reflect privacy status of original transferable -- RESOLVED
774963 Move the removeDataFromDomain API into its own service -- RESOLVED
788275 Tab preview should respect the private browsing mode when attempting to load a favicon -- RESOLVED
794606 nsDownload::Resume creates a channel out of thin air -- RESOLVED
795556 Propagate parent window privacy status to newly opened windows -- RESOLVED
795571 Various nsIChannel consumers are checking for private browsing incorrectly -- RESOLVED
798508 Implement an API for opening new windows in private browsing -- RESOLVED
798516 Remove support for gPrivateBrowsingUI.privateWindow -- RESOLVED
798957 Add MOZ_PER_WINDOW_PRIVATE_BROWSING to the list of preprocessor macros -- RESOLVED
798965 Avoid using gPrivateBrowsingUI.privateBrowsingEnabled in browser.js and use the per-window PB API instead -- RESOLVED
799001 Stub out the necessary UI to let us open a private browsing window -- RESOLVED
799229 Move the ForgetAboutSite module and the related tests from browser/components/privatebrowsing/ to toolkit -- RESOLVED
799314 Add utility method to PrivateBrowsingUtils to obtain nsILoadContext from window -- RESOLVED
799526 Split the private browsing browser-chrome test suite into three buckets to make it easier to transition to per-window private browsing -- RESOLVED
799664 Disable xpcshell tests in per-window PB builds for now -- RESOLVED
799780 Remove gPrivateBrowsingUI.autoStarted and move that API to PrivateBrowsingUtils -- RESOLVED
799784 Replace usages of nsIPrivateBrowsingService.autoStarted with PrivateBrowsingUtils.permanentPrivateBrowsing -- RESOLVED
800193 Make the nsILoadContext.usePrivateBrowsing property read-only -- RESOLVED
801151 When leaving private browsing, we set the non-private flag on the existing docshells too soon -- RESOLVED
801232 Support per-window private browsing in the Downloads Panel -- RESOLVED
801653 Tracking bug for porting global private browsing tests to per-window private browsing tests -- NEW
801823 Figure out the UI for per-window private browsing autostart mode -- RESOLVED
802274 Starting Firefox with -private flag incorrectly claims you are not in Private Browsing mode -- VERIFIED
804653 Make docshell trigger PB mode exit from Destroy, not destructor -- RESOLVED
808215 Disable social API in private windows (for per-window Private Browsing builds) -- VERIFIED
810208 Clicking the Downloads Finished alert box always brings up public download list P1 RESOLVED
814275 Private browsing not work correctly after opening new window with clicking dock icon -- RESOLVED
815363 PB changes broke autocomplete-will-enter-text notification -- RESOLVED
816406 Disallow dragging a private tab into a non-private window and vice versa -- RESOLVED
816524 Do not store information about closed private windows -- RESOLVED
816527 In per-window private browsing switch-to-tab should not show tabs from private windows -- VERIFIED
816914 per-window PB: about:privatebrowsing Information text is not correct -- RESOLVED
817931 browser_354894_perwindowpb.js fails on birch (on Linux) -- RESOLVED
818601 Closed private browsing windows show up in "Recently Closed Windows" -- RESOLVED
818732 Switch Nightly to per-window private browsing -- RESOLVED
818740 Update the help page for private browsing according to per-window private browsing -- RESOLVED
818800 Remove the global private browsing service in per-window PB builds -- RESOLVED
819202 Attempting to open a new public window when a private window is focused opens a new private window -- RESOLVED
819274 Disable the Restore Previous Session command in private windows -- RESOLVED
819387 Extensions broken by per-window private browsing changes -- RESOLVED
819457 tabs from last time come up instead of "show my home page" -- RESOLVED
819458 Unexpected session restore shows up after browser restart -- RESOLVED
819510 Quitting from a Private Browsing window makes Restore Previous Session fail once -- RESOLVED
819571 some urls resulting from add-on installation in PB mode show in about:cache -- RESOLVED
820120 Remove the pref: 'browser.privatebrowsing.dont_prompt_on_enter' -- RESOLVED
820136 Need "open new private window" in windows jump list -- RESOLVED
820254 Make the new tab preloader use the correct hidden window in private windows -- RESOLVED
822008 Only listen to private-browsing-transition-complete in global PB builds for determining the new tab URL -- RESOLVED
822016 Remove the global PB specific code from the tabview UI -- RESOLVED
822017 Remove the global PB specific code from the testpilot extension -- RESOLVED
822019 Remove the global PB specific code from the Telemetry -- RESOLVED
822020 Remove the global PB specific code from the toolkit downloads UI -- RESOLVED
822330 No showing DL meter in win7 taskbar with downloading on private window -- RESOLVED
823300 Per-window private browsing mode links are not marked as visited correctly -- RESOLVED
823580 Remove PlacesUIUtils.privateBrowsing usages in per-window PB builds -- RESOLVED
823683 PrivateBrowsingUtils.isWindowPrivate returns incorrect value when changing browser.privatebrowsing.autostart preference -- RESOLVED
823706 Remove global private browsing usage from pdf.js -- RESOLVED
823725 Remove the Svc.Private getter since it is no longer used -- RESOLVED
823732 Remove the usage of the global PB service from nsSessionStartup.js in per-window PB builds -- RESOLVED
823945 Port XUL Fennec's LoginManagerChild to use per-window PB APIs -- RESOLVED
823949 Port XUL Fennec's controller.js to per-window PB APIs -- RESOLVED
825295 Make the in-content downloads view show only private downloads when opened in a private browsing window -- VERIFIED
825454 Make contentAreaDownloadsView.xul a proper in-content UI -- RESOLVED
826037 Re-introduce an obsolete private browsing service in order to not break all Jetpack add-ons -- RESOLVED
826063 Provide scriptable alternative to NS_UsePrivateBrowsing -- RESOLVED
826079 Update the add-on verifier to check for usages of the global private browsing APIs -- RESOLVED
826371 Canceling UI for per-window private browsing autostart mode, doesn't revert browser.privatebrowsing.autostart preference -- RESOLVED
827454 Private browsing theme changes for Linux -- RESOLVED
827460 Copy/paste of "About Firefox" window's build version text no longer works when the window is closed -- VERIFIED
827954 Firefox button overlaps the first tab in private windows -- VERIFIED
828780 "last-pb-context-exiting" doesn't fire when private window is closed -- RESOLVED
829180 Externally opened urls may open in private browsing windows -- VERIFIED
829236 Firefox button on Nightly may overlap tab strip in Private window. -- RESOLVED
829383 gNumberOfAlwaysOpenPrivateDocShells will be incorrect when hidden private window contains child docshells -- RESOLVED
829568 Starting a private window from the Firefox icon pinned to the taskbar wipes your regular session -- VERIFIED
829870 Appear the non-expected color gap on window that are opened from private one. -- VERIFIED
830652 Starting Firefox with -private-window flag shows console error -- RESOLVED
832177 Opening a tab in Private window sometimes incorrectly shows about:newtab -- RESOLVED
832290 Proper styling for full-screen private windows on Mac -- VERIFIED
832325 Undo close tab no longer works in permanent Private Browsing mode (“Never remember history”). -- VERIFIED
837091 cookie-changed notifications are not useful for private cookies -- RESOLVED
838541 Storage events leak from private browsing windows to normal windows -- VERIFIED
839073 Titlebar is collapsed in private browsing windows -- RESOLVED
840739 Flash links in private windows are converting normal windows into private windows. -- VERIFIED
841359 Avoid logging script errors coming from private windows in the global error console -- RESOLVED
842015 Normal tabs are converted to private tabs -- VERIFIED
842290 "Restart Aurora" pop up doesn't appear when selecting private browsing mode -- VERIFIED
843247 Hyperlinks from other applications only open in new window when using private browsing (instead of in a new tab) -- RESOLVED
844427 New windows should maintain the openers private-browsing state -- RESOLVED
844561 Firefox prompts when closing private window -- RESOLVED
844671 Tabs sent through Sync should not open in private browsing windows -- RESOLVED
845892 New tab in perma-PB mode default to about:newtab -- RESOLVED
845893 window.close() on a private window doesn't pass through warnAboutClosingWindow -- RESOLVED
846300 Security Warning messages in console should have associated window ID -- RESOLVED
846911 Audit callers of getMostRecentBrowserWindow/getMostRecentWindow for privacy concerns -- NEW
854126 Lift the lightweight theme restrictions for private windows P2 VERIFIED
854926 Firefox prompts a warning when closing private windows with multiple tabs open -- VERIFIED
1097149 Add option to warn on closing Private Window with multiple tabs -- NEW

165 Total; 3 Open (1.82%); 141 Resolved (85.45%); 21 Verified (12.73%);


The current Private Browsing (PB) implementation in Firefox is very disruptive, because it closes all of your open tabs and windows. Although we do a relatively good job at restoring everything back to the state before initiating the PB session, we should really not require the user to give up their existing session.

This page summarizes the design which can lead to a per-window Private Browsing implementation for Desktop Firefox.

Global Private Browsing Mode Design

The global mode PB implementation is basically an application wide boolean flag which designates whether the PB mode is on or off. The private browsing service manages this global flag, and sends a bunch of notifications when the global mode is changing (for example, when the user invokes the Private Browsing flag). Each module which needs to store data which can identify the websites that a user has previously visited needs to handle these notifications in order to adjust its behavior depending on the PB mode flag, and refrain from storing such data inside this mode. For more information on how the existing API works, see this document.

Per-window Private Browsing Mode Design

In order to move towards a per-window PB design, we can't represent the PB state as a global flag any more. At a birds-eye view, we need to store a per-window boolean flag and each module which needs to handle its storage needs based on the PB status should somehow be able to know which window is ultimately responsible for the request at hand.

The per-window flag is going to be handled by a Gecko object called a docshell. A docshell is simply an object which stores the information associated which every document that Gecko loads which has a presentation. Each docshell can either be a content or a chrome docshell. A chrome docshell represents a document which has chrome privileges, such as browser.xul which renders Firefox's main window, or about:addons. A content docshell represents a document which does not have chrome privileges, which is most often used to load a website inside the browser.

The docshells are arranged in a tree. For example, in a simple browser window rendering Google's homepage, there is a chrome docshell representing the browser window, and a content docshell rendering the homepage (plus another content docshell as its child reprenting an iframe inside Google's home page).

We will leverage this structure in order to provide a per-tab Private Browsing API. Firefox is probably not going to expose per-tab PB mode, as it will be too confusing to users, but we can have a more flexible API which add-ons can leverage.


Here is a per-module specification for how the per-window Private Browsing mode will be implemented. This specification is borrowing from the specification for the global private browsing mode.


Each docshell maintains a privateBrowsing boolean flag.

  • Upon docshell creation:
    • For chrome docshells, the flag is initialized to false.
    • For content docshells, the flag is initialized to the value of the parent docshell's privateBrowsing flag.
  • On getting, the docshell returns the last value set for that flag.
  • On setting, the following happens:
    • If the docshell is a content docshell, the set operation fails, returning NS_ERROR_FAILURE, unless the set operation is being invoked by a parent docshell.
    • If the docshell is a chrome docshell, the set operation succeeds. The docshell will look into all of its child subtree, and set the privateBrowsing flag on each element in its subtree with its own flag.


Since nav history occurs through the docshell, we can block history (and related, eg. pushState) in private windows using available information. The History/nsNavHistory services currently have various checks about private browsing mode to avoid API callers from using them "incorrectly" in PB mode. The proposal is to remove these checks - any consumer that is explicitly using the API is assumed to know what it is doing (addons included).


Like above, we can remove any checks for private browsing - we already allow bookmark modifications in the existing PB mode.


HTTP connections from private browsing contexts go into a separate, in-memory cache session that can be evicted when all private browsing contexts are closed.


The Cookie Service will maintain an in-memory hashtable for the cookies set in PB mode. This table will be completely separate from the normal cookie storage that it uses, and it is possible for cookies with the same hostname and the same name to be stored in both tables.

  • On setting a cookie, the Cookie Service tries to get a docshell for the channel setting the cookie (hint: we may be able to borrow some code from nsCookiePermission::GetOriginatingURI). If there is a docshell associated with the channel, and its privateBrowsing flag is set to true, the Cookie Service will store the cookie in its in-memory PB mode hashtable. Otherwise, it will use its normal storage.
  • On getting a cookie, the Cookie Service tries to get a docshell for the channel getting the cookie. If there is a docshell associated with the channel, and its privateBrowsing flag is set to true, the Cookie Service will use its in-memory PB hashtable. Otherwise, it will use its normal storage.


Proposed changes: the download manager is given an optional nsILoadContext argument when adding downloads which is used to determine whether the download is private. All download databases (private and non-private) get an extra GUID column, and methods that refer to downloads by ID change to accept GUIDs instead. The download manager window is left alone and only displays non-private downloads; the forthcoming download panel is modified to display private downloads in private windows.


Various places need to make decisions about how much data to delete when asked to do so (ie. clear cookies, etc.), but we might be able to get away with always deleting private and public info. We need to ensure that any key/vakue caching mechanism that exists doesn't get confused if a site is using localStorage/sessionStorage in both PB and non-PB modes concurrently. We also need to ensure that a docshell that transitions into/out of PB mode drops references to any current cached storage object.

Implementation concerns


The design which requires access to docshells is fundamentally broken for e10s, where networking occurs in the parent process and the relevant docshell is in a content process. This patch demonstrates a correct implementation for HTTP channels, whereby a concrete nsHttpChannel object can be reliably queried as to its PB usage, regardless of process. Possible solutions:

  • add a new interface (or modify nsIChannel) to add private browsing status to every channel type, and propagate the information from the child to the parent as in the previously-linked HTTP patch
  • make docshells implement IPDL actors, so that querying the docshell on the parent gives something useful. probably a non-starter.

I've chosen to make a clean break and create an nsIPrivateBrowserConsumer interface. This patch demonstrates the application of this to FTP and HTTP channels in a very elegant manner.


The cookie service already has a non-PB DB and a PB DB. However, functions like GetEnumerator assume that they can enumerate all the cookies of the currently active DB - how should this interact with the cookie list, which call this to list all cookies?


Ehsan and I agreed that there should be more of a separation than just PB vs. non-PB for some concepts like cache sessions, downloads, HTTP connectinos, etc. These should be grouped based on root docshell, so that when a root docshell in PB is closed, the corresponding session data should be destroyed.