Bug 423132 - speed up sessionstore cookie bits. r=dietrich, sr=mconnor

This commit is contained in:
Dan Witte 2009-07-15 17:57:29 -07:00
Родитель f03f40764b
Коммит 73a268beee
9 изменённых файлов: 227 добавлений и 29 удалений

Просмотреть файл

@ -1507,17 +1507,12 @@ SessionStoreService.prototype = {
_updateCookieHosts: function sss_updateCookieHosts(aWindow) {
var hosts = this._windows[aWindow.__SSi]._hosts = {};
// get all possible subdomain levels for a given URL
var _this = this;
// get the domain for each URL
function extractHosts(aEntry) {
if (/^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.test(aEntry.url) &&
!hosts[RegExp.$1] && _this._checkPrivacyLevel(_this._getURIFromString(aEntry.url).schemeIs("https"))) {
var host = RegExp.$1;
var ix;
for (ix = host.indexOf(".") + 1; ix; ix = host.indexOf(".", ix) + 1) {
hosts[host.substr(ix)] = true;
if (/^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.test(aEntry.url)) {
if (!hosts[RegExp.$1] && this._checkPrivacyLevel(this._getURIFromString(aEntry.url).schemeIs("https"))) {
hosts[RegExp.$1] = true;
}
hosts[host] = true;
}
else if (/^file:\/\/([^\/]*)/.test(aEntry.url)) {
hosts[RegExp.$1] = true;
@ -1526,8 +1521,8 @@ SessionStoreService.prototype = {
aEntry.children.forEach(extractHosts);
}
}
this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts); });
this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts, this); }, this);
},
/**
@ -1536,36 +1531,59 @@ SessionStoreService.prototype = {
* array of Window references
*/
_updateCookies: function sss_updateCookies(aWindows) {
var cookiesEnum = Cc["@mozilla.org/cookiemanager;1"].
getService(Ci.nsICookieManager).enumerator;
function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
// lazily build up a 3-dimensional hash, with
// aHost, aPath, and aName as keys
if (!aHash[aHost])
aHash[aHost] = {};
if (!aHash[aHost][aPath])
aHash[aHost][aPath] = {};
if (!aHash[aHost][aPath][aName])
aHash[aHost][aPath][aName] = {};
aHash[aHost][aPath][aName] = aCookie;
}
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
// collect the cookies per window
for (var i = 0; i < aWindows.length; i++)
aWindows[i].cookies = [];
var jscookies = {};
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
var MAX_EXPIRY = Math.pow(2, 62);
while (cookiesEnum.hasMoreElements()) {
var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.isSession && this._checkPrivacyLevel(cookie.isSecure)) {
var jscookie = null;
aWindows.forEach(function(aWindow) {
if (aWindow._hosts && aWindow._hosts[cookie.rawHost]) {
// serialize the cookie when it's first needed
if (!jscookie) {
jscookie = { host: cookie.host, value: cookie.value };
aWindows.forEach(function(aWindow) {
for (var host in aWindow._hosts) {
var list = cm.getCookiesFromHost(host);
while (list.hasMoreElements()) {
var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.isSession && this._checkPrivacyLevel(cookie.isSecure)) {
// use the cookie's host, path, and name as keys into a hash,
// to make sure we serialize each cookie only once
var isInHash = false;
try {
if (jscookies[cookie.host][cookie.path][cookie.name])
isInHash = true;
} catch (e) {
// not in hash yet
}
if (!isInHash) {
var jscookie = { "host": cookie.host, "value": cookie.value };
// only add attributes with non-default values (saving a few bits)
if (cookie.path) jscookie.path = cookie.path;
if (cookie.name) jscookie.name = cookie.name;
if (cookie.isSecure) jscookie.secure = true;
if (cookie.isHttpOnly) jscookie.httponly = true;
if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
}
aWindow.cookies.push(jscookie);
aWindow.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
}
});
}
}
}
}, this);
// don't include empty cookie sections
for (i = 0; i < aWindows.length; i++)
if (aWindows[i].cookies.length == 0)

Просмотреть файл

@ -64,6 +64,8 @@ _BROWSER_TEST_FILES = \
browser_394759_privatebrowsing.js \
browser_408470.js \
browser_408470_sample.html \
browser_423132.js \
browser_423132_sample.html \
browser_447951.js \
browser_447951_sample.html \
browser_448741.js \

Просмотреть файл

@ -0,0 +1,107 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is sessionstore test code.
*
* The Initial Developer of the Original Code is
* Daniel Witte <dwitte@mozilla.com>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function test() {
// test that cookies are stored and restored correctly by sessionstore,
// bug 423132.
// test setup
waitForExplicitFinish();
let cs = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
cs.removeAll();
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
// make sure that sessionstore.js can be forced to be created by setting
// the interval pref to 0
gPrefService.setIntPref("browser.sessionstore.interval", 0);
const testURL = "http://localhost:8888/browser/" +
"browser/components/sessionstore/test/browser/browser_423132_sample.html";
// open a new window
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:blank");
// make sure sessionstore saves the cookie data, then close the window
newWin.addEventListener("load", function (aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
newWin.gBrowser.selectedBrowser.loadURI(testURL, null, null);
newWin.gBrowser.addEventListener("load", function (aEvent) {
newWin.gBrowser.removeEventListener("load", arguments.callee, true);
// get the sessionstore state for the window
let state = ss.getWindowState(newWin);
// verify our cookie got set during pageload
let e = cs.enumerator;
let cookie;
let i = 0;
while (e.hasMoreElements()) {
cookie = e.getNext().QueryInterface(Ci.nsICookie);
i++;
}
is(i, 1, "expected one cookie");
// remove the cookie
cs.removeAll();
// restore the window state
ss.setWindowState(newWin, state, true);
// at this point, the cookie should be restored...
e = cs.enumerator;
let cookie2;
while (e.hasMoreElements()) {
cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
if (cookie.name == cookie2.name)
break;
}
is(cookie.name, cookie2.name, "cookie name successfully restored");
is(cookie.value, cookie2.value, "cookie value successfully restored");
is(cookie.path, cookie2.path, "cookie path successfully restored");
// clean up
gPrefService.clearUserPref("browser.sessionstore.interval");
cs.removeAll();
newWin.close();
finish();
}, true);
}, false);
}

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
// generate an enormous random number...
var r = Math.floor(Math.random() * Math.pow(2, 62)).toString();
// ... and use it to set a randomly named cookie
document.cookie = r + "=value; path=/ohai";
</script>
<body>
</body>
</html>

Просмотреть файл

@ -44,7 +44,7 @@
* access of cookie objects
*/
[scriptable, uuid(8587f4e0-870c-11dd-ad8b-0800200c9a66)]
[scriptable, uuid(05c420e5-03d0-4c7b-a605-df7ebe5ca326)]
interface nsICookie2 : nsICookie
{
@ -84,5 +84,13 @@ interface nsICookie2 : nsICookie
*/
readonly attribute PRInt64 creationTime;
/**
* the last time the cookie was accessed (i.e. created,
* modified, or read by the server), in microseconds
* since midnight (00:00:00), January 1, 1970 UTC.
*
* note that this time may be approximate.
*/
readonly attribute PRInt64 lastAccessed;
};

Просмотреть файл

@ -44,7 +44,7 @@ interface nsIFile;
* Additions to the frozen nsICookieManager
*/
[scriptable, uuid(5047cab4-9cb2-4927-a4ab-77422bc3bc67)]
[scriptable, uuid(d1e9e50f-b78b-4e3b-a474-f3cbca59b013)]
interface nsICookieManager2 : nsICookieManager
{
/**
@ -110,6 +110,24 @@ interface nsICookieManager2 : nsICookieManager
*/
unsigned long countCookiesFromHost(in ACString aHost);
/**
* Returns an enumerator of cookies that would be returned to a given host,
* ignoring the cookie flags isDomain, isSecure, and isHttpOnly. Thus, for a
* host "weather.yahoo.com", host or domain cookies for "weather.yahoo.com"
* and "yahoo.com" would be returned, while a cookie for "my.weather.yahoo.com"
* would not.
*
* @param aHost
* the host string to look for, e.g. "google.com". this should consist
* of only the host portion of a URI, and should not contain a leading
* dot, a port, etc.
*
* @return an nsISimpleEnumerator of nsICookie2 objects.
*
* @see countCookiesFromHost
*/
nsISimpleEnumerator getCookiesFromHost(in ACString aHost);
/**
* Import an old-style cookie file. Imported cookies will be added to the
* existing database. If the database contains any cookies the same as those

Просмотреть файл

@ -143,6 +143,7 @@ NS_IMETHODIMP nsCookie::GetIsHttpOnly(PRBool *aHttpOnly) { *aHttpOnly = IsHttp
NS_IMETHODIMP nsCookie::GetStatus(nsCookieStatus *aStatus) { *aStatus = 0; return NS_OK; }
NS_IMETHODIMP nsCookie::GetPolicy(nsCookiePolicy *aPolicy) { *aPolicy = 0; return NS_OK; }
NS_IMETHODIMP nsCookie::GetCreationTime(PRInt64 *aCreation){ *aCreation = CreationID(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetLastAccessed(PRInt64 *aTime) { *aTime = LastAccessed(); return NS_OK; }
// compatibility method, for use with the legacy nsICookie interface.
// here, expires == 0 denotes a session cookie.

Просмотреть файл

@ -139,6 +139,7 @@ struct nsListIter
{
nsListIter() {}
explicit
nsListIter(nsCookieEntry *aEntry)
: entry(aEntry)
, prev(nsnull)
@ -2169,6 +2170,35 @@ nsCookieService::CountCookiesFromHost(const nsACString &aHost,
return NS_OK;
}
// get an enumerator of cookies stored by a particular host. this is provided by the
// nsICookieManager2 interface.
NS_IMETHODIMP
nsCookieService::GetCookiesFromHost(const nsACString &aHost,
nsISimpleEnumerator **aEnumerator)
{
nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
nsCAutoString hostWithDot(NS_LITERAL_CSTRING(".") + aHost);
PRInt64 currentTime = PR_Now() / PR_USEC_PER_SEC;
const char *currentDot = hostWithDot.get();
const char *nextDot = currentDot + 1;
do {
nsCookieEntry *entry = mHostTable->GetEntry(currentDot);
for (nsListIter iter(entry); iter.current; ++iter) {
// only append non-expired cookies
if (iter.current->Expiry() > currentTime)
cookieList.AppendObject(iter.current);
}
currentDot = nextDot;
if (currentDot)
nextDot = strchr(currentDot + 1, '.');
} while (currentDot);
return NS_NewArrayEnumerator(aEnumerator, cookieList);
}
// find an exact cookie specified by host, name, and path that hasn't expired.
PRBool
nsCookieService::FindCookie(const nsAFlatCString &aHost,

Просмотреть файл

@ -74,6 +74,7 @@ class nsCookieEntry : public PLDHashEntryHdr
typedef const char* KeyTypePointer;
// do nothing with aHost - we require mHead to be set before we're live!
explicit
nsCookieEntry(KeyTypePointer aHost)
: mHead(nsnull)
{