Bug 952092 - Get rid of post data in SessionStore r=smacleod
authorTim Taubert <ttaubert@mozilla.com>
Tue, 14 Jan 2014 18:21:48 +0100
changeset 180007 b6b750b677a7a98619ae7315296f59389454c6e3
parent 180006 b290c1d5630f404cb3e187f2157a9aa3c2e7f04c
child 180008 95ba0745030a95d4a634d3c9e06d62943ee44b7c
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmacleod
bugs952092
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 952092 - Get rid of post data in SessionStore r=smacleod From 243232f7d2522e82622c031923271ed76ffdc42a Mon Sep 17 00:00:00 2001
browser/app/profile/firefox.js
browser/components/sessionstore/nsISessionStartup.idl
browser/components/sessionstore/nsISessionStore.idl
browser/components/sessionstore/src/PrivacyLevel.jsm
browser/components/sessionstore/src/SessionHistory.jsm
browser/components/sessionstore/src/SessionWorker.js
browser/components/sessionstore/test/browser_telemetry.js
toolkit/components/telemetry/Histograms.json
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -837,19 +837,16 @@ pref("browser.rights.3.shown", false);
 pref("browser.rights.override", true);
 #endif
 
 pref("browser.sessionstore.resume_from_crash", true);
 pref("browser.sessionstore.resume_session_once", false);
 
 // minimal interval between two save operations in milliseconds
 pref("browser.sessionstore.interval", 15000);
-// maximum amount of POSTDATA to be saved in bytes per history entry (-1 = all of it)
-// (NB: POSTDATA will be saved either entirely or not at all)
-pref("browser.sessionstore.postdata", 0);
 // on which sites to save text data, POSTDATA and cookies
 // 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
 pref("browser.sessionstore.privacy_level", 0);
 // the same as browser.sessionstore.privacy_level, but for saving deferred session data
 pref("browser.sessionstore.privacy_level_deferred", 1);
 // how many tabs can be reopened (per window)
 pref("browser.sessionstore.max_tabs_undo", 10);
 // how many windows can be reopened (per session) - on non-OS X platforms this
