Index: browser/base/content/browser.js =================================================================== RCS file: /cvsroot/mozilla/browser/base/content/browser.js,v retrieving revision 1.783 diff -p -U 8 -r1.783 browser.js --- browser/base/content/browser.js 13 May 2007 15:47:05 -0000 1.783 +++ browser/base/content/browser.js 14 May 2007 09:20:59 -0000 @@ -778,16 +778,19 @@ function BrowserStartup() var uriToLoad = null; // Check for window.arguments[0]. If present, use that for uriToLoad. if ("arguments" in window && window.arguments[0]) uriToLoad = window.arguments[0]; gIsLoadingBlank = uriToLoad == "about:blank"; prepareForStartup(); + + if (uriToLoad && window.arguments.length >= 6 && window.arguments[5].cookieContainer) + getBrowser().docShell.QueryInterface(Components.interfaces.nsICookieCollector).cookieContainer = window.arguments[5].cookieContainer; #ifdef ENABLE_PAGE_CYCLER appCore.startPageCycler(); #else # only load url passed in when we're not page cycling if (uriToLoad && !gIsLoadingBlank) { if (window.arguments.length >= 3) loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, @@ -1845,17 +1848,17 @@ function openLocation() function openLocationCallback() { // make sure the DOM is ready setTimeout(function() { this.openLocation(); }, 0); } function BrowserOpenTab() { - gBrowser.loadOneTab("about:blank", null, null, null, false, false); + gBrowser.loadOneTab("about:blank", null, null, null, false, false, null); if (gURLBar) setTimeout(function() { gURLBar.focus(); }, 0); } /* Called from the openLocation dialog. This allows that dialog to instruct its opener to open a new window and then step completely out of the way. Anything less byzantine is causing horrible crashes, rather believably, though oddly only on Linux. */ @@ -1868,17 +1871,17 @@ function delayedOpenWindow(chrome, flags // window.arguments[1] be an integer instead of null. setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10); } /* Required because the tab needs time to set up its content viewers and get the load of the URI kicked off before becoming the active content area. */ function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup) { - gBrowser.loadOneTab(aUrl, aReferrer, aCharset, aPostData, false, aAllowThirdPartyFixup); + gBrowser.loadOneTab(aUrl, aReferrer, aCharset, aPostData, false, aAllowThirdPartyFixup, null); } function BrowserOpenFileWindow() { // Get filepicker component. try { const nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); @@ -1979,17 +1982,18 @@ function BrowserLoadURL(aTriggeringEvent true /* allow third party fixup */, aPostData); return; } if (aTriggeringEvent && aTriggeringEvent.altKey) { handleURLBarRevert(); content.focus(); gBrowser.loadOneTab(url, null, null, aPostData, false, - true /* allow third party fixup */); + true, /* allow third party fixup */ + null); aTriggeringEvent.preventDefault(); aTriggeringEvent.stopPropagation(); } else loadURI(url, null, aPostData, true /* allow third party fixup */); focusElement(content); } @@ -3143,17 +3147,17 @@ const BrowserSearch = { // with a text/html response type. This is unlikely (since // SearchService._addEngineToStore() should fail for such an engine), // but let's be on the safe side. if (!submission) return; if (useNewTab) { getBrowser().loadOneTab(submission.uri.spec, null, null, - submission.postData, null, false); + submission.postData, null, false, null); } else loadURI(submission.uri.spec, null, submission.postData, false); }, /** * Returns the search bar element if it is present in the toolbar and not * hidden, null otherwise. */ @@ -4123,17 +4127,17 @@ nsBrowserAccess.prototype = } var url = aURI ? aURI.spec : "about:blank"; switch(aWhere) { case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW : newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url); break; case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB : var loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"); - var newTab = gBrowser.loadOneTab("about:blank", null, null, null, loadInBackground, false); + var newTab = gBrowser.loadOneTab("about:blank", null, null, null, loadInBackground, false, null); newWindow = gBrowser.getBrowserForTab(newTab).docShell .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); try { if (aOpener) { location = aOpener.location; referrer = Components.classes["@mozilla.org/network/io-service;1"] Index: browser/base/content/utilityOverlay.js =================================================================== RCS file: /cvsroot/mozilla/browser/base/content/utilityOverlay.js,v retrieving revision 1.50 diff -p -U 8 -r1.50 utilityOverlay.js --- browser/base/content/utilityOverlay.js 7 Mar 2007 20:09:19 -0000 1.50 +++ browser/base/content/utilityOverlay.js 14 May 2007 09:21:00 -0000 @@ -219,17 +219,17 @@ function openUILinkIn( url, where, allow w.loadURI(url, referrerUrl, postData, allowThirdPartyFixup); break; case "tabshifted": loadInBackground = !loadInBackground; // fall through case "tab": var browser = w.getBrowser(); browser.loadOneTab(url, referrerUrl, null, postData, loadInBackground, - allowThirdPartyFixup || false); + allowThirdPartyFixup || false, null); break; } // Call focusElement(w.content) instead of w.content.focus() to make sure // that we don't raise the old window, since the URI we just loaded may have // resulted in a new frontmost window (e.g. "javascript:window.open('');"). focusElement(w.content); } @@ -551,18 +551,24 @@ function openNewTabWith(aURL, aDocument, var wintype = document.documentElement.getAttribute("windowtype"); var originCharset; if (wintype == "navigator:browser") originCharset = window.content.document.characterSet; // open link in new tab var referrerURI = aDocument ? aDocument.documentURIObject : null; var browser = top.document.getElementById("content"); + + var inheritOptions = { }; + // get current cookie container + browser.docShell.QueryInterface(Components.interfaces.nsICookieCollector); + inheritOptions.cookieContainer = browser.docShell.cookieContainer; + browser.loadOneTab(aURL, referrerURI, originCharset, aPostData, - loadInBackground, aAllowThirdPartyFixup || false); + loadInBackground, aAllowThirdPartyFixup || false, inheritOptions); } function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup) { if (aDocument) urlSecurityCheck(aURL, aDocument.nodePrincipal); // if and only if the current window is a browser window and it has a @@ -570,12 +576,19 @@ function openNewWindowWith(aURL, aDocume // setting from the current document and use it to initialize the new browser // window... var charsetArg = null; var wintype = document.documentElement.getAttribute("windowtype"); if (wintype == "navigator:browser") charsetArg = "charset=" + window.content.document.characterSet; var referrerURI = aDocument ? aDocument.documentURIObject : null; + var browser = top.document.getElementById("content"); + + var inheritOptions = { }; + // get current cookie container + browser.docShell.QueryInterface(Components.interfaces.nsICookieCollector); + inheritOptions.cookieContainer = browser.docShell.cookieContainer; + window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", aURL, charsetArg, referrerURI, aPostData, - aAllowThirdPartyFixup); + aAllowThirdPartyFixup, inheritOptions); } Index: browser/components/preferences/cookies.js =================================================================== RCS file: /cvsroot/mozilla/browser/components/preferences/cookies.js,v retrieving revision 1.15 diff -p -U 8 -r1.15 cookies.js --- browser/components/preferences/cookies.js 15 Sep 2006 21:28:47 -0000 1.15 +++ browser/components/preferences/cookies.js 14 May 2007 09:21:03 -0000 @@ -53,16 +53,20 @@ var gCookiesWindow = { var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.addObserver(this, "cookie-changed", false); os.addObserver(this, "perm-changed", false); this._bundle = document.getElementById("bundlePreferences"); this._tree = document.getElementById("cookiesList"); + // check if a containerId was passed when opening this window + if (window.arguments && window.arguments[0]) + this._cm = Components.classes["@mozilla.org/cookieService;1"].getService(Components.interfaces.nsICookieService).getContainer(window.arguments[0]).QueryInterface(Components.interfaces.nsICookieManager); + this._loadCookies(); this._tree.treeBoxObject.view = this._view; this.sort("rawHost"); if (this._view.rowCount > 0) this._tree.view.selection.select(0); document.getElementById("filter").focus(); }, Index: browser/components/sessionstore/src/nsSessionStore.js =================================================================== RCS file: /cvsroot/mozilla/browser/components/sessionstore/src/nsSessionStore.js,v retrieving revision 1.60 diff -p -U 8 -r1.60 nsSessionStore.js --- browser/components/sessionstore/src/nsSessionStore.js 12 Feb 2007 03:07:54 -0000 1.60 +++ browser/components/sessionstore/src/nsSessionStore.js 14 May 2007 09:21:09 -0000 @@ -15,16 +15,17 @@ * * The Initial Developer of the Original Code is * Simon Bünzli * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Dietrich Ayala + * Loune Lam * * 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 @@ -793,16 +794,18 @@ SessionStoreService.prototype = { } tabData.zoom = browser.markupDocumentViewer.textZoom; var disallow = CAPABILITIES.filter(function(aCapability) { return !browser.docShell["allow" + aCapability]; }); tabData.disallow = disallow.join(","); + tabData.cookieContainer = browser.docShell.QueryInterface(Ci.nsICookieCollector).cookieContainer.containerId; + var _this = this; var xulattr = Array.filter(tabbrowser.mTabs[i].attributes, function(aAttr) { return (_this.xulAttributes.indexOf(aAttr.name) > -1); }).map(function(aAttr) { return aAttr.name + "=" + encodeURI(aAttr.value); }); tabData.xultab = xulattr.join(" "); @@ -985,84 +988,108 @@ SessionStoreService.prototype = { }, /** * store all hosts for a URL * @param aWindow * Window reference */ _updateCookieHosts: function sss_updateCookieHosts(aWindow) { - var hosts = this._windows[aWindow.__SSi]._hosts = {}; + var hosts = this._windows[aWindow.__SSi]._containerHosts = {}; + var currentContainer; // get all possible subdomain levels for a given URL var _this = this; function extractHosts(aEntry) { if (/^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.test(aEntry.url) && - !hosts[RegExp.$1] && _this._checkPrivacyLevel(_this._getURIFromString(aEntry.url).schemeIs("https"))) { + !hosts[currentContainer][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; + hosts[currentContainer][host.substr(ix)] = true; } - hosts[host] = true; + hosts[currentContainer][host] = true; } if (aEntry.children) { aEntry.children.forEach(extractHosts); } } - this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts); }); + this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { + currentContainer = aTabData.cookieContainer; + if (!hosts[currentContainer]) + hosts[currentContainer] = {}; + aTabData.entries.forEach(extractHosts); + }); }, /** * Serialize cookie data * @param aWindows * array of Window references */ _updateCookies: function sss_updateCookies(aWindows) { - var cookiesEnum = Cc["@mozilla.org/cookiemanager;1"]. - getService(Ci.nsICookieManager).enumerator; + var cookieSvc = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); // collect the cookies per window for (var i = 0; i < aWindows.length; i++) { - aWindows[i].cookies = { count: 0 }; + aWindows[i].cookieContainers = { }; } - var _this = this; - while (cookiesEnum.hasMoreElements()) { - var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2); - if (cookie.isSession && cookie.host) { - var url = "", value = ""; - aWindows.forEach(function(aWindow) { - if (aWindow._hosts && aWindow._hosts[cookie.rawHost]) { - // make sure to construct URL and value only once per cookie - if (!url) { - var url = "http" + (cookie.isSecure ? "s" : "") + "://" + cookie.host + (cookie.path || "").replace(/^(?!\/)/, "/"); - if (_this._checkPrivacyLevel(cookie.isSecure)) { - value = (cookie.name || "name") + "=" + (cookie.value || "") + ";"; - value += cookie.isDomain ? "domain=" + cookie.rawHost + ";" : ""; - value += cookie.path ? "path=" + cookie.path + ";" : ""; - value += cookie.isSecure ? "secure;" : ""; - } - } - if (value) { - // in order to not unnecessarily bloat the session file, - // all window cookies are saved into one JS object - var cookies = aWindow.cookies; - cookies["domain" + ++cookies.count] = url; - cookies["value" + cookies.count] = value; - } - } - }); + var _this = this; + var containers = []; + // for each window, retrieve the associated container + aWindows.forEach(function(aWindow) { + for (var cookieContainerId in aWindow._containerHosts) { + var container = cookieSvc.getContainer(cookieContainerId); + if (containers.indexOf(container) == -1) + containers.push(container); + } + }); + + containers.forEach(function(aContainer) { + var containerId = aContainer.containerId; + dump("CCCCCCCCCCC PROCESSING "+containerId+"\n"); + var cookiesEnum = aContainer.QueryInterface(Ci.nsICookieManager).enumerator; + while (cookiesEnum.hasMoreElements()) { + var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2); + if (cookie.isSession && cookie.host) { + var url = "", value = ""; + aWindows.forEach(function(aWindow) { + if (aWindow._containerHosts[containerId] && aWindow._containerHosts[containerId][cookie.rawHost]) { + // make sure to construct URL and value only once per cookie + if (!url) { + var url = "http" + (cookie.isSecure ? "s" : "") + "://" + cookie.host + (cookie.path || "").replace(/^(?!\/)/, "/"); + if (_this._checkPrivacyLevel(cookie.isSecure)) { + value = (cookie.name || "name") + "=" + (cookie.value || "") + ";"; + value += cookie.isDomain ? "domain=" + cookie.rawHost + ";" : ""; + value += cookie.path ? "path=" + cookie.path + ";" : ""; + value += cookie.isSecure ? "secure;" : ""; + value += cookie.httpOnly ? "HttpOnly;" : ""; + } + } + if (value) { + // in order to not unnecessarily bloat the session file, + // all window cookies are saved into one JS object + var cookies = aWindow.cookieContainers; + if (!cookies[containerId]) + cookies[containerId] = { count: 0 }; + cookies[containerId]["domain" + ++cookies[containerId].count] = url; + cookies[containerId]["value" + cookies[containerId].count] = value; + } + dump("CCCCCCCCCCC ADD "+cookie.host+" "+cookie.name+"\n"); + } + }); + } } - } + }); // don't include empty cookie sections for (i = 0; i < aWindows.length; i++) { - if (aWindows[i].cookies.count == 0) { - delete aWindows[i].cookies; + if (aWindows[i].cookieContainers.count == 0) { + delete aWindows[i].cookieContainers; } } }, /** * Store window dimensions, visibility, sidebar * @param aWindow * Window reference @@ -1209,18 +1236,18 @@ SessionStoreService.prototype = { // when overwriting tabs, remove all superflous ones for (t = openTabCount - 1; t >= newTabCount; t--) { tabbrowser.removeTab(tabbrowser.mTabs[t]); } if (aOverwriteTabs) { this.restoreWindowFeatures(aWindow, winData, root.opener || null); } - if (winData.cookies) { - this.restoreCookies(winData.cookies); + if (winData.cookieContainers) { + this.restoreCookies(winData.cookieContainers); } if (winData.extData) { if (!this._windows[aWindow.__SSi].extData) { this._windows[aWindow.__SSi].extData = {} } for (var key in winData.extData) { this._windows[aWindow.__SSi].extData[key] = winData.extData[key]; } @@ -1339,16 +1366,21 @@ SessionStoreService.prototype = { if (tabData.xultab) { tabData.xultab.split(" ").forEach(function(aAttr) { if (/^([^\s=]+)=(.*)/.test(aAttr)) { tab.setAttribute(RegExp.$1, decodeURI(RegExp.$2)); } }); } + if (tabData.cookieContainer) { + var cookieSvc = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + browser.docShell.QueryInterface(Ci.nsICookieCollector).cookieContainer = cookieSvc.getContainer(tabData.cookieContainer); + } + // notify the tabbrowser that the tab chrome has been restored var event = aWindow.document.createEvent("Events"); event.initEvent("SSTabRestoring", true, false); tab.dispatchEvent(event); var activeIndex = (tabData.index || tabData.entries.length) - 1; try { browser.webNavigation.gotoIndex(activeIndex); @@ -1561,21 +1593,25 @@ SessionStoreService.prototype = { * Array of cookie data */ restoreCookies: function sss_restoreCookies(aCookies) { var cookieService = Cc["@mozilla.org/cookieService;1"]. getService(Ci.nsICookieService); var ioService = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); - for (var i = 1; i <= aCookies.count; i++) { - try { - cookieService.setCookieString(ioService.newURI(aCookies["domain" + i], null, null), null, aCookies["value" + i] + "expires=0;", null); + for (let [cookieContainerId, cookies] in aCookies) { + var cookieContainer = cookieService.getContainer(cookieContainerId); + for (var i = 1; i <= cookies.count; i++) { + try { + cookieService.setCookieString(cookieContainer, ioService.newURI(cookies["domain" + i], null, null), + null, cookies["value" + i] + "expires=0;", null); + } + catch (ex) { debug(ex); } // don't let a single cookie stop recovering (might happen if a user tried to edit the session file) } - catch (ex) { debug(ex); } // don't let a single cookie stop recovering (might happen if a user tried to edit the session file) } }, /** * Restart incomplete downloads * @param aWindow * Window reference */ Index: content/base/public/nsIDocument.h =================================================================== RCS file: /cvsroot/mozilla/content/base/public/nsIDocument.h,v retrieving revision 3.264 diff -p -U 8 -r3.264 nsIDocument.h --- content/base/public/nsIDocument.h 10 May 2007 20:21:12 -0000 3.264 +++ content/base/public/nsIDocument.h 14 May 2007 09:21:13 -0000 @@ -113,16 +113,17 @@ public: #ifdef MOZILLA_INTERNAL_API nsIDocument() : nsINode(nsnull), mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")), mBindingManager(nsnull), mNodeInfoManager(nsnull), mCompatMode(eCompatibility_FullStandards), mIsInitialDocumentInWindow(PR_FALSE), + mDocumentContainer(nsnull), mPartID(0) { mParentPtrBits |= PARENT_BIT_INDOCUMENT; } #endif /** * Let the document know that we're starting to load data into it. Index: content/base/src/nsContentSink.cpp =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsContentSink.cpp,v retrieving revision 1.75 diff -p -U 8 -r1.75 nsContentSink.cpp --- content/base/src/nsContentSink.cpp 14 May 2007 03:52:49 -0000 1.75 +++ content/base/src/nsContentSink.cpp 14 May 2007 09:21:15 -0000 @@ -81,16 +81,18 @@ #include "nsTimer.h" #include "nsIAppShell.h" #include "nsWidgetsCID.h" #include "nsIDOMNSDocument.h" #include "nsIRequest.h" #include "nsNodeUtils.h" #include "nsIDOMNode.h" #include "nsThreadUtils.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" PRLogModuleInfo* gContentSinkLogModuleInfo; class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver { public: nsScriptLoaderObserverProxy(nsIScriptLoaderObserver* aInner) : mInner(do_GetWeakReference(aInner)) @@ -427,17 +429,24 @@ nsContentSink::ProcessHeaderData(nsIAtom window->GetPrompter(getter_AddRefs(prompt)); } nsCOMPtr channel; if (mParser) { mParser->GetChannel(getter_AddRefs(channel)); } - rv = cookieServ->SetCookieString(codebaseURI, + // find the cookie container + nsCOMPtr docShell = mDocument->GetContainer(); + nsCOMPtr cookieCollector = do_QueryInterface(docShell); + nsCOMPtr cookieContainer; + cookieCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + + rv = cookieServ->SetCookieString(cookieContainer, + codebaseURI, prompt, NS_ConvertUTF16toUTF8(aValue).get(), channel); if (NS_FAILED(rv)) { return rv; } } else if (aHeader == nsGkAtoms::link) { Index: content/base/src/nsDocument.cpp =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsDocument.cpp,v retrieving revision 3.732 diff -p -U 8 -r3.732 nsDocument.cpp --- content/base/src/nsDocument.cpp 12 May 2007 15:36:28 -0000 3.732 +++ content/base/src/nsDocument.cpp 14 May 2007 09:21:21 -0000 @@ -133,16 +133,19 @@ static NS_DEFINE_CID(kDOMEventGroupCID, #include "nsIScriptContext.h" #include "nsBindingManager.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLFormElement.h" #include "nsIRequest.h" #include "nsILink.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + #include "nsICharsetAlias.h" #include "nsIParser.h" #include "nsIContentSink.h" #include "nsDateTimeFormatCID.h" #include "nsIDateTimeFormat.h" #include "nsEventDispatcher.h" #include "nsMutationEvent.h" @@ -901,16 +904,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY(nsIDOMNode) NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(nsIDOM3Node) NS_INTERFACE_MAP_ENTRY(nsIDOM3Document) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer) NS_INTERFACE_MAP_ENTRY(nsINode) NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocument) if (aIID.Equals(NS_GET_IID(nsIDOMXPathEvaluator)) || aIID.Equals(NS_GET_IID(nsIXPathEvaluatorInternal))) { if (!mXPathEvaluatorTearoff) { nsresult rv; mXPathEvaluatorTearoff = do_CreateInstance(NS_XPATH_EVALUATOR_CONTRACTID, NS_STATIC_CAST(nsIDocument *, this), &rv); @@ -5870,8 +5874,38 @@ nsDocument::GetScriptTypeID(PRUint32 *aS } NS_IMETHODIMP nsDocument::SetScriptTypeID(PRUint32 aScriptType) { NS_ERROR("Can't change default script type for a document"); return NS_ERROR_NOT_IMPLEMENTED; } + +NS_IMETHODIMP +nsDocument::GetInterface(const nsIID & aIID, void **aSink) +{ + NS_ENSURE_ARG_POINTER(aSink); + *aSink = nsnull; + nsresult rv; + + if (aIID.Equals(NS_GET_IID(nsICookieCollector))) { + *aSink = mDocumentContainer; + } + else if (aIID.Equals(NS_GET_IID(nsICookieContainer))) { + nsCOMPtr cookieCollector = do_QueryReferent(mDocumentContainer); + nsCOMPtr cookieContainer = nsnull; + if (cookieCollector) { + cookieCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + } + if (!cookieContainer) + return NS_NOINTERFACE; + *aSink = cookieContainer; + } + + if (*aSink) { + NS_IF_ADDREF(((nsISupports *) * aSink)); + return NS_OK; + } + else + return QueryInterface(aIID, aSink); +} + Index: content/base/src/nsDocument.h =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsDocument.h,v retrieving revision 3.340 diff -p -U 8 -r3.340 nsDocument.h --- content/base/src/nsDocument.h 12 May 2007 15:36:28 -0000 3.340 +++ content/base/src/nsDocument.h 14 May 2007 09:21:24 -0000 @@ -87,16 +87,17 @@ #include "nsIScriptEventManager.h" #include "nsILayoutHistoryState.h" #include "nsIRequest.h" #include "nsILoadGroup.h" #include "nsTObserverArray.h" #include "nsStubMutationObserver.h" #include "nsIChannel.h" #include "nsCycleCollectionParticipant.h" +#include "nsIInterfaceRequestor.h" // Put these here so all document impls get them automatically #include "nsHTMLStyleSheet.h" #include "nsIHTMLCSSStyleSheet.h" #include "nsStyleSet.h" #include "nsXMLEventsManager.h" #include "pldhash.h" @@ -288,17 +289,18 @@ class nsDocument : public nsIDocument, public nsIDOMDocumentXBL, public nsIDOM3Document, public nsSupportsWeakReference, public nsIDOMEventReceiver, public nsIDOM3EventTarget, public nsIDOMNSEventTarget, public nsIScriptObjectPrincipal, public nsIRadioGroupContainer, - public nsStubMutationObserver + public nsStubMutationObserver, + public nsIInterfaceRequestor { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup); virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, nsIPrincipal* aPrincipal); @@ -615,16 +617,19 @@ public: NS_DECL_NSIDOMEVENTTARGET // nsIDOM3EventTarget NS_DECL_NSIDOM3EVENTTARGET // nsIDOMNSEventTarget NS_DECL_NSIDOMNSEVENTTARGET + // nsIInterfaceRequestor + NS_DECL_NSIINTERFACEREQUESTOR + // nsIScriptObjectPrincipal virtual nsIPrincipal* GetPrincipal(); virtual nsresult Init(); virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement); virtual nsresult CreateElem(nsIAtom *aName, nsIAtom *aPrefix, Index: content/base/src/nsObjectLoadingContent.cpp =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsObjectLoadingContent.cpp,v retrieving revision 1.40 diff -p -U 8 -r1.40 nsObjectLoadingContent.cpp --- content/base/src/nsObjectLoadingContent.cpp 1 May 2007 22:24:20 -0000 1.40 +++ content/base/src/nsObjectLoadingContent.cpp 14 May 2007 09:21:27 -0000 @@ -55,16 +55,18 @@ #include "nsIPresShell.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" #include "nsIStreamConverterService.h" #include "nsIURILoader.h" #include "nsIURL.h" #include "nsIWebNavigation.h" #include "nsIWebNavigationInfo.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" #include "nsPluginError.h" // Util headers #include "prlog.h" #include "nsAutoPtr.h" #include "nsCURILoader.h" @@ -970,16 +972,23 @@ nsObjectLoadingContent::LoadObject(nsIUR NS_ENSURE_SUCCESS(rv, rv); // Referrer nsCOMPtr httpChan(do_QueryInterface(chan)); if (httpChan) { httpChan->SetReferrer(doc->GetDocumentURI()); } + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(chan)); + if (cookieCollector) { + nsCOMPtr container(do_GetInterface(doc)); + cookieCollector->SetCookieContainer(container); + } + // MIME Type hint if (!aTypeHint.IsEmpty()) { chan->SetContentType(aTypeHint); } // AsyncOpen can fail if a file does not exist. // Show fallback content in that case. rv = chan->AsyncOpen(this, nsnull); Index: content/base/src/nsScriptLoader.cpp =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsScriptLoader.cpp,v retrieving revision 1.104 diff -p -U 8 -r1.104 nsScriptLoader.cpp --- content/base/src/nsScriptLoader.cpp 11 Apr 2007 20:18:11 -0000 1.104 +++ content/base/src/nsScriptLoader.cpp 14 May 2007 09:21:29 -0000 @@ -64,16 +64,18 @@ #include "jscntxt.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" #include "nsAutoPtr.h" #include "nsIXPConnect.h" #include "nsContentErrors.h" #include "nsIParser.h" #include "nsThreadUtils.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" ////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////// // If aMaybeCertPrincipal is a cert principal and aNewPrincipal is not the same // as aMaybeCertPrincipal, downgrade aMaybeCertPrincipal to a codebase // principal. Return the downgraded principal, or aMaybeCertPrincipal if no @@ -463,16 +465,23 @@ nsScriptLoader::ProcessScriptElement(nsI if (httpChannel) { // HTTP content negotation has little value in this context. httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("*/*"), PR_FALSE); httpChannel->SetReferrer(mDocument->GetDocumentURI()); } + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + nsCOMPtr container(do_GetInterface(mDocument)); + cookieCollector->SetCookieContainer(container); + } + rv = NS_NewStreamLoader(getter_AddRefs(loader), this); NS_ENSURE_SUCCESS(rv, rv); rv = channel->AsyncOpen(loader, request); NS_ENSURE_SUCCESS(rv, rv); } else { request->mLoading = PR_FALSE; request->mIsInline = PR_TRUE; Index: content/base/src/nsXMLHttpRequest.cpp =================================================================== RCS file: /cvsroot/mozilla/content/base/src/nsXMLHttpRequest.cpp,v retrieving revision 1.179 diff -p -U 8 -r1.179 nsXMLHttpRequest.cpp --- content/base/src/nsXMLHttpRequest.cpp 13 May 2007 16:41:46 -0000 1.179 +++ content/base/src/nsXMLHttpRequest.cpp 14 May 2007 09:21:36 -0000 @@ -77,16 +77,18 @@ #include "nsDOMJSUtils.h" #include "nsCOMArray.h" #include "nsDOMClassInfo.h" #include "nsIScriptableUConv.h" #include "nsCycleCollectionParticipant.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsContentErrors.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" #define LOAD_STR "load" #define ERROR_STR "error" #define PROGRESS_STR "progress" #define UPLOADPROGRESS_STR "uploadprogress" #define READYSTATE_STR "readystatechange" // CIDs @@ -1090,16 +1092,25 @@ nsXMLHttpRequest::OpenRequest(const nsAC if (NS_FAILED(rv)) return rv; //mChannel->SetAuthTriedWithPrehost(authp); nsCOMPtr httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { rv = httpChannel->SetRequestMethod(method); } + if (NS_FAILED(rv)) return rv; + + // set the http request cookie container to that of the parent document + nsCOMPtr cookieCollector(do_QueryInterface(mChannel)); + if (cookieCollector && doc) { + // find the cookie container + nsCOMPtr cookieContainer = do_GetInterface(doc); + cookieCollector->SetCookieContainer(cookieContainer); + } ChangeState(XML_HTTP_REQUEST_OPENED); return rv; } /* void open (in AUTF8String method, in AUTF8String url); */ NS_IMETHODIMP Index: content/html/document/src/nsHTMLDocument.cpp =================================================================== RCS file: /cvsroot/mozilla/content/html/document/src/nsHTMLDocument.cpp,v retrieving revision 3.720 diff -p -U 8 -r3.720 nsHTMLDocument.cpp --- content/html/document/src/nsHTMLDocument.cpp 9 May 2007 02:47:58 -0000 3.720 +++ content/html/document/src/nsHTMLDocument.cpp 14 May 2007 09:21:44 -0000 @@ -123,16 +123,19 @@ #include "nsIDocumentViewer.h" #include "nsIWyciwygChannel.h" #include "nsIScriptElement.h" #include "nsIScriptError.h" #include "nsIMutableArray.h" #include "nsArrayUtils.h" #include "nsIEffectiveTLDService.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + #include "nsIPrompt.h" //AHMED 12-2 #include "nsBidiUtils.h" #include "nsIEditingSession.h" #include "nsIEditor.h" #include "nsNodeInfoManager.h" @@ -1962,19 +1965,24 @@ nsHTMLDocument::GetCookie(nsAString& aCo NodePrincipal()->GetURI(getter_AddRefs(codebaseURI)); if (!codebaseURI) { // Document's principal is not a codebase (may be system), so // can't set cookies return NS_OK; } + + // find the cookie container + nsCOMPtr cookieCollector = do_QueryReferent(mDocumentContainer); + nsCOMPtr cookieContainer; + cookieCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); nsXPIDLCString cookie; - service->GetCookieString(codebaseURI, mChannel, getter_Copies(cookie)); + service->GetCookieString(cookieContainer, codebaseURI, mChannel, getter_Copies(cookie)); CopyASCIItoUTF16(cookie, aCookie); } return NS_OK; } NS_IMETHODIMP nsHTMLDocument::SetCookie(const nsAString& aCookie) @@ -1993,18 +2001,23 @@ nsHTMLDocument::SetCookie(const nsAStrin if (!codebaseURI) { // Document's principal is not a codebase (may be system), so // can't set cookies return NS_OK; } + // find the cookie container + nsCOMPtr cookieCollector = do_QueryReferent(mDocumentContainer); + nsCOMPtr cookieContainer; + cookieCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + NS_LossyConvertUTF16toASCII cookie(aCookie); - service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel); + service->SetCookieString(cookieContainer, codebaseURI, prompt, cookie.get(), mChannel); } return NS_OK; } // XXX TBI: accepting arguments to the open method. nsresult nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) @@ -2101,16 +2114,23 @@ nsHTMLDocument::OpenCommon(const nsACStr nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, group); if (NS_FAILED(rv)) { return rv; } + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + nsCOMPtr cookieCollector = do_QueryReferent(mDocumentContainer); + nsCOMPtr cookieContainer; + cookieCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + } + // Set the caller principal, if any, on the channel so that we'll // make sure to use it when we reset. rv = channel->SetOwner(callerPrincipal); NS_ENSURE_SUCCESS(rv, rv); // Before we reset the doc notify the globalwindow of the change, // but only if we still have a window (i.e. our window object the // current inner window in our outer window). Index: content/xbl/src/nsXBLService.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xbl/src/nsXBLService.cpp,v retrieving revision 1.235 diff -p -U 8 -r1.235 nsXBLService.cpp --- content/xbl/src/nsXBLService.cpp 11 May 2007 13:34:07 -0000 1.235 +++ content/xbl/src/nsXBLService.cpp 14 May 2007 09:21:48 -0000 @@ -70,16 +70,19 @@ #include "nsXBLPrototypeBinding.h" #include "nsIXBLDocumentInfo.h" #include "nsCRT.h" #include "nsContentUtils.h" #include "nsSyncLoadService.h" #include "nsIDOM3Node.h" #include "nsContentPolicyUtils.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + #include "nsIPresShell.h" #include "nsIDocumentObserver.h" #include "nsFrameManager.h" #include "nsStyleContext.h" #include "nsIScriptSecurityManager.h" #include "nsIScriptError.h" #ifdef MOZ_XUL @@ -1126,16 +1129,23 @@ nsXBLService::FetchBindingDocument(nsICo rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nsnull); NS_ENSURE_SUCCESS(rv, rv); // Open channel nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nsnull, loadGroup); NS_ENSURE_SUCCESS(rv, rv); + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + nsCOMPtr container(do_GetInterface(aBoundDocument)); + cookieCollector->SetCookieContainer(container); + } + nsCOMPtr listener; rv = doc->StartDocumentLoad("loadAsInteractiveData", channel, loadGroup, nsnull, getter_AddRefs(listener), PR_TRUE, xblSink); Index: content/xml/document/src/nsXMLContentSink.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xml/document/src/nsXMLContentSink.cpp,v retrieving revision 1.413 diff -p -U 8 -r1.413 nsXMLContentSink.cpp --- content/xml/document/src/nsXMLContentSink.cpp 1 May 2007 22:24:21 -0000 1.413 +++ content/xml/document/src/nsXMLContentSink.cpp 14 May 2007 09:21:51 -0000 @@ -675,18 +675,17 @@ nsXMLContentSink::LoadXSLStyleSheet(nsIU mXSLTProcessor->SetTransformObserver(this); nsCOMPtr loadGroup = mDocument->GetDocumentLoadGroup(); if (!loadGroup) { mXSLTProcessor = nsnull; return NS_ERROR_FAILURE; } - return mXSLTProcessor->LoadStyleSheet(aUrl, loadGroup, - mDocument->NodePrincipal()); + return mXSLTProcessor->LoadStyleSheet(aUrl, loadGroup, mDocument); } nsresult nsXMLContentSink::ProcessStyleLink(nsIContent* aElement, const nsSubstring& aHref, PRBool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, Index: content/xml/document/src/nsXMLDocument.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xml/document/src/nsXMLDocument.cpp,v retrieving revision 1.256 diff -p -U 8 -r1.256 nsXMLDocument.cpp --- content/xml/document/src/nsXMLDocument.cpp 25 Apr 2007 16:35:28 -0000 1.256 +++ content/xml/document/src/nsXMLDocument.cpp 14 May 2007 09:21:52 -0000 @@ -86,16 +86,18 @@ #include "nsIWindowWatcher.h" #include "nsIAuthPrompt.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIJSContextStack.h" #include "nsContentCreatorFunctions.h" #include "nsIDOMUserDataHandler.h" #include "nsEventDispatcher.h" #include "nsNodeUtils.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" // ================================================================== // = // ================================================================== nsresult @@ -194,17 +196,16 @@ nsXMLDocument::~nsXMLDocument() NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLDocument, nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // QueryInterface implementation for nsXMLDocument NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLDocument) - NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) NS_INTERFACE_MAP_ENTRY(nsIDOMXMLDocument) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLDocument) NS_INTERFACE_MAP_END_INHERITING(nsDocument) NS_IMPL_ADDREF_INHERITED(nsXMLDocument, nsDocument) NS_IMPL_RELEASE_INHERITED(nsXMLDocument, nsDocument) @@ -241,38 +242,41 @@ nsXMLDocument::ResetToURI(nsIURI *aURI, } ///////////////////////////////////////////////////// // nsIInterfaceRequestor methods: // NS_IMETHODIMP nsXMLDocument::GetInterface(const nsIID& aIID, void** aSink) { - if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { - NS_ENSURE_ARG_POINTER(aSink); - *aSink = nsnull; + NS_ENSURE_ARG_POINTER(aSink); + *aSink = nsnull; + nsresult rv; - nsresult rv; + if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { nsCOMPtr ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr prompt; rv = ww->GetNewAuthPrompter(nsnull, getter_AddRefs(prompt)); if (NS_FAILED(rv)) return rv; nsIAuthPrompt *p = prompt.get(); NS_ADDREF(p); *aSink = p; return NS_OK; } - - return QueryInterface(aIID, aSink); + rv = nsDocument::GetInterface(aIID, aSink); + if (NS_FAILED(rv)) + return QueryInterface(aIID, aSink); + else + return NS_OK; } // nsIChannelEventSink NS_IMETHODIMP nsXMLDocument::OnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags) { @@ -481,16 +485,25 @@ nsXMLDocument::Load(const nsAString& aUr // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, // which in turn keeps STOP button from becoming active rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, loadGroup, this, nsIRequest::LOAD_BACKGROUND); if (NS_FAILED(rv)) { return rv; } + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + nsCOMPtr cookieContainer; + nsCOMPtr parentCollector = do_QueryReferent(mDocumentContainer); + parentCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + cookieCollector->SetCookieContainer(cookieContainer); + } + // Set a principal for this document // XXXbz StartDocumentLoad should handle that.... And we shouldn't be calling // StartDocumentLoad until we get an OnStartRequest from this channel! nsCOMPtr channelOwner; rv = channel->GetOwner(getter_AddRefs(channelOwner)); // We don't care if GetOwner() succeeded here, if it failed, // channelOwner will be null, which is what we want in that case. Index: content/xml/document/src/nsXMLDocument.h =================================================================== RCS file: /cvsroot/mozilla/content/xml/document/src/nsXMLDocument.h,v retrieving revision 1.86 diff -p -U 8 -r1.86 nsXMLDocument.h --- content/xml/document/src/nsXMLDocument.h 8 Mar 2007 11:17:13 -0000 1.86 +++ content/xml/document/src/nsXMLDocument.h 14 May 2007 09:21:52 -0000 @@ -48,17 +48,16 @@ #include "nsIHTMLCSSStyleSheet.h" class nsIParser; class nsIDOMNode; class nsIURI; class nsIChannel; class nsXMLDocument : public nsDocument, - public nsIInterfaceRequestor, public nsIChannelEventSink { public: nsXMLDocument(const char* aContentType = "application/xml"); virtual ~nsXMLDocument(); NS_DECL_ISUPPORTS_INHERITED Index: content/xslt/public/nsIDocumentTransformer.h =================================================================== RCS file: /cvsroot/mozilla/content/xslt/public/nsIDocumentTransformer.h,v retrieving revision 1.7 diff -p -U 8 -r1.7 nsIDocumentTransformer.h --- content/xslt/public/nsIDocumentTransformer.h 3 Oct 2006 02:22:22 -0000 1.7 +++ content/xslt/public/nsIDocumentTransformer.h 14 May 2007 09:21:53 -0000 @@ -36,16 +36,17 @@ * ***** END LICENSE BLOCK ***** */ #ifndef nsIDocumentTransformer_h__ #define nsIDocumentTransformer_h__ #include "nsISupports.h" class nsIDOMDocument; +class nsIDocument; class nsIDOMNode; class nsILoadGroup; class nsIURI; class nsIPrincipal; class nsString; #define NS_ITRANSFORMOBSERVER_IID \ { 0x04b2d17c, 0xe98d, 0x45f5, \ @@ -73,17 +74,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsITransfo class nsIDocumentTransformer : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENTTRANSFORMER_IID) NS_IMETHOD SetTransformObserver(nsITransformObserver* aObserver) = 0; NS_IMETHOD LoadStyleSheet(nsIURI* aUri, nsILoadGroup* aLoadGroup, - nsIPrincipal* aCallerPrincipal) = 0; + nsIDocument* aCallerDocument) = 0; NS_IMETHOD SetSourceContentModel(nsIDOMNode* aSource) = 0; NS_IMETHOD CancelLoads() = 0; NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix, const nsString& aNamespace) = 0; NS_IMETHOD AddXSLTParam(const nsString& aName, const nsString& aNamespace, const nsString& aValue, Index: content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp,v retrieving revision 1.48 diff -p -U 8 -r1.48 txMozillaStylesheetCompiler.cpp --- content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp 30 Jan 2007 21:21:04 -0000 1.48 +++ content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp 14 May 2007 09:21:56 -0000 @@ -64,16 +64,19 @@ #include "txAtoms.h" #include "txLog.h" #include "txMozillaXSLTProcessor.h" #include "txStylesheetCompiler.h" #include "txXMLUtils.h" #include "nsAttrName.h" #include "nsIScriptError.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); static void getSpec(nsIChannel* aChannel, nsAString& aSpec) { if (!aChannel) { return; } @@ -466,17 +469,17 @@ class txCompileObserver : public txAComp public: txCompileObserver(txMozillaXSLTProcessor* aProcessor, nsILoadGroup* aLoadGroup); virtual ~txCompileObserver(); TX_DECL_ACOMPILEOBSERVER; nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, - nsIURI* aReferrerURI); + nsIURI* aReferrerURI, nsICookieContainer* aCookieContainer); protected: nsAutoRefCnt mRefCnt; private: nsRefPtr mProcessor; nsCOMPtr mLoadGroup; @@ -529,17 +532,17 @@ txCompileObserver::loadURI(const nsAStri nsCOMPtr referrerUri; rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); NS_ENSURE_SUCCESS(rv, rv); // Do security check. rv = CheckLoadURI(uri, referrerUri, nsnull, nsnull); NS_ENSURE_SUCCESS(rv, rv); - return startLoad(uri, aCompiler, referrerUri); + return startLoad(uri, aCompiler, referrerUri, nsnull); } void txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, const PRUnichar *aErrorText, const PRUnichar *aParam) { @@ -548,17 +551,17 @@ txCompileObserver::onDoneCompiling(txSty } else { mProcessor->reportError(aResult, aErrorText, aParam); } } nsresult txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, - nsIURI* aReferrerURI) + nsIURI* aReferrerURI, nsICookieContainer* aCookieContainer) { nsCOMPtr channel; nsresult rv = NS_NewChannel(getter_AddRefs(channel), aUri); NS_ENSURE_SUCCESS(rv, rv); channel->SetLoadGroup(mLoadGroup); channel->SetContentType(NS_LITERAL_CSTRING("text/xml")); @@ -568,16 +571,20 @@ txCompileObserver::startLoad(nsIURI* aUr httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"), PR_FALSE); if (aReferrerURI) { httpChannel->SetReferrer(aReferrerURI); } } + nsCOMPtr cookieCollector = do_QueryInterface(channel); + if (cookieCollector) { + cookieCollector->SetCookieContainer(aCookieContainer); + } nsCOMPtr parser = do_CreateInstance(kCParserCID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr sink = new txStylesheetSink(aCompiler, parser); NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY); channel->SetNotificationCallbacks(sink); @@ -586,40 +593,45 @@ txCompileObserver::startLoad(nsIURI* aUr parser->SetContentSink(sink); parser->Parse(aUri); return channel->AsyncOpen(sink, parser); } nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, - nsILoadGroup* aLoadGroup, nsIPrincipal* aCallerPrincipal) + nsILoadGroup* aLoadGroup, nsIDocument* aCallerDocument) { nsCAutoString spec; aUri->GetSpec(spec); PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("TX_LoadSheet: %s\n", spec.get())); + nsCOMPtr callerPrincipal = aCallerDocument->NodePrincipal(); + nsCOMPtr referrerURI; - aCallerPrincipal->GetURI(getter_AddRefs(referrerURI)); + callerPrincipal->GetURI(getter_AddRefs(referrerURI)); NS_ASSERTION(referrerURI, "Caller principal must have a URI!"); + nsCOMPtr cookieContainer; + cookieContainer = do_GetInterface(callerPrincipal); + // Pass source document as the context - nsresult rv = CheckLoadURI(aUri, referrerURI, aCallerPrincipal, + nsresult rv = CheckLoadURI(aUri, referrerURI, callerPrincipal, aProcessor->GetSourceContentModel()); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr observer = new txCompileObserver(aProcessor, aLoadGroup); NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY); nsRefPtr compiler = new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), observer); NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - return observer->startLoad(aUri, compiler, referrerURI); + return observer->startLoad(aUri, compiler, referrerURI, cookieContainer); } /** * handling DOM->txStylesheet * Observer needs to do synchronous loads. */ static nsresult handleNode(nsIDOMNode* aNode, txStylesheetCompiler* aCompiler) Index: content/xslt/src/xslt/txMozillaXSLTProcessor.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp,v retrieving revision 1.65 diff -p -U 8 -r1.65 txMozillaXSLTProcessor.cpp --- content/xslt/src/xslt/txMozillaXSLTProcessor.cpp 22 Dec 2006 17:22:18 -0000 1.65 +++ content/xslt/src/xslt/txMozillaXSLTProcessor.cpp 14 May 2007 09:21:58 -0000 @@ -1017,19 +1017,19 @@ txMozillaXSLTProcessor::GetFlags(PRUint3 *aFlags = mFlags; return NS_OK; } NS_IMETHODIMP txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri, nsILoadGroup* aLoadGroup, - nsIPrincipal* aCallerPrincipal) + nsIDocument* aCallerDocument) { - nsresult rv = TX_LoadSheet(aUri, this, aLoadGroup, aCallerPrincipal); + nsresult rv = TX_LoadSheet(aUri, this, aLoadGroup, aCallerDocument); if (NS_FAILED(rv) && mObserver) { // This is most likely a network or security error, just // use the uri as context. nsCAutoString spec; aUri->GetSpec(spec); CopyUTF8toUTF16(spec, mSourceText); nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT ? rv : NS_ERROR_XSLT_NETWORK_ERROR; Index: content/xslt/src/xslt/txMozillaXSLTProcessor.h =================================================================== RCS file: /cvsroot/mozilla/content/xslt/src/xslt/txMozillaXSLTProcessor.h,v retrieving revision 1.26 diff -p -U 8 -r1.26 txMozillaXSLTProcessor.h --- content/xslt/src/xslt/txMozillaXSLTProcessor.h 23 Apr 2007 14:21:54 -0000 1.26 +++ content/xslt/src/xslt/txMozillaXSLTProcessor.h 14 May 2007 09:21:58 -0000 @@ -95,17 +95,17 @@ public: NS_DECL_NSIXSLTPROCESSOROBSOLETE // nsIXSLTProcessorPrivate interface NS_DECL_NSIXSLTPROCESSORPRIVATE // nsIDocumentTransformer interface NS_IMETHOD SetTransformObserver(nsITransformObserver* aObserver); NS_IMETHOD LoadStyleSheet(nsIURI* aUri, nsILoadGroup* aLoadGroup, - nsIPrincipal* aCallerPrincipal); + nsIDocument* aCallerDocument); NS_IMETHOD SetSourceContentModel(nsIDOMNode* aSource); NS_IMETHOD CancelLoads() {return NS_OK;} NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix, const nsString& aNamespace); NS_IMETHOD AddXSLTParam(const nsString& aName, const nsString& aNamespace, const nsString& aSelect, const nsString& aValue, @@ -157,15 +157,15 @@ private: txNamespaceMap mParamNamespaceMap; nsRefPtr mRecycler; PRUint32 mFlags; }; extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, nsILoadGroup* aLoadGroup, - nsIPrincipal* aCallerPrincipal); + nsIDocument* aCallerDocument); extern nsresult TX_CompileStylesheet(nsIDOMNode* aNode, txMozillaXSLTProcessor* aProcessor, txStylesheet** aStylesheet); #endif Index: docshell/base/nsDocShell.cpp =================================================================== RCS file: /cvsroot/mozilla/docshell/base/nsDocShell.cpp,v retrieving revision 1.833 diff -p -U 8 -r1.833 nsDocShell.cpp --- docshell/base/nsDocShell.cpp 6 May 2007 21:06:27 -0000 1.833 +++ docshell/base/nsDocShell.cpp 14 May 2007 09:22:19 -0000 @@ -133,16 +133,17 @@ #include "nsIHistoryEntry.h" #include "nsISHistoryListener.h" #include "nsIWindowWatcher.h" #include "nsIPromptFactory.h" #include "nsIObserver.h" #include "nsINestedURI.h" #include "nsITransportSecurityInfo.h" #include "nsINSSErrorsService.h" +#include "nsICookieService.h" // Editor-related #include "nsIEditingSession.h" #include "nsPIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsICachingChannel.h" #include "nsICacheVisitor.h" @@ -295,17 +296,18 @@ nsDocShell::nsDocShell(): mMarginWidth(0), mMarginHeight(0), mItemType(typeContent), mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto), mPreviousTransIndex(-1), mLoadedTransIndex(-1), mEditorData(nsnull), mTreeOwner(nsnull), - mChromeEventHandler(nsnull) + mChromeEventHandler(nsnull), + mCookieContainer(nsnull) { if (gDocShellCount++ == 0) { NS_ASSERTION(sURIFixup == nsnull, "Huh, sURIFixup not null in first nsDocShell ctor!"); CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup); } @@ -361,16 +363,24 @@ nsDocShell::Init() nsCOMPtr proxy = new InterfaceRequestorProxy(NS_STATIC_CAST(nsIInterfaceRequestor*, this)); NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); mLoadGroup->SetNotificationCallbacks(proxy); rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this); NS_ENSURE_SUCCESS(rv, rv); + + + // set to the default cookieContainer + nsCOMPtr csvc = do_GetService(NS_COOKIESERVICE_CONTRACTID); + nsCOMPtr container; + csvc->GetDefaultContainer(getter_AddRefs(container)); + SetCookieContainer(container); + // Add as |this| a progress listener to itself. A little weird, but // simpler than reproducing all the listener-notification logic in // overrides of the various methods via which nsDocLoader can be // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok. return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT | nsIWebProgress::NOTIFY_STATE_NETWORK); @@ -414,16 +424,17 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell) NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer) NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell) NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor) NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsICookieCollector) NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) ///***************************************************************************** // nsDocShell::nsIInterfaceRequestor //***************************************************************************** NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink) { NS_PRECONDITION(aSink, "null out param"); @@ -1744,16 +1755,25 @@ nsDocShell::SetDocLoaderParent(nsDocLoad SetAllowSubframes(value); } if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) { SetAllowImages(value); } } + // if parent is nsICookieCollector, inherit cookie container + nsCOMPtr parentAsCookieCollector(do_QueryInterface(parent)); + if (parentAsCookieCollector) + { + nsCOMPtr container; + if (NS_SUCCEEDED(parentAsCookieCollector->GetCookieContainer(getter_AddRefs(container)))) + SetCookieContainer(container); + } + nsCOMPtr parentURIListener(do_GetInterface(parent)); if (parentURIListener) mContentListener->SetParentContentListener(parentURIListener); return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent) @@ -6896,16 +6916,22 @@ nsDocShell::DoURILoad(nsIURI * aURI, if (httpChannelInternal) { if (aFirstParty) { httpChannelInternal->SetDocumentURI(aURI); } else { httpChannelInternal->SetDocumentURI(aReferrerURI); } } + // set the http request cookie container to that of the parent docshell + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + cookieCollector->SetCookieContainer(mCookieContainer); + } + nsCOMPtr props(do_QueryInterface(channel)); if (props) { // save true referrer for those who need it (e.g. xpinstall whitelisting) // Currently only http and ftp channels support this. props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"), aReferrerURI); } @@ -8906,9 +8932,30 @@ nsDocShell::IsAboutBlank(nsIURI* aURI) if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) { return PR_FALSE; } nsCAutoString str; aURI->GetSpec(str); return str.EqualsLiteral("about:blank"); } - + + +//***************************************************************************** +// nsDocShell::nsICookieCollector +//***************************************************************************** +/* attribute nsICookieContainer cookieContainer; */ +NS_IMETHODIMP +nsDocShell::GetCookieContainer(nsICookieContainer * *aCookieContainer) +{ + NS_ENSURE_ARG_POINTER(aCookieContainer); + *aCookieContainer = mCookieContainer; + NS_IF_ADDREF(*aCookieContainer); + return NS_OK; +} +NS_IMETHODIMP +nsDocShell::SetCookieContainer(nsICookieContainer * aCookieContainer) +{ + mCookieContainer = aCookieContainer; + NS_ENSURE_ARG_POINTER(aCookieContainer); + return NS_OK; +} + Index: docshell/base/nsDocShell.h =================================================================== RCS file: /cvsroot/mozilla/docshell/base/nsDocShell.h,v retrieving revision 1.211 diff -p -U 8 -r1.211 nsDocShell.h --- docshell/base/nsDocShell.h 6 May 2007 21:06:27 -0000 1.211 +++ docshell/base/nsDocShell.h 14 May 2007 09:22:21 -0000 @@ -100,16 +100,18 @@ #include "nsIWebBrowserFind.h" #include "nsIHttpChannel.h" #include "nsDocShellTransferableHooks.h" #include "nsIAuthPromptProvider.h" #include "nsISecureBrowserUI.h" #include "nsIObserver.h" #include "nsDocShellLoadTypes.h" #include "nsPIDOMEventTarget.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" class nsIScrollableView; /* load commands were moved to nsIDocShell.h */ /* load types were moved to nsDocShellLoadTypes.h */ /* internally used ViewMode types */ enum ViewMode { @@ -156,17 +158,18 @@ class nsDocShell : public nsDocLoader, public nsIDocCharset, public nsIContentViewerContainer, public nsIScriptGlobalObjectOwner, public nsIRefreshURI, public nsIWebProgressListener, public nsIEditorDocShell, public nsIWebPageDescriptor, public nsIAuthPromptProvider, - public nsIObserver + public nsIObserver, + public nsICookieCollector { friend class nsDSURIContentListener; public: // Object Management nsDocShell(); virtual nsresult Init(); @@ -185,16 +188,17 @@ public: NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIWEBPROGRESSLISTENER NS_DECL_NSIREFRESHURI NS_DECL_NSICONTENTVIEWERCONTAINER NS_DECL_NSIEDITORDOCSHELL NS_DECL_NSIWEBPAGEDESCRIPTOR NS_DECL_NSIAUTHPROMPTPROVIDER NS_DECL_NSIOBSERVER + NS_DECL_NSICOOKIECOLLECTOR NS_IMETHOD Stop() { // Need this here because otherwise nsIWebNavigation::Stop // overrides the docloader's Stop() return nsDocLoader::Stop(); } // Need to implement (and forward) nsISecurityEventSink, because @@ -594,16 +598,18 @@ protected: nsDocShellEditorData* mEditorData; // editor data, if any // Transferable hooks/callbacks nsCOMPtr mTransferableHookData; // Secure browser UI object nsCOMPtr mSecurityUI; + nsCOMPtr mCookieContainer; + // WEAK REFERENCES BELOW HERE. // Note these are intentionally not addrefd. Doing so will create a cycle. // For that reasons don't use nsCOMPtr. nsIDocShellTreeOwner * mTreeOwner; // Weak Reference nsPIDOMEventTarget * mChromeEventHandler; //Weak Reference static nsIURIFixup *sURIFixup; Index: docshell/base/nsWebShell.cpp =================================================================== RCS file: /cvsroot/mozilla/docshell/base/nsWebShell.cpp,v retrieving revision 1.684 diff -p -U 8 -r1.684 nsWebShell.cpp --- docshell/base/nsWebShell.cpp 18 Feb 2007 04:24:05 -0000 1.684 +++ docshell/base/nsWebShell.cpp 14 May 2007 09:22:24 -0000 @@ -108,16 +108,19 @@ #include "nsILocaleService.h" #include "nsIStringBundle.h" #include "nsICachingChannel.h" #include "nsIDocument.h" #include "nsITextToSubURI.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + #include "nsIExternalProtocolService.h" #include "nsCExternalHandlerService.h" #include "nsIIDNService.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsITimer.h" @@ -439,16 +442,25 @@ SendPing(void *closure, nsIContent *cont if (!httpChan) return; // This is needed in order for 3rd-party cookie blocking to work. nsCOMPtr httpInternal = do_QueryInterface(httpChan); if (httpInternal) httpInternal->SetDocumentURI(doc->GetDocumentURI()); + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(chan)); + if (cookieCollector) { + nsCOMPtr cookieContainer; + nsCOMPtr parentCollector = do_GetInterface(doc); + parentCollector->GetCookieContainer(getter_AddRefs(cookieContainer)); + cookieCollector->SetCookieContainer(cookieContainer); + } + if (info->referrer) httpChan->SetReferrer(info->referrer); httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST")); // Remove extraneous request headers (to reduce request size) httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"), EmptyCString(), PR_FALSE); Index: dom/src/base/nsGlobalWindow.cpp =================================================================== RCS file: /cvsroot/mozilla/dom/src/base/nsGlobalWindow.cpp,v retrieving revision 1.932 diff -p -U 8 -r1.932 nsGlobalWindow.cpp --- dom/src/base/nsGlobalWindow.cpp 11 May 2007 13:34:08 -0000 1.932 +++ dom/src/base/nsGlobalWindow.cpp 14 May 2007 09:22:40 -0000 @@ -151,16 +151,18 @@ #include "nsAutoPtr.h" #include "nsContentUtils.h" #include "nsCSSProps.h" #include "nsIURIFixup.h" #include "nsCDefaultURIFixup.h" #include "nsEventDispatcher.h" #include "nsIObserverService.h" #include "nsNetUtil.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" #include "plbase64.h" #ifdef NS_PRINTING #include "nsIPrintSettings.h" #include "nsIPrintSettingsService.h" #include "nsIWebBrowserPrint.h" #endif Index: embedding/components/windowwatcher/src/nsWindowWatcher.cpp =================================================================== RCS file: /cvsroot/mozilla/embedding/components/windowwatcher/src/nsWindowWatcher.cpp,v retrieving revision 1.129 diff -p -U 8 -r1.129 nsWindowWatcher.cpp --- embedding/components/windowwatcher/src/nsWindowWatcher.cpp 24 Oct 2006 21:42:18 -0000 1.129 +++ embedding/components/windowwatcher/src/nsWindowWatcher.cpp 14 May 2007 09:22:57 -0000 @@ -78,16 +78,18 @@ #include "nsIXPConnect.h" #include "nsPIDOMWindow.h" #include "nsIMarkupDocumentViewer.h" #include "nsIContentViewer.h" #include "nsIDocumentViewer.h" #include "nsIWindowProvider.h" #include "nsIMutableArray.h" #include "nsISupportsArray.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "jsinterp.h" // for js_AllocStack() and js_FreeStack() #ifdef USEWEAKREFS #include "nsIWeakReference.h" @@ -825,16 +827,26 @@ nsWindowWatcher::OpenWindowJSInternal(ns #ifdef DEBUG nsCOMPtr newDebugWindow = do_GetInterface(newDocShell); NS_ASSERTION(newWindow == newDebugWindow, "Different windows??"); #endif if (newWindow) { newWindow->SetOpenerScriptPrincipal(newWindowPrincipal); } } + + // new docShell inherits cookie container from old docShell + if (newDocShell && aParent) { + nsCOMPtr parent = do_QueryInterface(aParent); + nsCOMPtr parentCollector = do_QueryInterface(parent->GetDocShell()); + nsCOMPtr container; + parentCollector->GetCookieContainer(getter_AddRefs(container)); + nsCOMPtr collector = do_QueryInterface(newDocShell); + collector->SetCookieContainer(container); + } if (uriToLoad) { // get the script principal and pass it to docshell JSContextAutoPopper contextGuard; cx = GetJSContextFromCallStack(); // get the security manager if (!cx) Index: layout/style/nsCSSLoader.cpp =================================================================== RCS file: /cvsroot/mozilla/layout/style/nsCSSLoader.cpp,v retrieving revision 3.227 diff -p -U 8 -r3.227 nsCSSLoader.cpp --- layout/style/nsCSSLoader.cpp 3 May 2007 01:15:53 -0000 3.227 +++ layout/style/nsCSSLoader.cpp 14 May 2007 09:23:41 -0000 @@ -74,16 +74,18 @@ #include "nsICSSStyleSheet.h" #include "nsIStyleSheetLinkingElement.h" #include "nsICSSLoaderObserver.h" #include "nsICSSLoader.h" #include "nsICSSParser.h" #include "nsICSSImportRule.h" #include "nsThreadUtils.h" #include "nsGkAtoms.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" #endif #include "nsIMediaList.h" #include "nsIDOMStyleSheet.h" #include "nsIDOMCSSStyleSheet.h" @@ -1424,16 +1426,22 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* // send a minimal Accept header for text/css httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("text/css,*/*;q=0.1"), PR_FALSE); nsCOMPtr referrerURI = aLoadData->GetReferrerURI(); if (referrerURI) httpChannel->SetReferrer(referrerURI); } + // set the cookie container of the channel to that of the doc's + nsCOMPtr cookieCollector(do_QueryInterface(channel)); + if (cookieCollector) { + nsCOMPtr container(do_GetInterface(mDocument)); + cookieCollector->SetCookieContainer(container); + } // Now tell the channel we expect text/css data back.... We do // this before opening it, so it's only treated as a hint. channel->SetContentType(NS_LITERAL_CSTRING("text/css")); if (aLoadData->mLoaderPrincipal) { PRBool inherit; rv = NS_URIChainHasFlags(aLoadData->mURI, Index: modules/libpr0n/src/imgLoader.cpp =================================================================== RCS file: /cvsroot/mozilla/modules/libpr0n/src/imgLoader.cpp,v retrieving revision 1.93 diff -p -U 8 -r1.93 imgLoader.cpp --- modules/libpr0n/src/imgLoader.cpp 1 Jun 2006 18:35:20 -0000 1.93 +++ modules/libpr0n/src/imgLoader.cpp 14 May 2007 09:23:45 -0000 @@ -56,16 +56,20 @@ #include "imgRequest.h" #include "imgRequestProxy.h" #include "ImageErrors.h" #include "ImageLogging.h" #include "nsIComponentRegistrar.h" +// required for cookie container +#include "nsICookieContainer.h" +#include "nsICookieCollector.h" + // we want to explore making the document own the load group // so we can associate the document URI with the load group. // until this point, we have an evil hack: #include "nsIHttpChannelInternal.h" #if defined(DEBUG_pavlov) || defined(DEBUG_timeless) #include "nsISimpleEnumerator.h" #include "nsXPCOM.h" @@ -172,17 +176,18 @@ static PRBool RevalidateEntry(nsICacheEn } static nsresult NewImageChannel(nsIChannel **aResult, nsIURI *aURI, nsIURI *aInitialDocumentURI, nsIURI *aReferringURI, nsILoadGroup *aLoadGroup, - nsLoadFlags aLoadFlags) + nsLoadFlags aLoadFlags, + nsICookieContainer *aCookieContainer) { nsresult rv; nsCOMPtr newChannel; nsCOMPtr newHttpChannel; nsCOMPtr callbacks; if (aLoadGroup) { @@ -218,16 +223,20 @@ static nsresult NewImageChannel(nsIChann NS_LITERAL_CSTRING("image/png,*/*;q=0.5"), PR_FALSE); nsCOMPtr httpChannelInternal = do_QueryInterface(newHttpChannel); NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED); httpChannelInternal->SetDocumentURI(aInitialDocumentURI); newHttpChannel->SetReferrer(aReferringURI); } + nsCOMPtr cookieCollector = do_QueryInterface(*aResult); + if (cookieCollector) { + cookieCollector->SetCookieContainer(aCookieContainer); + } // Image channels are loaded by default with reduced priority. nsCOMPtr p = do_QueryInterface(*aResult); if (p) { PRUint32 priority = nsISupportsPriority::PRIORITY_LOW; if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) ++priority; // further reduce priority for background loads @@ -375,16 +384,23 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIUR "imgRequest [request=%p]\n", this, request)); entry = nsnull; NS_RELEASE(request); bCanCacheRequest = PR_FALSE; } + // find the document's cookie container + nsCOMPtr cookieContainer = nsnull; + if (aCX) { + // aCX is nsIDocument + cookieContainer = do_GetInterface(aCX); + } + // // Time to load the request... There are 3 possible cases: // ======================================================= // 1. There is no cached request (ie. nothing was found in the cache). // // 2. There is a cached request that must be validated. // // 3. There is a valid cached request. @@ -409,17 +425,18 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIUR } else { nsCOMPtr newChannel; rv = NewImageChannel(getter_AddRefs(newChannel), aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, - requestFlags); + requestFlags, + cookieContainer); if (NS_FAILED(rv)) { NS_RELEASE(request); return NS_ERROR_FAILURE; } nsCOMPtr cacheChan(do_QueryInterface(newChannel)); if (cacheChan) { @@ -465,17 +482,18 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIUR LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|"); nsCOMPtr newChannel; rv = NewImageChannel(getter_AddRefs(newChannel), aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, - requestFlags); + requestFlags, + cookieContainer); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; NS_NEWXPCOM(request, imgRequest); if (!request) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(request); Index: modules/plugin/base/src/nsPluginHostImpl.cpp =================================================================== RCS file: /cvsroot/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp,v retrieving revision 1.572 diff -p -U 8 -r1.572 nsPluginHostImpl.cpp --- modules/plugin/base/src/nsPluginHostImpl.cpp 1 May 2007 22:24:24 -0000 1.572 +++ modules/plugin/base/src/nsPluginHostImpl.cpp 14 May 2007 09:24:03 -0000 @@ -168,16 +168,19 @@ #include "nsISupportsArray.h" #include "nsIDocShell.h" #include "nsPluginNativeWindow.h" #include "nsIScriptSecurityManager.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsContentErrors.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" + #ifdef XP_UNIX #if defined (MOZ_WIDGET_GTK2) #include // for GDK_DISPLAY() #elif defined(MOZ_WIDGET_QT) #include // for qt_xdisplay() #endif #endif @@ -6190,17 +6193,21 @@ NS_IMETHODIMP nsPluginHostImpl::GetCooki } // make an nsURI from the argument url rv = ioService->NewURI(nsDependentCString(inCookieURL), nsnull, nsnull, getter_AddRefs(uriIn)); if (NS_FAILED(rv)) { return rv; } - rv = cookieService->GetCookieString(uriIn, nsnull, getter_Copies(cookieString)); + // get the default cookie container + nsCOMPtr container; + cookieService->GetDefaultContainer(getter_AddRefs(container)); + + rv = cookieService->GetCookieString(container, uriIn, nsnull, getter_Copies(cookieString)); if (NS_FAILED(rv) || (!cookieString) || (inOutCookieSize <= (cookieStringLen = PL_strlen(cookieString.get())))) { return NS_ERROR_FAILURE; } PL_strcpy((char *) inOutCookieBuffer, cookieString.get()); inOutCookieSize = cookieStringLen; @@ -6234,24 +6241,28 @@ NS_IMETHODIMP nsPluginHostImpl::SetCooki return NS_ERROR_FAILURE; } // make an nsURI from the argument url rv = ioService->NewURI(nsDependentCString(inCookieURL), nsnull, nsnull, getter_AddRefs(uriIn)); if (NS_FAILED(rv)) { return NS_ERROR_FAILURE; } + + // get the default cookie container + nsCOMPtr container; + cookieService->GetDefaultContainer(getter_AddRefs(container)); nsCOMPtr prompt; GetPrompt(nsnull, getter_AddRefs(prompt)); char * cookie = (char *)inCookieBuffer; char c = cookie[inCookieSize]; cookie[inCookieSize] = '\0'; - rv = cookieService->SetCookieString(uriIn, prompt, cookie, nsnull); + rv = cookieService->SetCookieString(container, uriIn, prompt, cookie, nsnull); cookie[inCookieSize] = c; return rv; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginHostImpl::Observe(nsISupports *aSubject, Index: netwerk/build/nsNetCID.h =================================================================== RCS file: /cvsroot/mozilla/netwerk/build/nsNetCID.h,v retrieving revision 1.65 diff -p -U 8 -r1.65 nsNetCID.h --- netwerk/build/nsNetCID.h 17 Feb 2007 03:47:27 -0000 1.65 +++ netwerk/build/nsNetCID.h 14 May 2007 09:24:07 -0000 @@ -748,16 +748,29 @@ #define NS_COOKIESERVICE_CID \ { /* c375fa80-150f-11d6-a618-0010a401eb10 */ \ 0xc375fa80, \ 0x150f, \ 0x11d6, \ { 0xa6, 0x18, 0x00, 0x10, 0xa4, 0x01, 0xeb, 0x10 } \ } +// the cookie container +#define NS_COOKIECONTAINER_CLASSNAME \ + "CookieContainer" +#define NS_COOKIECONTAINER_CONTRACTID \ + "@mozilla.org/cookie-container;1" +#define NS_COOKIECONTAINER_CID \ +{ /* f3a95abb-65f8-4203-acdb-3f309752433a */ \ + 0xf3a95abb, \ + 0x65f8, \ + 0x4203, \ + { 0xac, 0xdb, 0x3f, 0x30, 0x97, 0x52, 0x43, 0x3a } \ +} + /****************************************************************************** * netwerk/streamconv classes */ // service implementing nsIStreamConverterService #define NS_STREAMCONVERTERSERVICE_CONTRACTID \ "@mozilla.org/streamConverters;1" #define NS_STREAMCONVERTERSERVICE_CID \ Index: netwerk/build/nsNetModule.cpp =================================================================== RCS file: /cvsroot/mozilla/netwerk/build/nsNetModule.cpp,v retrieving revision 1.140 diff -p -U 8 -r1.140 nsNetModule.cpp --- netwerk/build/nsNetModule.cpp 15 Nov 2006 21:38:47 -0000 1.140 +++ netwerk/build/nsNetModule.cpp 14 May 2007 09:24:10 -0000 @@ -157,17 +157,19 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDirInde #include "nsStreamListenerTee.h" NS_GENERIC_FACTORY_CONSTRUCTOR(nsStreamListenerTee) /////////////////////////////////////////////////////////////////////////////// #ifdef NECKO_COOKIES #include "nsCookieService.h" +#include "nsCookieContainer.h" NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsCookieService, nsCookieService::GetSingleton) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookieContainer) #endif /////////////////////////////////////////////////////////////////////////////// // protocols /////////////////////////////////////////////////////////////////////////////// // about:blank is mandatory #include "nsAboutProtocolHandler.h" @@ -1029,24 +1031,30 @@ static const nsModuleComponentInfo gNetM NS_CACHESERVICE_CONTRACTID, nsCacheService::Create }, #ifdef NECKO_COOKIES { NS_COOKIEMANAGER_CLASSNAME, NS_COOKIEMANAGER_CID, NS_COOKIEMANAGER_CONTRACTID, - nsCookieServiceConstructor + nsCookieService::GetDefaultContainer }, { NS_COOKIESERVICE_CLASSNAME, NS_COOKIESERVICE_CID, NS_COOKIESERVICE_CONTRACTID, nsCookieServiceConstructor }, + + { NS_COOKIECONTAINER_CLASSNAME, + NS_COOKIECONTAINER_CID, + NS_COOKIECONTAINER_CONTRACTID, + nsCookieContainerConstructor + }, #endif #ifdef NECKO_PROTOCOL_gopher //gopher: { "The Gopher Protocol Handler", NS_GOPHERHANDLER_CID, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "gopher", nsGopherHandlerConstructor Index: netwerk/cookie/public/Makefile.in =================================================================== RCS file: /cvsroot/mozilla/netwerk/cookie/public/Makefile.in,v retrieving revision 1.10 diff -p -U 8 -r1.10 Makefile.in --- netwerk/cookie/public/Makefile.in 15 Sep 2005 11:03:08 -0000 1.10 +++ netwerk/cookie/public/Makefile.in 14 May 2007 09:24:10 -0000 @@ -48,18 +48,19 @@ GRE_MODULE = 1 SDK_XPIDLSRCS = \ nsICookieManager.idl \ nsICookie.idl \ $(NULL) XPIDLSRCS = \ nsICookieService.idl \ - nsICookieServiceInternal.idl \ nsICookie2.idl \ nsICookieConsent.idl \ nsICookieManager2.idl \ nsICookiePermission.idl \ + nsICookieContainer.idl \ + nsICookieCollector.idl \ $(NULL) include $(topsrcdir)/config/rules.mk DEFINES += -DIMPL_NS_NET Index: netwerk/cookie/public/nsICookieCollector.idl =================================================================== RCS file: netwerk/cookie/public/nsICookieCollector.idl diff -N netwerk/cookie/public/nsICookieCollector.idl --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ netwerk/cookie/public/nsICookieCollector.idl 14 May 2007 09:24:10 -0000 @@ -0,0 +1,50 @@ +/* ***** 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 the Cookie Containers Code. + * + * The Initial Developer of the Original Code is + * Loune Lam . + * + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Loune Lam + * + * 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 ***** */ + +#include "nsISupports.idl" + +interface nsICookieContainer; + +[scriptable, uuid(95D62733-7B16-42db-B795-FE385F44930A)] +interface nsICookieCollector : nsISupports +{ + /** + * The container this cookie collector holds + */ + attribute nsICookieContainer cookieContainer; +}; Index: netwerk/cookie/public/nsICookieContainer.idl =================================================================== RCS file: netwerk/cookie/public/nsICookieContainer.idl diff -N netwerk/cookie/public/nsICookieContainer.idl --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ netwerk/cookie/public/nsICookieContainer.idl 14 May 2007 09:24:11 -0000 @@ -0,0 +1,64 @@ +/* ***** 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 the Cookie Containers Code. + * + * The Initial Developer of the Original Code is + * Loune Lam . + * + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Loune Lam + * + * 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 ***** */ + +#include "nsISupports.idl" + +interface nsIURI; +interface nsICookie; +interface nsIArray; + +[scriptable, uuid(a5556dc0-1ca9-46e2-a30c-303d70976a7e)] +interface nsICookieContainer : nsISupports +{ + + void init(in string aContainerId); + + // + // An unique identifier for the container, mainly used by session restore to save the container + // must be in form of contractID + readonly attribute string containerId; + + // set a single cookie to the container + // will be responsible for notifying observers (ie cookie-changed) + void setCookie(in nsIURI aHostURI, in nsICookie cookie); + + // called by getCookie functions to retrieve the cookies satifsfying the criteria + nsIArray getCookieList(in nsIURI aHostURI, in string aName, in boolean aHttpBound); + + +}; Index: netwerk/cookie/public/nsICookieService.idl =================================================================== RCS file: /cvsroot/mozilla/netwerk/cookie/public/nsICookieService.idl,v retrieving revision 1.9 diff -p -U 8 -r1.9 nsICookieService.idl --- netwerk/cookie/public/nsICookieService.idl 14 Mar 2007 17:22:01 -0000 1.9 +++ netwerk/cookie/public/nsICookieService.idl 14 May 2007 09:24:11 -0000 @@ -15,16 +15,17 @@ * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Loune Lam * * 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 @@ -35,16 +36,17 @@ * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" interface nsIURI; interface nsIPrompt; interface nsIChannel; +interface nsICookieContainer; /** * nsICookieService * * Provides methods for setting and getting cookies in the context of a * page load. See nsICookieManager for methods to manipulate the cookie * database directly. This separation of interface is mainly historical. * @@ -75,72 +77,80 @@ interface nsIChannel; * data : none. */ [scriptable, uuid(011C3190-1434-11d6-A618-0010A401EB10)] interface nsICookieService : nsISupports { /* * Get the complete cookie string associated with the URI. * + * @param aContainer + * the container the cookie should be retrieved from * @param aURI * the URI of the document for which cookies are being queried. * @param aChannel * the channel used to load the document. this parameter may be null, * but it is strongly recommended that a non-null value be provided to * ensure that the cookie privacy preferences are honored. * * @return the resulting cookie string */ - string getCookieString(in nsIURI aURI, in nsIChannel aChannel); + string getCookieString(in nsICookieContainer aContainer, in nsIURI aURI, in nsIChannel aChannel); /* * Get the complete cookie string associated with the URI. * * This function is NOT redundant with getCookieString, as the result * will be different based on httponly (see bug 178993) * + * @param aContainer + * the container the cookie should be retrieved from * @param aURI * the URI of the document for which cookies are being queried. * @param aFirstURI * the URI that the user originally typed in or clicked on to initiate * the load of the document referenced by aURI. * @param aChannel * the channel used to load the document. this parameter may be null, * but it is strongly recommended that a non-null value be provided to * ensure that the cookie privacy preferences are honored. * * @return the resulting cookie string */ - string getCookieStringFromHttp(in nsIURI aURI, in nsIURI aFirstURI, in nsIChannel aChannel); + string getCookieStringFromHttp(in nsICookieContainer aContainer, in nsIURI aURI, in nsIURI aFirstURI, in nsIChannel aChannel); /* * Set the cookie string associated with the URI. * + * @param aContainer + * the container the cookie should be put into * @param aURI * the URI of the document for which cookies are being set. * @param aPrompt * the prompt to use for all user-level cookie notifications. * @param aCookie * the cookie string to set. * @param aChannel * the channel used to load the document. this parameter may be null, * but it is strongly recommended that a non-null value be provided to * ensure that the cookie privacy preferences are honored. * * XXX should be able to allow null aPrompt, since nsIPrompt can be queryied * from aChannel. */ - void setCookieString(in nsIURI aURI, in nsIPrompt aPrompt, in string aCookie, in nsIChannel aChannel); + void setCookieString(in nsICookieContainer aContainer, in nsIURI aURI, in nsIPrompt aPrompt, in string aCookie, in nsIChannel aChannel); /* * Set the cookie string and expires associated with the URI. * * This function is NOT redundant with setCookieString, as the result * will be different based on httponly (see bug 178993) * + * @param aContainer + * the container the cookie should be put into * @param aURI * the URI of the document for which cookies are being set. * @param aFirstURI * the URI that the user originally typed in or clicked on to initiate * the load of the document referenced by aURI. * @param aPrompt * the prompt to use for all user-level cookie notifications. * @param aCookie @@ -148,17 +158,37 @@ interface nsICookieService : nsISupports * @param aServerTime * the expiry information of the cookie (the Date header from the HTTP * response). * @param aChannel * the channel used to load the document. this parameter may be null, * but it is strongly recommended that a non-null value be provided to * ensure that the cookie privacy preferences are honored. */ - void setCookieStringFromHttp(in nsIURI aURI, in nsIURI aFirstURI, in nsIPrompt aPrompt, in string aCookie, in string aServerTime, in nsIChannel aChannel); + void setCookieStringFromHttp(in nsICookieContainer aContainer, in nsIURI aURI, in nsIURI aFirstURI, in nsIPrompt aPrompt, in string aCookie, in string aServerTime, in nsIChannel aChannel); /** * This attribute really doesn't belong on this interface. CVS blame will * tell you why it is here. It remains until we can find a better home for * it. Read the source if you want to know what it does :-( */ readonly attribute boolean cookieIconIsVisible; + + /** + * The default container that newly created channels and docshells use + */ + attribute nsICookieContainer defaultContainer; + + /** + * Get the container from the list of active containers by the aContainerId specified. + * + * If container not found in the list, the container is created from getting the nsICookieContainer contractId from the aContainerId. It'll then be added to the list of active containers + * + * This function is mainly used by session restore to get the correct container when restoring the cookies of the session. + * + * @param aContainerId + * The containerId which is in the form of contractId (of component) + * concatenated with hash and the name of the container instance + * eg. @mozilla.org/cookie-container;1#mycookiejar + */ + nsICookieContainer getContainer(in string aContainerId); + }; Index: netwerk/cookie/src/Makefile.in =================================================================== RCS file: /cvsroot/mozilla/netwerk/cookie/src/Makefile.in,v retrieving revision 1.5 diff -p -U 8 -r1.5 Makefile.in --- netwerk/cookie/src/Makefile.in 5 Apr 2005 17:40:21 -0000 1.5 +++ netwerk/cookie/src/Makefile.in 14 May 2007 09:24:11 -0000 @@ -50,13 +50,14 @@ REQUIRES = \ xpcom \ string \ pref \ $(NULL) CPPSRCS = \ nsCookie.cpp \ nsCookieService.cpp \ + nsCookieContainer.cpp \ $(NULL) include $(topsrcdir)/config/rules.mk DEFINES += -DIMPL_NS_NET Index: netwerk/cookie/src/nsCookieContainer.cpp =================================================================== RCS file: netwerk/cookie/src/nsCookieContainer.cpp diff -N netwerk/cookie/src/nsCookieContainer.cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ netwerk/cookie/src/nsCookieContainer.cpp 14 May 2007 09:24:14 -0000 @@ -0,0 +1,1268 @@ +// vim:ts=2:sw=2:et: +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Witte + * Michiel van Leeuwen + * Loune Lam + * + * 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 ***** */ + +#include "nsCookieContainer.h" +#include "nsIServiceManager.h" + +#include "nsIIOService.h" +#include "nsIPrefBranch.h" +#include "nsIPrefBranch2.h" +#include "nsIPrefService.h" +#include "nsICookieConsent.h" +#include "nsICookiePermission.h" +#include "nsIURI.h" +#include "nsIURL.h" +#include "nsIChannel.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" // evil hack! +#include "nsIPrompt.h" +#include "nsITimer.h" +#include "nsIFile.h" +#include "nsIObserverService.h" +#include "nsILineInputStream.h" + +#include "nsCOMArray.h" +#include "nsArrayEnumerator.h" +#include "nsAutoPtr.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "prtime.h" +#include "prprf.h" +#include "prnetdb.h" +#include "nsNetUtil.h" +#include "nsNetCID.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsIArray.h" +#include "nsIMutableArray.h" +#include "nsArrayUtils.h" + +/****************************************************************************** + * nsCookieService impl: + * useful types & constants + ******************************************************************************/ + +// XXX_hack. See bug 178993. +// This is a hack to hide HttpOnly cookies from older browsers +// +static const char kHttpOnlyPrefix[] = "#HttpOnly_"; + +static const char kCookieFileNamePrefix[] = "cookies"; +static const char kCookieFileNameExt[] = ".txt"; + +static const PRUint32 kLazyWriteTimeout = 5000; //msec + +#undef LIMIT +#define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default)) + +// default limits for the cookie list. these can be tuned by the +// network.cookie.maxNumber and network.cookie.maxPerHost prefs respectively. +static const PRUint32 kMaxNumberOfCookies = 1000; +static const PRUint32 kMaxCookiesPerHost = 50; + +// XXX these casts and constructs are horrible, but our nsInt64/nsTime +// classes are lacking so we need them for now. see bug 198694. +#define USEC_PER_SEC (nsInt64(1000000)) +#define NOW_IN_SECONDS (nsInt64(PR_Now()) / USEC_PER_SEC) + +// pref string constants +static const char kPrefCookiesPermissions[] = "network.cookie.cookieBehavior"; +static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber"; +static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost"; +static const char kInitialCookieContainerId[] = "@mozilla.org/cookie-container;1#MAIN"; + + +// stores linked list iteration state, and provides a rudimentary +// list traversal method +struct nsListIter +{ + nsListIter() {} + + nsListIter(nsCookieEntry *aEntry) + : entry(aEntry) + , prev(nsnull) + , current(aEntry ? aEntry->Head() : nsnull) {} + + nsListIter(nsCookieEntry *aEntry, + nsCookie *aPrev, + nsCookie *aCurrent) + : entry(aEntry) + , prev(aPrev) + , current(aCurrent) {} + + nsListIter& operator++() { prev = current; current = current->Next(); return *this; } + + nsCookieEntry *entry; + nsCookie *prev; + nsCookie *current; +}; + +// stores temporary data for enumerating over the hash entries, +// since enumeration is done using callback functions +struct nsEnumerationData +{ + nsEnumerationData(nsInt64 aCurrentTime, + PRInt64 aOldestTime) + : currentTime(aCurrentTime) + , oldestTime(aOldestTime) + , iter(nsnull, nsnull, nsnull) {} + + // the current time + nsInt64 currentTime; + + // oldest lastAccessed time in the cookie list. use aOldestTime = LL_MAXINT + // to enable this search, LL_MININT to disable it. + nsInt64 oldestTime; + + // an iterator object that points to the desired cookie + nsListIter iter; +}; + +/****************************************************************************** + * Cookie logging handlers + * used for logging in nsCookieService + ******************************************************************************/ + +// logging handlers +#ifdef MOZ_LOGGING +// in order to do logging, the following environment variables need to be set: +// +// set NSPR_LOG_MODULES=cookie:3 -- shows rejected cookies +// set NSPR_LOG_MODULES=cookie:4 -- shows accepted and rejected cookies +// set NSPR_LOG_FILE=cookie.log +// +// this next define has to appear before the include of prlog.h +#define FORCE_PR_LOG // Allow logging in the release build +#include "prlog.h" +#endif + +// define logging macros for convenience +#define SET_COOKIE PR_TRUE +#define GET_COOKIE PR_FALSE + +#ifdef PR_LOGGING +static PRLogModuleInfo *sCookieLog = PR_NewLogModule("cookie"); + +#define COOKIE_LOGFAILURE(a, b, c, d) LogFailure(a, b, c, d) +#define COOKIE_LOGSUCCESS(a, b, c, d) LogSuccess(a, b, c, d) + +static void +LogFailure(PRBool aSetCookie, nsIURI *aHostURI, const char *aCookieString, const char *aReason) +{ + // if logging isn't enabled, return now to save cycles + if (!PR_LOG_TEST(sCookieLog, PR_LOG_WARNING)) { + return; + } + + nsCAutoString spec; + if (aHostURI) + aHostURI->GetAsciiSpec(spec); + + PR_LOG(sCookieLog, PR_LOG_WARNING, + ("%s%s%s\n", "===== ", aSetCookie ? "COOKIE NOT ACCEPTED" : "COOKIE NOT SENT", " =====")); + PR_LOG(sCookieLog, PR_LOG_WARNING,("request URL: %s\n", spec.get())); + if (aSetCookie) { + PR_LOG(sCookieLog, PR_LOG_WARNING,("cookie string: %s\n", aCookieString)); + } + + PRExplodedTime explodedTime; + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime); + char timeString[40]; + PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime); + + PR_LOG(sCookieLog, PR_LOG_WARNING,("current time: %s", timeString)); + PR_LOG(sCookieLog, PR_LOG_WARNING,("rejected because %s\n", aReason)); + PR_LOG(sCookieLog, PR_LOG_WARNING,("\n")); +} + +static void +LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCookie *aCookie) +{ + // if logging isn't enabled, return now to save cycles + if (!PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) { + return; + } + + nsCAutoString spec; + aHostURI->GetAsciiSpec(spec); + + PR_LOG(sCookieLog, PR_LOG_DEBUG, + ("%s%s%s\n", "===== ", aSetCookie ? "COOKIE ACCEPTED" : "COOKIE SENT", " =====")); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("request URL: %s\n", spec.get())); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("cookie string: %s\n", aCookieString)); + + PRExplodedTime explodedTime; + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime); + char timeString[40]; + PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime); + + PR_LOG(sCookieLog, PR_LOG_DEBUG,("current time: %s", timeString)); + + if (aSetCookie) { + PR_LOG(sCookieLog, PR_LOG_DEBUG,("----------------\n")); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("name: %s\n", aCookie->Name().get())); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("value: %s\n", aCookie->Value().get())); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("%s: %s\n", aCookie->IsDomain() ? "domain" : "host", aCookie->Host().get())); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("path: %s\n", aCookie->Path().get())); + + if (!aCookie->IsSession()) { + PR_ExplodeTime(aCookie->Expiry() * USEC_PER_SEC, PR_GMTParameters, &explodedTime); + PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime); + } + + PR_LOG(sCookieLog, PR_LOG_DEBUG, + ("expires: %s", aCookie->IsSession() ? "at end of session" : timeString)); + PR_LOG(sCookieLog, PR_LOG_DEBUG,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false")); + } + PR_LOG(sCookieLog, PR_LOG_DEBUG,("\n")); +} + +// inline wrappers to make passing in nsAFlatCStrings easier +static inline void +LogFailure(PRBool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, const char *aReason) +{ + LogFailure(aSetCookie, aHostURI, aCookieString.get(), aReason); +} + +static inline void +LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, nsCookie *aCookie) +{ + LogSuccess(aSetCookie, aHostURI, aCookieString.get(), aCookie); +} + +#else +#define COOKIE_LOGFAILURE(a, b, c, d) /* nothing */ +#define COOKIE_LOGSUCCESS(a, b, c, d) /* nothing */ +#endif + +/****************************************************************************** + * nsCookieService impl: + * private list sorting callbacks + * + * these functions return: + * < 0 if the first element should come before the second element, + * 0 if the first element may come before or after the second element, + * > 0 if the first element should come after the second element. + ******************************************************************************/ + +// comparison function for sorting cookies before sending to a server. +PR_STATIC_CALLBACK(int) +compareCookiesForSending(const void *aElement1, + const void *aElement2, + void *aData) +{ + const nsCookie *cookie1 = NS_STATIC_CAST(const nsCookie*, aElement1); + const nsCookie *cookie2 = NS_STATIC_CAST(const nsCookie*, aElement2); + + // compare by cookie path length in accordance with RFC2109 + int rv = cookie2->Path().Length() - cookie1->Path().Length(); + if (rv == 0) { + // when path lengths match, older cookies should be listed first. this is + // required for backwards compatibility since some websites erroneously + // depend on receiving cookies in the order in which they were sent to the + // browser! see bug 236772. + rv = cookie1->CreationTime() - cookie2->CreationTime(); + } + return rv; +} + +// comparison function for sorting cookies by lastAccessed time, with most- +// recently-used cookies listed first. +PR_STATIC_CALLBACK(int) +compareCookiesForWriting(const void *aElement1, + const void *aElement2, + void *aData) +{ + const nsCookie *cookie1 = NS_STATIC_CAST(const nsCookie*, aElement1); + const nsCookie *cookie2 = NS_STATIC_CAST(const nsCookie*, aElement2); + + // we may have overflow problems returning the result directly, so we need branches + nsInt64 difference = cookie2->LastAccessed() - cookie1->LastAccessed(); + return (difference > nsInt64(0)) ? 1 : (difference < nsInt64(0)) ? -1 : 0; +} + +/****************************************************************************** + * nsCookieService impl: + * public methods + ******************************************************************************/ + +NS_IMPL_ISUPPORTS5(nsCookieContainer, + nsICookieContainer, + nsICookieManager, + nsICookieManager2, + nsIObserver, + nsISupportsWeakReference) + +nsCookieContainer::nsCookieContainer() + : mCookieCount(0) + , mCookieChanged(PR_FALSE) + , mMaxNumberOfCookies(kMaxNumberOfCookies) + , mMaxCookiesPerHost(kMaxCookiesPerHost) + , mSessionOnly(PR_FALSE) + , mCookieFile(nsnull) +{ +} + +nsCookieContainer::~nsCookieContainer() +{ + if (mWriteTimer) + mWriteTimer->Cancel(); +} + + +NS_IMETHODIMP +nsCookieContainer::Init(const char *aContainerId) +{ + // check containerId + const char* z = aContainerId; + while (*z != 0 && *z != '#') z++; + if (*z == 0) + return NS_ERROR_INVALID_ARG; + + if (*++z == '+') { + // has + prefix, which means session container, ie don't save + mSessionOnly = PR_TRUE; + } + + mContainerId.Assign(aContainerId); + + mObserverService = do_GetService("@mozilla.org/observer-service;1"); + + if (!mHostTable.Init()) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // trigger initialization code in profile-do-change + Observe(nsnull, "profile-do-change", nsnull); + return NS_OK; +} + +NS_IMETHODIMP +nsCookieContainer::Observe(nsISupports *aSubject, + const char *aTopic, + const PRUnichar *aData) +{ + // check the topic + if (!nsCRT::strcmp(aTopic, "profile-before-change")) { + // The profile is about to change, + // or is going away because the application is shutting down. + if (mWriteTimer) { + mWriteTimer->Cancel(); + mWriteTimer = 0; + } + + if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) { + RemoveAllFromMemory(); + // delete the cookie file + if (mCookieFile) { + mCookieFile->Remove(PR_FALSE); + } + } else { + if (!mSessionOnly) + Write(); + RemoveAllFromMemory(); + } + + } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { + // The profile has already changed. + // Now just read them from the new profile location. + // we also need to update the cached cookie file location + if (mSessionOnly) + return NS_OK; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mCookieFile)); + if (NS_SUCCEEDED(rv)) { + nsCAutoString cookieFileName(kCookieFileNamePrefix); + // we have a special name for the initial/main container (ie just cookies.txt) + if (!mContainerId.Equals(kInitialCookieContainerId)) { + cookieFileName.Append("_"); + // get the portion of the container id after the # as part of the filename + char* z = (char *)mContainerId.get(); + while (*z != 0 && *z != '#') z++; + z++; + cookieFileName.Append(z); + } + cookieFileName.Append(kCookieFileNameExt); + mCookieFile->AppendNative(cookieFileName); + } + Read(); + + } else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + nsCOMPtr prefBranch = do_QueryInterface(aSubject); + if (prefBranch) + PrefChanged(prefBranch); + } + + return NS_OK; +} + +// helper function for GetCookieList +static inline PRBool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; } + +NS_IMETHODIMP +nsCookieContainer::GetCookieList(nsIURI *aHostURI, + const char *aName, + PRBool aHttpBound, + nsIArray **aResult + /*nsAutoVoidArray &aResult*/) +{ + if (!aHostURI) { + COOKIE_LOGFAILURE(GET_COOKIE, nsnull, nsnull, "host URI is null"); + return NS_OK; + } + + nsAutoVoidArray result; + + // get host and path from the nsIURI + // note: there was a "check if host has embedded whitespace" here. + // it was removed since this check was added into the nsIURI impl (bug 146094). + nsCAutoString hostFromURI, pathFromURI; + if (NS_FAILED(aHostURI->GetAsciiHost(hostFromURI)) || + NS_FAILED(aHostURI->GetPath(pathFromURI))) { + COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "couldn't get host/path from URI"); + return NS_OK; + } + // trim trailing dots + hostFromURI.Trim("."); + // insert a leading dot, so we begin the hash lookup with the + // equivalent domain cookie host + hostFromURI.Insert(NS_LITERAL_CSTRING("."), 0); + ToLowerCase(hostFromURI); + + // check if aHostURI is using an https secure protocol. + // if it isn't, then we can't send a secure cookie over the connection. + // if SchemeIs fails, assume an insecure connection, to be on the safe side + PRBool isSecure; + if (NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) { + isSecure = PR_FALSE; + } + + nsCookie *cookie; + nsInt64 currentTime = NOW_IN_SECONDS; + const char *currentDot = hostFromURI.get(); + const char *nextDot = currentDot + 1; + + // begin hash lookup, walking up the subdomain levels. + // we use nextDot to force a lookup of the original host (without leading dot). + do { + nsCookieEntry *entry = mHostTable.GetEntry(currentDot); + cookie = entry ? entry->Head() : nsnull; + for (; cookie; cookie = cookie->Next()) { + // check the cookie name, if appropriate + if (aName && *aName != 0 && !cookie->Name().Equals(aName)) { + continue; + } + + // if the cookie is secure and the host scheme isn't, we can't send it + if (cookie->IsSecure() && !isSecure) { + continue; + } + + // if the cookie is httpOnly and it's not going directly to the HTTP + // connection, don't send it + if (cookie->IsHttpOnly() && !aHttpBound) { + continue; + } + + // calculate cookie path length, excluding trailing '/' + PRUint32 cookiePathLen = cookie->Path().Length(); + if (cookiePathLen > 0 && cookie->Path().Last() == '/') { + --cookiePathLen; + } + + // if the nsIURI path is shorter than the cookie path, don't send it back + if (!StringBeginsWith(pathFromURI, Substring(cookie->Path(), 0, cookiePathLen))) { + continue; + } + + if (pathFromURI.Length() > cookiePathLen && + !ispathdelimiter(pathFromURI.CharAt(cookiePathLen))) { + /* + * |ispathdelimiter| tests four cases: '/', '?', '#', and ';'. + * '/' is the "standard" case; the '?' test allows a site at host/abc?def + * to receive a cookie that has a path attribute of abc. this seems + * strange but at least one major site (citibank, bug 156725) depends + * on it. The test for # and ; are put in to proactively avoid problems + * with other sites - these are the only other chars allowed in the path. + */ + continue; + } + + // check if the cookie has expired + if (cookie->Expiry() <= currentTime) { + continue; + } + + // all checks passed - add to list and update lastAccessed stamp of cookie + result.AppendElement(cookie); + cookie->SetLastAccessed(currentTime); + } + + currentDot = nextDot; + if (currentDot) + nextDot = strchr(currentDot + 1, '.'); + + } while (currentDot); + + // return cookies in order of path length; longest to shortest. + // this is required per RFC2109. if cookies match in length, + // then sort by creation time (see bug 236772). + result.Sort(compareCookiesForSending, nsnull); + + // because nsIArray doesn't have sort() we used nsAutoVoidArray above + // now we need to copy it into our nsIArray + nsCOMPtr iarray = do_CreateInstance(NS_ARRAY_CONTRACTID); + for (int i = 0; i < result.Count(); i++) + iarray->AppendElement(NS_STATIC_CAST(nsICookie*, result[i]), PR_FALSE); + + *aResult = iarray; + NS_IF_ADDREF(*aResult); + + return NS_OK; +} + +NS_IMETHODIMP +nsCookieContainer::SetCookie(nsIURI *aHostURI, + nsICookie *cookie) +{ + AddInternal(NS_STATIC_CAST(nsCookie*, cookie), NOW_IN_SECONDS, aHostURI, nsnull); + + // write out the cookie file + if (!mSessionOnly) + LazyWrite(); + + return NS_OK; +} + +/* readonly attribute string containerId; */ +NS_IMETHODIMP +nsCookieContainer::GetContainerId(char * *aContainerId) +{ + *aContainerId = (char *)nsMemory::Alloc((mContainerId.Length()+1) * sizeof(char)); + memcpy(*aContainerId, mContainerId.get(), (mContainerId.Length()+1) * sizeof(char)); + return NS_OK; +} + +void +nsCookieContainer::LazyWrite() +{ + if (mWriteTimer) { + mWriteTimer->SetDelay(kLazyWriteTimeout); + } else { + mWriteTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (mWriteTimer) { + mWriteTimer->InitWithFuncCallback(DoLazyWrite, this, kLazyWriteTimeout, + nsITimer::TYPE_ONE_SHOT); + } + } +} + +void +nsCookieContainer::DoLazyWrite(nsITimer *aTimer, + void *aClosure) +{ + nsCookieContainer *container = NS_REINTERPRET_CAST(nsCookieContainer*, aClosure); + container->Write(); + container->mWriteTimer = 0; +} + +// notify observers that the cookie list changed. there are four possible +// values for aData: +// "deleted" means a cookie was deleted. aCookie is the deleted cookie. +// "added" means a cookie was added. aCookie is the added cookie. +// "changed" means a cookie was altered. aCookie is the new cookie. +// "cleared" means the entire cookie list was cleared. aCookie is null. +void +nsCookieContainer::NotifyChanged(nsICookie2 *aCookie, + const PRUnichar *aData) +{ + mCookieChanged = PR_TRUE; + + if (mObserverService) + mObserverService->NotifyObservers(aCookie, "cookie-changed", aData); + +} + +/****************************************************************************** + * nsCookieService: + * pref observer impl + ******************************************************************************/ + +void +nsCookieContainer::PrefChanged(nsIPrefBranch *aPrefBranch) +{ + PRInt32 val; + + mMaxNumberOfCookies = kMaxNumberOfCookies; + mMaxCookiesPerHost = kMaxCookiesPerHost; + + if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val))) + mMaxNumberOfCookies = LIMIT(val, 0, 0xFFFF, 0xFFFF); + + if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val))) + mMaxCookiesPerHost = LIMIT(val, 0, 0xFFFF, 0xFFFF); +} + +/****************************************************************************** + * nsICookieManager impl: + * nsICookieManager + ******************************************************************************/ + +NS_IMETHODIMP +nsCookieContainer::RemoveAll() +{ + RemoveAllFromMemory(); + NotifyChanged(nsnull, NS_LITERAL_STRING("cleared").get()); + if (!mSessionOnly) + Write(); + return NS_OK; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +COMArrayCallback(nsCookieEntry *aEntry, + void *aArg) +{ + for (nsCookie *cookie = aEntry->Head(); cookie; cookie = cookie->Next()) { + NS_STATIC_CAST(nsCOMArray*, aArg)->AppendObject(cookie); + } + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +nsCookieContainer::GetEnumerator(nsISimpleEnumerator **aEnumerator) +{ + RemoveExpiredCookies(NOW_IN_SECONDS); + + nsCOMArray cookieList(mCookieCount); + mHostTable.EnumerateEntries(COMArrayCallback, &cookieList); + + return NS_NewArrayEnumerator(aEnumerator, cookieList); +} + +NS_IMETHODIMP +nsCookieContainer::Add(const nsACString &aDomain, + const nsACString &aPath, + const nsACString &aName, + const nsACString &aValue, + PRBool aIsSecure, + PRBool aIsSession, + PRInt64 aExpiry) +{ + nsInt64 currentTime = NOW_IN_SECONDS; + + nsRefPtr cookie = + nsCookie::Create(aName, aValue, aDomain, aPath, + nsInt64(aExpiry), + currentTime, + aIsSession, + aIsSecure, + PR_FALSE, + nsICookie::STATUS_UNKNOWN, + nsICookie::POLICY_UNKNOWN); + if (!cookie) { + return NS_ERROR_OUT_OF_MEMORY; + } + + AddInternal(cookie, currentTime, nsnull, nsnull); + return NS_OK; +} + +NS_IMETHODIMP +nsCookieContainer::Remove(const nsACString &aHost, + const nsACString &aName, + const nsACString &aPath, + PRBool aBlocked) +{ + nsListIter matchIter; + if (FindCookie(PromiseFlatCString(aHost), + PromiseFlatCString(aName), + PromiseFlatCString(aPath), + matchIter)) { + nsRefPtr cookie = matchIter.current; + RemoveCookieFromList(matchIter); + NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get()); + + // check if we need to add the host to the permissions blacklist. + if (aBlocked) { + nsCOMPtr permissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID); + if (permissionService) { + nsCAutoString host(NS_LITERAL_CSTRING("http://") + cookie->RawHost()); + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), host); + + if (uri) + permissionService->SetAccess(uri, nsICookiePermission::ACCESS_DENY); + } + } + if (!mSessionOnly) + LazyWrite(); + } + return NS_OK; +} + +/****************************************************************************** + * nsCookieContainer impl: + * private file I/O functions + ******************************************************************************/ + +nsresult +nsCookieContainer::Read() +{ + nsresult rv; + nsCOMPtr fileInputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mCookieFile); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + static const char kTrue[] = "TRUE"; + + nsCAutoString buffer; + PRBool isMore = PR_TRUE; + PRInt32 hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex; + nsASingleFragmentCString::char_iterator iter; + PRInt32 numInts; + PRInt64 expires; + PRBool isDomain, isHttpOnly = PR_FALSE; + nsInt64 currentTime = NOW_IN_SECONDS; + // we use lastAccessedCounter to keep cookies in recently-used order, + // so we start by initializing to currentTime (somewhat arbitrary) + nsInt64 lastAccessedCounter = currentTime; + nsCookie *newCookie; + + /* file format is: + * + * host \t isDomain \t path \t secure \t expires \t name \t cookie + * + * if this format isn't respected we move onto the next line in the file. + * isDomain is "TRUE" or "FALSE" (default to "FALSE") + * isSecure is "TRUE" or "FALSE" (default to "TRUE") + * expires is a PRInt64 integer + * note 1: cookie can contain tabs. + * note 2: cookies are written in order of lastAccessed time: + * most-recently used come first; least-recently-used come last. + */ + + /* + * ...but due to bug 178933, we hide HttpOnly cookies from older code + * in a comment, so they don't expose HttpOnly cookies to JS. + * + * The format for HttpOnly cookies is + * + * #HttpOnly_host \t isDomain \t path \t secure \t expires \t name \t cookie + * + */ + + while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { + if (StringBeginsWith(buffer, NS_LITERAL_CSTRING(kHttpOnlyPrefix))) { + isHttpOnly = PR_TRUE; + hostIndex = sizeof(kHttpOnlyPrefix) - 1; + } else if (buffer.IsEmpty() || buffer.First() == '#') { + continue; + } else { + isHttpOnly = PR_FALSE; + hostIndex = 0; + } + + // this is a cheap, cheesy way of parsing a tab-delimited line into + // string indexes, which can be lopped off into substrings. just for + // purposes of obfuscation, it also checks that each token was found. + // todo: use iterators? + if ((isDomainIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || + (pathIndex = buffer.FindChar('\t', isDomainIndex) + 1) == 0 || + (secureIndex = buffer.FindChar('\t', pathIndex) + 1) == 0 || + (expiresIndex = buffer.FindChar('\t', secureIndex) + 1) == 0 || + (nameIndex = buffer.FindChar('\t', expiresIndex) + 1) == 0 || + (cookieIndex = buffer.FindChar('\t', nameIndex) + 1) == 0) { + continue; + } + + // check the expirytime first - if it's expired, ignore + // nullstomp the trailing tab, to avoid copying the string + buffer.BeginWriting(iter); + *(iter += nameIndex - 1) = char(0); + numInts = PR_sscanf(buffer.get() + expiresIndex, "%lld", &expires); + if (numInts != 1 || nsInt64(expires) < currentTime) { + continue; + } + + isDomain = Substring(buffer, isDomainIndex, pathIndex - isDomainIndex - 1) + .EqualsLiteral(kTrue); + const nsASingleFragmentCString &host = Substring(buffer, hostIndex, isDomainIndex - hostIndex - 1); + // check for bad legacy cookies (domain not starting with a dot, or containing a port), + // and discard + if (isDomain && !host.IsEmpty() && host.First() != '.' || + host.FindChar(':') != kNotFound) { + continue; + } + + // create a new nsCookie and assign the data. + newCookie = + nsCookie::Create(Substring(buffer, nameIndex, cookieIndex - nameIndex - 1), + Substring(buffer, cookieIndex, buffer.Length() - cookieIndex), + host, + Substring(buffer, pathIndex, secureIndex - pathIndex - 1), + nsInt64(expires), + lastAccessedCounter, + PR_FALSE, + Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue), + isHttpOnly, + nsICookie::STATUS_UNKNOWN, + nsICookie::POLICY_UNKNOWN); + if (!newCookie) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // trick: keep the cookies in most-recently-used order, + // by successively decrementing the lastAccessed time + lastAccessedCounter -= nsInt64(1); + + if (!AddCookieToList(newCookie)) { + // It is purpose that created us; purpose that connects us; + // purpose that pulls us; that guides us; that drives us. + // It is purpose that defines us; purpose that binds us. + // When a cookie no longer has purpose, it has a choice: + // it can return to the source to be deleted, or it can go + // into exile, and stay hidden inside the Matrix. + // Let's choose deletion. + delete newCookie; + } + } + + mCookieChanged = PR_FALSE; + return NS_OK; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +cookieListCallback(nsCookieEntry *aEntry, + void *aArg) +{ + for (nsCookie *cookie = aEntry->Head(); cookie; cookie = cookie->Next()) { + NS_STATIC_CAST(nsVoidArray*, aArg)->AppendElement(cookie); + } + return PL_DHASH_NEXT; +} + +nsresult +nsCookieContainer::Write() +{ + if (!mCookieChanged) { + return NS_OK; + } + + if (!mCookieFile) { + return NS_ERROR_NULL_POINTER; + } + + nsresult rv; + nsCOMPtr fileOutputStream; + rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), + mCookieFile, + -1, + 0600); + if (NS_FAILED(rv)) { + NS_ERROR("failed to open cookies.txt for writing"); + return rv; + } + + // get a buffered output stream 4096 bytes big, to optimize writes + nsCOMPtr bufferedOutputStream; + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096); + if (NS_FAILED(rv)) { + return rv; + } + + static const char kHeader[] = + "# HTTP Cookie File\n" + "# http://wp.netscape.com/newsref/std/cookie_spec.html\n" + "# This is a generated file! Do not edit.\n" + "# To delete cookies, use the Cookie Manager.\n\n"; + // note: kTrue and kFalse have leading/trailing tabs already added + static const char kTrue[] = "\tTRUE\t"; + static const char kFalse[] = "\tFALSE\t"; + static const char kTab[] = "\t"; + static const char kNew[] = "\n"; + + // create a new nsVoidArray to hold the cookie list, and sort it + // such that least-recently-used cookies come last + nsVoidArray sortedCookieList(mCookieCount); + mHostTable.EnumerateEntries(cookieListCallback, &sortedCookieList); + sortedCookieList.Sort(compareCookiesForWriting, nsnull); + + bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv); + + /* file format is: + * + * host \t isDomain \t path \t secure \t expires \t name \t cookie + * + * isDomain is "TRUE" or "FALSE" + * isSecure is "TRUE" or "FALSE" + * expires is a PRInt64 integer + * note 1: cookie can contain tabs. + * note 2: cookies are written in order of lastAccessed time: + * most-recently used come first; least-recently-used come last. + */ + + /* + * XXX but see above in ::Read for the HttpOnly hack + */ + + nsCookie *cookie; + nsInt64 currentTime = NOW_IN_SECONDS; + char dateString[22]; + PRUint32 dateLen; + for (PRUint32 i = 0; i < mCookieCount; ++i) { + cookie = NS_STATIC_CAST(nsCookie*, sortedCookieList.ElementAt(i)); + + // don't write entry if cookie has expired, or is a session cookie + if (cookie->IsSession() || cookie->Expiry() <= currentTime) { + continue; + } + + // XXX hack for HttpOnly. see bug 178993. + if (cookie->IsHttpOnly()) { + bufferedOutputStream->Write(kHttpOnlyPrefix, sizeof(kHttpOnlyPrefix) - 1, &rv); + } + bufferedOutputStream->Write(cookie->Host().get(), cookie->Host().Length(), &rv); + if (cookie->IsDomain()) { + bufferedOutputStream->Write(kTrue, sizeof(kTrue) - 1, &rv); + } else { + bufferedOutputStream->Write(kFalse, sizeof(kFalse) - 1, &rv); + } + bufferedOutputStream->Write(cookie->Path().get(), cookie->Path().Length(), &rv); + if (cookie->IsSecure()) { + bufferedOutputStream->Write(kTrue, sizeof(kTrue) - 1, &rv); + } else { + bufferedOutputStream->Write(kFalse, sizeof(kFalse) - 1, &rv); + } + dateLen = PR_snprintf(dateString, sizeof(dateString), "%lld", PRInt64(cookie->Expiry())); + bufferedOutputStream->Write(dateString, dateLen, &rv); + bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &rv); + bufferedOutputStream->Write(cookie->Name().get(), cookie->Name().Length(), &rv); + bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &rv); + bufferedOutputStream->Write(cookie->Value().get(), cookie->Value().Length(), &rv); + bufferedOutputStream->Write(kNew, sizeof(kNew) - 1, &rv); + } + + // All went ok. Maybe except for problems in Write(), but the stream detects + // that for us + nsCOMPtr safeStream = do_QueryInterface(bufferedOutputStream); + NS_ASSERTION(safeStream, "expected a safe output stream!"); + if (safeStream) { + rv = safeStream->Finish(); + if (NS_FAILED(rv)) { + NS_WARNING("failed to save cookie file! possible dataloss"); + return rv; + } + } + + mCookieChanged = PR_FALSE; + return NS_OK; +} + +// this is a backend function for adding a cookie to the list, via SetCookie. +// also used in the cookie manager, for profile migration from IE. +// it either replaces an existing cookie; or adds the cookie to the hashtable, +// and deletes a cookie (if maximum number of cookies has been +// reached). also performs list maintenance by removing expired cookies. +void +nsCookieContainer::AddInternal(nsCookie *aCookie, + nsInt64 aCurrentTime, + nsIURI *aHostURI, + const char *aCookieHeader) +{ + nsListIter matchIter; + const PRBool foundCookie = + FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(), matchIter); + + nsRefPtr oldCookie; + if (foundCookie) { + oldCookie = matchIter.current; + RemoveCookieFromList(matchIter); + + // check if the cookie has expired + if (aCookie->Expiry() <= aCurrentTime) { + COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "previously stored cookie was deleted"); + NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); + return; + } + + // preserve creation time of cookie + if (oldCookie) { + aCookie->SetCreationTime(oldCookie->CreationTime()); + } + + } else { + // check if cookie has already expired + if (aCookie->Expiry() <= aCurrentTime) { + COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie has already expired"); + return; + } + + // check if we have to delete an old cookie. + nsEnumerationData data(aCurrentTime, LL_MAXINT); + if (CountCookiesFromHostInternal(aCookie->RawHost(), data) >= mMaxCookiesPerHost) { + // remove the oldest cookie from host + oldCookie = data.iter.current; + RemoveCookieFromList(data.iter); + + } else if (mCookieCount >= mMaxNumberOfCookies) { + // try to make room, by removing expired cookies + RemoveExpiredCookies(aCurrentTime); + + // check if we still have to get rid of something + if (mCookieCount >= mMaxNumberOfCookies) { + // find the position of the oldest cookie, and remove it + data.oldestTime = LL_MAXINT; + FindOldestCookie(data); + oldCookie = data.iter.current; + RemoveCookieFromList(data.iter); + } + } + + // if we deleted an old cookie, notify consumers + if (oldCookie) + NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); + } + + // add the cookie to head of list + AddCookieToList(aCookie); + NotifyChanged(aCookie, foundCookie ? NS_LITERAL_STRING("changed").get() + : NS_LITERAL_STRING("added").get()); + + COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie); +} + +/****************************************************************************** + * nsCookieService impl: + * private cookielist management functions + ******************************************************************************/ + +void +nsCookieContainer::RemoveAllFromMemory() +{ + // clearing the hashtable will call each nsCookieEntry's dtor, + // which releases all their respective children. + mHostTable.Clear(); + mCookieCount = 0; + mCookieChanged = PR_TRUE; +} + +PLDHashOperator PR_CALLBACK +removeExpiredCallback(nsCookieEntry *aEntry, + void *aArg) +{ + nsCookieContainer* container = NS_REINTERPRET_CAST(nsCookieContainer*, ((void**)aArg)[0]); + const nsInt64 ¤tTime = *NS_STATIC_CAST(nsInt64*, ((void**)aArg)[1]); + for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ) { + if (iter.current->Expiry() <= currentTime) + // remove from list. this takes care of updating the iterator for us + container->RemoveCookieFromList(iter); + else + ++iter; + } + return PL_DHASH_NEXT; +} + +// removes any expired cookies from memory +void +nsCookieContainer::RemoveExpiredCookies(nsInt64 aCurrentTime) +{ + void* args[2]; + args[0] = this; + args[1] = &aCurrentTime; + mHostTable.EnumerateEntries(removeExpiredCallback, &args); +} + +// find whether a given cookie has been previously set. this is provided by the +// nsICookieManager2 interface. +NS_IMETHODIMP +nsCookieContainer::CookieExists(nsICookie2 *aCookie, + PRBool *aFoundCookie) +{ + NS_ENSURE_ARG_POINTER(aCookie); + + // just a placeholder + nsEnumerationData data(NOW_IN_SECONDS, LL_MININT); + nsCookie *cookie = NS_STATIC_CAST(nsCookie*, aCookie); + + *aFoundCookie = FindCookie(cookie->Host(), cookie->Name(), cookie->Path(), data.iter); + return NS_OK; +} + +// count the number of cookies from a given host, and simultaneously find the +// oldest cookie from the host. +PRUint32 +nsCookieContainer::CountCookiesFromHostInternal(const nsACString &aHost, + nsEnumerationData &aData) +{ + PRUint32 countFromHost = 0; + + nsCAutoString hostWithDot(NS_LITERAL_CSTRING(".") + aHost); + + const char *currentDot = hostWithDot.get(); + const char *nextDot = currentDot + 1; + do { + nsCookieEntry *entry = mHostTable.GetEntry(currentDot); + for (nsListIter iter(entry); iter.current; ++iter) { + // only count non-expired cookies + if (iter.current->Expiry() > aData.currentTime) { + ++countFromHost; + + // check if we've found the oldest cookie so far + if (aData.oldestTime > iter.current->LastAccessed()) { + aData.oldestTime = iter.current->LastAccessed(); + aData.iter = iter; + } + } + } + + currentDot = nextDot; + if (currentDot) + nextDot = strchr(currentDot + 1, '.'); + + } while (currentDot); + + return countFromHost; +} + +// count the number of cookies stored by a particular host. this is provided by the +// nsICookieManager2 interface. +NS_IMETHODIMP +nsCookieContainer::CountCookiesFromHost(const nsACString &aHost, + PRUint32 *aCountFromHost) +{ + // we don't care about finding the oldest cookie here, so disable the search + nsEnumerationData data(NOW_IN_SECONDS, LL_MININT); + + *aCountFromHost = CountCookiesFromHostInternal(aHost, data); + return NS_OK; +} + +// find an exact previous match. +PRBool +nsCookieContainer::FindCookie(const nsAFlatCString &aHost, + const nsAFlatCString &aName, + const nsAFlatCString &aPath, + nsListIter &aIter) +{ + nsCookieEntry *entry = mHostTable.GetEntry(aHost.get()); + for (aIter = nsListIter(entry); aIter.current; ++aIter) { + if (aPath.Equals(aIter.current->Path()) && + aName.Equals(aIter.current->Name())) { + return PR_TRUE; + } + } + + return PR_FALSE; +} + +// removes a cookie from the hashtable, and update the iterator state. +void +nsCookieContainer::RemoveCookieFromList(nsListIter &aIter) +{ + if (!aIter.prev && !aIter.current->Next()) { + // we're removing the last element in the list - so just remove the entry + // from the hash. note that the entryclass' dtor will take care of + // releasing this last element for us! + mHostTable.RawRemoveEntry(aIter.entry); + aIter.current = nsnull; + + } else { + // just remove the element from the list, and increment the iterator + nsCookie *next = aIter.current->Next(); + NS_RELEASE(aIter.current); + if (aIter.prev) { + // element to remove is not the head + aIter.current = aIter.prev->Next() = next; + } else { + // element to remove is the head + aIter.current = aIter.entry->Head() = next; + } + } + + --mCookieCount; + mCookieChanged = PR_TRUE; +} + +PRBool +nsCookieContainer::AddCookieToList(nsCookie *aCookie) +{ + nsCookieEntry *entry = mHostTable.PutEntry(aCookie->Host().get()); + + if (!entry) { + NS_ERROR("can't insert element into a null entry!"); + return PR_FALSE; + } + + NS_ADDREF(aCookie); + + aCookie->Next() = entry->Head(); + entry->Head() = aCookie; + ++mCookieCount; + mCookieChanged = PR_TRUE; + + return PR_TRUE; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +findOldestCallback(nsCookieEntry *aEntry, + void *aArg) +{ + nsEnumerationData *data = NS_STATIC_CAST(nsEnumerationData*, aArg); + for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ++iter) { + // check if we've found the oldest cookie so far + if (data->oldestTime > iter.current->LastAccessed()) { + data->oldestTime = iter.current->LastAccessed(); + data->iter = iter; + } + } + return PL_DHASH_NEXT; +} + +void +nsCookieContainer::FindOldestCookie(nsEnumerationData &aData) +{ + mHostTable.EnumerateEntries(findOldestCallback, &aData); +} Index: netwerk/cookie/src/nsCookieContainer.h =================================================================== RCS file: netwerk/cookie/src/nsCookieContainer.h diff -N netwerk/cookie/src/nsCookieContainer.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ netwerk/cookie/src/nsCookieContainer.h 14 May 2007 09:24:17 -0000 @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Witte + * Michiel van Leeuwen + * Loune Lam + * + * 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 ***** */ + +#ifndef nsCookieContainer_h__ +#define nsCookieContainer_h__ + +#include "nsICookieManager.h" +#include "nsICookieManager2.h" +#include "nsICookieContainer.h" +#include "nsICookieService.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +#include "nsCookie.h" +#include "nsString.h" +#include "nsTHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsHashKeys.h" + +#include "nsInt64.h" + +struct nsCookieAttributes; +struct nsListIter; +struct nsEnumerationData; + +class nsAutoVoidArray; + +class nsIPrefBranch; +class nsICookieConsent; +class nsICookiePermission; +class nsIPrefBranch; +class nsIObserverService; +class nsIURI; +class nsIChannel; +class nsITimer; +class nsIFile; + +// hash entry class +class nsCookieEntry : public PLDHashEntryHdr +{ + public: + // Hash methods + typedef const char* KeyType; + typedef const char* KeyTypePointer; + + // do nothing with aHost - we require mHead to be set before we're live! + nsCookieEntry(KeyTypePointer aHost) + : mHead(nsnull) + { + } + + nsCookieEntry(const nsCookieEntry& toCopy) + { + // if we end up here, things will break. nsTHashtable shouldn't + // allow this, since we set ALLOW_MEMMOVE to true. + NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!"); + } + + ~nsCookieEntry() + { + // walk the linked list, and de-link everything by releasing & nulling. + // this allows the parent host entry to be deleted by the hashtable. + // note: we know mHead cannot be null here - we always set mHead to a + // valid nsCookie (if it were null, the hashtable wouldn't be able to find + // this entry, because the key string is provided by mHead). + nsCookie *current = mHead, *next; + do { + next = current->Next(); + NS_RELEASE(current); + } while ((current = next)); + } + + KeyType GetKey() const + { + return HostPtr(); + } + + PRBool KeyEquals(KeyTypePointer aKey) const + { + return !strcmp(HostPtr(), aKey); + } + + static KeyTypePointer KeyToPointer(KeyType aKey) + { + return aKey; + } + + static PLDHashNumber HashKey(KeyTypePointer aKey) + { + // PL_DHashStringKey doesn't use the table parameter, so we can safely + // pass nsnull + return PL_DHashStringKey(nsnull, aKey); + } + + enum { ALLOW_MEMMOVE = PR_TRUE }; + + // get methods + inline const nsDependentCString Host() const { return mHead->Host(); } + + // linked list management helper + inline nsCookie*& Head() { return mHead; } + + inline KeyTypePointer HostPtr() const + { + return mHead->Host().get(); + } + + private: + nsCookie *mHead; +}; + +/****************************************************************************** + * nsCookieContainer: + * class declaration + ******************************************************************************/ + +class nsCookieContainer : public nsICookieContainer + , public nsICookieManager2 + , public nsIObserver + , public nsSupportsWeakReference +{ + public: + // nsISupports + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSICOOKIECONTAINER + NS_DECL_NSICOOKIEMANAGER + NS_DECL_NSICOOKIEMANAGER2 + + nsCookieContainer(); + virtual ~nsCookieContainer(); + + protected: + void PrefChanged(nsIPrefBranch *aPrefBranch); + nsresult Read(); + nsresult Write(); + void AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader); + void RemoveCookieFromList(nsListIter &aIter); + PRBool AddCookieToList(nsCookie *aCookie); + void RemoveAllFromMemory(); + void RemoveExpiredCookies(nsInt64 aCurrentTime); + PRBool FindCookie(const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter); + void FindOldestCookie(nsEnumerationData &aData); + PRUint32 CountCookiesFromHostInternal(const nsACString &aHost, nsEnumerationData &aData); + void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData); + + // Use LazyWrite to save the cookies file on a timer. It will write + // the file only once if repeatedly hammered quickly. + void LazyWrite(); + static void DoLazyWrite(nsITimer *aTimer, void *aClosure); + + protected: + // cached members + nsCOMPtr mCookieFile; + nsCOMPtr mObserverService; + + // impl members + nsCOMPtr mWriteTimer; + nsTHashtable mHostTable; + PRUint32 mCookieCount; + PRPackedBool mCookieChanged; + + nsCString mContainerId; + PRBool mSessionOnly; + + // cached prefs + PRUint8 mCookiesPermissions; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, P3P} + PRUint16 mMaxNumberOfCookies; + PRUint16 mMaxCookiesPerHost; + + // this callback needs access to member functions + friend PLDHashOperator PR_CALLBACK removeExpiredCallback(nsCookieEntry *aEntry, void *aArg); +}; + +#endif // nsCookieContainer_h__ Index: netwerk/cookie/src/nsCookieService.cpp =================================================================== RCS file: /cvsroot/mozilla/netwerk/cookie/src/nsCookieService.cpp,v retrieving revision 1.57 diff -p -U 8 -r1.57 nsCookieService.cpp --- netwerk/cookie/src/nsCookieService.cpp 7 May 2007 11:14:01 -0000 1.57 +++ netwerk/cookie/src/nsCookieService.cpp 14 May 2007 09:24:21 -0000 @@ -18,16 +18,17 @@ * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2003 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Daniel Witte (dwitte@stanford.edu) * Michiel van Leeuwen (mvl@exedo.nl) + * Loune Lam (lpgcritter at nasquan dot com) * * 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 @@ -64,38 +65,30 @@ #include "nsReadableUtils.h" #include "nsCRT.h" #include "prtime.h" #include "prprf.h" #include "prnetdb.h" #include "nsNetUtil.h" #include "nsNetCID.h" #include "nsAppDirectoryServiceDefs.h" +#include "nsIArray.h" +#include "nsIMutableArray.h" +#include "nsArrayUtils.h" /****************************************************************************** * nsCookieService impl: * useful types & constants ******************************************************************************/ -// XXX_hack. See bug 178993. -// This is a hack to hide HttpOnly cookies from older browsers -// -static const char kHttpOnlyPrefix[] = "#HttpOnly_"; - -static const char kCookieFileName[] = "cookies.txt"; - -static const PRUint32 kLazyWriteTimeout = 5000; //msec - #undef LIMIT #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default)) // default limits for the cookie list. these can be tuned by the // network.cookie.maxNumber and network.cookie.maxPerHost prefs respectively. -static const PRUint32 kMaxNumberOfCookies = 1000; -static const PRUint32 kMaxCookiesPerHost = 50; static const PRUint32 kMaxBytesPerCookie = 4096; static const PRUint32 kMaxBytesPerPath = 1024; // this constant augments those defined on nsICookie, and indicates // the cookie should be rejected because of an error (rather than // something the user can control). this is used for notifying about // rejected cookies, since we only want to notify of rejections where // the user can do something about it (e.g. whitelist the site). @@ -111,78 +104,33 @@ static const PRUint32 BEHAVIOR_ACCEPT static const PRUint32 BEHAVIOR_REJECTFOREIGN = 1; static const PRUint32 BEHAVIOR_REJECT = 2; static const PRUint32 BEHAVIOR_P3P = 3; // pref string constants static const char kPrefCookiesPermissions[] = "network.cookie.cookieBehavior"; static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber"; static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost"; +static const char kInitialCookieContainerId[] = "@mozilla.org/cookie-container;1#MAIN"; // struct for temporarily storing cookie attributes during header parsing struct nsCookieAttributes { nsCAutoString name; nsCAutoString value; nsCAutoString host; nsCAutoString path; nsCAutoString expires; nsCAutoString maxage; nsInt64 expiryTime; PRBool isSession; PRBool isSecure; PRBool isHttpOnly; }; -// stores linked list iteration state, and provides a rudimentary -// list traversal method -struct nsListIter -{ - nsListIter() {} - - nsListIter(nsCookieEntry *aEntry) - : entry(aEntry) - , prev(nsnull) - , current(aEntry ? aEntry->Head() : nsnull) {} - - nsListIter(nsCookieEntry *aEntry, - nsCookie *aPrev, - nsCookie *aCurrent) - : entry(aEntry) - , prev(aPrev) - , current(aCurrent) {} - - nsListIter& operator++() { prev = current; current = current->Next(); return *this; } - - nsCookieEntry *entry; - nsCookie *prev; - nsCookie *current; -}; - -// stores temporary data for enumerating over the hash entries, -// since enumeration is done using callback functions -struct nsEnumerationData -{ - nsEnumerationData(nsInt64 aCurrentTime, - PRInt64 aOldestTime) - : currentTime(aCurrentTime) - , oldestTime(aOldestTime) - , iter(nsnull, nsnull, nsnull) {} - - // the current time - nsInt64 currentTime; - - // oldest lastAccessed time in the cookie list. use aOldestTime = LL_MAXINT - // to enable this search, LL_MININT to disable it. - nsInt64 oldestTime; - - // an iterator object that points to the desired cookie - nsListIter iter; -}; - /****************************************************************************** * Cookie logging handlers * used for logging in nsCookieService ******************************************************************************/ // logging handlers #ifdef MOZ_LOGGING // in order to do logging, the following environment variables need to be set: @@ -368,440 +316,364 @@ nsCookieService::GetSingleton() if (NS_FAILED(gCookieService->Init())) { NS_RELEASE(gCookieService); } } return gCookieService; } +NS_METHOD +nsCookieService::GetDefaultContainer(nsISupports* aOuter, const nsIID& aIID, void* *aResult) +{ + nsresult rv; + + if (aOuter != nsnull) + return NS_ERROR_NO_AGGREGATION; + + nsCookieService *svc = GetSingleton(); // ref added in the getter + + nsCOMPtr container; + rv = svc->GetDefaultContainer(getter_AddRefs(container)); + if (NS_FAILED(rv)) return rv;; + NS_RELEASE(svc); + + rv = container->QueryInterface(aIID, aResult); + + return rv; +} + /****************************************************************************** * nsCookieService impl: * public methods ******************************************************************************/ -NS_IMPL_ISUPPORTS6(nsCookieService, +NS_IMPL_ISUPPORTS3(nsCookieService, nsICookieService, - nsICookieServiceInternal, - nsICookieManager, - nsICookieManager2, nsIObserver, nsISupportsWeakReference) nsCookieService::nsCookieService() - : mCookieCount(0) - , mCookieChanged(PR_FALSE) - , mCookieIconVisible(PR_FALSE) + : mCookieIconVisible(PR_FALSE) , mCookiesPermissions(BEHAVIOR_ACCEPT) - , mMaxNumberOfCookies(kMaxNumberOfCookies) - , mMaxCookiesPerHost(kMaxCookiesPerHost) + , mDefaultContainerId(kInitialCookieContainerId) { } nsresult nsCookieService::Init() { - if (!mHostTable.Init()) { + if (!mContainerTable.Init()) { return NS_ERROR_OUT_OF_MEMORY; } // init our pref and observer nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefBranch) { prefBranch->AddObserver(kPrefCookiesPermissions, this, PR_TRUE); prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, PR_TRUE); prefBranch->AddObserver(kPrefMaxCookiesPerHost, this, PR_TRUE); - PrefChanged(prefBranch); - } - - // cache mCookieFile - NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mCookieFile)); - if (mCookieFile) { - mCookieFile->AppendNative(NS_LITERAL_CSTRING(kCookieFileName)); + Observe(prefBranch, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, nsnull); } - Read(); - mObserverService = do_GetService("@mozilla.org/observer-service;1"); if (mObserverService) { mObserverService->AddObserver(this, "profile-before-change", PR_TRUE); mObserverService->AddObserver(this, "profile-do-change", PR_TRUE); mObserverService->AddObserver(this, "cookieIcon", PR_TRUE); + mObserverService->AddObserver(this, "cookie-changed", PR_TRUE); } mPermissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID); + // create initial cookie container + nsCOMPtr container; + GetDefaultContainer(getter_AddRefs(container)); + return NS_OK; } nsCookieService::~nsCookieService() { gCookieService = nsnull; +} - if (mWriteTimer) - mWriteTimer->Cancel(); +PR_STATIC_CALLBACK(PLDHashOperator) +containersNotifyCallback(const char* aKey, nsICookieContainer* aData, void* userArg) +{ + nsCOMPtr observer = do_QueryInterface(aData); + if (observer) { + observer->Observe(NS_REINTERPRET_CAST(nsISupports*, ((void**)userArg)[0]), + NS_REINTERPRET_CAST(const char*, ((void**)userArg)[1]), + NS_REINTERPRET_CAST(const PRUnichar *, ((void**)userArg)[2])); + } + return PL_DHASH_NEXT; } NS_IMETHODIMP nsCookieService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { // check the topic - if (!nsCRT::strcmp(aTopic, "profile-before-change")) { - // The profile is about to change, - // or is going away because the application is shutting down. - if (mWriteTimer) { - mWriteTimer->Cancel(); - mWriteTimer = 0; - } - - if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) { - RemoveAllFromMemory(); - // delete the cookie file - if (mCookieFile) { - mCookieFile->Remove(PR_FALSE); - } - } else { - Write(); - RemoveAllFromMemory(); - } - - } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { - // The profile has already changed. - // Now just read them from the new profile location. - // we also need to update the cached cookie file location - nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mCookieFile)); - if (NS_SUCCEEDED(rv)) { - mCookieFile->AppendNative(NS_LITERAL_CSTRING(kCookieFileName)); - } - Read(); - - } else if (!nsCRT::strcmp(aTopic, "cookieIcon")) { + if (!nsCRT::strcmp(aTopic, "cookieIcon")) { // this is an evil trick to avoid the blatant inefficiency of // (!nsCRT::strcmp(aData, NS_LITERAL_STRING("on").get())) mCookieIconVisible = (aData[0] == 'o' && aData[1] == 'n' && aData[2] == '\0'); - } else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { - nsCOMPtr prefBranch = do_QueryInterface(aSubject); - if (prefBranch) - PrefChanged(prefBranch); + } else if (!nsCRT::strcmp(aTopic, "profile-before-change") || + !nsCRT::strcmp(aTopic, "profile-do-change") || + !nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + + if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + // prefs changed, update default permissions + nsCOMPtr prefBranch = do_QueryInterface(aSubject); + if (prefBranch) { + mCookiesPermissions = BEHAVIOR_ACCEPT; // default + PRInt32 val; + if (NS_SUCCEEDED(prefBranch->GetIntPref(kPrefCookiesPermissions, &val))) + mCookiesPermissions = LIMIT(val, 0, 3, 0); + } + } + // relay notification to containers + void* args[3]; + args[0] = aSubject; + args[1] = (void *)aTopic; + args[2] = (void *)aData; + mContainerTable.EnumerateRead(containersNotifyCallback, &args); + + } else if (!nsCRT::strcmp(aTopic, "cookie-changed")) { + // legacy code originally in NotifyChanged() + + // fire a cookieIcon notification if the cookie was downgraded or flagged + // by p3p. the cookieIcon notification is now deprecated, but we still need + // this until consumers can be fixed. to see if cookies have been + // downgraded or flagged, listen to cookie-changed directly. + nsCOMPtr cookie = do_QueryInterface(aSubject); + if (mCookiesPermissions == BEHAVIOR_P3P && + (!nsCRT::strcmp(aData, NS_LITERAL_STRING("added").get()) || + !nsCRT::strcmp(aData, NS_LITERAL_STRING("changed").get()))) { + nsCookieStatus status; + cookie->GetStatus(&status); + if (status == nsICookie::STATUS_DOWNGRADED || + status == nsICookie::STATUS_FLAGGED) { + mCookieIconVisible = PR_TRUE; + if (mObserverService) + mObserverService->NotifyObservers(nsnull, "cookieIcon", NS_LITERAL_STRING("on").get()); + } + } } return NS_OK; } -// helper function for GetCookieList -static inline PRBool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; } -void -nsCookieService::GetCookieList(nsIURI *aHostURI, - nsIURI *aFirstURI, - nsIChannel *aChannel, - const nsACString *aName, - PRBool aHttpBound, - nsAutoVoidArray &aResult) +/* attribute string containerId; */ +NS_IMETHODIMP +nsCookieService::SetDefaultContainer(nsICookieContainer *aContainer) { - if (!aHostURI) { - COOKIE_LOGFAILURE(GET_COOKIE, nsnull, nsnull, "host URI is null"); - return; - } - - // check default prefs - nsCookiePolicy cookiePolicy; // we don't use this here... just a placeholder - nsCookieStatus cookieStatus = CheckPrefs(aHostURI, aFirstURI, aChannel, nsnull, cookiePolicy); - // for GetCookie(), we don't fire rejection notifications. - switch (cookieStatus) { - case nsICookie::STATUS_REJECTED: - case STATUS_REJECTED_WITH_ERROR: - return; - } - - // get host and path from the nsIURI - // note: there was a "check if host has embedded whitespace" here. - // it was removed since this check was added into the nsIURI impl (bug 146094). - nsCAutoString hostFromURI, pathFromURI; - if (NS_FAILED(aHostURI->GetAsciiHost(hostFromURI)) || - NS_FAILED(aHostURI->GetPath(pathFromURI))) { - COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "couldn't get host/path from URI"); - return; - } - // trim trailing dots - hostFromURI.Trim("."); - // insert a leading dot, so we begin the hash lookup with the - // equivalent domain cookie host - hostFromURI.Insert(NS_LITERAL_CSTRING("."), 0); - ToLowerCase(hostFromURI); - - // check if aHostURI is using an https secure protocol. - // if it isn't, then we can't send a secure cookie over the connection. - // if SchemeIs fails, assume an insecure connection, to be on the safe side - PRBool isSecure; - if (NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) { - isSecure = PR_FALSE; - } - - nsCookie *cookie; - nsInt64 currentTime = NOW_IN_SECONDS; - const char *currentDot = hostFromURI.get(); - const char *nextDot = currentDot + 1; - - // begin hash lookup, walking up the subdomain levels. - // we use nextDot to force a lookup of the original host (without leading dot). - do { - nsCookieEntry *entry = mHostTable.GetEntry(currentDot); - cookie = entry ? entry->Head() : nsnull; - for (; cookie; cookie = cookie->Next()) { - // check the cookie name, if appropriate - if (aName && !aName->Equals(cookie->Name())) { - continue; - } - - // if the cookie is secure and the host scheme isn't, we can't send it - if (cookie->IsSecure() && !isSecure) { - continue; - } - - // if the cookie is httpOnly and it's not going directly to the HTTP - // connection, don't send it - if (cookie->IsHttpOnly() && !aHttpBound) { - continue; - } - - // calculate cookie path length, excluding trailing '/' - PRUint32 cookiePathLen = cookie->Path().Length(); - if (cookiePathLen > 0 && cookie->Path().Last() == '/') { - --cookiePathLen; - } - - // if the nsIURI path is shorter than the cookie path, don't send it back - if (!StringBeginsWith(pathFromURI, Substring(cookie->Path(), 0, cookiePathLen))) { - continue; - } - - if (pathFromURI.Length() > cookiePathLen && - !ispathdelimiter(pathFromURI.CharAt(cookiePathLen))) { - /* - * |ispathdelimiter| tests four cases: '/', '?', '#', and ';'. - * '/' is the "standard" case; the '?' test allows a site at host/abc?def - * to receive a cookie that has a path attribute of abc. this seems - * strange but at least one major site (citibank, bug 156725) depends - * on it. The test for # and ; are put in to proactively avoid problems - * with other sites - these are the only other chars allowed in the path. - */ - continue; - } - - // check if the cookie has expired - if (cookie->Expiry() <= currentTime) { - continue; - } - - // all checks passed - add to list and update lastAccessed stamp of cookie - aResult.AppendElement(cookie); - cookie->SetLastAccessed(currentTime); + if (!aContainer) { + mDefaultContainerId.Assign(""); + } else { + char* id; + aContainer->GetContainerId(&id); + mDefaultContainerId.Assign(id); + nsMemory::Free(id); } - - currentDot = nextDot; - if (currentDot) - nextDot = strchr(currentDot + 1, '.'); - - } while (currentDot); - - // return cookies in order of path length; longest to shortest. - // this is required per RFC2109. if cookies match in length, - // then sort by creation time (see bug 236772). - aResult.Sort(compareCookiesForSending, nsnull); + return NS_OK; } - NS_IMETHODIMP -nsCookieService::GetCookieValue(nsIURI *aHostURI, - nsIChannel *aChannel, - const nsACString& aName, - nsACString& aResult) +nsCookieService::GetDefaultContainer(nsICookieContainer **_retval) { - aResult.Truncate(); - - // try to determine first party URI - nsCOMPtr firstURI; - if (aChannel) { - nsCOMPtr httpInternal = do_QueryInterface(aChannel); - if (httpInternal) - httpInternal->GetDocumentURI(getter_AddRefs(firstURI)); - } + return GetContainer(mDefaultContainerId.get(), _retval); +} - nsAutoVoidArray foundCookieList; - GetCookieList(aHostURI, firstURI, aChannel, &aName, PR_FALSE, - foundCookieList); - if (!foundCookieList.Count()) - return NS_ERROR_NOT_AVAILABLE; +/* nsICookieContainer getContainer (in string aContainerId); */ +NS_IMETHODIMP +nsCookieService::GetContainer(const char *aContainerId, + nsICookieContainer **_retval) +{ + nsresult rv; - nsCookie *cookie = NS_STATIC_CAST(nsCookie*, foundCookieList[0]); + nsCOMPtr container; + // find and get the container. If not found, create as a new container + if (!mContainerTable.Get(aContainerId, getter_AddRefs(container))) { + // extract the contractId from container id (everything up to the #) + char *contractId = (char *)nsMemory::Alloc(strlen(aContainerId) +1 * sizeof(char)); + PRInt32 i; + for (i = 0; aContainerId[i] != 0 && aContainerId[i] != '#'; i++) { + contractId[i] = aContainerId[i]; + } + contractId[i] = 0; + container = do_CreateInstance(contractId); + nsMemory::Free(contractId); + if (!container) + return NS_ERROR_FAILURE; + + rv = container->Init(aContainerId); + NS_ENSURE_SUCCESS(rv, rv); + mContainerTable.Put(aContainerId, container); + } + *_retval = container; + NS_IF_ADDREF(*_retval); - aResult.Assign(cookie->Value()); - return NS_OK; + return NS_OK; } + NS_IMETHODIMP -nsCookieService::GetCookieString(nsIURI *aHostURI, +nsCookieService::GetCookieString(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIChannel *aChannel, char **aCookie) { // try to determine first party URI nsCOMPtr firstURI; if (aChannel) { nsCOMPtr httpInternal = do_QueryInterface(aChannel); if (httpInternal) httpInternal->GetDocumentURI(getter_AddRefs(firstURI)); } - nsAutoVoidArray foundCookieList; - GetCookieList(aHostURI, firstURI, aChannel, nsnull, PR_FALSE, - foundCookieList); + // check default prefs + nsCOMPtr permissionService = do_QueryInterface(aContainer); + if (!permissionService) + permissionService = mPermissionService; + nsCookiePolicy cookiePolicy; // we don't use this here... just a placeholder + nsCookieStatus cookieStatus = CheckPrefs(permissionService, aHostURI, firstURI, aChannel, nsnull, cookiePolicy); + // for GetCookie(), we don't fire rejection notifications. + switch (cookieStatus) { + case nsICookie::STATUS_REJECTED: + case STATUS_REJECTED_WITH_ERROR: + return NS_OK; + } + + nsCOMPtr foundCookieList; + aContainer->GetCookieList(aHostURI, nsnull, PR_FALSE, + getter_AddRefs(foundCookieList)); *aCookie = CookieStringFromArray(foundCookieList, aHostURI); return NS_OK; } NS_IMETHODIMP -nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI, +nsCookieService::GetCookieStringFromHttp(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, char **aCookie) { - nsAutoVoidArray foundCookieList; - GetCookieList(aHostURI, aFirstURI, aChannel, nsnull, PR_TRUE, - foundCookieList); + // check default prefs + nsCOMPtr permissionService = do_QueryInterface(aContainer); + if (!permissionService) + permissionService = mPermissionService; + nsCookiePolicy cookiePolicy; // we don't use this here... just a placeholder + nsCookieStatus cookieStatus = CheckPrefs(permissionService, aHostURI, aFirstURI, aChannel, nsnull, cookiePolicy); + // for GetCookie(), we don't fire rejection notifications. + switch (cookieStatus) { + case nsICookie::STATUS_REJECTED: + case STATUS_REJECTED_WITH_ERROR: + return NS_OK; + } + + nsCOMPtr foundCookieList; + aContainer->GetCookieList(aHostURI, nsnull, PR_TRUE, + getter_AddRefs(foundCookieList)); *aCookie = CookieStringFromArray(foundCookieList, aHostURI); return NS_OK; } char* -nsCookieService::CookieStringFromArray(const nsAutoVoidArray& aCookieList, +nsCookieService::CookieStringFromArray(nsIArray *aCookieList, nsIURI *aHostURI) { - nsCAutoString cookieData; - PRInt32 count = aCookieList.Count(); - for (PRInt32 i = 0; i < count; ++i) { - nsCookie *cookie = NS_STATIC_CAST(nsCookie*, aCookieList.ElementAt(i)); + nsCAutoString cookieData, cookieName, cookieValue; + PRUint32 count = 0; + aCookieList->GetLength(&count); + for (PRUint32 i = 0; i < count; ++i) { + nsCOMPtr cookie = do_QueryElementAt(aCookieList, i); + cookie->GetName(cookieName); + cookie->GetValue(cookieValue); // check if we have anything to write - if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) { + if (!cookieName.IsEmpty() || !cookieValue.IsEmpty()) { // if we've already added a cookie to the return list, append a "; " so // that subsequent cookies are delimited in the final list. if (!cookieData.IsEmpty()) { cookieData.AppendLiteral("; "); } - if (!cookie->Name().IsEmpty()) { + if (!cookieName.IsEmpty()) { // we have a name and value - write both - cookieData += cookie->Name() + NS_LITERAL_CSTRING("=") + cookie->Value(); + cookieData += cookieName + NS_LITERAL_CSTRING("=") + cookieValue; } else { // just write value - cookieData += cookie->Value(); + cookieData += cookieValue; } } } // it's wasteful to alloc a new string; but we have no other choice, until we // fix the callers to use nsACStrings. if (!cookieData.IsEmpty()) { COOKIE_LOGSUCCESS(GET_COOKIE, aHostURI, cookieData, nsnull); return ToNewCString(cookieData); } return nsnull; } NS_IMETHODIMP -nsCookieService::SetCookieValue(nsIURI *aHostURI, - nsIChannel *aChannel, - const nsACString& aDomain, - const nsACString& aPath, - const nsACString& aName, - const nsACString& aValue, - PRBool aIsSession, - PRInt64 aExpiry) -{ - // try to determine first party URI - nsCOMPtr firstURI; - - if (aChannel) { - nsCOMPtr httpInternal = do_QueryInterface(aChannel); - if (httpInternal) - httpInternal->GetDocumentURI(getter_AddRefs(firstURI)); - } - - // check default prefs - nsCookiePolicy cookiePolicy = nsICookie::POLICY_UNKNOWN; - nsCookieStatus cookieStatus = CheckPrefs(aHostURI, firstURI, aChannel, "", cookiePolicy); - // fire a notification if cookie was rejected (but not if there was an error) - switch (cookieStatus) { - case nsICookie::STATUS_REJECTED: - NotifyRejected(aHostURI); - case STATUS_REJECTED_WITH_ERROR: - return NS_OK; - } - - nsCookieAttributes attributes; - attributes.name = aName; - attributes.value = aValue; - attributes.host = aDomain; - attributes.path = aPath; - attributes.expiryTime = aExpiry; - attributes.isSession = aIsSession; - - attributes.isSecure = PR_FALSE; - aHostURI->SchemeIs("https", &attributes.isSecure); - - CheckAndAdd(aHostURI, aChannel, attributes, - cookieStatus, cookiePolicy, EmptyCString()); - return NS_OK; -} - -NS_IMETHODIMP -nsCookieService::SetCookieString(nsIURI *aHostURI, +nsCookieService::SetCookieString(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIPrompt *aPrompt, const char *aCookieHeader, nsIChannel *aChannel) { // try to determine first party URI nsCOMPtr firstURI; if (aChannel) { nsCOMPtr httpInternal = do_QueryInterface(aChannel); if (httpInternal) httpInternal->GetDocumentURI(getter_AddRefs(firstURI)); } - return SetCookieStringFromHttp(aHostURI, firstURI, aPrompt, aCookieHeader, nsnull, aChannel); + return SetCookieStringFromHttp(aContainer, aHostURI, firstURI, aPrompt, aCookieHeader, nsnull, aChannel); } NS_IMETHODIMP -nsCookieService::SetCookieStringFromHttp(nsIURI *aHostURI, +nsCookieService::SetCookieStringFromHttp(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIURI *aFirstURI, nsIPrompt *aPrompt, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel) { if (!aHostURI) { COOKIE_LOGFAILURE(SET_COOKIE, nsnull, aCookieHeader, "host URI is null"); return NS_OK; } // check default prefs + nsCOMPtr permissionService = do_QueryInterface(aContainer); + if (!permissionService) + permissionService = mPermissionService; nsCookiePolicy cookiePolicy = nsICookie::POLICY_UNKNOWN; - nsCookieStatus cookieStatus = CheckPrefs(aHostURI, aFirstURI, aChannel, aCookieHeader, cookiePolicy); + nsCookieStatus cookieStatus = CheckPrefs(permissionService, aHostURI, aFirstURI, aChannel, aCookieHeader, cookiePolicy); // fire a notification if cookie was rejected (but not if there was an error) switch (cookieStatus) { case nsICookie::STATUS_REJECTED: - NotifyRejected(aHostURI); + char *cid; + aContainer->GetContainerId(&cid); + NotifyRejected(aHostURI, NS_ConvertASCIItoUTF16(cid).get()); + nsMemory::Free(cid); case STATUS_REJECTED_WITH_ERROR: return NS_OK; } // parse server local time. this is not just done here for efficiency // reasons - if there's an error parsing it, and we need to default it // to the current time, we must do it here since the current time in // SetCookieInternal() will change for each cookie processed (e.g. if the @@ -811,483 +683,41 @@ nsCookieService::SetCookieStringFromHttp if (aServerTime && PR_ParseTimeString(aServerTime, PR_TRUE, &tempServerTime) == PR_SUCCESS) { serverTime = nsInt64(tempServerTime) / USEC_PER_SEC; } else { serverTime = NOW_IN_SECONDS; } // switch to a nice string type now, and process each cookie in the header nsDependentCString cookieHeader(aCookieHeader); - while (SetCookieInternal(aHostURI, aChannel, + while (SetCookieInternal(aContainer, aHostURI, aChannel, cookieHeader, serverTime, cookieStatus, cookiePolicy)); - // write out the cookie file - LazyWrite(); return NS_OK; } -void -nsCookieService::LazyWrite() -{ - if (mWriteTimer) { - mWriteTimer->SetDelay(kLazyWriteTimeout); - } else { - mWriteTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (mWriteTimer) { - mWriteTimer->InitWithFuncCallback(DoLazyWrite, this, kLazyWriteTimeout, - nsITimer::TYPE_ONE_SHOT); - } - } -} - -void -nsCookieService::DoLazyWrite(nsITimer *aTimer, - void *aClosure) -{ - nsCookieService *service = NS_REINTERPRET_CAST(nsCookieService*, aClosure); - service->Write(); - service->mWriteTimer = 0; -} - -// notify observers that a cookie was rejected due to the users' prefs. -void -nsCookieService::NotifyRejected(nsIURI *aHostURI) -{ - if (mObserverService) - mObserverService->NotifyObservers(aHostURI, "cookie-rejected", nsnull); -} - -// notify observers that the cookie list changed. there are four possible -// values for aData: -// "deleted" means a cookie was deleted. aCookie is the deleted cookie. -// "added" means a cookie was added. aCookie is the added cookie. -// "changed" means a cookie was altered. aCookie is the new cookie. -// "cleared" means the entire cookie list was cleared. aCookie is null. -void -nsCookieService::NotifyChanged(nsICookie2 *aCookie, - const PRUnichar *aData) -{ - mCookieChanged = PR_TRUE; - - if (mObserverService) - mObserverService->NotifyObservers(aCookie, "cookie-changed", aData); - - // fire a cookieIcon notification if the cookie was downgraded or flagged - // by p3p. the cookieIcon notification is now deprecated, but we still need - // this until consumers can be fixed. to see if cookies have been - // downgraded or flagged, listen to cookie-changed directly. - if (mCookiesPermissions == BEHAVIOR_P3P && - (!nsCRT::strcmp(aData, NS_LITERAL_STRING("added").get()) || - !nsCRT::strcmp(aData, NS_LITERAL_STRING("changed").get()))) { - nsCookieStatus status; - aCookie->GetStatus(&status); - if (status == nsICookie::STATUS_DOWNGRADED || - status == nsICookie::STATUS_FLAGGED) { - mCookieIconVisible = PR_TRUE; - if (mObserverService) - mObserverService->NotifyObservers(nsnull, "cookieIcon", NS_LITERAL_STRING("on").get()); - } - } -} - // this method is deprecated. listen to the cookie-changed notification instead. NS_IMETHODIMP nsCookieService::GetCookieIconIsVisible(PRBool *aIsVisible) { *aIsVisible = mCookieIconVisible; return NS_OK; } /****************************************************************************** - * nsCookieService: - * pref observer impl - ******************************************************************************/ - -void -nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch) -{ - PRInt32 val; - if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiesPermissions, &val))) - mCookiesPermissions = LIMIT(val, 0, 3, 0); - - if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val))) - mMaxNumberOfCookies = LIMIT(val, 0, 0xFFFF, 0xFFFF); - - if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val))) - mMaxCookiesPerHost = LIMIT(val, 0, 0xFFFF, 0xFFFF); -} - -/****************************************************************************** - * nsICookieManager impl: - * nsICookieManager - ******************************************************************************/ - -NS_IMETHODIMP -nsCookieService::RemoveAll() -{ - RemoveAllFromMemory(); - NotifyChanged(nsnull, NS_LITERAL_STRING("cleared").get()); - Write(); - return NS_OK; -} - -PR_STATIC_CALLBACK(PLDHashOperator) -COMArrayCallback(nsCookieEntry *aEntry, - void *aArg) -{ - for (nsCookie *cookie = aEntry->Head(); cookie; cookie = cookie->Next()) { - NS_STATIC_CAST(nsCOMArray*, aArg)->AppendObject(cookie); - } - return PL_DHASH_NEXT; -} - -NS_IMETHODIMP -nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator) -{ - RemoveExpiredCookies(NOW_IN_SECONDS); - - nsCOMArray cookieList(mCookieCount); - mHostTable.EnumerateEntries(COMArrayCallback, &cookieList); - - return NS_NewArrayEnumerator(aEnumerator, cookieList); -} - -NS_IMETHODIMP -nsCookieService::Add(const nsACString &aDomain, - const nsACString &aPath, - const nsACString &aName, - const nsACString &aValue, - PRBool aIsSecure, - PRBool aIsSession, - PRInt64 aExpiry) -{ - nsInt64 currentTime = NOW_IN_SECONDS; - - nsRefPtr cookie = - nsCookie::Create(aName, aValue, aDomain, aPath, - nsInt64(aExpiry), - currentTime, - aIsSession, - aIsSecure, - PR_FALSE, - nsICookie::STATUS_UNKNOWN, - nsICookie::POLICY_UNKNOWN); - if (!cookie) { - return NS_ERROR_OUT_OF_MEMORY; - } - - AddInternal(cookie, currentTime, nsnull, nsnull); - return NS_OK; -} - -NS_IMETHODIMP -nsCookieService::Remove(const nsACString &aHost, - const nsACString &aName, - const nsACString &aPath, - PRBool aBlocked) -{ - nsListIter matchIter; - if (FindCookie(PromiseFlatCString(aHost), - PromiseFlatCString(aName), - PromiseFlatCString(aPath), - matchIter)) { - nsRefPtr cookie = matchIter.current; - RemoveCookieFromList(matchIter); - NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get()); - - // check if we need to add the host to the permissions blacklist. - if (aBlocked && mPermissionService) { - nsCAutoString host(NS_LITERAL_CSTRING("http://") + cookie->RawHost()); - nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), host); - - if (uri) - mPermissionService->SetAccess(uri, nsICookiePermission::ACCESS_DENY); - } - - LazyWrite(); - } - return NS_OK; -} - -/****************************************************************************** - * nsCookieService impl: - * private file I/O functions - ******************************************************************************/ - -nsresult -nsCookieService::Read() -{ - nsresult rv; - nsCOMPtr fileInputStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mCookieFile); - if (NS_FAILED(rv)) { - return rv; - } - - nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); - if (NS_FAILED(rv)) { - return rv; - } - - static const char kTrue[] = "TRUE"; - - nsCAutoString buffer; - PRBool isMore = PR_TRUE; - PRInt32 hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex; - nsASingleFragmentCString::char_iterator iter; - PRInt32 numInts; - PRInt64 expires; - PRBool isDomain, isHttpOnly = PR_FALSE; - nsInt64 currentTime = NOW_IN_SECONDS; - // we use lastAccessedCounter to keep cookies in recently-used order, - // so we start by initializing to currentTime (somewhat arbitrary) - nsInt64 lastAccessedCounter = currentTime; - nsCookie *newCookie; - - /* file format is: - * - * host \t isDomain \t path \t secure \t expires \t name \t cookie - * - * if this format isn't respected we move onto the next line in the file. - * isDomain is "TRUE" or "FALSE" (default to "FALSE") - * isSecure is "TRUE" or "FALSE" (default to "TRUE") - * expires is a PRInt64 integer - * note 1: cookie can contain tabs. - * note 2: cookies are written in order of lastAccessed time: - * most-recently used come first; least-recently-used come last. - */ - - /* - * ...but due to bug 178933, we hide HttpOnly cookies from older code - * in a comment, so they don't expose HttpOnly cookies to JS. - * - * The format for HttpOnly cookies is - * - * #HttpOnly_host \t isDomain \t path \t secure \t expires \t name \t cookie - * - */ - - while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { - if (StringBeginsWith(buffer, NS_LITERAL_CSTRING(kHttpOnlyPrefix))) { - isHttpOnly = PR_TRUE; - hostIndex = sizeof(kHttpOnlyPrefix) - 1; - } else if (buffer.IsEmpty() || buffer.First() == '#') { - continue; - } else { - isHttpOnly = PR_FALSE; - hostIndex = 0; - } - - // this is a cheap, cheesy way of parsing a tab-delimited line into - // string indexes, which can be lopped off into substrings. just for - // purposes of obfuscation, it also checks that each token was found. - // todo: use iterators? - if ((isDomainIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || - (pathIndex = buffer.FindChar('\t', isDomainIndex) + 1) == 0 || - (secureIndex = buffer.FindChar('\t', pathIndex) + 1) == 0 || - (expiresIndex = buffer.FindChar('\t', secureIndex) + 1) == 0 || - (nameIndex = buffer.FindChar('\t', expiresIndex) + 1) == 0 || - (cookieIndex = buffer.FindChar('\t', nameIndex) + 1) == 0) { - continue; - } - - // check the expirytime first - if it's expired, ignore - // nullstomp the trailing tab, to avoid copying the string - buffer.BeginWriting(iter); - *(iter += nameIndex - 1) = char(0); - numInts = PR_sscanf(buffer.get() + expiresIndex, "%lld", &expires); - if (numInts != 1 || nsInt64(expires) < currentTime) { - continue; - } - - isDomain = Substring(buffer, isDomainIndex, pathIndex - isDomainIndex - 1) - .EqualsLiteral(kTrue); - const nsASingleFragmentCString &host = Substring(buffer, hostIndex, isDomainIndex - hostIndex - 1); - // check for bad legacy cookies (domain not starting with a dot, or containing a port), - // and discard - if (isDomain && !host.IsEmpty() && host.First() != '.' || - host.FindChar(':') != kNotFound) { - continue; - } - - // create a new nsCookie and assign the data. - newCookie = - nsCookie::Create(Substring(buffer, nameIndex, cookieIndex - nameIndex - 1), - Substring(buffer, cookieIndex, buffer.Length() - cookieIndex), - host, - Substring(buffer, pathIndex, secureIndex - pathIndex - 1), - nsInt64(expires), - lastAccessedCounter, - PR_FALSE, - Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue), - isHttpOnly, - nsICookie::STATUS_UNKNOWN, - nsICookie::POLICY_UNKNOWN); - if (!newCookie) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // trick: keep the cookies in most-recently-used order, - // by successively decrementing the lastAccessed time - lastAccessedCounter -= nsInt64(1); - - if (!AddCookieToList(newCookie)) { - // It is purpose that created us; purpose that connects us; - // purpose that pulls us; that guides us; that drives us. - // It is purpose that defines us; purpose that binds us. - // When a cookie no longer has purpose, it has a choice: - // it can return to the source to be deleted, or it can go - // into exile, and stay hidden inside the Matrix. - // Let's choose deletion. - delete newCookie; - } - } - - mCookieChanged = PR_FALSE; - return NS_OK; -} - -PR_STATIC_CALLBACK(PLDHashOperator) -cookieListCallback(nsCookieEntry *aEntry, - void *aArg) -{ - for (nsCookie *cookie = aEntry->Head(); cookie; cookie = cookie->Next()) { - NS_STATIC_CAST(nsVoidArray*, aArg)->AppendElement(cookie); - } - return PL_DHASH_NEXT; -} - -nsresult -nsCookieService::Write() -{ - if (!mCookieChanged) { - return NS_OK; - } - - if (!mCookieFile) { - return NS_ERROR_NULL_POINTER; - } - - nsresult rv; - nsCOMPtr fileOutputStream; - rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), - mCookieFile, - -1, - 0600); - if (NS_FAILED(rv)) { - NS_ERROR("failed to open cookies.txt for writing"); - return rv; - } - - // get a buffered output stream 4096 bytes big, to optimize writes - nsCOMPtr bufferedOutputStream; - rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096); - if (NS_FAILED(rv)) { - return rv; - } - - static const char kHeader[] = - "# HTTP Cookie File\n" - "# http://wp.netscape.com/newsref/std/cookie_spec.html\n" - "# This is a generated file! Do not edit.\n" - "# To delete cookies, use the Cookie Manager.\n\n"; - // note: kTrue and kFalse have leading/trailing tabs already added - static const char kTrue[] = "\tTRUE\t"; - static const char kFalse[] = "\tFALSE\t"; - static const char kTab[] = "\t"; - static const char kNew[] = "\n"; - - // create a new nsVoidArray to hold the cookie list, and sort it - // such that least-recently-used cookies come last - nsVoidArray sortedCookieList(mCookieCount); - mHostTable.EnumerateEntries(cookieListCallback, &sortedCookieList); - sortedCookieList.Sort(compareCookiesForWriting, nsnull); - - bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv); - - /* file format is: - * - * host \t isDomain \t path \t secure \t expires \t name \t cookie - * - * isDomain is "TRUE" or "FALSE" - * isSecure is "TRUE" or "FALSE" - * expires is a PRInt64 integer - * note 1: cookie can contain tabs. - * note 2: cookies are written in order of lastAccessed time: - * most-recently used come first; least-recently-used come last. - */ - - /* - * XXX but see above in ::Read for the HttpOnly hack - */ - - nsCookie *cookie; - nsInt64 currentTime = NOW_IN_SECONDS; - char dateString[22]; - PRUint32 dateLen; - for (PRUint32 i = 0; i < mCookieCount; ++i) { - cookie = NS_STATIC_CAST(nsCookie*, sortedCookieList.ElementAt(i)); - - // don't write entry if cookie has expired, or is a session cookie - if (cookie->IsSession() || cookie->Expiry() <= currentTime) { - continue; - } - - // XXX hack for HttpOnly. see bug 178993. - if (cookie->IsHttpOnly()) { - bufferedOutputStream->Write(kHttpOnlyPrefix, sizeof(kHttpOnlyPrefix) - 1, &rv); - } - bufferedOutputStream->Write(cookie->Host().get(), cookie->Host().Length(), &rv); - if (cookie->IsDomain()) { - bufferedOutputStream->Write(kTrue, sizeof(kTrue) - 1, &rv); - } else { - bufferedOutputStream->Write(kFalse, sizeof(kFalse) - 1, &rv); - } - bufferedOutputStream->Write(cookie->Path().get(), cookie->Path().Length(), &rv); - if (cookie->IsSecure()) { - bufferedOutputStream->Write(kTrue, sizeof(kTrue) - 1, &rv); - } else { - bufferedOutputStream->Write(kFalse, sizeof(kFalse) - 1, &rv); - } - dateLen = PR_snprintf(dateString, sizeof(dateString), "%lld", PRInt64(cookie->Expiry())); - bufferedOutputStream->Write(dateString, dateLen, &rv); - bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &rv); - bufferedOutputStream->Write(cookie->Name().get(), cookie->Name().Length(), &rv); - bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &rv); - bufferedOutputStream->Write(cookie->Value().get(), cookie->Value().Length(), &rv); - bufferedOutputStream->Write(kNew, sizeof(kNew) - 1, &rv); - } - - // All went ok. Maybe except for problems in Write(), but the stream detects - // that for us - nsCOMPtr safeStream = do_QueryInterface(bufferedOutputStream); - NS_ASSERTION(safeStream, "expected a safe output stream!"); - if (safeStream) { - rv = safeStream->Finish(); - if (NS_FAILED(rv)) { - NS_WARNING("failed to save cookie file! possible dataloss"); - return rv; - } - } - - mCookieChanged = PR_FALSE; - return NS_OK; -} - -/****************************************************************************** * nsCookieService impl: * private GetCookie/SetCookie helpers ******************************************************************************/ // processes a single cookie, and returns PR_TRUE if there are more cookies // to be processed PRBool -nsCookieService::SetCookieInternal(nsIURI *aHostURI, +nsCookieService::SetCookieInternal(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy) { // create a stack-based nsCookieAttributes, to store all the // attributes parsed from the cookie @@ -1305,24 +735,25 @@ nsCookieService::SetCookieInternal(nsIUR const PRBool newCookie = ParseAttributes(aCookieHeader, cookieAttributes); // calculate expiry time of cookie. we need to pass in cookieStatus, since // the cookie may have been downgraded to a session cookie by p3p. const nsInt64 currentTime = NOW_IN_SECONDS; cookieAttributes.isSession = GetExpiry(cookieAttributes, aServerTime, currentTime, aStatus); - CheckAndAdd(aHostURI, aChannel, cookieAttributes, + CheckAndAdd(aContainer, aHostURI, aChannel, cookieAttributes, aStatus, aPolicy, savedCookieHeader); return newCookie; } void -nsCookieService::CheckAndAdd(nsIURI *aHostURI, +nsCookieService::CheckAndAdd(nsICookieContainer *aContainer, + nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, nsCookieStatus aStatus, const nsCookiePolicy aPolicy, const nsAFlatCString &aCookieHeader) { // reject cookie if it's over the size limit, per RFC2109 if ((aAttributes.name.Length() + aAttributes.value.Length()) > kMaxBytesPerCookie) { @@ -1361,112 +792,46 @@ nsCookieService::CheckAndAdd(nsIURI aStatus, aPolicy); if (!cookie) { return; } // check permissions from site permission list, or ask the user, // to determine if we can set the cookie - if (mPermissionService) { + nsCOMPtr permissionService = do_QueryInterface(aContainer); + if (!permissionService) + permissionService = mPermissionService; + + if (permissionService) { PRBool permission; // we need to think about prompters/parent windows here - TestPermission // needs one to prompt, so right now it has to fend for itself to get one - mPermissionService->CanSetCookie(aHostURI, + permissionService->CanSetCookie(aHostURI, aChannel, NS_STATIC_CAST(nsICookie2*, NS_STATIC_CAST(nsCookie*, cookie)), &aAttributes.isSession, &aAttributes.expiryTime.mValue, &permission); if (!permission) { COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie rejected by permission manager"); - NotifyRejected(aHostURI); + char *cid; + aContainer->GetContainerId(&cid); + NotifyRejected(aHostURI, NS_ConvertASCIItoUTF16(cid).get()); + nsMemory::Free(cid); return; } // update isSession and expiry attributes, in case they changed cookie->SetIsSession(aAttributes.isSession); cookie->SetExpiry(aAttributes.expiryTime); } - // add the cookie to the list. AddInternal() takes care of logging. - AddInternal(cookie, NOW_IN_SECONDS, aHostURI, aCookieHeader.get()); -} - -// this is a backend function for adding a cookie to the list, via SetCookie. -// also used in the cookie manager, for profile migration from IE. -// it either replaces an existing cookie; or adds the cookie to the hashtable, -// and deletes a cookie (if maximum number of cookies has been -// reached). also performs list maintenance by removing expired cookies. -void -nsCookieService::AddInternal(nsCookie *aCookie, - nsInt64 aCurrentTime, - nsIURI *aHostURI, - const char *aCookieHeader) -{ - nsListIter matchIter; - const PRBool foundCookie = - FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(), matchIter); - - nsRefPtr oldCookie; - if (foundCookie) { - oldCookie = matchIter.current; - RemoveCookieFromList(matchIter); - - // check if the cookie has expired - if (aCookie->Expiry() <= aCurrentTime) { - COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "previously stored cookie was deleted"); - NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); - return; - } - - // preserve creation time of cookie - if (oldCookie) { - aCookie->SetCreationTime(oldCookie->CreationTime()); - } - - } else { - // check if cookie has already expired - if (aCookie->Expiry() <= aCurrentTime) { - COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie has already expired"); - return; - } - - // check if we have to delete an old cookie. - nsEnumerationData data(aCurrentTime, LL_MAXINT); - if (CountCookiesFromHostInternal(aCookie->RawHost(), data) >= mMaxCookiesPerHost) { - // remove the oldest cookie from host - oldCookie = data.iter.current; - RemoveCookieFromList(data.iter); - - } else if (mCookieCount >= mMaxNumberOfCookies) { - // try to make room, by removing expired cookies - RemoveExpiredCookies(aCurrentTime); - - // check if we still have to get rid of something - if (mCookieCount >= mMaxNumberOfCookies) { - // find the position of the oldest cookie, and remove it - data.oldestTime = LL_MAXINT; - FindOldestCookie(data); - oldCookie = data.iter.current; - RemoveCookieFromList(data.iter); - } - } - - // if we deleted an old cookie, notify consumers - if (oldCookie) - NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get()); - } - - // add the cookie to head of list - AddCookieToList(aCookie); - NotifyChanged(aCookie, foundCookie ? NS_LITERAL_STRING("changed").get() - : NS_LITERAL_STRING("added").get()); - - COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie); + // add the cookie to the container + aContainer->SetCookie(aHostURI, cookie); } /****************************************************************************** * nsCookieService impl: * private cookie header parsing functions ******************************************************************************/ // The following comment block elucidates the function of ParseAttributes. @@ -1708,16 +1073,74 @@ nsCookieService::ParseAttributes(nsDepen aCookieAttributes.isHttpOnly = PR_TRUE; } // rebind aCookieHeader, in case we need to process another cookie aCookieHeader.Rebind(cookieStart, cookieEnd); return newCookie; } +PRBool +nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes, + nsInt64 aServerTime, + nsInt64 aCurrentTime, + nsCookieStatus aStatus) +{ + /* Determine when the cookie should expire. This is done by taking the difference between + * the server time and the time the server wants the cookie to expire, and adding that + * difference to the client time. This localizes the client time regardless of whether or + * not the TZ environment variable was set on the client. + * + * Note: We need to consider accounting for network lag here, per RFC. + */ + nsInt64 delta; + + // check for max-age attribute first; this overrides expires attribute + if (!aCookieAttributes.maxage.IsEmpty()) { + // obtain numeric value of maxageAttribute + PRInt64 maxage; + PRInt32 numInts = PR_sscanf(aCookieAttributes.maxage.get(), "%lld", &maxage); + + // default to session cookie if the conversion failed + if (numInts != 1) { + return PR_TRUE; + } + + delta = nsInt64(maxage); + + // check for expires attribute + } else if (!aCookieAttributes.expires.IsEmpty()) { + nsInt64 expires; + PRTime tempExpires; + + // parse expiry time + if (PR_ParseTimeString(aCookieAttributes.expires.get(), PR_TRUE, &tempExpires) == PR_SUCCESS) { + expires = nsInt64(tempExpires) / USEC_PER_SEC; + } else { + return PR_TRUE; + } + + delta = expires - aServerTime; + + // default to session cookie if no attributes found + } else { + return PR_TRUE; + } + + // if this addition overflows, expiryTime will be less than currentTime + // and the cookie will be expired - that's okay. + aCookieAttributes.expiryTime = aCurrentTime + delta; + + // we need to return whether the cookie is a session cookie or not: + // the cookie may have been previously downgraded by p3p prefs, + // so we take that into account here. only applies to non-expired cookies. + return aStatus == nsICookie::STATUS_DOWNGRADED && + aCookieAttributes.expiryTime > aCurrentTime; +} + /****************************************************************************** * nsCookieService impl: * private domain & permission compliance enforcement functions ******************************************************************************/ // returns PR_TRUE if aHost is an IP address PRBool nsCookieService::IsIPAddress(const nsAFlatCString &aHost) @@ -1828,17 +1251,18 @@ nsCookieService::IsForeign(nsIURI *aHost } // don't have enough dots to chop firstHost, or the subdomain levels differ; // so we just do the plain old check, IsInDomain(firstHost, currentHost). return !IsInDomain(NS_LITERAL_CSTRING(".") + firstHost, currentHost); } nsCookieStatus -nsCookieService::CheckPrefs(nsIURI *aHostURI, +nsCookieService::CheckPrefs(nsICookiePermission *aPermissionService, + nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const char *aCookieHeader, nsCookiePolicy &aPolicy) { // pref tree: // 0) get the scheme strings from the two URI's // 1) disallow ftp @@ -1870,19 +1294,19 @@ nsCookieService::CheckPrefs(nsIURI // don't let ftp sites get/set cookies (could be a security issue) if (currentURIScheme.EqualsLiteral("ftp")) { COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "ftp sites cannot read cookies"); return STATUS_REJECTED_WITH_ERROR; } // check the permission list first; if we find an entry, it overrides // default prefs. see bug 184059. - if (mPermissionService) { + if (aPermissionService) { nsCookieAccess access; - rv = mPermissionService->CanAccess(aHostURI, aFirstURI, aChannel, &access); + rv = aPermissionService->CanAccess(aHostURI, aFirstURI, aChannel, &access); // if we found an entry, use it if (NS_SUCCEEDED(rv)) { switch (access) { case nsICookiePermission::ACCESS_DENY: COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are blocked for this site"); return nsICookie::STATUS_REJECTED; @@ -2047,254 +1471,15 @@ nsCookieService::CheckPath(nsCookieAttri if (aCookieAttributes.path.Length() > kMaxBytesPerPath || aCookieAttributes.path.FindChar('\t') != kNotFound ) return PR_FALSE; return PR_TRUE; } -PRBool -nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes, - nsInt64 aServerTime, - nsInt64 aCurrentTime, - nsCookieStatus aStatus) -{ - /* Determine when the cookie should expire. This is done by taking the difference between - * the server time and the time the server wants the cookie to expire, and adding that - * difference to the client time. This localizes the client time regardless of whether or - * not the TZ environment variable was set on the client. - * - * Note: We need to consider accounting for network lag here, per RFC. - */ - nsInt64 delta; - - // check for max-age attribute first; this overrides expires attribute - if (!aCookieAttributes.maxage.IsEmpty()) { - // obtain numeric value of maxageAttribute - PRInt64 maxage; - PRInt32 numInts = PR_sscanf(aCookieAttributes.maxage.get(), "%lld", &maxage); - - // default to session cookie if the conversion failed - if (numInts != 1) { - return PR_TRUE; - } - - delta = nsInt64(maxage); - - // check for expires attribute - } else if (!aCookieAttributes.expires.IsEmpty()) { - nsInt64 expires; - PRTime tempExpires; - - // parse expiry time - if (PR_ParseTimeString(aCookieAttributes.expires.get(), PR_TRUE, &tempExpires) == PR_SUCCESS) { - expires = nsInt64(tempExpires) / USEC_PER_SEC; - } else { - return PR_TRUE; - } - - delta = expires - aServerTime; - - // default to session cookie if no attributes found - } else { - return PR_TRUE; - } - - // if this addition overflows, expiryTime will be less than currentTime - // and the cookie will be expired - that's okay. - aCookieAttributes.expiryTime = aCurrentTime + delta; - - // we need to return whether the cookie is a session cookie or not: - // the cookie may have been previously downgraded by p3p prefs, - // so we take that into account here. only applies to non-expired cookies. - return aStatus == nsICookie::STATUS_DOWNGRADED && - aCookieAttributes.expiryTime > aCurrentTime; -} - -/****************************************************************************** - * nsCookieService impl: - * private cookielist management functions - ******************************************************************************/ - -void -nsCookieService::RemoveAllFromMemory() -{ - // clearing the hashtable will call each nsCookieEntry's dtor, - // which releases all their respective children. - mHostTable.Clear(); - mCookieCount = 0; - mCookieChanged = PR_TRUE; -} - -PLDHashOperator PR_CALLBACK -removeExpiredCallback(nsCookieEntry *aEntry, - void *aArg) -{ - const nsInt64 ¤tTime = *NS_STATIC_CAST(nsInt64*, aArg); - for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ) { - if (iter.current->Expiry() <= currentTime) - // remove from list. this takes care of updating the iterator for us - nsCookieService::gCookieService->RemoveCookieFromList(iter); - else - ++iter; - } - return PL_DHASH_NEXT; -} - -// removes any expired cookies from memory -void -nsCookieService::RemoveExpiredCookies(nsInt64 aCurrentTime) -{ - mHostTable.EnumerateEntries(removeExpiredCallback, &aCurrentTime); -} - -// find whether a given cookie has been previously set. this is provided by the -// nsICookieManager2 interface. -NS_IMETHODIMP -nsCookieService::CookieExists(nsICookie2 *aCookie, - PRBool *aFoundCookie) -{ - NS_ENSURE_ARG_POINTER(aCookie); - - // just a placeholder - nsEnumerationData data(NOW_IN_SECONDS, LL_MININT); - nsCookie *cookie = NS_STATIC_CAST(nsCookie*, aCookie); - - *aFoundCookie = FindCookie(cookie->Host(), cookie->Name(), cookie->Path(), data.iter); - return NS_OK; -} - -// count the number of cookies from a given host, and simultaneously find the -// oldest cookie from the host. -PRUint32 -nsCookieService::CountCookiesFromHostInternal(const nsACString &aHost, - nsEnumerationData &aData) -{ - PRUint32 countFromHost = 0; - - nsCAutoString hostWithDot(NS_LITERAL_CSTRING(".") + aHost); - - const char *currentDot = hostWithDot.get(); - const char *nextDot = currentDot + 1; - do { - nsCookieEntry *entry = mHostTable.GetEntry(currentDot); - for (nsListIter iter(entry); iter.current; ++iter) { - // only count non-expired cookies - if (iter.current->Expiry() > aData.currentTime) { - ++countFromHost; - - // check if we've found the oldest cookie so far - if (aData.oldestTime > iter.current->LastAccessed()) { - aData.oldestTime = iter.current->LastAccessed(); - aData.iter = iter; - } - } - } - - currentDot = nextDot; - if (currentDot) - nextDot = strchr(currentDot + 1, '.'); - - } while (currentDot); - - return countFromHost; -} - -// count the number of cookies stored by a particular host. this is provided by the -// nsICookieManager2 interface. -NS_IMETHODIMP -nsCookieService::CountCookiesFromHost(const nsACString &aHost, - PRUint32 *aCountFromHost) -{ - // we don't care about finding the oldest cookie here, so disable the search - nsEnumerationData data(NOW_IN_SECONDS, LL_MININT); - - *aCountFromHost = CountCookiesFromHostInternal(aHost, data); - return NS_OK; -} - -// find an exact previous match. -PRBool -nsCookieService::FindCookie(const nsAFlatCString &aHost, - const nsAFlatCString &aName, - const nsAFlatCString &aPath, - nsListIter &aIter) -{ - nsCookieEntry *entry = mHostTable.GetEntry(aHost.get()); - for (aIter = nsListIter(entry); aIter.current; ++aIter) { - if (aPath.Equals(aIter.current->Path()) && - aName.Equals(aIter.current->Name())) { - return PR_TRUE; - } - } - - return PR_FALSE; -} - -// removes a cookie from the hashtable, and update the iterator state. -void -nsCookieService::RemoveCookieFromList(nsListIter &aIter) -{ - if (!aIter.prev && !aIter.current->Next()) { - // we're removing the last element in the list - so just remove the entry - // from the hash. note that the entryclass' dtor will take care of - // releasing this last element for us! - mHostTable.RawRemoveEntry(aIter.entry); - aIter.current = nsnull; - - } else { - // just remove the element from the list, and increment the iterator - nsCookie *next = aIter.current->Next(); - NS_RELEASE(aIter.current); - if (aIter.prev) { - // element to remove is not the head - aIter.current = aIter.prev->Next() = next; - } else { - // element to remove is the head - aIter.current = aIter.entry->Head() = next; - } - } - - --mCookieCount; - mCookieChanged = PR_TRUE; -} - -PRBool -nsCookieService::AddCookieToList(nsCookie *aCookie) -{ - nsCookieEntry *entry = mHostTable.PutEntry(aCookie->Host().get()); - - if (!entry) { - NS_ERROR("can't insert element into a null entry!"); - return PR_FALSE; - } - - NS_ADDREF(aCookie); - - aCookie->Next() = entry->Head(); - entry->Head() = aCookie; - ++mCookieCount; - mCookieChanged = PR_TRUE; - - return PR_TRUE; -} - -PR_STATIC_CALLBACK(PLDHashOperator) -findOldestCallback(nsCookieEntry *aEntry, - void *aArg) -{ - nsEnumerationData *data = NS_STATIC_CAST(nsEnumerationData*, aArg); - for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ++iter) { - // check if we've found the oldest cookie so far - if (data->oldestTime > iter.current->LastAccessed()) { - data->oldestTime = iter.current->LastAccessed(); - data->iter = iter; - } - } - return PL_DHASH_NEXT; -} - +// notify observers that a cookie was rejected due to the users' prefs. void -nsCookieService::FindOldestCookie(nsEnumerationData &aData) +nsCookieService::NotifyRejected(nsIURI *aHostURI, const PRUnichar *aData) { - mHostTable.EnumerateEntries(findOldestCallback, &aData); + if (mObserverService) + mObserverService->NotifyObservers(aHostURI, "cookie-rejected", aData); } Index: netwerk/cookie/src/nsCookieService.h =================================================================== RCS file: /cvsroot/mozilla/netwerk/cookie/src/nsCookieService.h,v retrieving revision 1.25 diff -p -U 8 -r1.25 nsCookieService.h --- netwerk/cookie/src/nsCookieService.h 5 May 2007 21:09:54 -0000 1.25 +++ netwerk/cookie/src/nsCookieService.h 14 May 2007 09:24:22 -0000 @@ -17,16 +17,17 @@ * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2003 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Daniel Witte (dwitte@stanford.edu) * Michiel van Leeuwen (mvl@exedo.nl) + * Loune Lam (lpgcritter at nasquan dot com) * * 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 @@ -35,195 +36,93 @@ * 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 ***** */ #ifndef nsCookieService_h__ #define nsCookieService_h__ -#include "nsICookieServiceInternal.h" #include "nsICookieManager.h" #include "nsICookieManager2.h" +#include "nsICookieContainer.h" +#include "nsICookieService.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include "nsCookie.h" #include "nsString.h" #include "nsTHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsHashKeys.h" #include "nsInt64.h" struct nsCookieAttributes; struct nsListIter; struct nsEnumerationData; -class nsAutoVoidArray; - class nsIPrefBranch; class nsICookieConsent; class nsICookiePermission; class nsIPrefBranch; class nsIObserverService; class nsIURI; class nsIChannel; -class nsITimer; -class nsIFile; - -// hash entry class -class nsCookieEntry : public PLDHashEntryHdr -{ - public: - // Hash methods - typedef const char* KeyType; - typedef const char* KeyTypePointer; - - // do nothing with aHost - we require mHead to be set before we're live! - nsCookieEntry(KeyTypePointer aHost) - : mHead(nsnull) - { - } - - nsCookieEntry(const nsCookieEntry& toCopy) - { - // if we end up here, things will break. nsTHashtable shouldn't - // allow this, since we set ALLOW_MEMMOVE to true. - NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!"); - } - - ~nsCookieEntry() - { - // walk the linked list, and de-link everything by releasing & nulling. - // this allows the parent host entry to be deleted by the hashtable. - // note: we know mHead cannot be null here - we always set mHead to a - // valid nsCookie (if it were null, the hashtable wouldn't be able to find - // this entry, because the key string is provided by mHead). - nsCookie *current = mHead, *next; - do { - next = current->Next(); - NS_RELEASE(current); - } while ((current = next)); - } - - KeyType GetKey() const - { - return HostPtr(); - } - - PRBool KeyEquals(KeyTypePointer aKey) const - { - return !strcmp(HostPtr(), aKey); - } - - static KeyTypePointer KeyToPointer(KeyType aKey) - { - return aKey; - } - - static PLDHashNumber HashKey(KeyTypePointer aKey) - { - // PL_DHashStringKey doesn't use the table parameter, so we can safely - // pass nsnull - return PL_DHashStringKey(nsnull, aKey); - } - - enum { ALLOW_MEMMOVE = PR_TRUE }; - - // get methods - inline const nsDependentCString Host() const { return mHead->Host(); } - - // linked list management helper - inline nsCookie*& Head() { return mHead; } - - inline KeyTypePointer HostPtr() const - { - return mHead->Host().get(); - } - - private: - nsCookie *mHead; -}; /****************************************************************************** * nsCookieService: * class declaration ******************************************************************************/ -class nsCookieService : public nsICookieServiceInternal - , public nsICookieManager2 +class nsCookieService : public nsICookieService , public nsIObserver , public nsSupportsWeakReference { public: // nsISupports NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSICOOKIESERVICE - NS_DECL_NSICOOKIESERVICEINTERNAL - NS_DECL_NSICOOKIEMANAGER - NS_DECL_NSICOOKIEMANAGER2 nsCookieService(); virtual ~nsCookieService(); static nsCookieService* GetSingleton(); + static NS_METHOD GetDefaultContainer(nsISupports* aOuter, const nsIID& aIID, void* *aResult); nsresult Init(); protected: - void PrefChanged(nsIPrefBranch *aPrefBranch); - nsresult Read(); - nsresult Write(); - void GetCookieList(nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const nsACString *aName, PRBool isHttpBound, nsAutoVoidArray &aResult); - char* CookieStringFromArray(const nsAutoVoidArray& aCookieList, nsIURI *aHostURI); - PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy); - void CheckAndAdd(nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, nsCookieStatus aStatus, nsCookiePolicy aPolicy, const nsAFlatCString &aCookieHeader); - void AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader); - void RemoveCookieFromList(nsListIter &aIter); - PRBool AddCookieToList(nsCookie *aCookie); + + char* CookieStringFromArray(nsIArray* aCookieList, nsIURI *aHostURI); + PRBool SetCookieInternal(nsICookieContainer *aContainer, nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy); + void CheckAndAdd(nsICookieContainer *aContainer, nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, nsCookieStatus aStatus, nsCookiePolicy aPolicy, const nsAFlatCString &aCookieHeader); static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound); static PRBool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie); + static PRBool GetExpiry(nsCookieAttributes &aCookie, nsInt64 aServerTime, nsInt64 aCurrentTime, nsCookieStatus aStatus); + static PRBool IsIPAddress(const nsAFlatCString &aHost); static PRBool IsInDomain(const nsACString &aDomain, const nsACString &aHost, PRBool aIsDomain = PR_TRUE); static PRBool IsForeign(nsIURI *aHostURI, nsIURI *aFirstURI); - nsCookieStatus CheckPrefs(nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const char *aCookieHeader, nsCookiePolicy &aPolicy); + nsCookieStatus CheckPrefs(nsICookiePermission *aPermissionService, nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const char *aCookieHeader, nsCookiePolicy &aPolicy); static PRBool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI); static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI); - static PRBool GetExpiry(nsCookieAttributes &aCookie, nsInt64 aServerTime, nsInt64 aCurrentTime, nsCookieStatus aStatus); - void RemoveAllFromMemory(); - void RemoveExpiredCookies(nsInt64 aCurrentTime); - PRBool FindCookie(const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter); - void FindOldestCookie(nsEnumerationData &aData); - PRUint32 CountCookiesFromHostInternal(const nsACString &aHost, nsEnumerationData &aData); - void NotifyRejected(nsIURI *aHostURI); - void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData); - - // Use LazyWrite to save the cookies file on a timer. It will write - // the file only once if repeatedly hammered quickly. - void LazyWrite(); - static void DoLazyWrite(nsITimer *aTimer, void *aClosure); + void NotifyRejected(nsIURI *aHostURI, const PRUnichar *aData); protected: // cached members - nsCOMPtr mCookieFile; nsCOMPtr mObserverService; nsCOMPtr mP3PService; nsCOMPtr mPermissionService; // impl members - nsCOMPtr mWriteTimer; - nsTHashtable mHostTable; - PRUint32 mCookieCount; - PRPackedBool mCookieChanged; + nsInterfaceHashtable mContainerTable; PRPackedBool mCookieIconVisible; + nsCString mDefaultContainerId; // cached prefs PRUint8 mCookiesPermissions; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, P3P} - PRUint16 mMaxNumberOfCookies; - PRUint16 mMaxCookiesPerHost; // private static member, used to cache a ptr to nsCookieService, // so we can make nsCookieService a singleton xpcom object. static nsCookieService *gCookieService; - - // this callback needs access to member functions - friend PLDHashOperator PR_CALLBACK removeExpiredCallback(nsCookieEntry *aEntry, void *aArg); }; #endif // nsCookieService_h__ Index: netwerk/protocol/http/src/nsHttpChannel.cpp =================================================================== RCS file: /cvsroot/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp,v retrieving revision 1.308 diff -p -U 8 -r1.308 nsHttpChannel.cpp --- netwerk/protocol/http/src/nsHttpChannel.cpp 13 May 2007 17:09:51 -0000 1.308 +++ netwerk/protocol/http/src/nsHttpChannel.cpp 14 May 2007 09:24:36 -0000 @@ -115,16 +115,17 @@ nsHttpChannel::nsHttpChannel() , mCanceled(PR_FALSE) , mTransactionReplaced(PR_FALSE) , mUploadStreamHasHeaders(PR_FALSE) , mAuthRetryPending(PR_FALSE) , mSuppressDefensiveAuth(PR_FALSE) , mResuming(PR_FALSE) , mInitedCacheEntry(PR_FALSE) , mCacheForOfflineUse(PR_FALSE) + , mCookieContainer(nsnull) { LOG(("Creating nsHttpChannel @%x\n", this)); // grab a reference to the handler to ensure that it doesn't go away. nsHttpHandler *handler = gHttpHandler; NS_ADDREF(handler); } @@ -224,16 +225,22 @@ nsHttpChannel::Init(nsIURI *uri, rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); if (NS_FAILED(rv)) return rv; rv = gHttpHandler-> AddStandardRequestHeaders(&mRequestHead.Headers(), caps, !mConnectionInfo->UsingSSL() && mConnectionInfo->UsingHttpProxy()); + // set to the default cookie container + nsICookieService *cs = gHttpHandler->GetCookieService(); + nsCOMPtr container; + cs->GetDefaultContainer(getter_AddRefs(container)); + SetCookieContainer(container); + return rv; } //----------------------------------------------------------------------------- // nsHttpChannel //----------------------------------------------------------------------------- nsresult @@ -583,17 +590,18 @@ nsHttpChannel::SetupTransaction() void nsHttpChannel::AddCookiesToRequest() { nsXPIDLCString cookie; nsICookieService *cs = gHttpHandler->GetCookieService(); if (cs) - cs->GetCookieStringFromHttp(mURI, + cs->GetCookieStringFromHttp(mCookieContainer, + mURI, mDocumentURI ? mDocumentURI : mOriginalURI, this, getter_Copies(cookie)); if (cookie.IsEmpty()) cookie = mUserSetCookieHeader; else if (!mUserSetCookieHeader.IsEmpty()) cookie.Append(NS_LITERAL_CSTRING("; ") + mUserSetCookieHeader); @@ -2118,16 +2126,22 @@ nsHttpChannel::SetupReplacementChannel(n if (mConnectionInfo->UsingSSL()) newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; newChannel->SetOriginalURI(mOriginalURI); newChannel->SetLoadGroup(mLoadGroup); newChannel->SetNotificationCallbacks(mCallbacks); newChannel->SetLoadFlags(newLoadFlags); + // set the new channel cookie container to that of this channel + nsCOMPtr cookieCollector(do_QueryInterface(newChannel)); + if (cookieCollector) { + cookieCollector->SetCookieContainer(mCookieContainer); + } + nsCOMPtr httpChannel = do_QueryInterface(newChannel); if (!httpChannel) return NS_OK; // no other options to set if (preserveMethod) { nsCOMPtr uploadChannel = do_QueryInterface(httpChannel); if (mUploadStream && uploadChannel) { // rewind upload stream @@ -3228,16 +3242,17 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel) NS_INTERFACE_MAP_ENTRY(nsICacheListener) NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel) NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) + NS_INTERFACE_MAP_ENTRY(nsICookieCollector) NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) //----------------------------------------------------------------------------- // nsHttpChannel::nsIRequest //----------------------------------------------------------------------------- NS_IMETHODIMP nsHttpChannel::GetName(nsACString &aName) @@ -4061,17 +4076,18 @@ nsHttpChannel::SetCookie(const char *aCo return NS_OK; nsICookieService *cs = gHttpHandler->GetCookieService(); NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); nsCOMPtr prompt; GetCallback(prompt); - return cs->SetCookieStringFromHttp(mURI, + return cs->SetCookieStringFromHttp(mCookieContainer, + mURI, mDocumentURI ? mDocumentURI : mOriginalURI, prompt, aCookieHeader, mResponseHead->PeekHeader(nsHttp::Date), this); } //----------------------------------------------------------------------------- @@ -4842,8 +4858,29 @@ nsHttpChannel::nsContentEncodings::Prepa nsCaseInsensitiveCStringComparator())) { mCurEnd = mCurStart; return PrepareForNext(); } mReady = PR_TRUE; return NS_OK; } + + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsICookieCollector +//----------------------------------------------------------------------------- +/* attribute nsICookieContainer cookieContainer; */ +NS_IMETHODIMP +nsHttpChannel::GetCookieContainer(nsICookieContainer * *aCookieContainer) +{ + NS_ENSURE_ARG_POINTER(aCookieContainer); + *aCookieContainer = mCookieContainer; + NS_IF_ADDREF(*aCookieContainer); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetCookieContainer(nsICookieContainer * aCookieContainer) +{ + mCookieContainer = aCookieContainer; + return NS_OK; +} + Index: netwerk/protocol/http/src/nsHttpChannel.h =================================================================== RCS file: /cvsroot/mozilla/netwerk/protocol/http/src/nsHttpChannel.h,v retrieving revision 1.89 diff -p -U 8 -r1.89 nsHttpChannel.h --- netwerk/protocol/http/src/nsHttpChannel.h 23 Mar 2007 21:56:32 -0000 1.89 +++ netwerk/protocol/http/src/nsHttpChannel.h 14 May 2007 09:24:37 -0000 @@ -75,16 +75,18 @@ #include "nsIOutputStream.h" #include "nsIAsyncInputStream.h" #include "nsIPrompt.h" #include "nsIResumableChannel.h" #include "nsISupportsPriority.h" #include "nsIProtocolProxyCallback.h" #include "nsICancelable.h" #include "nsIProxiedChannel.h" +#include "nsICookieCollector.h" +#include "nsICookieContainer.h" class nsHttpResponseHead; class nsAHttpConnection; class nsIHttpAuthenticator; class nsProxyInfo; //----------------------------------------------------------------------------- // nsHttpChannel @@ -98,16 +100,17 @@ class nsHttpChannel : public nsHashPrope , public nsIUploadChannel , public nsICacheListener , public nsIEncodedChannel , public nsITransportEventSink , public nsIResumableChannel , public nsISupportsPriority , public nsIProtocolProxyCallback , public nsIProxiedChannel + , public nsICookieCollector { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL NS_DECL_NSIHTTPCHANNEL NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER @@ -116,16 +119,17 @@ public: NS_DECL_NSICACHELISTENER NS_DECL_NSIENCODEDCHANNEL NS_DECL_NSIHTTPCHANNELINTERNAL NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSIRESUMABLECHANNEL NS_DECL_NSISUPPORTSPRIORITY NS_DECL_NSIPROTOCOLPROXYCALLBACK NS_DECL_NSIPROXIEDCHANNEL + NS_DECL_NSICOOKIECOLLECTOR nsHttpChannel(); virtual ~nsHttpChannel(); nsresult Init(nsIURI *uri, PRUint8 capabilities, nsProxyInfo* proxyInfo); @@ -221,16 +225,18 @@ private: nsCOMPtr mOwner; nsCOMPtr mCallbacks; nsCOMPtr mProgressSink; nsCOMPtr mUploadStream; nsCOMPtr mReferrer; nsCOMPtr mSecurityInfo; nsCOMPtr mProxyRequest; + nsCOMPtr mCookieContainer; + nsHttpRequestHead mRequestHead; nsHttpResponseHead *mResponseHead; nsRefPtr mTransactionPump; nsHttpTransaction *mTransaction; // hard ref nsHttpConnectionInfo *mConnectionInfo; // hard ref nsCString mSpec; // ASCII encoded URL spec Index: toolkit/content/widgets/tabbrowser.xml =================================================================== RCS file: /cvsroot/mozilla/toolkit/content/widgets/tabbrowser.xml,v retrieving revision 1.228 diff -p -U 8 -r1.228 tabbrowser.xml --- toolkit/content/widgets/tabbrowser.xml 2 May 2007 20:09:02 -0000 1.228 +++ toolkit/content/widgets/tabbrowser.xml 14 May 2007 09:25:09 -0000 @@ -1051,23 +1051,24 @@ + +