Merge mozilla-central into mozilla-inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 06 Dec 2012 23:13:32 -0500
changeset 120844 2a54ecb3c0aac9e3ed047ca0a0f58ae9095bff48
parent 120843 4d10b613761fae384e257e8720e902316fbe853e (current diff)
parent 120748 739f20de3c1e0dd1ebaaf9c8130a4194846e86fe (diff)
child 120845 f586bd6769d386dc7544051b1c947ab0a77631d8
push idunknown
push userunknown
push dateunknown
milestone20.0a1
Merge mozilla-central into mozilla-inbound
browser/components/privatebrowsing/test/unit/test_geoClearCookie.js
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -39,16 +39,18 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
                                   "resource://gre/modules/DownloadUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsPanel
 
 /**
  * Main entry point for the downloads panel interface.
  */
 const DownloadsPanel = {
@@ -102,17 +104,17 @@ const DownloadsPanel = {
     // panel, starting the service will make us load their data asynchronously.
     Services.downloads;
 
     // Now that data loading has eventually started, load the required XUL
     // elements and initialize our views.
     DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
                                                function DP_I_callback() {
       DownloadsViewController.initialize();
-      DownloadsCommon.data.addView(DownloadsView);
+      DownloadsCommon.getData(window).addView(DownloadsView);
       DownloadsPanel._attachEventListeners();
       aCallback();
     });
   },
 
   /**
    * Closes the downloads panel and frees the internal resources related to the
    * downloads.  The downloads panel can be reopened later, even after this
@@ -125,20 +127,22 @@ const DownloadsPanel = {
     }
 
     window.removeEventListener("unload", this.onWindowUnload, false);
 
     // Ensure that the panel is closed before shutting down.
     this.hidePanel();
 
     DownloadsViewController.terminate();
-    DownloadsCommon.data.removeView(DownloadsView);
+    DownloadsCommon.getData(window).removeView(DownloadsView);
     this._unattachEventListeners();
 
     this._state = this.kStateUninitialized;
+
+    DownloadsSummary.active = false;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Panel interface
 
   /**
    * Main panel element in the browser window.
    */