--- a/browser/components/sessionstore/nsISessionStartup.idl
+++ b/browser/components/sessionstore/nsISessionStartup.idl
@@ -1,17 +1,17 @@
 /* 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/. */
 
 #include "nsISupports.idl"
 
 /**
  * nsISessionStore keeps track of the current browsing state - i.e.
- * tab history, cookies, scroll state, form data, POSTDATA and window features
+ * tab history, cookies, scroll state, form data, and window features
  * - and allows to restore everything into one window.
  */
 
 [scriptable, uuid(934697e4-3807-47f8-b6c9-6caa8d83ccd1)]
 interface nsISessionStartup: nsISupports
 {
   /**
    * Return a promise that is resolved once initialization
--- a/browser/components/sessionstore/nsISessionStore.idl
+++ b/browser/components/sessionstore/nsISessionStore.idl
@@ -4,17 +4,17 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 interface nsIDOMNode;
 
 /**
  * nsISessionStore keeps track of the current browsing state - i.e.
- * tab history, cookies, scroll state, form data, POSTDATA and window features
+ * tab history, cookies, scroll state, form data, and window features
  * - and allows to restore everything into one browser window.
  *
  * The nsISessionStore API operates mostly on browser windows and the tabbrowser
  * tabs contained in them:
  *
  * * "Browser windows" are those DOM windows having loaded
  * chrome://browser/content/browser.xul . From overlays you can just pass the
  * global |window| object to the API, though (or |top| from a sidebar).
--- a/browser/components/sessionstore/src/PrivacyLevel.jsm
+++ b/browser/components/sessionstore/src/PrivacyLevel.jsm
@@ -10,17 +10,17 @@ const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 const PREF_NORMAL = "browser.sessionstore.privacy_level";
 const PREF_DEFERRED = "browser.sessionstore.privacy_level_deferred";
 
 // The following constants represent the different possible privacy levels that
 // can be set by the user and that we need to consider when collecting text
-// data, cookies, and POSTDATA.
+// data, and cookies.
 //
 // Collect data from all sites (http and https).
 const PRIVACY_NONE = 0;
 // Collect data from unencrypted sites (http), only.
 const PRIVACY_ENCRYPTED = 1;
 // Collect no data.
 const PRIVACY_FULL = 2;
 
--- a/browser/components/sessionstore/src/SessionHistory.jsm
+++ b/browser/components/sessionstore/src/SessionHistory.jsm
@@ -8,73 +8,57 @@ this.EXPORTED_SYMBOLS = ["SessionHistory
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
-  "resource:///modules/sessionstore/PrivacyLevel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
   "resource:///modules/sessionstore/Utils.jsm");
 
 function debug(msg) {
   Services.console.logStringMessage("SessionHistory: " + msg);
 }
 
-// The preference value that determines how much post data to save.
-XPCOMUtils.defineLazyGetter(this, "gPostData", function () {
-  const PREF = "browser.sessionstore.postdata";
-
-  // Observer that updates the cached value when the preference changes.
-  Services.prefs.addObserver(PREF, () => {
-    this.gPostData = Services.prefs.getIntPref(PREF);
-  }, false);
-
-  return Services.prefs.getIntPref(PREF);
-});
-
 /**
  * The external API exported by this module.
  */
 this.SessionHistory = Object.freeze({
-  collect: function (docShell, includePrivateData) {
-    return SessionHistoryInternal.collect(docShell, includePrivateData);
+  collect: function (docShell) {
+    return SessionHistoryInternal.collect(docShell);
   },
 
   restore: function (docShell, tabData) {
     SessionHistoryInternal.restore(docShell, tabData);
   }
 });
 
 /**
  * The internal API for the SessionHistory module.
  */
 let SessionHistoryInternal = {
   /**
    * Collects session history data for a given docShell.
    *
    * @param docShell
    *        The docShell that owns the session history.
-   * @param includePrivateData (optional)
-   *        True to always include private data and skip any privacy checks.
    */
-  collect: function (docShell, includePrivateData = false) {
+  collect: function (docShell) {
     let data = {entries: []};
     let isPinned = docShell.isAppTab;
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory;
 
     if (history && history.count > 0) {
       try {
         for (let i = 0; i < history.count; i++) {
           let shEntry = history.getEntryAtIndex(i, false);
-          let entry = this.serializeEntry(shEntry, includePrivateData, isPinned);
+          let entry = this.serializeEntry(shEntry, isPinned);
           data.entries.push(entry);
         }
       } catch (ex) {
         // In some cases, getEntryAtIndex will throw. This seems to be due to
         // history.count being higher than it should be. By doing this in a
         // try-catch, we'll update history to where it breaks, print an error
         // message, and still save sessionstore.js.
         debug("SessionStore failed gathering complete history " +
@@ -104,23 +88,21 @@ let SessionHistoryInternal = {
     return data;
   },
 
   /**
    * Get an object that is a serialized representation of a History entry.
    *
    * @param shEntry
    *        nsISHEntry instance
-   * @param includePrivateData
-   *        Always return privacy sensitive data (use with care).
    * @param isPinned
    *        The tab is pinned and should be treated differently for privacy.
    * @return object
    */
-  serializeEntry: function (shEntry, includePrivateData, isPinned) {
+  serializeEntry: function (shEntry, isPinned) {
     let entry = { url: shEntry.URI.spec };
 
     // Save some bytes and don't include the title property
     // if that's identical to the current entry's URL.
     if (shEntry.title && shEntry.title != entry.url) {
       entry.title = shEntry.title;
     }
     if (shEntry.isSubFrame) {
@@ -151,27 +133,16 @@ let SessionHistoryInternal = {
     if (shEntry.contentType)
       entry.contentType = shEntry.contentType;
 
     let x = {}, y = {};
     shEntry.getScrollPosition(x, y);
     if (x.value != 0 || y.value != 0)
       entry.scroll = x.value + "," + y.value;
 
-    // Collect post data for the current history entry.
-    try {
-      let postdata = this.serializePostData(shEntry, isPinned);
-      if (postdata) {
-        entry.postdata_b64 = postdata;
-      }
-    } catch (ex) {
-      // POSTDATA is tricky - especially since some extensions don't get it right
-      debug("Failed serializing post data: " + ex);
-    }
-
     // Collect owner data for the current history entry.
     try {
       let owner = this.serializeOwner(shEntry);
       if (owner) {
         entry.owner_b64 = owner;
       }
     } catch (ex) {
       // Not catching anything specific here, just possible errors
@@ -198,63 +169,29 @@ let SessionHistoryInternal = {
         if (child) {
           // Don't try to restore framesets containing wyciwyg URLs.
           // (cf. bug 424689 and bug 450595)
           if (child.URI.schemeIs("wyciwyg")) {
             children.length = 0;
             break;
           }
 
-          children.push(this.serializeEntry(child, includePrivateData, isPinned));
+          children.push(this.serializeEntry(child, isPinned));
         }
       }
 
       if (children.length) {
         entry.children = children;
       }
     }
 
     return entry;
   },
 
   /**
-   * Serialize post data contained in the given session history entry.
-   *
-   * @param shEntry
-   *        The session history entry.
-   * @param isPinned
-   *        Whether the docShell is owned by a pinned tab.
-   * @return The base64 encoded post data.
-   */
-  serializePostData: function (shEntry, isPinned) {
-    let isHttps = shEntry.URI.schemeIs("https");
-    if (!shEntry.postData || !gPostData ||
-        !PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
-      return null;
-    }
-
-    shEntry.postData.QueryInterface(Ci.nsISeekableStream)
-                    .seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
-    let stream = Cc["@mozilla.org/binaryinputstream;1"]
-                   .createInstance(Ci.nsIBinaryInputStream);
-    stream.setInputStream(shEntry.postData);
-    let postBytes = stream.readByteArray(stream.available());
-    let postdata = String.fromCharCode.apply(null, postBytes);
-    if (gPostData != -1 &&
-        postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length > gPostData) {
-      return null;
-    }
-
-    // We can stop doing base64 encoding once our serialization into JSON
-    // is guaranteed to handle all chars in strings, including embedded
-    // nulls.
-    return btoa(postdata);
-  },
-
-  /**
    * Serialize owner data contained in the given session history entry.
    *
    * @param shEntry
    *        The session history entry.
    * @return The base64 encoded owner data.
    */
   serializeOwner: function (shEntry) {
     if (!shEntry.owner) {
@@ -370,24 +307,16 @@ let SessionHistoryInternal = {
     }
 
     if (entry.scroll) {
       var scrollPos = (entry.scroll || "0,0").split(",");
       scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
       shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
     }
 
-    if (entry.postdata_b64) {
-      var postdata = atob(entry.postdata_b64);
-      var stream = Cc["@mozilla.org/io/string-input-stream;1"].
-                   createInstance(Ci.nsIStringInputStream);
-      stream.setData(postdata, postdata.length);
-      shEntry.postData = stream;
-    }
-
     let childDocIdents = {};
     if (entry.docIdentifier) {
       // If we have a serialized document identifier, try to find an SHEntry
       // which matches that doc identifier and adopt that SHEntry's
       // BFCacheEntry.  If we don't find a match, insert shEntry as the match
       // for the document identifier.
       let matchingEntry = docIdentMap[entry.docIdentifier];
       if (!matchingEntry) {
--- a/browser/components/sessionstore/src/SessionWorker.js
+++ b/browser/components/sessionstore/src/SessionWorker.js
@@ -308,18 +308,16 @@ let Statistics = {
   /**
    * Collect data that requires walking through the data structure
    */
   gatherComplexData: function(state, subsets) {
     // The subset of sessionstore.js dealing with DOM storage
     subsets.DOM_STORAGE = [];
     // The subset of sessionstore.js storing form data
     subsets.FORMDATA = [];
-    // The subset of sessionstore.js storing POST data in history
-    subsets.POSTDATA = [];
     // The subset of sessionstore.js storing history
     subsets.HISTORY = [];
 
 
     this.walk(state, function(k, value) {
       let dest;
       switch (k) {
         case "entries":
@@ -328,19 +326,16 @@ let Statistics = {
         case "storage":
           subsets.DOM_STORAGE.push(value);
           // Never visit storage, it's full of weird stuff
           return false;
         case "formdata":
           subsets.FORMDATA.push(value);
           // Never visit formdata, it's full of weird stuff
           return false;
-        case "postdata_b64":
-          subsets.POSTDATA.push(value);
-          return false; // Nothing to visit anyway
         case "cookies": // Don't visit these places, they are full of weird stuff
         case "extData":
           return false;
         default:
           return true;
       }
     });
 
--- a/browser/components/sessionstore/test/browser_telemetry.js
+++ b/browser/components/sessionstore/test/browser_telemetry.js
@@ -4,17 +4,17 @@
 
 let tmp = {};
 Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
 Cu.import("resource:///modules/sessionstore/TabStateCache.jsm", tmp);
 let {SessionFile, TabStateCache} = tmp;
 
 // Shortcuts for histogram names
 let Keys = {};
-for (let k of ["HISTORY", "FORMDATA", "OPEN_WINDOWS", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS", "DOM_STORAGE", "POSTDATA"]) {
+for (let k of ["HISTORY", "FORMDATA", "OPEN_WINDOWS", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS", "DOM_STORAGE"]) {
   Keys[k] = "FX_SESSION_RESTORE_TOTAL_" + k + "_SIZE_BYTES";
 }
 
 function lt(a, b, message) {
   isnot(a, undefined, message + " (sanity check)");
   isnot(b, undefined, message + " (sanity check)");
   ok(a < b, message + " ( " + a + " < " + b + ")");
 }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3403,23 +3403,16 @@
   },
   "FX_SESSION_RESTORE_TOTAL_HISTORY_SIZE_BYTES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "50000000",
     "n_buckets": 30,
     "description": "The subset of sessionstore.js dealing with storing history (total size, in bytes)"
   },
-  "FX_SESSION_RESTORE_TOTAL_POSTDATA_SIZE_BYTES": {
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": "50000000",
-    "n_buckets": 30,
-    "description": "The subset of sessionstore.js dealing with storing POST data (total size, in bytes)"
-  },
   "FX_SESSION_RESTORE_INDIVIDUAL_OPEN_WINDOWS_SIZE_BYTES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "5000000",
     "n_buckets": 30,
     "description": "Session restore: The subset of sessionrestore.js representing open windows (item size, in bytes)"
   },
   "FX_SESSION_RESTORE_INDIVIDUAL_CLOSED_WINDOWS_SIZE_BYTES": {
@@ -3459,23 +3452,16 @@
   },
   "FX_SESSION_RESTORE_INDIVIDUAL_HISTORY_SIZE_BYTES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "5000000",
     "n_buckets": 30,
     "description": "The subset of sessionstore.js dealing with storing history (item size, in bytes)"
   },
-  "FX_SESSION_RESTORE_INDIVIDUAL_POSTDATA_SIZE_BYTES": {
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": "5000000",
-    "n_buckets": 30,
-    "description": "The subset of sessionstore.js dealing with storing history POST data (item size, in bytes)"
-  },
   "INNERWINDOWS_WITH_MUTATION_LISTENERS": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Deleted or to-be-reused innerwindow which has had mutation event listeners."
   },
   "CHARSET_OVERRIDE_SITUATION": {
     "expires_in_version": "never",
     "kind": "enumerated",