Fix session restore to save and restore principals, so that about:blank, javascript:, data: stuff gets restored correctly. Bug 389274, r+sr=jst for the docshell changes, r=zeniko@gmail.com for the session restore changes, a=mconnor
authorbzbarsky@mit.edu
Mon, 17 Sep 2007 15:27:17 -0700
changeset 6007 21b902a3dd6eca626f2708cef9e37c924d7339db
parent 6006 ec08e71069666acf3adcd471e2df2c6263854e76
child 6008 eeb65bca61d86e48472a18384cadf52b7e316175
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerszeniko, mconnor
bugs389274
milestone1.9a8pre
Fix session restore to save and restore principals, so that about:blank, javascript:, data: stuff gets restored correctly. Bug 389274, r+sr=jst for the docshell changes, r=zeniko@gmail.com for the session restore changes, a=mconnor
browser/components/sessionstore/src/nsSessionStore.js
docshell/shistory/public/nsISHEntry.idl
docshell/shistory/src/nsSHEntry.cpp
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -864,39 +864,75 @@ SessionStoreService.prototype = {
       entry.subframe = true;
     }
     if (!(aEntry instanceof Ci.nsISHEntry)) {
       return entry;
     }
     
     var cacheKey = aEntry.cacheKey;
     if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32) {
+      // XXXbz would be better to have cache keys implement
+      // nsISerializable or something.
       entry.cacheKey = cacheKey.data;
     }
     entry.ID = aEntry.ID;
     
+    entry.contentType = aEntry.contentType;
+    
     var x = {}, y = {};
     aEntry.getScrollPosition(x, y);
     entry.scroll = x.value + "," + y.value;
     
     try {
       var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
       if (prefPostdata && aEntry.postData && this._checkPrivacyLevel(aEntry.URI.schemeIs("https"))) {
         aEntry.postData.QueryInterface(Ci.nsISeekableStream).
                         seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
-        var stream = Cc["@mozilla.org/scriptableinputstream;1"].
-                     createInstance(Ci.nsIScriptableInputStream);
-        stream.init(aEntry.postData);
-        var postdata = stream.read(stream.available());
-        if (prefPostdata == -1 || postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <= prefPostdata) {
-          entry.postdata = postdata;
+        var stream = Cc["@mozilla.org/binaryinputstream;1"].
+                     createInstance(Ci.nsIBinaryInputStream);
+        stream.setInputStream(aEntry.postData);
+        var postBytes = stream.readByteArray(stream.available());
+        var postdata = String.fromCharCode.apply(null, postBytes);
+        if (prefPostdata == -1 ||
+            postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <=
+              prefPostdata) {
+          // We can stop doing base64 encoding once our serialization into JSON
+          // is guaranteed to handle all chars in strings, including embedded
+          // nulls.
+          entry.postdata_b64 = btoa(postdata);
         }
       }
     }
     catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right
+
+    if (aEntry.owner) {
+      // Not catching anything specific here, just possible errors
+      // from writeCompoundObject and the like.
+      try {
+        var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
+                           createInstance(Ci.nsIObjectOutputStream);
+        var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
+        pipe.init(false, false, 0, 0xffffffff, null);
+        binaryStream.setOutputStream(pipe.outputStream);
+        binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
+        binaryStream.close();
+
+        // Now we want to read the data from the pipe's input end and encode it.
+        var scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
+                               createInstance(Ci.nsIBinaryInputStream);
+        scriptableStream.setInputStream(pipe.inputStream);
+        var ownerBytes =
+          scriptableStream.readByteArray(scriptableStream.available());
+        // We can stop doing base64 encoding once our serialization into JSON
+        // is guaranteed to handle all chars in strings, including embedded
+        // nulls.
+        entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
+      }
+      catch (ex) { debug(ex); }
+    }
     
     if (!(aEntry instanceof Ci.nsISHContainer)) {
       return entry;
     }
     
     for (var i = 0; i < aEntry.childCount; i++) {
       var child = aEntry.GetChildAt(i);
       if (child) {
@@ -1415,16 +1451,17 @@ SessionStoreService.prototype = {
                   createInstance(Ci.nsISHEntry);
     
     var ioService = Cc["@mozilla.org/network/io-service;1"].
                     getService(Ci.nsIIOService);
     shEntry.setURI(ioService.newURI(aEntry.url, null, null));
     shEntry.setTitle(aEntry.title || aEntry.url);
     shEntry.setIsSubFrame(aEntry.subframe || false);
     shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
+    shEntry.contentType = aEntry.contentType;
     
     if (aEntry.cacheKey) {
       var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
                      createInstance(Ci.nsISupportsPRUint32);
       cacheKey.data = aEntry.cacheKey;
       shEntry.cacheKey = cacheKey;
     }
     if (aEntry.ID) {
@@ -1437,23 +1474,48 @@ SessionStoreService.prototype = {
         aIdMap.used[id] = true;
       }
       shEntry.ID = id;
     }
     
     var scrollPos = (aEntry.scroll || "0,0").split(",");
     scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
     shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
-    
-    if (aEntry.postdata) {
+
+    var postdata;
+    if (aEntry.postdata_b64) {  // Firefox 3
+      postdata = atob(aEntry.postdata_b64);
+    } else if (aEntry.postdata) { // Firefox 2
+      postdata = aEntry.postdata;
+    }
+
+    if (postdata) {
       var stream = Cc["@mozilla.org/io/string-input-stream;1"].
                    createInstance(Ci.nsIStringInputStream);
-      stream.setData(aEntry.postdata, -1);
+      stream.setData(postdata, postdata.length);
       shEntry.postData = stream;
     }
+
+    if (aEntry.owner_b64) {  // Firefox 3
+      var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
+                       createInstance(Ci.nsIStringInputStream);
+      var binaryData = atob(aEntry.owner_b64);
+      ownerInput.setData(binaryData, binaryData.length);
+      var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
+                         createInstance(Ci.nsIObjectInputStream);
+      binaryStream.setInputStream(ownerInput);
+      try { // Catch possible deserialization exceptions
+        shEntry.owner = binaryStream.readObject(true);
+      } catch (ex) { debug(ex); }
+    } else if (aEntry.ownerURI) { // Firefox 2
+      var uriObj = ioService.newURI(aEntry.ownerURI, null, null);
+      shEntry.owner = Cc["@mozilla.org/scriptsecuritymanager;1"].
+                      getService(Ci.nsIScriptSecurityManager).
+                      getCodebasePrincipal(uriObj);
+    }
     
     if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
       for (var i = 0; i < aEntry.children.length; i++) {
         shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap), i);
       }
     }
     
     return shEntry;
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -50,17 +50,17 @@ interface nsIURI;
 interface nsIInputStream;
 interface nsIDocShellTreeItem;
 interface nsISupportsArray;
 %{C++
 struct nsRect;
 %}
 [ref] native nsRect(nsRect);
 
-[scriptable, uuid(9b4c7bf5-5e68-4406-9bb4-a4408c8e8bb5)]
+[scriptable, uuid(abe54136-49e5-44ca-a749-290038c6b85d)]
 interface nsISHEntry : nsIHistoryEntry
 {
     /** URI for the document */
     void setURI(in nsIURI aURI);
 
     /** Referrer URI */
     attribute nsIURI referrerURI;
 
@@ -161,38 +161,38 @@ interface nsISHEntry : nsIHistoryEntry
      */
     attribute ACString contentType; 
  
     /** Set/Get scrollers' positon in anchored pages */
     void setScrollPosition(in long x, in long y);
     void getScrollPosition(out long x, out long y);
 
     /** Additional ways to create an entry */
-    void create(in nsIURI URI, in AString title,
-                in nsIInputStream inputStream,
-                in nsILayoutHistoryState layoutHistoryState,
-                in nsISupports cacheKey, in ACString contentType,
-                in nsISupports owner);
+    [noscript] void create(in nsIURI URI, in AString title,
+                           in nsIInputStream inputStream,
+                           in nsILayoutHistoryState layoutHistoryState,
+                           in nsISupports cacheKey, in ACString contentType,
+                           in nsISupports owner);
 
     nsISHEntry clone();
 
     /** Attribute that indicates if this entry is for a subframe navigation */
     void setIsSubFrame(in boolean aFlag);
 
     /** Return any content viewer present in or below this node in the
         nsSHEntry tree.  This will differ from contentViewer in the case
         where a child nsSHEntry has the content viewer for this tree. */
     nsIContentViewer getAnyContentViewer(out nsISHEntry ownerEntry);
 
     /**
      * Get the owner, if any, that was associated with the channel
      * that the document that was loaded to create this history entry
      * came from.
      */
-    readonly attribute nsISupports owner;
+    attribute nsISupports owner;
 };
 
 
 %{ C++
 // {BFD1A791-AD9F-11d3-BDC7-0050040A9B44}
 #define NS_SHENTRY_CID \
 {0xbfd1a791, 0xad9f, 0x11d3, {0xbd, 0xc7, 0x0, 0x50, 0x4, 0xa, 0x9b, 0x44}}
 
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -495,16 +495,23 @@ nsSHEntry::GetViewerBounds(nsRect &aBoun
 
 NS_IMETHODIMP
 nsSHEntry::GetOwner(nsISupports **aOwner)
 {
   NS_IF_ADDREF(*aOwner = mOwner);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSHEntry::SetOwner(nsISupports *aOwner)
+{
+  mOwner = aOwner;
+  return NS_OK;
+}
+
 //*****************************************************************************
 //    nsSHEntry: nsISHContainer
 //*****************************************************************************
 
 NS_IMETHODIMP 
 nsSHEntry::GetChildCount(PRInt32 * aCount)
 {
   *aCount = mChildren.Count();