зеркало из https://github.com/mozilla/gecko-dev.git
Bug 423132 - speed up sessionstore cookie bits. r=dietrich, sr=mconnor
This commit is contained in:
Родитель
f03f40764b
Коммит
73a268beee
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче