User:Ehsan/PrivateBrowsingForExtensions

From MozillaWiki
Jump to: navigation, search

Introduction

Many extensions may store data which can reveal information about user's browsing history. For example, and extension may record the last visit date for all the websites that the user visits. There is currently no reliable way for us to disable storing such data by extensions, therefore it is very important to provide simple to use APIs as well as code samples which extension developers can use to integrate the Private Browsing mode with their extensions.

APIs for Extensions

The Private Browsing service

The Private Browsing service can be used in order to get and set the current status of private browsing. The contract ID for this service is @mozilla.org/browser/privatebrowsing;1. It supports the nsIPrivateBrowsingService interface:

[scriptable, uuid(d2943870-c781-11dc-95ff-0800200c9a66)]
interface nsIPrivateBrowsingService : nsISupports
{
    // Whether or not the private browsing mode is currently active.
    attribute boolean privateBrowsing;
};

The privateBrowsing attribute is key to the functionality of this service. By setting it to true, private browsing mode will be initiated. Likewise, by setting it to false, the private browsing mode will be terminated. The current status of the private browsing mode can be queried by reading the value of this attribute.

Private Browsing notifications

Extensions can be notified of private browsing status changes, i.e., when the user enters or leaves the private browsing mode. This is done via standard XPCOM observer service mechanisms. The notification's name is browser:private-browsing. The data parameter can be either enter (indicating that the private browsing mode has been initiated) or exit (indicating that the private browsing mode has been terminated). The subject parameter is an object implementing the nsISupportsPRBool interface, whose data member determines whether we're leaving the private browsing mode because Firefox is being shutdown or not (true if Firefox is being shut down, false otherwise).

The second code sample below implements a full blown listener for the private browsing mode, which can both be used to query its current status, and set a watcher to watch for entering and/or leaving the private mode.

Some extensions might benefit from preventing the user from entering the private browsing mode. An example would be a download manager extension, which needs to ask the user if he wants to switch the private mode if a download is currently in progress. Those extensions can handle the private-browsing-request notification. This notification sends either enter or exit as the data parameter in order to indicate whether the user has requested entering or leaving the private browsing mode. The subject parameter to this notification implements the nsISupportsPRBool interface. If the extensions wants to prevent the user from entering or leaving the private browsing mode, it should set the data field of the subject parameter to true. Extensions which handle the private-browsing-request notification are supposed to ignore this notification if the subject's data member equals true (which means that the user has already canceled the notification).

The code samples section below contains a sample of an extension which chooses to cancel leaving the private browsing mode (which is not a good practice; in the real worlds, the extension should just ask the user what she wants to do).

Another possibility for extensibility with the private browsing service is the private-browsing-enter notification. This notification is sent by the private browsing service before entering the private mode, and is meant to give extension developers a chance to provide their own UI for handling the decision on whether to keep the current session open or save and close all the current windows and tabs, and then restore them when exiting the private browsing mode. The subject parameter of this notification is a nsISupportsPRUint32 which is supposed to be set to a value which reflects the user's decision. Returning 0 in the subject variable causes the browser to keep the current session, and returning 1 causes the current session to be saved and closed. Returning any other value would trigger the default behavior, which is a dialog box shown to the user asking for his decision. The fourth code sample below implements a custom handler for this notification.

Code Samples

Extensions that want to check the status of the Private Browsing mode

var pbs = Components.classes["@mozilla.org/browser/privatebrowsing;1"]
                    .getService(Components.interfaces.nsIPrivateBrowsingService);

// are we currently in the Private Browsing mode?
var inPrivateBrowsingMode = pbs.privateBrowsing;

Extensions that want to listen to the Private Browsing mode changes

function PrivateBrowsingListener() {
  this.init();
}

PrivateBrowsingListener.prototype = {
  _os: null,
  _inPrivateBrowsing: false, // whether we are in private browsing mode
  _inited: false,
  _watcher: null, // the watcher object

  QueryInterface : function (iid) {
    if (iid.equals(Components.interfaces.nsISupports) ||
        iid.equals(Components.interfaces.nsIObserver))
      return this;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  init : function () {
    if (!this._inited) {
      this._inited = true;
      this._os = Components.classes["@mozilla.org/observer-service;1"]
                           .getService(Components.interfaces.nsIObserverService);
      this._os.addObserver(this, "browser:private-browsing", false);
      this._os.addObserver(this, "quit-application", false);
    }
  },

  observe : function (aSubject, aTopic, aData) {
    if (aTopic == "browser:private-browsing") {
      if (aData == "enter") {
        this._inPrivateBrowsing = true;
        if (this.watcher &&
            "onEnterPrivateBrowsing" in this._watcher) {
          this.watcher.onEnterPrivateBrowsing();
        }
      } else if (aData == "exit") {
        this._inPrivateBrowsing = false;
        if (this.watcher &&
            "onExitPrivateBrowsing" in this._watcher) {
          this.watcher.onExitPrivateBrowsing();
        }
      }
    } else if (aTopic == "quit-application") {
      this._os.removeObserver(this, "quit-application");
      this._os.removeObserver(this, "browser:private-browsing");
    }
  },

  get inPrivateBrowsing() {
    return this._inPrivateBrowsing;
  },

  get watcher() {
    return this._watcher;
  },

  set watcher(val) {
    this._watcher = val;
  }
};

var listener = PrivateBrowsingListener();

if (listener.inPrivateBrowsing) {
  // we are in the private mode!
} else {
  // we are not in the private mode!
}

listener.watcher = {
  onEnterPrivateBrowsing : function() {
    // we have just entered the private browsing mode!
  },

  onExitPrivateBrowsing : function() {
    // we have just left the private browsing mode!
  }
};

Extensions that want to initiate or terminate a Private Browsing session

var pbs = Components.classes["@mozilla.org/browser/privatebrowsing;1"]
                    .getService(Components.interfaces.nsIPrivateBrowsingService);

// enter the Private Browsing mode
pbs.privateBrowsing = true;

// now, whatever we do remains private!

// exit the Private Browsing mode
pbs.privateBrowsing = false;

Extensions that want to provide a custom UI to ask the user whether to keep the current session

var os = Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
os.addObserver(function (aSubject, aTopic, aData) {
    // somehow ask the user for his decision,
    // e.g., show a dialog box...

    aSubject.QueryInterface(Components.interfaces.nsISupportsPRUint32);
    if (/* user wants to close the current session */)
      aSubject.data = 1;
    else /* user wants to keep the current session */
      aSubject.data = 0;
  }, "private-browsing-enter", false);

Extensions that want to prevent the user from leaving the private mode

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) {
      if (aData == "exit") { // if we are leaving the private mode
        aSubject.data = true; // cancel the operation
      }
    }
  }, "private-browsing-request", false);