@@ -225,17 +229,17 @@ const DownloadsPanel = {
     // Ignore events raised by nested popups.
     if (aEvent.target != aEvent.currentTarget) {
       return;
     }
 
     this._state = this.kStateShown;
 
     // Since at most one popup is open at any given time, we can set globally.
-    DownloadsCommon.indicatorData.attentionSuppressed = true;
+    DownloadsCommon.getIndicatorData(window).attentionSuppressed = true;
 
     // Ensure that an item is selected when the panel is focused.
     if (DownloadsView.richListBox.itemCount > 0 &&
         !DownloadsView.richListBox.selectedItem) {
       DownloadsView.richListBox.selectedIndex = 0;
     }
 
     this._focusPanel();
@@ -244,17 +248,17 @@ const DownloadsPanel = {
   onPopupHidden: function DP_onPopupHidden(aEvent)
   {
     // Ignore events raised by nested popups.
     if (aEvent.target != aEvent.currentTarget) {
       return;
     }
 
     // Since at most one popup is open at any given time, we can set globally.
-    DownloadsCommon.indicatorData.attentionSuppressed = false;
+    DownloadsCommon.getIndicatorData(window).attentionSuppressed = false;
 
     // Allow the anchor to be hidden.
     DownloadsButton.releaseAnchor();
 
     // Allow the panel to be reopened.
     this._state = this.kStateHidden;
   },
 
@@ -1105,17 +1109,21 @@ const DownloadsViewController = {
     // ancestors of the focused element.
     return !!element;
   },
 
   isCommandEnabled: function DVC_isCommandEnabled(aCommand)
   {
     // Handle commands that are not selection-specific.
     if (aCommand == "downloadsCmd_clearList") {
-      return Services.downloads.canCleanUp;
+      if (PrivateBrowsingUtils.isWindowPrivate(window)) {
+        return Services.downloads.canCleanUpPrivate;
+      } else {
+        return Services.downloads.canCleanUp;
+      }
     }
 
     // Other commands are selection-specific.
     let element = DownloadsView.richListBox.selectedItem;
     return element &&
            new DownloadsViewItemController(element).isCommandEnabled(aCommand);
   },
 
@@ -1152,31 +1160,35 @@ const DownloadsViewController = {
 
   /**
    * This object contains one key for each command that operates regardless of
    * the currently selected item in the list.
    */
   commands: {
     downloadsCmd_clearList: function DVC_downloadsCmd_clearList()
     {
-      Services.downloads.cleanUp();
+      if (PrivateBrowsingUtils.isWindowPrivate(window)) {
+        Services.downloads.cleanUpPrivate();
+      } else {
+        Services.downloads.cleanUp();
+      }
     }
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsViewItemController
 
 /**
  * Handles all the user interaction events, in particular the "commands",
  * related to a single item in the downloads list widgets.
  */
 function DownloadsViewItemController(aElement) {
   let downloadGuid = aElement.getAttribute("downloadGuid");
-  this.dataItem = DownloadsCommon.data.dataItems[downloadGuid];
+  this.dataItem = DownloadsCommon.getData(window).dataItems[downloadGuid];
 }
 
 DownloadsViewItemController.prototype = {
   //////////////////////////////////////////////////////////////////////////////
   //// Constants
 
   get kPrefBdmAlertOnExeOpen() "browser.download.manager.alertOnEXEOpen",
   get kPrefBdmScanWhenDone() "browser.download.manager.scanWhenDone",
@@ -1452,20 +1464,20 @@ const DownloadsSummary = {
    *        Set to true to activate the summary.
    */
   set active(aActive)
   {
     if (aActive == this._active || !this._summaryNode) {
       return this._active;
     }
     if (aActive) {
-      DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
+      DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
                      .addView(this);
     } else {
-      DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
+      DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
                      .removeView(this);
       DownloadsFooter.showingSummary = false;
     }
 
     return this._active = aActive;
   },
 
   /**
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -281,31 +281,31 @@ const DownloadsIndicatorView = {
   ensureInitialized: function DIV_ensureInitialized()
   {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
     window.addEventListener("unload", this.onWindowUnload, false);
-    DownloadsCommon.indicatorData.addView(this);
+    DownloadsCommon.getIndicatorData(window).addView(this);
   },
 
   /**
    * Frees the internal resources related to the indicator.
    */
   ensureTerminated: function DIV_ensureTerminated()
   {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
 
     window.removeEventListener("unload", this.onWindowUnload, false);
-    DownloadsCommon.indicatorData.removeView(this);
+    DownloadsCommon.getIndicatorData(window).removeView(this);
 
     // Reset the view properties, so that a neutral indicator is displayed if we
     // are visible only temporarily as an anchor.
     this.counter = "";
     this.percentComplete = 0;
     this.paused = false;
     this.attention = false;
   },
@@ -322,17 +322,17 @@ const DownloadsIndicatorView = {
     }
 
     function DIV_EO_callback() {
       this._operational = true;
 
       // If the view is initialized, we need to update the elements now that
       // they are finally available in the document.
       if (this._initialized) {
-        DownloadsCommon.indicatorData.refreshView(this);
+        DownloadsCommon.getIndicatorData(window).refreshView(this);
       }
 
       aCallback();
     }
 
     DownloadsOverlayLoader.ensureOverlayLoaded(
                                  DownloadsButton.kIndicatorOverlay,
                                  DIV_EO_callback.bind(this));
@@ -503,17 +503,17 @@ const DownloadsIndicatorView = {
     // This function is registered as an event listener, we can't use "this".
     DownloadsIndicatorView.ensureTerminated();
   },
 
   onCommand: function DIV_onCommand(aEvent)
   {
     if (DownloadsCommon.useToolkitUI) {
       // The panel won't suppress attention for us, we need to clear now.
-      DownloadsCommon.indicatorData.attention = false;
+      DownloadsCommon.getIndicatorData(window).attention = false;
       BrowserDownloadsUI();
     } else {
       DownloadsPanel.showPanel();
     }
 
     aEvent.stopPropagation();
   },
 
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -51,16 +51,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/browser/browserglue;1",
                                    "nsIBrowserGlue");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
                                   "resource://gre/modules/DownloadUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
+                                  "resource:///modules/RecentWindow.jsm");
 
 const nsIDM = Ci.nsIDownloadManager;
 
 const kDownloadsStringBundleUrl =
   "chrome://browser/locale/downloads/downloads.properties";
 
 const kDownloadsStringsRequiringFormatting = {
   sizeWithUnits: true,
@@ -168,47 +172,120 @@ this.DownloadsCommon = {
   {
     try {
       return Services.prefs.getBoolPref("browser.download.useToolkitUI");
     } catch (ex) { }
     return false;
   },
 
   /**
-   * Returns a reference to the DownloadsData singleton.
+   * Get access to one of the DownloadsData or PrivateDownloadsData objects,
+   * depending on the privacy status of the window in question.
    *
-   * This does not need to be a lazy getter, since no initialization is required
-   * at present.
+   * @param aWindow
+   *        The browser window which owns the download button.
    */
-  get data() DownloadsData,
+  getData: function DC_getData(aWindow) {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+      return PrivateDownloadsData;
+    } else {
+      return DownloadsData;
+    }
+#else
+    return DownloadsData;
+#endif
+  },
+
+  /**
+   * Initializes the data link for both the private and non-private downloads
+   * data objects.
+   *
+   * @param aDownloadManagerService
+   *        Reference to the service implementing nsIDownloadManager.  We need
+   *        this because getService isn't available for us when this method is
+   *        called, and we must ensure to register our listeners before the
+   *        getService call for the Download Manager returns.
+   */
+  initializeAllDataLinks: function DC_initializeAllDataLinks(aDownloadManagerService) {
+    DownloadsData.initializeDataLink(aDownloadManagerService);
+    PrivateDownloadsData.initializeDataLink(aDownloadManagerService);
+  },
 
   /**
-   * Returns a reference to the DownloadsData singleton.
+   * Terminates the data link for both the private and non-private downloads
+   * data objects.
+   */
+  terminateAllDataLinks: function DC_terminateAllDataLinks() {
+    DownloadsData.terminateDataLink();
+    PrivateDownloadsData.terminateDataLink();
+  },
+
+  /**
+   * Reloads the specified kind of downloads from the non-private store.
+   * This method must only be called when Private Browsing Mode is disabled.
    *
-   * This does not need to be a lazy getter, since no initialization is required
-   * at present.
+   * @param aActiveOnly
+   *        True to load only active downloads from the database.
    */
-  get indicatorData() DownloadsIndicatorData,
+  ensureAllPersistentDataLoaded:
+  function DC_ensureAllPersistentDataLoaded(aActiveOnly) {
+    DownloadsData.ensurePersistentDataLoaded(aActiveOnly);
+  },
+
+  /**
+   * Get access to one of the DownloadsIndicatorData or
+   * PrivateDownloadsIndicatorData objects, depending on the privacy status of
+   * the window in question.
+   */
+  getIndicatorData: function DC_getIndicatorData(aWindow) {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+      return PrivateDownloadsIndicatorData;
+    } else {
+      return DownloadsIndicatorData;
+    }
+#else
+    return DownloadsIndicatorData;
+#endif
+  },
 
   /**
    * Returns a reference to the DownloadsSummaryData singleton - creating one
    * in the process if one hasn't been instantiated yet.
    *
+   * @param aWindow
+   *        The browser window which owns the download button.
    * @param aNumToExclude
    *        The number of items on the top of the downloads list to exclude
    *        from the summary.
    */
-  _summary: null,
-  getSummary: function DC_getSummary(aNumToExclude)
+  getSummary: function DC_getSummary(aWindow, aNumToExclude)
   {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+      if (this._privateSummary) {
+        return this._privateSummary;
+      }
+      return this._privateSummary = new DownloadsSummaryData(true, aNumToExclude);
+    } else {
+      if (this._summary) {
+        return this._summary;
+      }
+      return this._summary = new DownloadsSummaryData(false, aNumToExclude);
+    }
+#else
     if (this._summary) {
       return this._summary;
     }
-    return this._summary = new DownloadsSummaryData(aNumToExclude);
+    return this._summary = new DownloadsSummaryData(false, aNumToExclude);
+#endif
   },
+  _summary: null,
+  _privateSummary: null,
 
   /**
    * Given an iterable collection of DownloadDataItems, generates and returns
    * statistics about that collection.
    *
    * @param aDataItems An iterable collection of DownloadDataItems.
    *
    * @return Object whose properties are the generated statistics. Currently,
@@ -349,59 +426,82 @@ XPCOMUtils.defineLazyGetter(DownloadsCom
  * data.  For example, the deletion of one or more downloads is notified through
  * the nsIObserver interface, while any state or progress change is notified
  * through the nsIDownloadProgressListener interface.
  *
  * Note that using this object does not automatically start the Download Manager
  * service.  Consumers will see an empty list of downloads until the service is
  * actually started.  This is useful to display a neutral progress indicator in
  * the main browser window until the autostart timeout elapses.
+ *
+ * Note that DownloadsData and PrivateDownloadsData are two equivalent singleton
+ * objects, one accessing non-private downloads, and the other accessing private
+ * ones.
  */
-const DownloadsData = {
+function DownloadsDataCtor(aPrivate) {
+  this._isPrivate = aPrivate;
+
+  // This Object contains all the available DownloadsDataItem objects, indexed by
+  // their globally unique identifier.  The identifiers of downloads that have
+  // been removed from the Download Manager data are still present, however the
+  // associated objects are replaced with the value "null".  This is required to
+  // prevent race conditions when populating the list asynchronously.
+  this.dataItems = {};
+
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  // While operating in Private Browsing Mode, persistent data items are parked
+  // here until we return to the normal mode.
+  this._persistentDataItems = {};
+#endif
+
+  // Array of view objects that should be notified when the available download
+  // data changes.
+  this._views = [];
+}
+
+DownloadsDataCtor.prototype = {
   /**
    * Starts receiving events for current downloads.
    *
    * @param aDownloadManagerService
    *        Reference to the service implementing nsIDownloadManager.  We need
    *        this because getService isn't available for us when this method is
    *        called, and we must ensure to register our listeners before the
    *        getService call for the Download Manager returns.
    */
   initializeDataLink: function DD_initializeDataLink(aDownloadManagerService)
   {
     // Start receiving real-time events.
-    aDownloadManagerService.addListener(this);
+    aDownloadManagerService.addPrivacyAwareListener(this);
     Services.obs.addObserver(this, "download-manager-remove-download-guid", false);
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.addObserver(this, "download-manager-database-type-changed",
                              false);
+#endif
   },
 
   /**
    * Stops receiving events for current downloads and cancels any pending read.
    */
   terminateDataLink: function DD_terminateDataLink()
   {
     this._terminateDataAccess();
 
     // Stop receiving real-time events.
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.removeObserver(this, "download-manager-database-type-changed");
+#endif
     Services.obs.removeObserver(this, "download-manager-remove-download-guid");
     Services.downloads.removeListener(this);
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Registration of views
 
   /**
-   * Array of view objects that should be notified when the available download
-   * data changes.
-   */
-  _views: [],
-
-  /**
    * Adds an object to be notified when the available download data changes.
    * The specified object is initialized with the currently available downloads.
    *
    * @param aView
    *        DownloadsView object to be added.  This reference must be passed to
    *        removeView before termination.
    */
   addView: function DD_addView(aView)
@@ -450,31 +550,16 @@ const DownloadsData = {
       aView.onDataLoadCompleted();
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// In-memory downloads data store
 
   /**
-   * Object containing all the available DownloadsDataItem objects, indexed by
-   * their numeric download identifier.  The identifiers of downloads that have
-   * been removed from the Download Manager data are still present, however the
-   * associated objects are replaced with the value "null".  This is required to
-   * prevent race conditions when populating the list asynchronously.
-   */
-  dataItems: {},
-
-  /**
-   * While operating in Private Browsing Mode, persistent data items are parked
-   * here until we return to the normal mode.
-   */
-  _persistentDataItems: {},
-
-  /**
    * Clears the loaded data.
    */
   clear: function DD_clear()
   {
     this._terminateDataAccess();
     this.dataItems = {};
   },
 
@@ -586,17 +671,19 @@ const DownloadsData = {
       if (this._loadState == this.kLoadNone) {
         // Indicate to the views that a batch loading operation is in progress.
         this._views.forEach(
           function (view) view.onDataLoadStarting()
         );
 
         // Reload the list using the Download Manager service.  The list is
         // returned in no particular order.
-        let downloads = Services.downloads.activeDownloads;
+        let downloads = this._isPrivate ?
+                          Services.downloads.activePrivateDownloads :
+                          Services.downloads.activeDownloads;
         while (downloads.hasMoreElements()) {
           let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
           this._getOrAddDataItem(download, true);
         }
         this._loadState = this.kLoadActive;
 
         // Indicate to the views that the batch loading operation is complete.
         this._views.forEach(
@@ -604,17 +691,20 @@ const DownloadsData = {
         );
       }
     } else {
       if (this._loadState != this.kLoadAll) {
         // Load only the relevant columns from the downloads database.  The
         // columns are read in the _initFromDataRow method of DownloadsDataItem.
         // Order by descending download identifier so that the most recent
         // downloads are notified first to the listening views.
-        let statement = Services.downloads.DBConnection.createAsyncStatement(
+        let dbConnection = this._isPrivate ?
+                             Services.downloads.privateDBConnection :
+                             Services.downloads.DBConnection;
+        let statement = dbConnection.createAsyncStatement(
           "SELECT guid, target, name, source, referrer, state, "
         +        "startTime, endTime, currBytes, maxBytes "
         + "FROM moz_downloads "
         + "ORDER BY startTime DESC"
         );
         try {
           this._pendingStatement = statement.executeAsync(this);
         } finally {
@@ -709,16 +799,17 @@ const DownloadsData = {
               if (aStatus == Components.results.NS_ERROR_NOT_AVAILABLE) {
                 this._removeDataItem(dataItemBinding.downloadGuid);
               }
             }.bind(this));
           }
         }
         break;
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       case "download-manager-database-type-changed":
         let pbs = Cc["@mozilla.org/privatebrowsing;1"]
                   .getService(Ci.nsIPrivateBrowsingService);
         if (pbs.privateBrowsingEnabled) {
           // Save a reference to the persistent store before terminating access.
           this._persistentDataItems = this.dataItems;
           this.clear();
         } else {
@@ -726,24 +817,33 @@ const DownloadsData = {
           this.clear();
           this.dataItems = this._persistentDataItems;
           this._persistentDataItems = null;
         }
         // Reinitialize the views with the current items.  View data has been
         // already invalidated by the previous calls.
         this._views.forEach(this._updateView, this);
         break;
+#endif
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsIDownloadProgressListener
 
   onDownloadStateChange: function DD_onDownloadStateChange(aState, aDownload)
   {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (aDownload.isPrivate != this._isPrivate) {
+      // Ignore the downloads with a privacy status other than what we are
+      // tracking.
+      return;
+    }
+#endif
+
     // When a new download is added, it may have the same identifier of a
     // download that we previously deleted during this session, and we also
     // want to provide a visible indication that the download started.
     let isNew = aState == nsIDM.DOWNLOAD_NOTSTARTED ||
                 aState == nsIDM.DOWNLOAD_QUEUED;
 
     let dataItem = this._getOrAddDataItem(aDownload, isNew);
     if (!dataItem) {
@@ -779,16 +879,24 @@ const DownloadsData = {
   },
 
   onProgressChange: function DD_onProgressChange(aWebProgress, aRequest,
                                                   aCurSelfProgress,
                                                   aMaxSelfProgress,
                                                   aCurTotalProgress,
                                                   aMaxTotalProgress, aDownload)
   {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (aDownload.isPrivate != this._isPrivate) {
+      // Ignore the downloads with a privacy status other than what we are
+      // tracking.
+      return;
+    }
+#endif
+
     let dataItem = this._getOrAddDataItem(aDownload, false);
     if (!dataItem) {
       return;
     }
 
     dataItem.currBytes = aDownload.amountTransferred;
     dataItem.maxBytes = aDownload.size;
     dataItem.speed = aDownload.speed;
@@ -828,33 +936,41 @@ const DownloadsData = {
    */
   _notifyNewDownload: function DD_notifyNewDownload()
   {
     if (DownloadsCommon.useToolkitUI) {
       return;
     }
 
     // Show the panel in the most recent browser window, if present.
-    let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
+    let browserWin = RecentWindow.getMostRecentBrowserWindow({ private: this._isPrivate });
     if (!browserWin) {
       return;
     }
 
     if (this.panelHasShownBefore) {
       // For new downloads after the first one, don't show the panel
       // automatically, but provide a visible notification in the topmost
       // browser window, if the status indicator is already visible.
       browserWin.DownloadsIndicatorView.showEventNotification();
       return;
     }
     this.panelHasShownBefore = true;
     browserWin.DownloadsPanel.showPanel();
   }
 };
 
+XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsData", function() {
+  return new DownloadsDataCtor(true);
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
+  return new DownloadsDataCtor(false);
+});
+
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsDataItem
 
 /**
  * Represents a single item in the list of downloads.  This object either wraps
  * an existing nsIDownload from the Download Manager, or provides the same
  * information read directly from the downloads database, with the possibility
  * of querying the nsIDownload lazily, for performance reasons.
@@ -1111,32 +1227,46 @@ DownloadsDataItem.prototype = {
  */
 const DownloadsViewPrototype = {
   //////////////////////////////////////////////////////////////////////////////
   //// Registration of views
 
   /**
    * Array of view objects that should be notified when the available status
    * data changes.
+   *
+   * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
    */
-  _views: [],
+  _views: null,
+
+  /**
+   * Determines whether this view object is over the private or non-private
+   * downloads.
+   *
+   * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
+   */
+  _isPrivate: false,
 
   /**
    * Adds an object to be notified when the available status data changes.
    * The specified object is initialized with the currently available status.
    *
    * @param aView
    *        View object to be added.  This reference must be
    *        passed to removeView before termination.
    */
   addView: function DVP_addView(aView)
   {
     // Start receiving events when the first of our views is registered.
     if (this._views.length == 0) {
-      DownloadsCommon.data.addView(this);
+      if (this._isPrivate) {
+        PrivateDownloadsData.addView(this);
+      } else {
+        DownloadsData.addView(this);
+      }
     }
 
     this._views.push(aView);
     this.refreshView(aView);
   },
 
   /**
    * Updates the properties of an object previously added using addView.
@@ -1162,17 +1292,21 @@ const DownloadsViewPrototype = {
   {
     let index = this._views.indexOf(aView);
     if (index != -1) {
       this._views.splice(index, 1);
     }
 
     // Stop receiving events when the last of our views is unregistered.
     if (this._views.length == 0) {
-      DownloadsCommon.data.removeView(this);
+      if (this._isPrivate) {
+        PrivateDownloadsData.removeView(this);
+      } else {
+        DownloadsData.removeView(this);
+      }
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Callback functions from DownloadsData
 
   /**
    * Indicates whether we are still loading downloads data asynchronously.
@@ -1286,17 +1420,21 @@ const DownloadsViewPrototype = {
  * notifications it receives into overall status data, that is then broadcast to
  * the registered download status indicators.
  *
  * Note that using this object does not automatically start the Download Manager
  * service.  Consumers will see an empty list of downloads until the service is
  * actually started.  This is useful to display a neutral progress indicator in
  * the main browser window until the autostart timeout elapses.
  */
-const DownloadsIndicatorData = {
+function DownloadsIndicatorDataCtor(aPrivate) {
+  this._isPrivate = aPrivate;
+  this._views = [];
+}
+DownloadsIndicatorDataCtor.prototype = {
   __proto__: DownloadsViewPrototype,
 
   /**
    * Removes an object previously added using addView.
    *
    * @param aView
    *        DownloadsIndicatorView object to be removed.
    */
@@ -1368,33 +1506,35 @@ const DownloadsIndicatorData = {
    *
    * @param aDataItem
    *        DownloadsDataItem object for which the view item is requested.
    *
    * @return Object that can be used to notify item status events.
    */
   getViewItem: function DID_getViewItem(aDataItem)
   {
+    let data = this._isPrivate ? PrivateDownloadsIndicatorData
+                               : DownloadsIndicatorData;
     return Object.freeze({
       onStateChange: function DIVI_onStateChange()
       {
         if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
             aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
-          DownloadsIndicatorData.attention = true;
+          data.attention = true;
         }
 
         // Since the state of a download changed, reset the estimated time left.
-        DownloadsIndicatorData._lastRawTimeLeft = -1;
-        DownloadsIndicatorData._lastTimeLeft = -1;
+        data._lastRawTimeLeft = -1;
+        data._lastTimeLeft = -1;
 
-        DownloadsIndicatorData._updateViews();
+        data._updateViews();
       },
       onProgressChange: function DIVI_onProgressChange()
       {
-        DownloadsIndicatorData._updateViews();
+        data._updateViews();
       }
     });
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Propagation of properties to our views
 
   // The following properties are updated by _refreshProperties and are then
@@ -1484,17 +1624,19 @@ const DownloadsIndicatorData = {
   /**
    * A generator function for the dataItems that this summary is currently
    * interested in. This generator is passed off to summarizeDownloads in order
    * to generate statistics about the dataItems we care about - in this case,
    * it's all dataItems for active downloads.
    */
   _activeDataItems: function DID_activeDataItems()
   {
-    for each (let dataItem in DownloadsCommon.data.dataItems) {
+    let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
+                                    : DownloadsData.dataItems;
+    for each (let dataItem in dataItems) {
       if (dataItem && dataItem.inProgress) {
         yield dataItem;
       }
     }
   },
 
   /**
    * Computes aggregate values based on the current state of downloads.
@@ -1524,38 +1666,48 @@ const DownloadsIndicatorData = {
       if (this._lastRawTimeLeft != summary.rawTimeLeft) {
         this._lastRawTimeLeft = summary.rawTimeLeft;
         this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft,
                                                            this._lastTimeLeft);
       }
       this._counter = DownloadsCommon.formatTimeLeft(this._lastTimeLeft);
     }
   }
-}
+};
+
+XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsIndicatorData", function() {
+  return new DownloadsIndicatorDataCtor(true);
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsIndicatorData", function() {
+  return new DownloadsIndicatorDataCtor(false);
+});
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsSummaryData
 
 /**
  * DownloadsSummaryData is a view for DownloadsData that produces a summary
  * of all downloads after a certain exclusion point aNumToExclude. For example,
  * if there were 5 downloads in progress, and a DownloadsSummaryData was
  * constructed with aNumToExclude equal to 3, then that DownloadsSummaryData
  * would produce a summary of the last 2 downloads.
  *
+ * @param aIsPrivate
+ *        True if the browser window which owns the download button is a private
+ *        window.
  * @param aNumToExclude
  *        The number of items to exclude from the summary, starting from the
  *        top of the list.
  */
-function DownloadsSummaryData(aNumToExclude) {
+function DownloadsSummaryData(aIsPrivate, aNumToExclude) {
   this._numToExclude = aNumToExclude;
   // Since we can have multiple instances of DownloadsSummaryData, we
   // override these values from the prototype so that each instance can be
   // completely separated from one another.
-  this._views = [];
   this._loading = false;
 
   this._dataItems = [];
 
   // Floating point value indicating the last number of seconds estimated until
   // the longest download will finish.  We need to store this value so that we
   // don't continuously apply smoothing if the actual download state has not
   // changed.  This is set to -1 if the previous value is unknown.
@@ -1569,16 +1721,19 @@ function DownloadsSummaryData(aNumToExcl
 
   // The following properties are updated by _refreshProperties and are then
   // propagated to the views.
   this._showingProgress = false;
   this._details = "";
   this._description = "";
   this._numActive = 0;
   this._percentComplete = -1;
+
+  this._isPrivate = aIsPrivate;
+  this._views = [];
 }
 
 DownloadsSummaryData.prototype = {
   __proto__: DownloadsViewPrototype,
 
   /**
    * Removes an object previously added using addView.
    *
--- a/browser/components/downloads/src/DownloadsStartup.js
+++ b/browser/components/downloads/src/DownloadsStartup.js
@@ -26,26 +26,32 @@ const Cr = Components.results;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
                                    "@mozilla.org/browser/sessionstartup;1",
                                    "nsISessionStartup");
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsingService",
                                    "@mozilla.org/privatebrowsing;1",
                                    "nsIPrivateBrowsingService");
+#endif
 
 const kObservedTopics = [
   "sessionstore-windows-restored",
   "sessionstore-browser-state-restored",
   "download-manager-initialized",
   "download-manager-change-retention",
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  "last-pb-context-exited",
+#else
   "private-browsing-transition-complete",
+#endif
   "browser-lastwindow-close-granted",
   "quit-application",
   "profile-change-teardown",
 ];
 
 /**
  * CID of our implementation of nsIDownloadManagerUI.
  */
@@ -108,18 +114,18 @@ DownloadsStartup.prototype = {
         // are initializing the Download Manager service during shutdown.
         if (this._shuttingDown) {
           break;
         }
 
         // Start receiving events for active and new downloads before we return
         // from this observer function.  We can't defer the execution of this
         // step, to ensure that we don't lose events raised in the meantime.
-        DownloadsCommon.data.initializeDataLink(
-                             aSubject.QueryInterface(Ci.nsIDownloadManager));
+        DownloadsCommon.initializeAllDataLinks(
+                        aSubject.QueryInterface(Ci.nsIDownloadManager));
 
         this._downloadsServiceInitialized = true;
 
         // Since this notification is generated during the getService call and
         // we need to get the Download Manager service ourselves, we must post
         // the handler on the event queue to be executed later.
         Services.tm.mainThread.dispatch(this._ensureDataLoaded.bind(this),
                                         Ci.nsIThread.DISPATCH_NORMAL);
@@ -134,47 +140,59 @@ DownloadsStartup.prototype = {
         if (!DownloadsCommon.useToolkitUI) {
           let removeFinishedDownloads = Services.prefs.getBoolPref(
                             "browser.download.panel.removeFinishedDownloads");
           aSubject.QueryInterface(Ci.nsISupportsPRInt32)
                   .data = removeFinishedDownloads ? 0 : 2;
         }
         break;
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       case "private-browsing-transition-complete":
         // Ensure that persistent data is reloaded only when the database
         // connection is available again.
         this._ensureDataLoaded();
         break;
+#endif
 
       case "browser-lastwindow-close-granted":
         // When using the panel interface, downloads that are already completed
         // should be removed when the last full browser window is closed.  This
         // event is invoked only if the application is not shutting down yet.
         // If the Download Manager service is not initialized, we don't want to
         // initialize it just to clean up completed downloads, because they can
         // be present only in case there was a browser crash or restart.
         if (this._downloadsServiceInitialized &&
             !DownloadsCommon.useToolkitUI) {
           Services.downloads.cleanUp();
         }
         break;
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      case "last-pb-context-exited":
+        // Similar to the above notification, but for private downloads.
+        if (this._downloadsServiceInitialized &&
+            !DownloadsCommon.useToolkitUI) {
+          Services.downloads.cleanUpPrivate();
+        }
+        break;
+#endif
+
       case "quit-application":
         // When the application is shutting down, we must free all resources in
         // addition to cleaning up completed downloads.  If the Download Manager
         // service is not initialized, we don't want to initialize it just to
         // clean up completed downloads, because they can be present only in
         // case there was a browser crash or restart.
         this._shuttingDown = true;
         if (!this._downloadsServiceInitialized) {
           break;
         }
 
-        DownloadsCommon.data.terminateDataLink();
+        DownloadsCommon.terminateAllDataLinks();
 
         // When using the panel interface, downloads that are already completed
         // should be removed when quitting the application.
         if (!DownloadsCommon.useToolkitUI && aData != "restart") {
           this._cleanupOnShutdown = true;
         }
         break;
 
@@ -253,24 +271,27 @@ DownloadsStartup.prototype = {
     return aValue;
   },
 
   /**
    * Ensures that persistent download data is reloaded at the appropriate time.
    */
   _ensureDataLoaded: function DS_ensureDataLoaded()
   {
-    if (!this._downloadsServiceInitialized ||
-        gPrivateBrowsingService.privateBrowsingEnabled) {
+    if (!this._downloadsServiceInitialized
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        || gPrivateBrowsingService.privateBrowsingEnabled
+#endif
+       ) {
       return;
     }
 
     // If the previous session has been already restored, then we ensure that
     // all the downloads are loaded.  Otherwise, we only ensure that the active
     // downloads from the previous session are loaded.
-    DownloadsCommon.data.ensurePersistentDataLoaded(!this._recoverAllDownloads);
+    DownloadsCommon.ensureAllPersistentDataLoaded(!this._recoverAllDownloads);
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Module
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]);
--- a/browser/components/downloads/src/Makefile.in
+++ b/browser/components/downloads/src/Makefile.in
@@ -6,17 +6,20 @@ DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_COMPONENTS = \
   BrowserDownloads.manifest \
-  DownloadsStartup.js \
   DownloadsUI.js \
   $(NULL)
 
-EXTRA_JS_MODULES = \
+EXTRA_PP_COMPONENTS = \
+  DownloadsStartup.js \
+  $(NULL)
+
+EXTRA_PP_JS_MODULES = \
   DownloadsCommon.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -28,23 +28,23 @@ function gen_test()
   var originalCountLimit = DownloadsView.kItemCountLimit;
   DownloadsView.kItemCountLimit = DownloadData.length;
   registerCleanupFunction(function () {
     DownloadsView.kItemCountLimit = originalCountLimit;
   });
 
   try {
     // Ensure that state is reset in case previous tests didn't finish.
-    for (let yy in gen_resetState()) yield;
+    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
 
     // Populate the downloads database with the data required by this test.
     for (let yy in gen_addDownloadRows(DownloadData)) yield;
 
     // Open the user interface and wait for data to be fully loaded.
-    for (let yy in gen_openPanel()) yield;
+    for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield;
 
     // Test item data and count.  This also tests the ordering of the display.
     let richlistbox = document.getElementById("downloadsListBox");
 /* disabled for failing intermittently (bug 767828)
     is(richlistbox.children.length, DownloadData.length,
        "There is the correct number of richlistitems");
 */
     for (let i = 0; i < richlistbox.children.length; i++) {
@@ -52,11 +52,11 @@ function gen_test()
       let dataItem = new DownloadsViewItemController(element).dataItem;
       is(dataItem.target, DownloadData[i].name, "Download names match up");
       is(dataItem.state, DownloadData[i].state, "Download states match up");
       is(dataItem.file, DownloadData[i].target, "Download targets match up");
       is(dataItem.uri, DownloadData[i].source, "Download sources match up");
     }
   } finally {
     // Clean up when the test finishes.
-    for (let yy in gen_resetState()) yield;
+    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
   }
 }
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -7,42 +7,42 @@
  * Make sure the downloads panel only opens automatically on the first
  * download it notices. All subsequent downloads, even across sessions, should
  * not open the panel automatically.
  */
 function gen_test()
 {
   try {
     // Ensure that state is reset in case previous tests didn't finish.
-    for (let yy in gen_resetState()) yield;
+    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
 
     // With this set to false, we should automatically open the panel
     // the first time a download is started.
-    DownloadsCommon.data.panelHasShownBefore = false;
+    DownloadsCommon.getData(window).panelHasShownBefore = false;
 
     prepareForPanelOpen();
-    DownloadsCommon.data._notifyNewDownload();
+    DownloadsCommon.getData(window)._notifyNewDownload();
     yield;
 
     // If we got here, that means the panel opened.
     DownloadsPanel.hidePanel();
 
-    ok(DownloadsCommon.data.panelHasShownBefore,
+    ok(DownloadsCommon.getData(window).panelHasShownBefore,
        "Should have recorded that the panel was opened on a download.")
 
     // Next, make sure that if we start another download, we don't open
     // the panel automatically.
     panelShouldNotOpen();
-    DownloadsCommon.data._notifyNewDownload();
+    DownloadsCommon.getData(window)._notifyNewDownload();
     yield waitFor(2);
   } catch(e) {
     ok(false, e);
   } finally {
     // Clean up when the test finishes.
-    for (let yy in gen_resetState()) yield;
+    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
   }
 }
 
 /**
  * Call this to record a test failure for the next time the downloads panel
  * opens.
  */
 function panelShouldNotOpen()
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -125,17 +125,17 @@ var testRunner = {
 //
 // The following functions are all generators that can be used inside the main
 // test generator to perform specific tasks asynchronously.  To invoke these
 // subroutines correctly, an iteration syntax should be used:
 //
 //   for (let yy in gen_example("Parameter")) yield;
 //
 
-function gen_resetState()
+function gen_resetState(aData)
 {
   let statement = Services.downloads.DBConnection.createAsyncStatement(
                   "DELETE FROM moz_downloads");
   try {
     statement.executeAsync({
       handleResult: function(aResultSet) { },
       handleError: function(aError)
       {
@@ -150,18 +150,18 @@ function gen_resetState()
   } finally {
     statement.finalize();
   }
 
   // Reset any prefs that might have been changed.
   Services.prefs.clearUserPref("browser.download.panel.shown");
 
   // Ensure that the panel is closed and data is unloaded.
-  DownloadsCommon.data.clear();
-  DownloadsCommon.data._loadState = DownloadsCommon.data.kLoadNone;
+  aData.clear();
+  aData._loadState = aData.kLoadNone;
   DownloadsPanel.hidePanel();
 
   // Wait for focus on the main window.
   waitForFocus(testRunner.continueTest);
   yield;
 }
 
 function gen_addDownloadRows(aDataRows)
@@ -219,17 +219,17 @@ function gen_openPanel(aData)
   let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
   DownloadsPanel.onViewLoadCompleted = function () {
     DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
     originalOnViewLoadCompleted.apply(this);
     testRunner.continueTest();
   };
 
   // Start loading all the downloads from the database asynchronously.
-  DownloadsCommon.data.ensurePersistentDataLoaded(false);
+  aData.ensurePersistentDataLoaded(false);
 
   // Wait for focus on the main window.
   waitForFocus(testRunner.continueTest);
   yield;
 
   // Open the downloads panel, waiting until loading is completed.
   DownloadsPanel.showPanel();
   yield;
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -439,20 +439,16 @@ PrivateBrowsingService.prototype = {
         this._unload();
         break;
       case "private-browsing":
         // clear all auth tokens
         let sdr = Cc["@mozilla.org/security/sdr;1"].
                   getService(Ci.nsISecretDecoderRing);
         sdr.logoutAndTeardown();
     
-        try {
-          this._prefs.deleteBranch("geo.wifi.access_token.");
-        } catch (ex) {}
-
         if (!this._inPrivateBrowsing) {
           // Clear the error console
           let consoleService = Cc["@mozilla.org/consoleservice;1"].
                                getService(Ci.nsIConsoleService);
           consoleService.logStringMessage(null); // trigger the listeners
           consoleService.reset();
         }
         break;
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/unit/test_geoClearCookie.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Test to ensure the geolocation token is cleared when changing the private
-// browsing mode
-
-const accessToken = '{"location":{"latitude":51.5090332,"longitude":-0.1212726,"accuracy":150.0},"access_token":"2:jVhRZJ-j6PiRchH_:RGMrR0W1BiwdZs12"}'
-function run_test_on_service() {
-  var prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                   getService(Ci.nsIPrefBranch);
-  var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-           getService(Ci.nsIPrivateBrowsingService);
-  prefBranch.setCharPref("geo.wifi.access_token.test", accessToken);
-  var token = prefBranch.getCharPref("geo.wifi.access_token.test");
-  do_check_eq(token, accessToken);
-  pb.privateBrowsingEnabled = true;
-  token = "";
-  try {
-    token = prefBranch.getCharPref("geo.wifi.access_token.test");
-  }
-  catch(e){}
-  finally {
-    do_check_eq(token, "");
-  }
-  token = "";
-  prefBranch.setCharPref("geo.wifi.access_token.test", accessToken);
-  pb.privateBrowsingEnabled = false;
-  try {
-    token = prefBranch.getCharPref("geo.wifi.access_token.test");
-  }
-  catch(e){}
-  finally {
-    do_check_eq(token, "");
-  }
-}
-
-// Support running tests on both the service itself and its wrapper
-function run_test() {
-  run_test_on_all_services();
-}
--- a/browser/components/privatebrowsing/test/unit/xpcshell.ini
+++ b/browser/components/privatebrowsing/test/unit/xpcshell.ini
@@ -1,16 +1,15 @@
 [DEFAULT]
 head = head_privatebrowsing.js
 tail = tail_privatebrowsing.js
 
 [test_0-privatebrowsing.js]
 [test_0-privatebrowsingwrapper.js]
 [test_aboutprivatebrowsing.js]
-[test_geoClearCookie.js]
 [test_httpauth.js]
 [test_placesTitleNoUpdate.js]
 [test_privatebrowsing_autostart.js]
 [test_privatebrowsing_commandline.js]
 [test_privatebrowsing_exit.js]
 [test_privatebrowsing_telemetry.js]
 [test_privatebrowsingwrapper_autostart.js]
 [test_privatebrowsingwrapper_commandline.js]
--- a/browser/components/sessionstore/test/browser_480148.js
+++ b/browser/components/sessionstore/test/browser_480148.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   /** Test for Bug 484108 **/
   waitForExplicitFinish();
-  requestLongerTimeout(3);
+  requestLongerTimeout(4);
 
   // builds the tests state based on a few parameters
   function buildTestState(num, selected, hidden, pinned) {
     let state = { windows: [ { "tabs": [], "selected": selected + 1 } ] };
     while (num--) {
       state.windows[0].tabs.push({
         entries: [
           { url: "http://example.com/?t=" + state.windows[0].tabs.length }
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -162,18 +162,18 @@ public:
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
 };
 
 #define NS_PRINCIPAL_CLASSNAME  "principal"
 #define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
 #define NS_PRINCIPAL_CID \
-  { 0x36102b6b, 0x7b62, 0x451a, \
-    { 0xa1, 0xc8, 0xa0, 0xd4, 0x56, 0xc9, 0x2d, 0xc5 }}
+  { 0x09b7e598, 0x490d, 0x423f, \
+    { 0xa8, 0xa6, 0x2e, 0x6c, 0x4e, 0xc8, 0x77, 0x50 }}
 
 #define NS_EXPANDEDPRINCIPAL_CLASSNAME  "expandedprincipal"
 #define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
 #define NS_EXPANDEDPRINCIPAL_CID \
   { 0xb33a3807, 0xb76c, 0x44e5, \
     { 0xb9, 0x9d, 0x95, 0x7e, 0xe9, 0xba, 0x6e, 0x39 }}
 
 #endif // nsPrincipal_h__
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -387,20 +387,25 @@ nsGeolocationRequest::Cancel()
 
   NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::Allow()
 {
-  nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+  nsCOMPtr<nsIDOMWindow> window;
+  GetWindow(getter_AddRefs(window));
+  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+  bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
 
   // Kick off the geo device, if it isn't already running
-  nsresult rv = gs->StartDevice();
+  nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+  nsresult rv = gs->StartDevice(isPrivate);
 
   if (NS_FAILED(rv)) {
     // Location provider error
     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMGeoPosition> lastPosition = gs->GetCachedPosition();
@@ -911,17 +916,17 @@ nsGeolocationService::SetCachedPosition(
 
 nsIDOMGeoPosition*
 nsGeolocationService::GetCachedPosition()
 {
   return mLastPosition;
 }
 
 nsresult
-nsGeolocationService::StartDevice()
+nsGeolocationService::StartDevice(bool aRequestPrivate)
 {
   if (!sGeoEnabled || sGeoInitPending) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // we do not want to keep the geolocation devices online
   // indefinitely.  Close them down after a reasonable period of
   // inactivivity
@@ -936,17 +941,17 @@ nsGeolocationService::StartDevice()
   // Start them up!
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
 
   for (int32_t i = 0; i < mProviders.Count(); i++) {
     mProviders[i]->Startup();
-    mProviders[i]->Watch(this);
+    mProviders[i]->Watch(this, aRequestPrivate);
     obs->NotifyObservers(mProviders[i],
                          "geolocation-device-events",
                          NS_LITERAL_STRING("starting").get());
   }
 
   return NS_OK;
 }
 
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -120,17 +120,17 @@ public:
   void AddLocator(nsGeolocation* locator);
   void RemoveLocator(nsGeolocation* locator);
 
   void SetCachedPosition(nsIDOMGeoPosition* aPosition);
   nsIDOMGeoPosition* GetCachedPosition();
   PRBool IsBetterPosition(nsIDOMGeoPosition *aSomewhere);
 
   // Find and startup a geolocation device (gps, nmea, etc.)
-  nsresult StartDevice();
+  nsresult StartDevice(bool aRequestPrivate);
 
   // Stop the started geolocation device (gps, nmea, etc.)
   void     StopDevice();
 
   // create, or reinitalize the callback timer
   void     SetDisconnectTimer();
 
   // request higher accuracy, if possible
--- a/dom/system/GPSDGeolocationProvider.js
+++ b/dom/system/GPSDGeolocationProvider.js
@@ -116,17 +116,17 @@ GPSDProvider.prototype = {
   
   shutdown: function() {
     LOG("shutdown called\n"); 
     this.outputStream.close();
     this.inputStream.close();
     this.transport.close(Components.results.NS_OK);
   },
   
-  watch: function(c) {
+  watch: function(c, isPrivate) {
     LOG("watch called\n");    
     try {
         // Go into "watcher" mode
         var mode = '?WATCH={"enable":true,"json":true}';
         this.outputStream.write(mode, mode.length);
     } catch (e) { return; }
 
     var dataListener = {
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -11,16 +11,19 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 let gLoggingEnabled = false;
 let gTestingEnabled = false;
 
+let gPrivateAccessToken = '';
+let gPrivateAccessTime = 0;
+
 function LOG(aMsg) {
   if (gLoggingEnabled)
   {
     aMsg = "*** WIFI GEO: " + aMsg + "\n";
     Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(aMsg);
     dump(aMsg);
   }
 }
@@ -53,29 +56,37 @@ WifiGeoPositionObject.prototype = {
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition]),
 
   // Class Info is required to be able to pass objects back into the DOM.
   classInfo: XPCOMUtils.generateCI({interfaces: [Ci.nsIDOMGeoPosition],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "wifi geo location position object"}),
 };
 
+function privateBrowsingObserver(aSubject, aTopic, aData) {
+  gPrivateAccessToken = '';
+  gPrivateAccessTime = 0;
+}
+
 function WifiGeoPositionProvider() {
   try {
     gLoggingEnabled = Services.prefs.getBoolPref("geo.wifi.logging.enabled");
   } catch (e) {}
 
   try {
     gTestingEnabled = Services.prefs.getBoolPref("geo.wifi.testing");
   } catch (e) {}
 
   this.wifiService = null;
   this.timer = null;
   this.hasSeenWiFi = false;
   this.started = false;
+  this.lastRequestPrivate = false;
+
+  Services.obs.addObserver(privateBrowsingObserver, "last-pb-context-exited", false);
 }
 
 WifiGeoPositionProvider.prototype = {
   classID:          Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
                                            Ci.nsIWifiListener,
                                            Ci.nsITimerCallback]),
   startup:  function() {
@@ -91,26 +102,28 @@ WifiGeoPositionProvider.prototype = {
     // always sending data.  It doesn't matter if we have an access point or not.
     this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     if (!gTestingEnabled)
       this.timer.initWithCallback(this, 5000, this.timer.TYPE_ONE_SHOT);
     else
       this.timer.initWithCallback(this, 200, this.timer.TYPE_REPEATING_SLACK);
   },
 
-  watch: function(c) {
+  watch: function(c, requestPrivate) {
     LOG("watch called");
     if (!this.wifiService) {
       this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Components.interfaces.nsIWifiMonitor);
       this.wifiService.startWatching(this);
+      this.lastRequestPrivate = requestPrivate;
     }
     if (this.hasSeenWiFi) {
       this.hasSeenWiFi = false;
       this.wifiService.stopWatching(this);
       this.wifiService.startWatching(this);
+      this.lastRequestPrivate = requestPrivate;
     }
   },
 
   shutdown: function() { 
     LOG("shutdown called");
     if(this.wifiService) {
       this.wifiService.stopWatching(this);
       this.wifiService = null;
@@ -133,21 +146,30 @@ WifiGeoPositionProvider.prototype = {
   setHighAccuracy: function(enable) {
   },
 
   getAccessTokenForURL: function(url)
   {
     // check to see if we have an access token:
     let accessToken = "";
     try {
-      let accessTokenPrefName = "geo.wifi.access_token." + url;
-      accessToken = Services.prefs.getCharPref(accessTokenPrefName);
+      if (this.lastRequestPrivate) {
+        accessToken = gPrivateAccessToken;
+      } else {
+        let accessTokenPrefName = "geo.wifi.access_token." + url;
+        accessToken = Services.prefs.getCharPref(accessTokenPrefName);
+      }
 
       // check to see if it has expired
-      let accessTokenDate = Services.prefs.getIntPref(accessTokenPrefName + ".time");
+      let accessTokenDate;
+      if (this.lastRequestPrivate) {
+        accessTokenDate = gPrivateAccessTime;
+      } else {
+        Services.prefs.getIntPref(accessTokenPrefName + ".time");
+      }
       
       let accessTokenInterval = 1209600;  // seconds in 2 weeks
       try {
         accessTokenInterval = Services.prefs.getIntPref("geo.wifi.access_token.recycle_interval");
       } catch (e) {}
             
       if ((Date.now() / 1000) - accessTokenDate > accessTokenInterval)
         accessToken = "";
@@ -247,27 +269,36 @@ WifiGeoPositionProvider.prototype = {
         }
 
         // Check to see if we have a new access token
         let newAccessToken = response.access_token;
         if (newAccessToken !== undefined)
         {
           let accessToken = "";
           let accessTokenPrefName = "geo.wifi.access_token." + providerUrlBase;
-          try { accessToken = Services.prefs.getCharPref(accessTokenPrefName); } catch (e) {}
+          if (this.lastRequestPrivate) {
+            accessTokenPrefName = gPrivateAccessToken;
+          } else {
+            try { accessToken = Services.prefs.getCharPref(accessTokenPrefName); } catch (e) {}
+          }
 
           if (accessToken != newAccessToken) {
             // no match, lets cache
-              LOG("New Access Token: " + newAccessToken + "\n" + accessTokenPrefName);
+            LOG("New Access Token: " + newAccessToken + "\n" + accessTokenPrefName);
+            if (this.lastRequestPrivate) {
+              gPrivateAccessToken = newAccessToken;
+              gPrivateAccessTime = nowInSeconds();
+            } else {
               try {
                 Services.prefs.setIntPref(accessTokenPrefName + ".time", nowInSeconds());
                 Services.prefs.setCharPref(accessTokenPrefName, newAccessToken);
               } catch (x) {
                   // XXX temporary hack for bug 575346 to allow geolocation to function
               }
+            }
           }
         }
     }, false);
 
     LOG("************************************* ------>>>> sending.");
     xhr.send(null);
   },
 
--- a/dom/system/android/AndroidLocationProvider.cpp
+++ b/dom/system/android/AndroidLocationProvider.cpp
@@ -28,17 +28,17 @@ AndroidLocationProvider::Startup()
 {
     if (!AndroidBridge::Bridge())
         return NS_ERROR_NOT_IMPLEMENTED;
     AndroidBridge::Bridge()->EnableLocation(true);
     return NS_OK;
 }
 
 NS_IMETHODIMP
-AndroidLocationProvider::Watch(nsIGeolocationUpdate* aCallback)
+AndroidLocationProvider::Watch(nsIGeolocationUpdate* aCallback, bool aRequestPrivate)
 {
     NS_IF_RELEASE(gLocationCallback);
     gLocationCallback = aCallback;
     NS_IF_ADDREF(gLocationCallback);
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -580,17 +580,17 @@ GonkGPSGeolocationProvider::Startup()
   mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::Init),
                         NS_DISPATCH_NORMAL);
 
   mStarted = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
+GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback, bool aPrivate)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mLocationCallback = aCallback;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/tests/browser/Makefile.in
+++ b/dom/tests/browser/Makefile.in
@@ -15,21 +15,28 @@ MOCHITEST_BROWSER_FILES := \
   browser_focus_steal_from_chrome_during_mousedown.js \
   browser_autofocus_background.js \
   browser_ConsoleAPITests.js \
   test-console-api.html \
   browser_ConsoleStorageAPITests.js \
   browser_autofocus_preference.js \
   browser_bug396843.js \
   browser_xhr_sandbox.js \
+  browser_geolocation_privatebrowsing_page.html \
+  network_geolocation.sjs \
   $(NULL)
 
-ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+MOCHITEST_BROWSER_FILES += \
+  browser_geolocation_privatebrowsing_perwindowpb.js \
+  $(NULL)
+else
 MOCHITEST_BROWSER_FILES += \
   browser_ConsoleStoragePBTest.js \
+  browser_geolocation_privatebrowsing.js \
   $(NULL)
 endif
 
 ifeq (Linux,$(OS_ARCH))
 MOCHITEST_BROWSER_FILES += \
   browser_webapps_permissions.js \
   test-webapp.webapp \
   test-webapp-reinstall.webapp \
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_geolocation_privatebrowsing.js
@@ -0,0 +1,50 @@
+function test() {
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+  let baseProvider = "http://mochi.test:8888/browser/dom/tests/browser/network_geolocation.sjs";
+  prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=fff");
+
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+
+  prefs.setBoolPref("geo.prompt.testing", true);
+  prefs.setBoolPref("geo.prompt.testing.allow", true);
+
+  prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+
+  const testPageURL = "http://mochi.test:8888/browser/" +
+    "dom/tests/browser/browser_geolocation_privatebrowsing_page.html";
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("georesult", function load(ev) {
+    gBrowser.selectedBrowser.removeEventListener("georesult", load, false);
+    is(ev.detail, 200, "unexpected access token");
+    gBrowser.removeCurrentTab();
+
+    prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=ggg");
+
+    pb.privateBrowsingEnabled = true;
+    gBrowser.selectedTab = gBrowser.addTab();
+    gBrowser.selectedBrowser.addEventListener("georesult", function load2(ev) {
+      gBrowser.selectedBrowser.removeEventListener("georesult", load2, false);
+      is(ev.detail, 200, "unexpected access token");
+      gBrowser.removeCurrentTab();
+
+      prefs.setCharPref("geo.wifi.uri", baseProvider + "?expected_access_token=fff");
+      pb.privateBrowsingEnabled = false;
+      gBrowser.selectedTab = gBrowser.addTab();
+      gBrowser.selectedBrowser.addEventListener("georesult", function load3(ev) {
+        gBrowser.selectedBrowser.removeEventListener("georesult", load3, false);
+        is(ev.detail, 200, "unexpected access token");
+        gBrowser.removeCurrentTab();
+        prefs.setBoolPref("geo.prompt.testing", false);
+        prefs.setBoolPref("geo.prompt.testing.allow", false);
+        prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
+        finish();
+      }, false, true);
+      content.location = testPageURL;
+    }, false, true);
+    content.location = testPageURL;
+  }, false, true);
+  content.location = testPageURL;
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_geolocation_privatebrowsing_page.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<script>
+  navigator.geolocation.getCurrentPosition(function(pos) {
+    var evt = document.createEvent('CustomEvent');
+    evt.initCustomEvent('georesult', true, false, pos.coords.latitude);
+    document.dispatchEvent(evt);
+  });
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_geolocation_privatebrowsing_perwindowpb.js
@@ -0,0 +1,54 @@
+function test() {
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+  let baseProvider = "http://mochi.test:8888/browser/dom/tests/browser/network_geolocation.sjs";
+  prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=fff");
+
+  prefs.setBoolPref("geo.prompt.testing", true);
+  prefs.setBoolPref("geo.prompt.testing.allow", true);
+
+  const testPageURL = "http://mochi.test:8888/browser/" +
+    "dom/tests/browser/browser_geolocation_privatebrowsing_page.html";
+  waitForExplicitFinish();
+
+  function testOnWindow(aIsPrivate, aCallback) {
+    var win = OpenBrowserWindow({private: aIsPrivate});
+    waitForFocus(aCallback, win);
+  }
+
+  testOnWindow(false, function(aNormalWindow) {
+    aNormalWindow.gBrowser.selectedTab = aNormalWindow.gBrowser.addTab();
+    aNormalWindow.gBrowser.selectedBrowser.addEventListener("georesult", function load(ev) {
+      aNormalWindow.gBrowser.selectedBrowser.removeEventListener("georesult", load, false);
+      is(ev.detail, 200, "unexpected access token");
+
+      prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=ggg");
+      aNormalWindow.close();
+
+      testOnWindow(true, function(aPrivateWindow) {
+        aPrivateWindow.gBrowser.selectedTab = aPrivateWindow.gBrowser.addTab();
+        aPrivateWindow.gBrowser.selectedBrowser.addEventListener("georesult", function load2(ev) {
+          aPrivateWindow.gBrowser.selectedBrowser.removeEventListener("georesult", load2, false);
+          is(ev.detail, 200, "unexpected access token");
+
+          prefs.setCharPref("geo.wifi.uri", baseProvider + "?expected_access_token=fff");
+          aPrivateWindow.close();
+
+          testOnWindow(false, function(aNormalWindow) {
+            aNormalWindow.gBrowser.selectedTab = aNormalWindow.gBrowser.addTab();
+            aNormalWindow.gBrowser.selectedBrowser.addEventListener("georesult", function load3(ev) {
+              aNormalWindow.gBrowser.selectedBrowser.removeEventListener("georesult", load3, false);
+              is(ev.detail, 200, "unexpected access token");
+              prefs.setBoolPref("geo.prompt.testing", false);
+              prefs.setBoolPref("geo.prompt.testing.allow", false);
+              aNormalWindow.close();
+              finish();
+            }, false, true);
+            aNormalWindow.content.location = testPageURL;
+          });
+        }, false, true);
+        aPrivateWindow.content.location = testPageURL;
+      });
+    }, false, true);
+    aNormalWindow.content.location = testPageURL;
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/network_geolocation.sjs
@@ -0,0 +1,45 @@
+function parseQueryString(str)
+{
+  if (str == "")
+    return {};
+
+  var paramArray = str.split("&");
+  var regex = /^([^=]+)=(.*)$/;
+  var params = {};
+  for (var i = 0, sz = paramArray.length; i < sz; i++)
+  {
+    var match = regex.exec(paramArray[i]);
+    if (!match)
+      throw "Bad parameter in queryString!  '" + paramArray[i] + "'";
+    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
+  }
+
+  return params;
+}
+
+function getPosition(expectedAccessToken, providedAccessToken, desiredAccessToken)
+{  
+  var response = {
+    status: "OK",
+    location: {
+      lat: providedAccessToken ?
+             (expectedAccessToken == providedAccessToken ? 200 : 404) : 200,
+      lng: -122.08769,
+    },
+    accuracy: 100,
+    access_token: desiredAccessToken
+  };
+  
+  return JSON.stringify(response);
+}
+
+function handleRequest(request, response)
+{
+  var params = parseQueryString(request.queryString);
+
+  response.setStatusLine("1.0", 200, "OK");
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setHeader("Content-Type", "aplication/x-javascript", false);
+  response.write(getPosition(params.expected_access_token, params.access_token,
+                             params.desired_access_token));
+}
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -269,16 +269,19 @@ nsHTMLEditRules::Init(nsPlaintextEditor 
   res = mHTMLEditor->AddEditActionListener(this);
 
   return res;
 }
 
 NS_IMETHODIMP
 nsHTMLEditRules::DetachEditor()
 {
+  if (mHTMLEditor) {
+    mHTMLEditor->RemoveEditActionListener(this);
+  }
   mHTMLEditor = nullptr;
   return nsTextEditRules::DetachEditor();
 }
 
 NS_IMETHODIMP
 nsHTMLEditRules::BeforeEdit(EditAction action,
                             nsIEditor::EDirection aDirection)
 {
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -479,16 +479,17 @@ nsHTMLEditor::SetFlags(uint32_t aFlags)
   mCSSAware = !NoCSS() && !IsMailEditor();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::InitRules()
 {
+  MOZ_ASSERT(!mRules);
   // instantiate the rules for the html editor
   mRules = new nsHTMLEditRules();
   return mRules->Init(static_cast<nsPlaintextEditor*>(this));
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::BeginningOfDocument()
 {
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -116,16 +116,20 @@ NS_IMETHODIMP nsPlaintextEditor::Init(ns
                                       nsIContent *aRoot,
                                       nsISelectionController *aSelCon,
                                       uint32_t aFlags)
 {
   NS_PRECONDITION(aDoc, "bad arg");
   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
   
   nsresult res = NS_OK, rulesRes = NS_OK;
+  if (mRules) {
+    mRules->DetachEditor();
+    mRules = nullptr;
+  }
   
   if (1)
   {
     // block to scope nsAutoEditInitRulesTrigger
     nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
   
     // Init the base editor
     res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
@@ -308,16 +312,17 @@ nsPlaintextEditor::UpdateMetaCharset(nsI
                                   NS_ConvertASCIItoUTF16(aCharacterSet));
     return NS_SUCCEEDED(rv);
   }
   return false;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::InitRules()
 {
+  MOZ_ASSERT(!mRules);
   // instantiate the rules for this text editor
   mRules = new nsTextEditRules();
   return mRules->Init(this);
 }
 
 
 NS_IMETHODIMP
 nsPlaintextEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
--- a/toolkit/components/places/tests/browser/Makefile.in
+++ b/toolkit/components/places/tests/browser/Makefile.in
@@ -28,16 +28,17 @@ MOCHITEST_BROWSER_FILES = \
 	colorAnalyzer/extensionGeneric-16.png \
 	colorAnalyzer/localeGeneric.png \
 	$(NULL)
 
 ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 MOCHITEST_BROWSER_FILES += \
 	browser_bug248970.js \
 	browser_favicon_setAndFetchFaviconForPage.js \
+	browser_favicon_setAndFetchFaviconForPage_failures.js \
 	$(NULL)
 else
 MOCHITEST_BROWSER_FILES += \
 	browser_visituri_privatebrowsing.js \
 	$(NULL)
 endif
 
 # These are files that need to be loaded via the HTTP proxy server
@@ -52,13 +53,14 @@ MOCHITEST_FILES = \
 	redirect.sjs \
 	redirect-target.html \
 	settitle/title1.html \
 	settitle/title2.html \
 	visituri/begin.html \
 	visituri/redirect_twice.sjs \
 	visituri/redirect_once.sjs \
 	visituri/final.html \
+	favicon-normal16.png \
 	favicon-normal32.png \
 	favicon.html \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
copy from toolkit/components/places/tests/favicons/test_setAndFetchFaviconForPage_failures.js
copy to toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js
--- a/toolkit/components/places/tests/favicons/test_setAndFetchFaviconForPage_failures.js
+++ b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js
@@ -1,173 +1,254 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * This file tests setAndFetchFaviconForPage when it is called with invalid
  * arguments, and when no favicon is stored for the given arguments.
  */
-
-////////////////////////////////////////////////////////////////////////////////
-/// Globals
-
-const FAVICON_URI = NetUtil.newURI(do_get_file("favicon-normal16.png"));
-const LAST_PAGE_URI = NetUtil.newURI("http://example.com/verification");
-const LAST_FAVICON_URI = NetUtil.newURI(do_get_file("favicon-normal32.png"));
+function test() {
+  // Initialization
+  waitForExplicitFinish();
+  let windowsToClose = [];
+  let favIcon16Location =
+    "http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal16.png";
+  let favIcon32Location =
+    "http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal32.png";
+  let favIcon16URI = NetUtil.newURI(favIcon16Location);
+  let favIcon32URI = NetUtil.newURI(favIcon32Location);
+  let lastPageURI = NetUtil.newURI("http://example.com/verification");
+  // This error icon must stay in sync with FAVICON_ERRORPAGE_URL in
+  // nsIFaviconService.idl, aboutCertError.xhtml and netError.xhtml.
+  let favIconErrorPageURI =
+    NetUtil.newURI("chrome://global/skin/icons/warning-16.png");
+  let favIconsResultCount = 0;
+  let pageURI;
 
-////////////////////////////////////////////////////////////////////////////////
-/// Tests
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      windowsToClose.push(aWin);
+      executeSoon(function() aCallback(aWin));
+    });
+  };
 
-function run_test()
-{
-  // We run all the tests that follow, but only the last one should raise the
-  // onPageChanged notification, executing the waitForFaviconChanged callback.
-  waitForFaviconChanged(LAST_PAGE_URI, LAST_FAVICON_URI,
-                        function final_callback() {
-    // Check that only one record corresponding to the last favicon is present.
-    let resultCount = 0;
+  // This function is called after calling finish() on the test.
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(aWin) {
+      aWin.close();
+    });
+  });
+
+  function checkFavIconsDBCount(aCallback) {
     let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons");
     stmt.executeAsync({
       handleResult: function final_handleResult(aResultSet) {
         for (let row; (row = aResultSet.getNextRow()); ) {
-          do_check_eq(LAST_FAVICON_URI.spec, row.getResultByIndex(0));
-          resultCount++;
+          favIconsResultCount++;
         }
       },
       handleError: function final_handleError(aError) {
-        do_throw("Unexpected error (" + aError.result + "): " + aError.message);
+        throw("Unexpected error (" + aError.result + "): " + aError.message);
       },
       handleCompletion: function final_handleCompletion(aReason) {
-        do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
-        do_check_eq(1, resultCount);
-        run_next_test();
+        //begin testing
+        info("Previous records in moz_favicons: " + favIconsResultCount);
+        if (aCallback) {
+          aCallback();
+        }
       }
     });
     stmt.finalize();
-  });
+  }
 
-  run_next_test();
-}
+  function testNullPageURI(aWindow, aCallback) {
+    try {
+      aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(null, favIcon16URI,
+        true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+      throw("Exception expected because aPageURI is null.");
+    } catch (ex) {
+      // We expected an exception.
+      ok(true, "Exception expected because aPageURI is null");
+    }
+
+    if (aCallback) {
+      aCallback();
+    }
+  }
 
-add_test(function test_null_pageURI()
-{
-  try {
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         null,
-                         FAVICON_URI, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-    do_throw("Exception expected because aPageURI is null.");
-  } catch (ex) {
-    // We expected an exception.
+  function testNullFavIconURI(aWindow, aCallback) {
+    try {
+      aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(
+        NetUtil.newURI("http://example.com/null_faviconURI"), null, true,
+          aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+      throw("Exception expected because aFaviconURI is null.");
+    } catch (ex) {
+      // We expected an exception.
+      ok(true, "Exception expected because aFaviconURI is null.");
+    }
+
+    if (aCallback) {
+      aCallback();
+    }
+  }
+
+  function testAboutURI(aWindow, aCallback) {
+    aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(
+      NetUtil.newURI("about:testAboutURI"), favIcon16URI, true,
+        aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+
+    if (aCallback) {
+      aCallback();
+    }
   }
 
-  run_next_test();
-});
+  function testPrivateBrowsingNonBookmarkedURI(aWindow, aCallback) {
+    let pageURI = NetUtil.newURI("http://example.com/privateBrowsing");
+    addVisits({ uri: pageURI, transitionType: TRANSITION_TYPED }, aWindow,
+      function () {
+        aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI,
+          favIcon16URI, true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_PRIVATE);
 
-add_test(function test_null_faviconURI()
-{
-  try {
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         NetUtil.newURI("http://example.com/null_faviconURI"),
-                         null, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-    do_throw("Exception expected because aFaviconURI is null.");
-  } catch (ex) {
-    // We expected an exception.
+        if (aCallback) {
+          aCallback();
+        }
+    });
   }
 
-  run_next_test();
-});
+  function testDisabledHistory(aWindow, aCallback) {
+    let pageURI = NetUtil.newURI("http://example.com/disabledHistory");
+    addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, aWindow,
+      function () {
+        aWindow.Services.prefs.setBoolPref("places.history.enabled", false);
 
-add_test(function test_aboutURI()
-{
-  PlacesUtils.favicons.setAndFetchFaviconForPage(
-                       NetUtil.newURI("about:testAboutURI"),
-                       FAVICON_URI, true,
-                       PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+        aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI,
+          favIcon16URI, true,
+            aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+
+        // The setAndFetchFaviconForPage function calls CanAddURI synchronously, thus
+        // we can set the preference back to true immediately . We don't clear the
+        // preference because not all products enable Places by default.
+        aWindow.Services.prefs.setBoolPref("places.history.enabled", true);
 
-  run_next_test();
-});
+        if (aCallback) {
+          aCallback();
+        }
+    });
+  }
 
-add_test(function test_privateBrowsing_nonBookmarkedURI()
-{
-  if (!("@mozilla.org/privatebrowsing;1" in Cc)) {
-    do_log_info("Private Browsing service is not available, bail out.");
-    run_next_test();
-    return;
+  function testErrorIcon(aWindow, aCallback) {
+    let pageURI = NetUtil.newURI("http://example.com/errorIcon");
+    let places = [{ uri: pageURI, transition: TRANSITION_TYPED }];
+    addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, aWindow,
+      function () {
+        aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI,
+          favIconErrorPageURI, true,
+            aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+
+      if (aCallback) {
+        aCallback();
+      }
+    });
   }
 
-  let pageURI = NetUtil.newURI("http://example.com/privateBrowsing");
-  addVisits({ uri: pageURI, transitionType: TRANSITION_TYPED }, function () {
-    let pb = Cc["@mozilla.org/privatebrowsing;1"]
-             .getService(Ci.nsIPrivateBrowsingService);
-    Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session",
-                               true);
-    pb.privateBrowsingEnabled = true;
+  function testNonExistingPage(aWindow, aCallback) {
+    aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(
+      NetUtil.newURI("http://example.com/nonexistingPage"), favIcon16URI, true,
+        aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+
+    if (aCallback) {
+      aCallback();
+    }
+  }
 
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         pageURI,
-                         FAVICON_URI, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_PRIVATE);
+  function testFinalVerification(aWindow, aCallback) {
+    // Only the last test should raise the onPageChanged notification,
+    // executing the waitForFaviconChanged callback.
+    waitForFaviconChanged(lastPageURI, favIcon32URI, aWindow,
+      function final_callback() {
+        // Check that only one record corresponding to the last favicon is present.
+        let resultCount = 0;
+        let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons");
+        stmt.executeAsync({
+          handleResult: function final_handleResult(aResultSet) {
 
-    // The setAndFetchFaviconForPage function calls CanAddURI synchronously,
-    // thus we can exit Private Browsing Mode immediately.
-    pb.privateBrowsingEnabled = false;
-    Services.prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
-    run_next_test();
-  });
-});
+            // If the moz_favicons DB had been previously loaded (before our
+            // test began), we should focus only in the URI we are testing and
+            // skip the URIs not related to our test.
+            if (favIconsResultCount > 0) {
+              for (let row; (row = aResultSet.getNextRow()); ) {
+                if (favIcon32URI.spec === row.getResultByIndex(0)) {
+                  is(favIcon32URI.spec, row.getResultByIndex(0),
+                    "Check equal favicons");
+                  resultCount++;
+                }
+              }
+            } else {
+              for (let row; (row = aResultSet.getNextRow()); ) {
+                is(favIcon32URI.spec, row.getResultByIndex(0),
+                  "Check equal favicons");
+                resultCount++;
+              }
+            }
+          },
+          handleError: function final_handleError(aError) {
+            throw("Unexpected error (" + aError.result + "): " + aError.message);
+          },
+          handleCompletion: function final_handleCompletion(aReason) {
+            is(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason,
+              "Check reasons are equal");
+            is(1, resultCount, "Check result count");
+            if (aCallback) {
+              aCallback();
+            }
+          }
+        });
+        stmt.finalize();
+    });
 
-add_test(function test_disabledHistory()
-{
-  let pageURI = NetUtil.newURI("http://example.com/disabledHistory");
-  addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, function () {
-    Services.prefs.setBoolPref("places.history.enabled", false);
+    // This is the only test that should cause the waitForFaviconChanged
+    // callback to be invoked.  In turn, the callback will invoke
+    // finish() causing the tests to finish.
+    addVisits({ uri: lastPageURI, transition: TRANSITION_TYPED }, aWindow,
+      function () {
+        aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(lastPageURI,
+          favIcon32URI, true,
+            aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
+    });
+  }
 
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         pageURI,
-                         FAVICON_URI, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-
-    // The setAndFetchFaviconForPage function calls CanAddURI synchronously, thus
-    // we can set the preference back to true immediately . We don't clear the
-    // preference because not all products enable Places by default.
-    Services.prefs.setBoolPref("places.history.enabled", true);
-    run_next_test();
+  checkFavIconsDBCount(function () {
+    testOnWindow({}, function(aWin) {
+      testNullPageURI(aWin, function () {
+        testOnWindow({}, function(aWin) {
+          testNullFavIconURI(aWin, function() {
+            testOnWindow({}, function(aWin) {
+              testAboutURI(aWin, function() {
+                testOnWindow({private: true}, function(aWin) {
+                  testPrivateBrowsingNonBookmarkedURI(aWin, function () {
+                    testOnWindow({}, function(aWin) {
+                      testDisabledHistory(aWin, function () {
+                        testOnWindow({}, function(aWin) {
+                          testErrorIcon(aWin, function() {
+                            testOnWindow({}, function(aWin) {
+                              testNonExistingPage(aWin, function() {
+                                testOnWindow({}, function(aWin) {
+                                  testFinalVerification(aWin, function() {
+                                    finish();
+                                  });
+                                });
+                              });
+                            });
+                          });
+                        });
+                      });
+                    });
+                  });
+                });
+              });
+            });
+          });
+        });
+      });
+    });
   });
-});
-
-add_test(function test_errorIcon()
-{
-  let pageURI = NetUtil.newURI("http://example.com/errorIcon");
-  let places = [{ uri: pageURI, transition: TRANSITION_TYPED }];
-  addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, function () {
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         pageURI,
-                         FAVICON_ERRORPAGE_URI, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-
-    run_next_test();
-  });
-});
-
-add_test(function test_nonexistingPage()
-{
-  PlacesUtils.favicons.setAndFetchFaviconForPage(
-                       NetUtil.newURI("http://example.com/nonexistingPage"),
-                       FAVICON_URI, true,
-                       PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-
-  run_next_test();
-});
-
-add_test(function test_finalVerification()
-{
-  // This is the only test that should cause the waitForFaviconChanged callback
-  // we set up earlier to be invoked.  In turn, the callback will invoke
-  // run_next_test() causing the tests to finish.
-  addVisits({ uri: LAST_PAGE_URI, transition: TRANSITION_TYPED }, function () {
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-                         LAST_PAGE_URI,
-                         LAST_FAVICON_URI, true,
-                         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
-  });
-});
+}
copy from toolkit/components/places/tests/favicons/favicon-normal16.png
copy to toolkit/components/places/tests/browser/favicon-normal16.png
--- a/toolkit/components/places/tests/browser/head.js
+++ b/toolkit/components/places/tests/browser/head.js
@@ -341,8 +341,240 @@ function DBConn(aForceNewConnection) {
     Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
       Services.obs.removeObserver(DBCloseCallback, aTopic);
       dbConn.asyncClose();
     }, "profile-before-change", false);
   }
 
   return gDBConn.connectionReady ? gDBConn : null;
 }
+
+function whenNewWindowLoaded(aOptions, aCallback) {
+  let win = OpenBrowserWindow(aOptions);
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    aCallback(win);
+  }, false);
+}
+
+/**
+ * Generic nsINavHistoryObserver that doesn't implement anything, but provides
+ * dummy methods to prevent errors about an object not having a certain method.
+ */
+function NavHistoryObserver() {}
+
+NavHistoryObserver.prototype = {
+  onBeginUpdateBatch: function () {},
+  onEndUpdateBatch: function () {},
+  onVisit: function () {},
+  onTitleChanged: function () {},
+  onBeforeDeleteURI: function () {},
+  onDeleteURI: function () {},
+  onClearHistory: function () {},
+  onPageChanged: function () {},
+  onDeleteVisits: function () {},
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsINavHistoryObserver,
+  ])
+};
+
+/**
+ * Waits for the first OnPageChanged notification for ATTRIBUTE_FAVICON, and
+ * verifies that it matches the expected page URI and associated favicon URI.
+ *
+ * This function also double-checks the GUID parameter of the notification.
+ *
+ * @param aExpectedPageURI
+ *        nsIURI object of the page whose favicon should change.
+ * @param aExpectedFaviconURI
+ *        nsIURI object of the newly associated favicon.
+ * @param aCallback
+ *        This function is called after the check finished.
+ */
+function waitForFaviconChanged(aExpectedPageURI, aExpectedFaviconURI, aWindow,
+                               aCallback) {
+  let historyObserver = {
+    __proto__: NavHistoryObserver.prototype,
+    onPageChanged: function WFFC_onPageChanged(aURI, aWhat, aValue, aGUID) {
+      if (aWhat != Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
+        return;
+      }
+      aWindow.PlacesUtils.history.removeObserver(this);
+
+      ok(aURI.equals(aExpectedPageURI),
+        "Check URIs are equal for the page which favicon changed");
+      is(aValue, aExpectedFaviconURI.spec,
+        "Check changed favicon URI is the expected");
+      checkGuidForURI(aURI, aGUID);
+
+      if (aCallback) {
+        aCallback();
+      }
+    }
+  };
+  aWindow.PlacesUtils.history.addObserver(historyObserver, false);
+}
+
+/**
+ * Asynchronously adds visits to a page, invoking a callback function when done.
+ *
+ * @param aPlaceInfo
+ *        Can be an nsIURI, in such a case a single LINK visit will be added.
+ *        Otherwise can be an object describing the visit to add, or an array
+ *        of these objects:
+ *          { uri: nsIURI of the page,
+ *            transition: one of the TRANSITION_* from nsINavHistoryService,
+ *            [optional] title: title of the page,
+ *            [optional] visitDate: visit date in microseconds from the epoch
+ *            [optional] referrer: nsIURI of the referrer for this visit
+ *          }
+ * @param [optional] aCallback
+ *        Function to be invoked on completion.
+ * @param [optional] aStack
+ *        The stack frame used to report errors.
+ */
+function addVisits(aPlaceInfo, aWindow, aCallback, aStack) {
+  let stack = aStack || Components.stack.caller;
+  let places = [];
+  if (aPlaceInfo instanceof Ci.nsIURI) {
+    places.push({ uri: aPlaceInfo });
+  }
+  else if (Array.isArray(aPlaceInfo)) {
+    places = places.concat(aPlaceInfo);
+  } else {
+    places.push(aPlaceInfo)
+  }
+
+  // Create mozIVisitInfo for each entry.
+  let now = Date.now();
+  for (let i = 0; i < places.length; i++) {
+    if (!places[i].title) {
+      places[i].title = "test visit for " + places[i].uri.spec;
+    }
+    places[i].visits = [{
+      transitionType: places[i].transition === undefined ? TRANSITION_LINK
+                                                         : places[i].transition,
+      visitDate: places[i].visitDate || (now++) * 1000,
+      referrerURI: places[i].referrer
+    }];
+  }
+
+  aWindow.PlacesUtils.asyncHistory.updatePlaces(
+    places,
+    {
+      handleError: function AAV_handleError() {
+        throw("Unexpected error in adding visit.");
+      },
+      handleResult: function () {},
+      handleCompletion: function UP_handleCompletion() {
+        if (aCallback)
+          aCallback();
+      }
+    }
+  );
+}
+
+/**
+ * Checks that the favicon for the given page matches the provided data.
+ *
+ * @param aPageURI
+ *        nsIURI object for the page to check.
+ * @param aExpectedMimeType
+ *        Expected MIME type of the icon, for example "image/png".
+ * @param aExpectedData
+ *        Expected icon data, expressed as an array of byte values.
+ * @param aCallback
+ *        This function is called after the check finished.
+ */
+function checkFaviconDataForPage(aPageURI, aExpectedMimeType, aExpectedData,
+  aWindow, aCallback) {
+  aWindow.PlacesUtils.favicons.getFaviconDataForPage(aPageURI,
+    function (aURI, aDataLen, aData, aMimeType) {
+      is(aExpectedMimeType, aMimeType, "Check expected MimeType");
+      is(aExpectedData.length, aData.length,
+        "Check favicon data for the given page matches the provided data");
+      checkGuidForURI(aPageURI);
+      aCallback();
+    });
+}
+
+/**
+ * Tests that a guid was set in moz_places for a given uri.
+ *
+ * @param aURI
+ *        The uri to check.
+ * @param [optional] aGUID
+ *        The expected guid in the database.
+ */
+function checkGuidForURI(aURI, aGUID) {
+  let guid = doGetGuidForURI(aURI);
+  if (aGUID) {
+    doCheckValidPlacesGuid(aGUID);
+    is(guid, aGUID, "Check equal guid for URIs");
+  }
+}
+
+/**
+ * Retrieves the guid for a given uri.
+ *
+ * @param aURI
+ *        The uri to check.
+ * @return the associated the guid.
+ */
+function doGetGuidForURI(aURI) {
+  let stmt = DBConn().createStatement(
+    "SELECT guid "
+    + "FROM moz_places "
+    + "WHERE url = :url "
+  );
+  stmt.params.url = aURI.spec;
+  ok(stmt.executeStep(), "Check get guid for uri from moz_places");
+  let guid = stmt.row.guid;
+  stmt.finalize();
+  doCheckValidPlacesGuid(guid);
+  return guid;
+}
+
+/**
+ * Tests if a given guid is valid for use in Places or not.
+ *
+ * @param aGuid
+ *        The guid to test.
+ */
+function doCheckValidPlacesGuid(aGuid) {
+  ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), "Check guid for valid places");
+}
+
+/**
+ * Gets the database connection.  If the Places connection is invalid it will
+ * try to create a new connection.
+ *
+ * @param [optional] aForceNewConnection
+ *        Forces creation of a new connection to the database.  When a
+ *        connection is asyncClosed it cannot anymore schedule async statements,
+ *        though connectionReady will keep returning true (Bug 726990).
+ *
+ * @return The database connection or null if unable to get one.
+ */
+function DBConn(aForceNewConnection) {
+  let gDBConn;
+  if (!aForceNewConnection) {
+    let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+      .DBConnection;
+    if (db.connectionReady)
+      return db;
+  }
+
+  // If the Places database connection has been closed, create a new connection.
+  if (!gDBConn || aForceNewConnection) {
+    let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
+    file.append("places.sqlite");
+    let dbConn = gDBConn = Services.storage.openDatabase(file);
+
+    // Be sure to cleanly close this connection.
+    Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(DBCloseCallback, aTopic);
+      dbConn.asyncClose();
+    }, "profile-before-change", false);
+  }
+
+  return gDBConn.connectionReady ? gDBConn : null;
+}
--- a/xpcom/system/nsIGeolocationProvider.idl
+++ b/xpcom/system/nsIGeolocationProvider.idl
@@ -30,30 +30,33 @@ interface nsIGeolocationUpdate : nsISupp
 
 
 /**
  * Interface provides location information to the nsGeolocator
  * via the nsIDOMGeolocationCallback interface.  After
  * startup is called, any geo location change should call
  * callback.update().
  */
-[scriptable, uuid(483BE98B-F747-490A-8AF1-53146D2D5373)]
+[scriptable, uuid(d32b87b3-fe96-4f42-81ab-2f39f7ec43ff)]
 interface nsIGeolocationProvider : nsISupports {
 
   /**
    * Start up the provider.  This is called before any other
    * method.  may be called multiple times.
    */
   void startup();
 
   /**
    * watch
-   * When a location change is observed, notify the callback
+   * When a location change is observed, notify the callback. The privacy
+   * argument informs the provider whether the initiating request came from
+   * a private context; it is up to the provider to use that information
+   * in a sensible manner.
    */
-  void watch(in nsIGeolocationUpdate callback);
+  void watch(in nsIGeolocationUpdate callback, in boolean requestPrivate);
 
   /**
    * shutdown
    * Shuts down the location device.
    */
   void shutdown();
 
   /**