Supporting per-window private browsing

  • Revision slug: Supporting_per-window_private_browsing
  • Revision title: Supporting per-window private browsing
  • Revision id: 346501
  • Created:
  • Creator: jdm
  • Is current revision? No
  • Comment

Revision Content

Firefox 20 introduced per-window private browsing mode, in which private information that should be stored is accessible concurrently with public information.

Detecting private browsing mode

Determining whether or not a given DOM window is private is simple: import chrome://gre/modules/PrivateBrowsingUtils.jsm and use PrivateBrowsingUtils.isWindowPrivate(win). You can then take action based on this value, as any data or actions originating from this window should be considered private.

Obtaining an nsILoadContext for privacy-sensitive APIs

Some APIs (such as nsITransferable and nsIWebBrowserPersist) take nsILoadContext arguments that are used to determine whether they should be classed as private or not (for example, whether the URI being persisted by saveURI should be added to the permanent download history). Just import chrome://gre/modules/PrivateBrowsingUtils.jsm and use PrivateBrowsingUtils.getPrivacyContextFromWindow(win), passing a Window object that is related to the content in question.

As an example, if an addon adds a context menu item that accesses an API that requires an nsILoadContext, the most relevant window would be the owning one of the element being targeted by the context menu (element.ownerDocument.defaultView). If some action triggered by a chrome element (such as a button) requires an API that takes a privacy context, the most relevant window would be the one containing the chrome element.

In some cases there is no logical window object to use (such as when data or an action originate from some other source than web content). In these cases, it is permissable to pass null as the argument instead of a real privacy context, but this can lead to privacy leaks (such as cache and download entries) if not used carefully.

Clearing any temporarily-stored private data

It is permissable to store private data in non-persistent ways for the duration of a private browsing session. To be notified when such a session ends (ie. when the last private window is closed), observe the last-pb-context-exited notification.

function pbObserver() { /* clear private data */ }
var os = Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
os.addObserver(pbObserver, "last-pb-context-exited", false);

Preventing a private session from ending

If there are unfinished transactions involving private data that will be terminated by the ending of a private session, an addon can vote to prevent the session from doing so (prompting the user is recommended). To do this, observe the last-pb-context-exiting notification and set the data field of the nsISupportsPRBool subject to true.

var os = Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
os.addObserver(function (aSubject, aTopic, aData) {
    aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool);
    // if another extension has not already canceled entering the private mode
    if (!aSubject.data) {
      /* you should display some user interface here */
      aSubject.data = true; // cancel the operation
   }
}, "last-pb-context-exiting", false);

Revision Source

<p>Firefox 20 introduced per-window private browsing mode, in which private information that should be stored is accessible concurrently with public information.</p>
<h2 id="Detecting_private_browsing_mode">Detecting private browsing mode</h2>
<p>Determining whether or not a given DOM window is private is simple: import <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PrivateBrowsingUtils.jsm" title="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PrivateBrowsingUtils.jsm"><code>chrome://gre/modules/PrivateBrowsingUtils.jsm</code></a> and use <code>PrivateBrowsingUtils.isWindowPrivate(win)</code>. You can then take action based on this value, as any data or actions originating from this window should be considered private.</p>
<h2 id="Obtaining_an_nsILoadContext_for_privacy-sensitive_APIs">Obtaining an nsILoadContext for privacy-sensitive APIs</h2>
<p>Some APIs (such as <code>nsITransferable</code> and <code>nsIWebBrowserPersist</code>) take <code>nsILoadContext </code>arguments that are used to determine whether they should be classed as private or not (for example, whether the URI being persisted by <code>saveURI</code> should be added to the permanent download history). Just import <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PrivateBrowsingUtils.jsm" title="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PrivateBrowsingUtils.jsm"><code>chrome://gre/modules/PrivateBrowsingUtils.jsm</code></a> and use <code>PrivateBrowsingUtils.getPrivacyContextFromWindow(win)</code>, passing a Window object that is related to the content in question.</p>
<p>As an example, if an addon adds a context menu item that accesses an API that requires an <code>nsILoadContext</code>, the most relevant window would be the owning one of the element being targeted by the context menu (<code>element.ownerDocument.defaultView</code>). If some action triggered by a chrome element (such as a button) requires an API that takes a privacy context, the most relevant window would be the one containing the chrome element.</p>
<p>In some cases there is no logical window object to use (such as when data or an action originate from some other source than web content). In these cases, it is permissable to pass <code>null</code> as the argument instead of a real privacy context, but this can lead to privacy leaks (such as cache and download entries) if not used carefully.</p>
<h2 id="Clearing_any_temporarily-stored_private_data">Clearing any temporarily-stored private data</h2>
<p>It is permissable to store private data in non-persistent ways for the duration of a private browsing session. To be notified when such a session ends (ie. when the last private window is closed), observe the <code>last-pb-context-exited</code> notification.</p>
<pre class="brush: js">
function pbObserver() { /* clear private data */ }
var os = Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
os.addObserver(pbObserver, "last-pb-context-exited", false);</pre>
<h2 id="Preventing_a_private_session_from_ending">Preventing a private session from ending</h2>
<p>If there are unfinished transactions involving private data that will be terminated by the ending of a private session, an addon can vote to prevent the session from doing so (prompting the user is recommended). To do this, observe the <code>last-pb-context-exiting</code> notification and set the <code>data</code> field of the <code>nsISupportsPRBool</code> subject to true.</p>
<pre class="brush: js">
var os = Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
os.addObserver(function (aSubject, aTopic, aData) {
    aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool);
    // if another extension has not already canceled entering the private mode
    if (!aSubject.data) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;you should display some user interface here */
      aSubject.data = true; // cancel the operation
   }
}, "last-pb-context-exiting", false);</pre>
Revert to this revision