switch cookies over to mozStorage. b=230933, r=sdwilsh, sr=mconnor.
authordwitte@stanford.edu
Sun, 17 Jun 2007 14:52:22 -0700
changeset 2508 73ba0b2406dd74c2d1bd9ef351648e00535d383c
parent 2507 4f029b0a754bedd1e0e9b2c2e29cb84a0742a5f1
child 2509 8eb079ae66255a186779d126916b1569dc3dd3b9
push idunknown
push userunknown
push dateunknown
reviewerssdwilsh, mconnor
bugs230933, 384225
milestone1.9a6pre
switch cookies over to mozStorage. b=230933, r=sdwilsh, sr=mconnor. remove nsInt64 usage from cookies, b=384225, r+sr=biesi.
extensions/cookie/nsCookiePermission.cpp
extensions/cookie/nsCookiePermission.h
netwerk/cookie/public/nsICookie.idl
netwerk/cookie/src/Makefile.in
netwerk/cookie/src/nsCookie.cpp
netwerk/cookie/src/nsCookie.h
netwerk/cookie/src/nsCookieService.cpp
netwerk/cookie/src/nsCookieService.h
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -82,21 +82,16 @@ static const char kCookiesDisabledForMai
 static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
 // obsolete pref names for migration
 static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
 static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
 static const char kCookiesAskPermission[] = "network.cookie.warnAboutCookies";
 
 static const char kPermissionType[] = "cookie";
 
-// 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)
-
 #ifdef MOZ_MAIL_NEWS
 // returns PR_TRUE if URI appears to be the URI of a mailnews protocol
 static PRBool
 IsFromMailNews(nsIURI *aURI)
 {
   static const char *kMailNewsProtocols[] =
       { "imap", "news", "snews", "mailbox", nsnull };
   PRBool result;
@@ -161,17 +156,17 @@ nsCookiePermission::Init()
 
   return NS_OK;
 }
 
 void
 nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
                                 const char    *aPref)
 {
-  PRBool val;
+  PRInt32 val;
 
 #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
 
   if (PREF_CHANGED(kCookiesLifetimePolicy) &&
       NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val)))
     mCookiesLifetimePolicy = val;
 
   if (PREF_CHANGED(kCookiesLifetimeDays) &&
@@ -307,18 +302,18 @@ nsCookiePermission::CanSetCookie(nsIURI 
     // now we need to figure out what type of accept policy we're dealing with
     // if we accept cookies normally, just bail and return
     if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
       *aResult = PR_TRUE;
       return NS_OK;
     }
     
     // declare this here since it'll be used in all of the remaining cases
-    nsInt64 currentTime = NOW_IN_SECONDS;
-    nsInt64 delta = nsInt64(*aExpiry) - currentTime;
+    PRInt64 currentTime = PR_Now() / PR_USEC_PER_SEC;
+    PRInt64 delta = *aExpiry - currentTime;
     
     // check whether the user wants to be prompted
     if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
       // if it's a session cookie and the user wants to accept these 
       // without asking, just accept the cookie and return
       if (*aIsSession && mCookiesAlwaysAcceptSession) {
         *aResult = PR_TRUE;
         return NS_OK;
@@ -372,17 +367,17 @@ nsCookiePermission::CanSetCookie(nsIURI 
         if (NS_SUCCEEDED(rv) && countFromHost > 0)
           rv = cookieManager->CookieExists(aCookie, &foundCookie);
       }
       if (NS_FAILED(rv)) return rv;
 
       // check if the cookie we're trying to set is already expired, and return;
       // but only if there's no previous cookie, because then we need to delete the previous
       // cookie. we need this check to avoid prompting the user for already-expired cookies.
-      if (!foundCookie && !*aIsSession && delta <= nsInt64(0)) {
+      if (!foundCookie && !*aIsSession && delta <= 0) {
         // the cookie has already expired. accept it, and let the backend figure
         // out it's expired, so that we get correct logging & notifications.
         *aResult = PR_TRUE;
         return rv;
       }
 
       PRBool rememberDecision = PR_FALSE;
       rv = cookiePromptService->CookieDialog(parent, aCookie, hostPort, 
@@ -406,17 +401,17 @@ nsCookiePermission::CanSetCookie(nsIURI 
             break;
           default:
             break;
         }
       }
     } else {
       // we're not prompting, so we must be limiting the lifetime somehow
       // if it's a session cookie, we do nothing
-      if (!*aIsSession && delta > nsInt64(0)) {
+      if (!*aIsSession && delta > 0) {
         if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
           // limit lifetime to session
           *aIsSession = PR_TRUE;
         } else if (delta > mCookiesLifetimeSec) {
           // limit lifetime to specified time
           *aExpiry = currentTime + mCookiesLifetimeSec;
         }
       }
--- a/extensions/cookie/nsCookiePermission.h
+++ b/extensions/cookie/nsCookiePermission.h
@@ -37,17 +37,16 @@
 
 #ifndef nsCookiePermission_h__
 #define nsCookiePermission_h__
 
 #include "nsICookiePermission.h"
 #include "nsIPermissionManager.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
-#include "nsInt64.h"
 #include "prlong.h"
 
 class nsIPrefBranch;
 
 class nsCookiePermission : public nsICookiePermission
                          , public nsIObserver
 {
 public:
@@ -66,17 +65,17 @@ public:
   virtual ~nsCookiePermission() {}
 
   nsresult Init();
   void     PrefChanged(nsIPrefBranch *, const char *);
 
 private:
   nsCOMPtr<nsIPermissionManager> mPermMgr;
 
-  nsInt64      mCookiesLifetimeSec;            // lifetime limit specified in seconds
+  PRInt64      mCookiesLifetimeSec;            // lifetime limit specified in seconds
   PRUint8      mCookiesLifetimePolicy;         // pref for how long cookies are stored
   PRPackedBool mCookiesAlwaysAcceptSession;    // don't prompt for session cookies
 #ifdef MOZ_MAIL_NEWS
   PRPackedBool mCookiesDisabledForMailNews;
 #endif
 
 };
 
--- a/netwerk/cookie/public/nsICookie.idl
+++ b/netwerk/cookie/public/nsICookie.idl
@@ -78,17 +78,21 @@ interface nsICookie : nsISupports {
     readonly attribute AUTF8String path;
 
     /**
      * true if the cookie was transmitted over ssl, false otherwise
      */
     readonly attribute boolean isSecure;
 
     /**
-     * expiration time (local timezone) expressed as number of seconds since Jan 1, 1970
+     * @DEPRECATED - use nsICookie2.expiry and nsICookie2.isSession instead.
+     *
+     * expiration time in seconds since midnight (00:00:00), January 1, 1970 UTC.
+     * expires = 0 represents a session cookie.
+     * expires = 1 represents an expiration time earlier than Jan 1, 1970.
      */
     readonly attribute PRUint64 expires;
 
     /**
      * P3P status of cookie.  Values are
      *
      *   STATUS_UNKNOWN -- cookie collected in a previous session and this info no longer available
      *   STATUS_ACCEPTED -- cookie was accepted as it
--- a/netwerk/cookie/src/Makefile.in
+++ b/netwerk/cookie/src/Makefile.in
@@ -45,16 +45,17 @@ MODULE		= necko
 LIBRARY_NAME	= neckocookie_s
 FORCE_STATIC_LIB	= 1
 LIBXUL_LIBRARY  = 1
 
 REQUIRES	= \
 		xpcom \
 		string \
 		pref \
+		storage \
 		$(NULL)
 
 CPPSRCS		= \
 		nsCookie.cpp \
 		nsCookieService.cpp \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/netwerk/cookie/src/nsCookie.cpp
+++ b/netwerk/cookie/src/nsCookie.cpp
@@ -70,28 +70,31 @@ StrBlockCopy(const nsACString &aSource1,
   aDestEnd = toBegin;
 }
 
 /******************************************************************************
  * nsCookie:
  * creation helper
  ******************************************************************************/
 
-// This is a counter that is incremented each time we allocate a new nsCookie.
-// The value of the counter is stored with each nsCookie so that we can sort
-// cookies by creation time (within the current browser session).
-static PRUint32 gLastCreationTime;
+// This is a counter that keeps track of the last used creation id, each time we
+// create a new nsCookie. The creation id is nominally the time (in microseconds)
+// the cookie was created. This id also corresponds to the row id used in the
+// sqlite database, which must be unique. However, since it's possible two cookies
+// may be created at the same time, or the system clock isn't monotonic, we must
+// check each id to enforce monotonicity.
+static PRInt64 gLastCreationID;
 
 nsCookie *
 nsCookie::Create(const nsACString &aName,
                  const nsACString &aValue,
                  const nsACString &aHost,
                  const nsACString &aPath,
-                 nsInt64          aExpiry,
-                 nsInt64          aLastAccessed,
+                 PRInt64          aExpiry,
+                 PRInt64          aCreationID,
                  PRBool           aIsSession,
                  PRBool           aIsSecure,
                  PRBool           aIsHttpOnly,
                  nsCookieStatus   aStatus,
                  nsCookiePolicy   aPolicy)
 {
   // find the required string buffer size, adding 4 for the terminating nulls
   const PRUint32 stringLength = aName.Length() + aValue.Length() +
@@ -104,19 +107,26 @@ nsCookie::Create(const nsACString &aName
     return nsnull;
 
   // assign string members
   char *name, *value, *host, *path, *end;
   name = NS_STATIC_CAST(char *, place) + sizeof(nsCookie);
   StrBlockCopy(aName, aValue, aHost, aPath,
                name, value, host, path, end);
 
+  // check if the creation id given to us is greater than the running maximum
+  // (it should always be monotonically increasing). if it's not, make up our own.
+  if (aCreationID > gLastCreationID)
+    gLastCreationID = aCreationID;
+  else
+    aCreationID = ++gLastCreationID;
+
   // construct the cookie. placement new, oh yeah!
   return new (place) nsCookie(name, value, host, path, end,
-                              aExpiry, aLastAccessed, ++gLastCreationTime,
+                              aExpiry, aCreationID,
                               aIsSession, aIsSecure, aIsHttpOnly,
                               aStatus, aPolicy);
 }
 
 /******************************************************************************
  * nsCookie:
  * xpcom impl
  ******************************************************************************/
@@ -138,14 +148,14 @@ NS_IMETHODIMP nsCookie::GetHttpOnly(PRBo
 // compatibility method, for use with the legacy nsICookie interface.
 // here, expires == 0 denotes a session cookie.
 NS_IMETHODIMP
 nsCookie::GetExpires(PRUint64 *aExpires)
 {
   if (IsSession()) {
     *aExpires = 0;
   } else {
-    *aExpires = Expiry() > nsInt64(0) ? PRInt64(Expiry()) : 1;
+    *aExpires = Expiry() > 0 ? Expiry() : 1;
   }
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS2(nsCookie, nsICookie2, nsICookie)
--- a/netwerk/cookie/src/nsCookie.h
+++ b/netwerk/cookie/src/nsCookie.h
@@ -37,17 +37,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsCookie_h__
 #define nsCookie_h__
 
 #include "nsICookie.h"
 #include "nsICookie2.h"
 #include "nsString.h"
-#include "nsInt64.h"
 
 /** 
  * The nsCookie class is the main cookie storage medium for use within cookie
  * code. It implements nsICookie2, which extends nsICookie, a frozen interface
  * for xpcom access of cookie objects.
  */
 
 /******************************************************************************
@@ -70,80 +69,80 @@ class nsCookie : public nsICookie2
 
   private:
     // for internal use only. see nsCookie::Create().
     nsCookie(const char     *aName,
              const char     *aValue,
              const char     *aHost,
              const char     *aPath,
              const char     *aEnd,
-             nsInt64         aExpiry,
-             nsInt64         aLastAccessed,
-             PRUint32        aCreationTime,
+             PRInt64         aExpiry,
+             PRInt64         aCreationID,
              PRBool          aIsSession,
              PRBool          aIsSecure,
              PRBool          aIsHttpOnly,
              nsCookieStatus  aStatus,
              nsCookiePolicy  aPolicy)
      : mNext(nsnull)
      , mName(aName)
      , mValue(aValue)
      , mHost(aHost)
      , mPath(aPath)
      , mEnd(aEnd)
      , mExpiry(aExpiry)
-     , mLastAccessed(aLastAccessed)
-     , mCreationTime(aCreationTime)
+     , mCreationID(aCreationID)
      , mRefCnt(0)
      , mIsSession(aIsSession != PR_FALSE)
      , mIsSecure(aIsSecure != PR_FALSE)
      , mIsHttpOnly(aIsHttpOnly != PR_FALSE)
      , mStatus(aStatus)
      , mPolicy(aPolicy)
     {
     }
 
   public:
     // public helper to create an nsCookie object. use |operator delete|
     // to destroy an object created by this method.
     static nsCookie * Create(const nsACString &aName,
                              const nsACString &aValue,
                              const nsACString &aHost,
                              const nsACString &aPath,
-                             nsInt64           aExpiry,
-                             nsInt64           aLastAccessed,
+                             PRInt64           aExpiry,
+                             PRInt64           aCreationID,
                              PRBool            aIsSession,
                              PRBool            aIsSecure,
                              PRBool            aIsHttpOnly,
                              nsCookieStatus    aStatus,
                              nsCookiePolicy    aPolicy);
 
     virtual ~nsCookie() {}
 
     // fast (inline, non-xpcom) getters
     inline const nsDependentCString Name()  const { return nsDependentCString(mName, mValue - 1); }
     inline const nsDependentCString Value() const { return nsDependentCString(mValue, mHost - 1); }
     inline const nsDependentCString Host()  const { return nsDependentCString(mHost, mPath - 1); }
     inline const nsDependentCString RawHost() const { return nsDependentCString(IsDomain() ? mHost + 1 : mHost, mPath - 1); }
     inline const nsDependentCString Path()  const { return nsDependentCString(mPath, mEnd); }
-    inline nsInt64 Expiry()                 const { return mExpiry; }
-    inline nsInt64 LastAccessed()           const { return mLastAccessed; }
-    inline PRUint32 CreationTime()          const { return mCreationTime; }
+    inline PRInt64 Expiry()                 const { return mExpiry; }
+    inline PRInt64 CreationID()             const { return mCreationID; }
+    // cookie creation time, in seconds
+    inline PRInt64 CreationTime()           const { return mCreationID / PR_USEC_PER_SEC; }
     inline PRBool IsSession()               const { return mIsSession; }
     inline PRBool IsDomain()                const { return *mHost == '.'; }
     inline PRBool IsSecure()                const { return mIsSecure; }
     inline PRBool IsHttpOnly()              const { return mIsHttpOnly; }
     inline nsCookieStatus Status()          const { return mStatus; }
     inline nsCookiePolicy Policy()          const { return mPolicy; }
 
     // setters
-    inline void SetLastAccessed(nsInt64 aLastAccessed) { mLastAccessed = aLastAccessed; }
-    inline void SetExpiry(PRInt64 aExpiry)             { mExpiry = aExpiry; }
-    inline void SetIsSession(PRBool aIsSession)        { mIsSession = aIsSession; }
-    inline void SetCreationTime(PRUint32 aCT)          { mCreationTime = aCT; }
+    inline void SetExpiry(PRInt64 aExpiry)        { mExpiry = aExpiry; }
+    inline void SetIsSession(PRBool aIsSession)   { mIsSession = aIsSession; }
+    // set the creation id manually, overriding the monotonicity checks in Create().
+    // use with caution!
+    inline void SetCreationID(PRInt64 aID)        { mCreationID = aID; }
 
     // linked list management helper
     inline nsCookie*& Next() { return mNext; }
 
   protected:
     // member variables
     // we use char* ptrs to store the strings in a contiguous block,
     // so we save on the overhead of using nsCStrings. However, we
@@ -151,19 +150,20 @@ class nsCookie : public nsICookie2
     // out as nsAFlatCStrings.
 
     nsCookie   *mNext;
     const char *mName;
     const char *mValue;
     const char *mHost;
     const char *mPath;
     const char *mEnd;
-    nsInt64     mExpiry;
-    nsInt64     mLastAccessed;
-    PRUint32    mCreationTime;
+    PRInt64     mExpiry;
+    // creation id is unique for each cookie and approximately represents the cookie
+    // creation time, in microseconds.
+    PRInt64     mCreationID;
     PRUint32    mRefCnt    : 16;
     PRUint32    mIsSession : 1;
     PRUint32    mIsSecure  : 1;
     PRUint32    mIsHttpOnly: 1;
     PRUint32    mStatus    : 3;
     PRUint32    mPolicy    : 3;
 };
 
--- a/netwerk/cookie/src/nsCookieService.cpp
+++ b/netwerk/cookie/src/nsCookieService.cpp
@@ -48,46 +48,50 @@
 #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 "mozIStorageService.h"
+#include "mozIStorageStatement.h"
+#include "mozIStorageConnection.h"
+#include "mozStorageHelper.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 char kCookieFileName[] = "cookies.sqlite";
+#define COOKIES_SCHEMA_VERSION 1
 
-static const PRUint32 kLazyWriteTimeout = 5000; //msec
+static const char kOldCookieFileName[] = "cookies.txt";
 
 #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;
@@ -96,21 +100,16 @@ static const PRUint32 kMaxBytesPerPath  
 
 // 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).
 static const nsCookieStatus STATUS_REJECTED_WITH_ERROR = 5;
 
-// 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)
-
 // behavior pref constants 
 static const PRUint32 BEHAVIOR_ACCEPT        = 0;
 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";
@@ -121,17 +120,18 @@ static const char kPrefMaxCookiesPerHost
 struct nsCookieAttributes
 {
   nsCAutoString name;
   nsCAutoString value;
   nsCAutoString host;
   nsCAutoString path;
   nsCAutoString expires;
   nsCAutoString maxage;
-  nsInt64 expiryTime;
+  PRInt64 expiryTime;
+  PRInt64 creationID;
   PRBool isSession;
   PRBool isSecure;
   PRBool isHttpOnly;
 };
 
 // stores linked list iteration state, and provides a rudimentary
 // list traversal method
 struct nsListIter
@@ -156,28 +156,28 @@ struct nsListIter
   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)
+  nsEnumerationData(PRInt64 aCurrentTime,
+                    PRInt64 aOldestID)
    : currentTime(aCurrentTime)
-   , oldestTime(aOldestTime)
+   , oldestID(aOldestID)
    , iter(nsnull, nsnull, nsnull) {}
 
   // the current time
-  nsInt64 currentTime;
+  PRInt64 currentTime;
 
-  // oldest lastAccessed time in the cookie list. use aOldestTime = LL_MAXINT
+  // oldest creation id in the cookie list. use aOldestID = LL_MAXINT
   // to enable this search, LL_MININT to disable it.
-  nsInt64 oldestTime;
+  PRInt64 oldestID;
 
   // an iterator object that points to the desired cookie
   nsListIter iter;
 };
 
 /******************************************************************************
  * Cookie logging handlers
  * used for logging in nsCookieService
@@ -205,30 +205,28 @@ static PRLogModuleInfo *sCookieLog = PR_
 
 #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)) {
+  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", " ====="));
+    ("===== %s =====\n", aSetCookie ? "COOKIE NOT ACCEPTED" : "COOKIE NOT SENT"));
   PR_LOG(sCookieLog, PR_LOG_WARNING,("request URL: %s\n", spec.get()));
-  if (aSetCookie) {
+  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));
@@ -239,20 +237,21 @@ 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);
+  if (aHostURI)
+    aHostURI->GetAsciiSpec(spec);
 
   PR_LOG(sCookieLog, PR_LOG_DEBUG,
-    ("%s%s%s\n", "===== ", aSetCookie ? "COOKIE ACCEPTED" : "COOKIE SENT", " ====="));
+    ("===== %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);
 
@@ -260,24 +259,28 @@ LogSuccess(PRBool aSetCookie, nsIURI *aH
 
   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_ExplodeTime(aCookie->Expiry() * PR_USEC_PER_SEC, PR_GMTParameters, &explodedTime);
+    PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
+    PR_LOG(sCookieLog, PR_LOG_DEBUG,
+      ("expires: %s%s", timeString, aCookie->IsSession() ? " (at end of session)" : ""));
 
+    PR_ExplodeTime(aCookie->CreationID(), 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));
+      ("created: %s (id %lld)", timeString, aCookie->CreationID()));
+
     PR_LOG(sCookieLog, PR_LOG_DEBUG,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
+    PR_LOG(sCookieLog, PR_LOG_DEBUG,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "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)
 {
@@ -316,36 +319,23 @@ compareCookiesForSending(const void *aEl
 
   // 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();
+    // note: CreationID is unique, so two id's can never be equal.
+    // we may have overflow problems returning the result directly, so we need branches
+    rv = (cookie1->CreationID() > cookie2->CreationID() ? 1 : -1);
   }
   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:
  * singleton instance ctor/dtor methods
  ******************************************************************************/
 
 nsCookieService *nsCookieService::gCookieService = nsnull;
 
 nsCookieService*
@@ -383,17 +373,16 @@ NS_IMPL_ISUPPORTS6(nsCookieService,
                    nsICookieServiceInternal,
                    nsICookieManager,
                    nsICookieManager2,
                    nsIObserver,
                    nsISupportsWeakReference)
 
 nsCookieService::nsCookieService()
  : mCookieCount(0)
- , mCookieChanged(PR_FALSE)
  , mCookieIconVisible(PR_FALSE)
  , mCookiesPermissions(BEHAVIOR_ACCEPT)
  , mMaxNumberOfCookies(kMaxNumberOfCookies)
  , mMaxCookiesPerHost(kMaxCookiesPerHost)
 {
 }
 
 nsresult
@@ -407,85 +396,206 @@ nsCookieService::Init()
   nsCOMPtr<nsIPrefBranch2> 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));
-  }
-
-  Read();
+  // ignore failure here, since it's non-fatal (we can run fine without
+  // persistent storage - e.g. if there's no profile)
+  InitDB();
 
   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);
   }
 
   mPermissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
 
   return NS_OK;
 }
 
+nsresult
+nsCookieService::InitDB()
+{
+  nsCOMPtr<nsIFile> cookieFile;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(cookieFile));
+  if (!cookieFile)
+    return NS_ERROR_UNEXPECTED;
+
+  cookieFile->AppendNative(NS_LITERAL_CSTRING(kCookieFileName));
+
+  nsCOMPtr<mozIStorageService> storage = do_GetService("@mozilla.org/storage/service;1");
+  if (!storage)
+    return NS_ERROR_UNEXPECTED;
+
+  // cache a connection to the cookie database
+  nsresult rv = storage->OpenDatabase(cookieFile, getter_AddRefs(mDBConn));
+  if (rv == NS_ERROR_FILE_CORRUPTED) {
+    // delete and try again
+    cookieFile->Remove(PR_FALSE);
+    rv = storage->OpenDatabase(cookieFile, getter_AddRefs(mDBConn));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool tableExists = PR_FALSE;
+  mDBConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"), &tableExists);
+  if (!tableExists) {
+      rv = CreateTable();
+      NS_ENSURE_SUCCESS(rv, rv);
+
+  } else {
+    // table already exists; check the schema version before reading
+    PRInt32 dbSchemaVersion;
+    {
+      // scope the statement, so the write lock is released when finished
+      nsCOMPtr<mozIStorageStatement> stmt;
+      rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
+                                    getter_AddRefs(stmt));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      PRBool hasResult;
+      rv = stmt->ExecuteStep(&hasResult);
+      if (NS_SUCCEEDED(rv) && hasResult) {
+        dbSchemaVersion = stmt->AsInt32(0);
+      } else {
+        NS_WARNING("couldn't get schema version!");
+        stmt = nsnull;
+        
+        // the table may be usable; someone might've just clobbered the schema
+        // version. we can treat this case like a downgrade using the codepath
+        // below, by verifying the columns we care about are all there. for now,
+        // re-set the schema version in the db, in case the checks succeed (if
+        // they don't, we're dropping the table anyway).
+        nsCAutoString stmtString(NS_LITERAL_CSTRING("PRAGMA user_version="));
+        stmtString.AppendInt(COOKIES_SCHEMA_VERSION);
+        nsresult rv = mDBConn->ExecuteSimpleSQL(stmtString);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // set this to a large number, to force the downgrade codepath
+        dbSchemaVersion = PR_UINT32_MAX;
+      }
+    }
+
+    if (dbSchemaVersion != COOKIES_SCHEMA_VERSION) {
+      // migration how-to:
+      //
+      // 1. increment COOKIES_SCHEMA_VERSION.
+      // 2. implement a method that performs up/sidegrade to your version
+      //    from the current version.
+
+      if (dbSchemaVersion > COOKIES_SCHEMA_VERSION) {
+        // downgrading.
+        // if columns have been added to the table, we can still use the ones we
+        // understand safely. if columns have been deleted or altered, just
+        // blow away the table and start from scratch! if you change the way
+        // a column is interpreted, make sure you also change its name so this
+        // check will catch it.
+        
+        // NOTE: if you change the code below, make sure the db schema version
+        // getter above still falls through to this codepath on failure!
+        
+        // check if all the expected columns exist
+        nsCOMPtr<mozIStorageStatement> stmt;
+        rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+          "SELECT id, name, value, host, path, expiry, isSecure, isHttpOnly "
+          "FROM moz_cookies"), getter_AddRefs(stmt));
+        if (NS_SUCCEEDED(rv)) {
+          PRBool hasResult;
+          rv = stmt->ExecuteStep(&hasResult);
+        }
+        
+        if (NS_FAILED(rv)) {   
+          // our columns aren't there - drop the table!
+          rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_cookies"));
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          rv = CreateTable();
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+      }
+    }
+  }
+
+  // make operations on the table asynchronous, for performance
+  mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
+
+  // cache frequently used statements (for insertion, deletion, and updating)
+  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO moz_cookies "
+    "(id, name, value, host, path, expiry, isSecure, isHttpOnly) "
+    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "DELETE FROM moz_cookies WHERE id = ?1"), getter_AddRefs(mStmtDelete));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // check whether to import or just read in the db
+  if (!tableExists)
+    return ImportCookies();
+
+  return Read();
+}
+
+// sets the schema version and creates the moz_cookies table.
+nsresult
+nsCookieService::CreateTable()
+{
+  // set the schema version, before creating the table
+  nsCAutoString stmtString(NS_LITERAL_CSTRING("PRAGMA user_version="));
+  stmtString.AppendInt(COOKIES_SCHEMA_VERSION);
+  nsresult rv = mDBConn->ExecuteSimpleSQL(stmtString);
+  if (NS_FAILED(rv)) return rv;
+
+  // create the table
+  return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE moz_cookies ("
+    "id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,"
+    "expiry INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"));
+}
+
 nsCookieService::~nsCookieService()
 {
   gCookieService = nsnull;
-
-  if (mWriteTimer)
-    mWriteTimer->Cancel();
 }
 
 NS_IMETHODIMP
 nsCookieService::Observe(nsISupports     *aSubject,
                          const char      *aTopic,
                          const PRUnichar *aData)
 {
   // check the topic
-  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
+  if (!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;
-    }
+    RemoveAllFromMemory();
 
-    if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
-      RemoveAllFromMemory();
-      // delete the cookie file
-      if (mCookieFile) {
-        mCookieFile->Remove(PR_FALSE);
+    if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get()) && mDBConn)
+      // clear the cookie file
+      if (mDBConn) {
+        nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
+        if (NS_FAILED(rv))
+          NS_WARNING("db delete failed");
       }
-    } 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 (!strcmp(aTopic, "profile-do-change")) {
+    // the profile has already changed; init the db from the new location
+    InitDB();
 
-  } else if (!nsCRT::strcmp(aTopic, "cookieIcon")) {
+  } else if (!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)) {
+  } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
     if (prefBranch)
       PrefChanged(prefBranch);
   }
 
   return NS_OK;
 }
 
@@ -535,17 +645,17 @@ nsCookieService::GetCookieList(nsIURI   
   // 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;
+  PRInt64 currentTime = PR_Now() / PR_USEC_PER_SEC;
   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;
@@ -590,19 +700,18 @@ nsCookieService::GetCookieList(nsIURI   
         continue;
       }
 
       // check if the cookie has expired
       if (cookie->Expiry() <= currentTime) {
         continue;
       }
 
-      // all checks passed - add to list and update lastAccessed stamp of cookie
+      // all checks passed - add to list
       aResult.AppendElement(cookie);
-      cookie->SetLastAccessed(currentTime);
     }
 
     currentDot = nextDot;
     if (currentDot)
       nextDot = strchr(currentDot + 1, '.');
 
   } while (currentDot);
 
@@ -744,16 +853,17 @@ nsCookieService::SetCookieValue(nsIURI *
   }
 
   nsCookieAttributes attributes;
   attributes.name = aName;
   attributes.value = aValue;
   attributes.host = aDomain;
   attributes.path = aPath;
   attributes.expiryTime = aExpiry;
+  attributes.creationID = PR_Now();
   attributes.isSession = aIsSession;
 
   attributes.isSecure = PR_FALSE;
   aHostURI->SchemeIs("https", &attributes.isSecure);
 
   CheckAndAdd(aHostURI, aChannel, attributes,
               cookieStatus, cookiePolicy, EmptyCString());
   return NS_OK;
@@ -801,58 +911,37 @@ nsCookieService::SetCookieStringFromHttp
     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
   // user is prompted).
-  nsInt64 serverTime;
   PRTime tempServerTime;
+  PRInt64 serverTime;
   if (aServerTime && PR_ParseTimeString(aServerTime, PR_TRUE, &tempServerTime) == PR_SUCCESS) {
-    serverTime = nsInt64(tempServerTime) / USEC_PER_SEC;
+    serverTime = tempServerTime / PR_USEC_PER_SEC;
   } else {
-    serverTime = NOW_IN_SECONDS;
+    serverTime = PR_Now() / PR_USEC_PER_SEC;
   }
 
+  // start a transaction on the storage db, to optimize insertions.
+  // transaction will automically commit on completion
+  mozStorageTransaction transaction(mDBConn, PR_TRUE);
+ 
   // switch to a nice string type now, and process each cookie in the header
   nsDependentCString cookieHeader(aCookieHeader);
   while (SetCookieInternal(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);
 }
 
@@ -861,18 +950,16 @@ nsCookieService::NotifyRejected(nsIURI *
 // "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 &&
@@ -921,66 +1008,73 @@ nsCookieService::PrefChanged(nsIPrefBran
  * nsICookieManager
  ******************************************************************************/
 
 NS_IMETHODIMP
 nsCookieService::RemoveAll()
 {
   RemoveAllFromMemory();
   NotifyChanged(nsnull, NS_LITERAL_STRING("cleared").get());
-  Write();
+
+  // clear the cookie file
+  if (mDBConn) {
+    nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
+    if (NS_FAILED(rv))
+      NS_WARNING("db delete failed");
+  }
+
   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<nsICookie>*, aArg)->AppendObject(cookie);
   }
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
 {
-  RemoveExpiredCookies(NOW_IN_SECONDS);
+  RemoveExpiredCookies(PR_Now() / PR_USEC_PER_SEC);
 
   nsCOMArray<nsICookie> 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;
+  PRInt64 currentTimeInUsec = PR_Now();
 
   nsRefPtr<nsCookie> cookie =
     nsCookie::Create(aName, aValue, aDomain, aPath,
-                     nsInt64(aExpiry),
-                     currentTime,
+                     aExpiry,
+                     currentTimeInUsec,
                      aIsSession,
                      aIsSecure,
                      PR_FALSE,
                      nsICookie::STATUS_UNKNOWN,
                      nsICookie::POLICY_UNKNOWN);
   if (!cookie) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  AddInternal(cookie, currentTime, nsnull, nsnull);
+  AddInternal(cookie, currentTimeInUsec / PR_USEC_PER_SEC, nsnull, nsnull);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCookieService::Remove(const nsACString &aHost,
                         const nsACString &aName,
                         const nsACString &aPath,
                         PRBool           aBlocked)
@@ -998,68 +1092,141 @@ nsCookieService::Remove(const nsACString
     if (aBlocked && mPermissionService) {
       nsCAutoString host(NS_LITERAL_CSTRING("http://") + cookie->RawHost());
       nsCOMPtr<nsIURI> 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<nsIInputStream> fileInputStream;
-  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mCookieFile);
-  if (NS_FAILED(rv)) {
-    return rv;
+
+  // delete expired cookies, before we read in the db
+  {
+    // scope the deletion, so the write lock is released when finished
+    nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
+    rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cookies WHERE expiry <= ?1"),
+                                  getter_AddRefs(stmtDeleteExpired));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmtDeleteExpired->BindInt64Parameter(0, PR_Now() / PR_USEC_PER_SEC);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRBool hasResult;
+    rv = stmtDeleteExpired->ExecuteStep(&hasResult);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // let the reading begin!
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT id, name, value, host, path, expiry, isSecure, isHttpOnly "
+    "FROM moz_cookies"), getter_AddRefs(stmt));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString name, value, host, path;
+  PRBool hasResult;
+  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
+    PRInt64 creationID = stmt->AsInt64(0);
+    
+    stmt->GetUTF8String(1, name);
+    stmt->GetUTF8String(2, value);
+    stmt->GetUTF8String(3, host);
+    stmt->GetUTF8String(4, path);
+
+    PRInt64 expiry = stmt->AsInt64(5);
+    PRBool isSecure = stmt->AsInt32(6);
+    PRBool isHttpOnly = stmt->AsInt32(7);
+
+    // create a new nsCookie and assign the data.
+    nsCookie* newCookie =
+      nsCookie::Create(name, value, host, path,
+                       expiry,
+                       creationID,
+                       PR_FALSE,
+                       isSecure,
+                       isHttpOnly,
+                       nsICookie::STATUS_UNKNOWN,
+                       nsICookie::POLICY_UNKNOWN);
+    if (!newCookie)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    if (!AddCookieToList(newCookie, PR_FALSE))
+      // 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;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsCookieService::ImportCookies()
+{
+  nsCOMPtr<nsIFile> cookieFile;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(cookieFile));
+  if (cookieFile)
+    cookieFile->AppendNative(NS_LITERAL_CSTRING(kOldCookieFileName));
+
+  nsresult rv;
+  nsCOMPtr<nsIInputStream> fileInputStream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), cookieFile);
+  if (NS_FAILED(rv)) return rv;
+
   nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  if (NS_FAILED(rv)) return rv;
+
+  // start a transaction on the storage db, to optimize insertions.
+  // transaction will automically commit on completion
+  mozStorageTransaction transaction(mDBConn, PR_TRUE);
 
   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;
 
+  // generate a creation id for all the cookies we're going to read in, by
+  // using the current time and successively decrementing it, to keep
+  // the most-recently-used cookie ordering. the actual creation time is
+  // unknown, so this is the best we can do.
+  PRInt64 creationIDCounter = PR_Now();
+  PRInt64 currentTime = creationIDCounter / PR_USEC_PER_SEC;
+
   /* 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.
+   * note: cookie can contain tabs.
    */
 
   /*
    * ...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
    *
@@ -1091,229 +1258,107 @@ nsCookieService::Read()
       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) {
+    if (numInts != 1 || expires < currentTime) {
       continue;
     }
 
-    isDomain = Substring(buffer, isDomainIndex, pathIndex - isDomainIndex - 1)
-               .EqualsLiteral(kTrue);
+    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,
+                       expires,
+                       creationIDCounter,
                        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);
+    
+    // manually set the creation id. this is okay, since nsCookie::Create() will keep
+    // track of the largest id we've used, and we're setting it to a smaller
+    // known unique one here.
+    newCookie->SetCreationID(--creationIDCounter);
 
     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<nsIOutputStream> 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<nsIOutputStream> 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);
+  // we're done importing - delete the old cookie file
+  cookieFile->Remove(PR_FALSE);
 
-  /* 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<nsISafeOutputStream> 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,
                                    nsIChannel         *aChannel,
                                    nsDependentCString &aCookieHeader,
-                                   nsInt64             aServerTime,
+                                   PRInt64             aServerTime,
                                    nsCookieStatus      aStatus,
                                    nsCookiePolicy      aPolicy)
 {
   // create a stack-based nsCookieAttributes, to store all the
   // attributes parsed from the cookie
   nsCookieAttributes cookieAttributes;
 
   // init expiryTime such that session cookies won't prematurely expire
   cookieAttributes.expiryTime = LL_MAXINT;
 
   // aCookieHeader is an in/out param to point to the next cookie, if
   // there is one. Save the present value to pass to CheckAndAdd
   nsDependentCString savedCookieHeader(aCookieHeader);
 
   // newCookie says whether there are multiple cookies in the header;
   // so we can handle them separately.
-  const PRBool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
+  PRBool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
+
+  // generate a creation id for the cookie
+  PRInt64 currentTimeInUsec = PR_Now();
+  cookieAttributes.creationID = currentTimeInUsec;
 
   // 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);
+                                         currentTimeInUsec / PR_USEC_PER_SEC, aStatus);
 
   CheckAndAdd(aHostURI, aChannel, cookieAttributes,
               aStatus, aPolicy, savedCookieHeader);
 
   return newCookie;
 }
 
 void
@@ -1340,26 +1385,24 @@ nsCookieService::CheckAndAdd(nsIURI     
     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "failed the domain tests");
     return;
   }
   if (!CheckPath(aAttributes, aHostURI)) {
     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "failed the path tests");
     return;
   }
 
-  const nsInt64 currentTime = NOW_IN_SECONDS;
-
   // create a new nsCookie and copy attributes
   nsRefPtr<nsCookie> cookie =
     nsCookie::Create(aAttributes.name,
                      aAttributes.value,
                      aAttributes.host,
                      aAttributes.path,
                      aAttributes.expiryTime,
-                     currentTime,
+                     aAttributes.creationID,
                      aAttributes.isSession,
                      aAttributes.isSecure,
                      aAttributes.isHttpOnly,
                      aStatus,
                      aPolicy);
   if (!cookie) {
     return;
   }
@@ -1369,65 +1412,66 @@ nsCookieService::CheckAndAdd(nsIURI     
   if (mPermissionService) {
     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,
                                      aChannel,
                                      NS_STATIC_CAST(nsICookie2*, NS_STATIC_CAST(nsCookie*, cookie)),
                                      &aAttributes.isSession,
-                                     &aAttributes.expiryTime.mValue,
+                                     &aAttributes.expiryTime,
                                      &permission);
     if (!permission) {
       COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie rejected by permission manager");
       NotifyRejected(aHostURI);
       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());
+  // we get the current time again here, since it may have changed during prompting
+  AddInternal(cookie, PR_Now() / PR_USEC_PER_SEC, 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,
+                             PRInt64     aCurrentTime,
                              nsIURI     *aHostURI,
                              const char *aCookieHeader)
 {
+  // start a transaction on the storage db, to optimize deletions/insertions.
+  // transaction will automically commit on completion. if we already have a
+  // transaction (e.g. from SetCookie*()), this will have no effect. 
+  mozStorageTransaction transaction(mDBConn, PR_TRUE);
+
   nsListIter matchIter;
   const PRBool foundCookie =
     FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(), matchIter);
 
   nsRefPtr<nsCookie> 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.
@@ -1439,17 +1483,17 @@ nsCookieService::AddInternal(nsCookie   
 
     } 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;
+        data.oldestID = LL_MAXINT;
         FindOldestCookie(data);
         oldCookie = data.iter.current;
         RemoveCookieFromList(data.iter);
       }
     }
 
     // if we deleted an old cookie, notify consumers
     if (oldCookie)
@@ -2049,50 +2093,50 @@ nsCookieService::CheckPath(nsCookieAttri
       aCookieAttributes.path.FindChar('\t') != kNotFound )
     return PR_FALSE;
 
   return PR_TRUE;
 }
 
 PRBool
 nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes,
-                           nsInt64            aServerTime,
-                           nsInt64            aCurrentTime,
-                           nsCookieStatus     aStatus)
+                           PRInt64             aServerTime,
+                           PRInt64             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;
+  PRInt64 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);
+    delta = maxage;
 
   // check for expires attribute
   } else if (!aCookieAttributes.expires.IsEmpty()) {
-    nsInt64 expires;
     PRTime tempExpires;
+    PRInt64 expires;
 
     // parse expiry time
     if (PR_ParseTimeString(aCookieAttributes.expires.get(), PR_TRUE, &tempExpires) == PR_SUCCESS) {
-      expires = nsInt64(tempExpires) / USEC_PER_SEC;
+      expires = tempExpires / PR_USEC_PER_SEC;
     } else {
       return PR_TRUE;
     }
 
     delta = expires - aServerTime;
 
   // default to session cookie if no attributes found
   } else {
@@ -2117,51 +2161,50 @@ nsCookieService::GetExpiry(nsCookieAttri
 
 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 &currentTime = *NS_STATIC_CAST(nsInt64*, aArg);
+  const PRInt64 &currentTime = *NS_STATIC_CAST(PRInt64*, 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)
+nsCookieService::RemoveExpiredCookies(PRInt64 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);
+  nsEnumerationData data(PR_Now() / PR_USEC_PER_SEC, 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.
@@ -2178,18 +2221,18 @@ nsCookieService::CountCookiesFromHostInt
   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();
+        if (aData.oldestID > iter.current->CreationID()) {
+          aData.oldestID = iter.current->CreationID();
           aData.iter = iter;
         }
       }
     }
 
     currentDot = nextDot;
     if (currentDot)
       nextDot = strchr(currentDot + 1, '.');
@@ -2201,17 +2244,17 @@ nsCookieService::CountCookiesFromHostInt
 
 // 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);
+  nsEnumerationData data(PR_Now() / PR_USEC_PER_SEC, LL_MININT);
   
   *aCountFromHost = CountCookiesFromHostInternal(aHost, data);
   return NS_OK;
 }
 
 // find an exact previous match.
 PRBool
 nsCookieService::FindCookie(const nsAFlatCString &aHost,
@@ -2229,16 +2272,30 @@ nsCookieService::FindCookie(const nsAFla
 
   return PR_FALSE;
 }
 
 // removes a cookie from the hashtable, and update the iterator state.
 void
 nsCookieService::RemoveCookieFromList(nsListIter &aIter)
 {
+  // if it's a non-session cookie, remove it from the db
+  if (!aIter.current->IsSession() && mStmtDelete) {
+    // use our cached sqlite "delete" statement
+    mozStorageStatementScoper scoper(mStmtDelete);
+
+    nsresult rv = mStmtDelete->BindInt64Parameter(0, aIter.current->CreationID());
+    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "binding parameter failed");
+    if (NS_SUCCEEDED(rv)) {
+      PRBool hasResult;
+      rv = mStmtDelete->ExecuteStep(&hasResult);
+      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "db remove failed!");
+    }
+  }
+
   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 {
@@ -2250,48 +2307,89 @@ nsCookieService::RemoveCookieFromList(ns
       aIter.current = aIter.prev->Next() = next;
     } else {
       // element to remove is the head
       aIter.current = aIter.entry->Head() = next;
     }
   }
 
   --mCookieCount;
-  mCookieChanged = PR_TRUE;
+}
+
+nsresult
+bindCookieParameters(mozIStorageStatement* aStmt, const nsCookie* aCookie)
+{
+  nsresult rv;
+  
+  rv = aStmt->BindInt64Parameter(0, aCookie->CreationID());
+  if (NS_FAILED(rv)) return rv;
+
+  rv = aStmt->BindUTF8StringParameter(1, aCookie->Name());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindUTF8StringParameter(2, aCookie->Value());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindUTF8StringParameter(3, aCookie->Host());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindUTF8StringParameter(4, aCookie->Path());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindInt64Parameter(5, aCookie->Expiry());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindInt32Parameter(6, aCookie->IsSecure());
+  if (NS_FAILED(rv)) return rv;
+  
+  rv = aStmt->BindInt32Parameter(7, aCookie->IsHttpOnly());
+  return rv;
 }
 
 PRBool
-nsCookieService::AddCookieToList(nsCookie *aCookie)
+nsCookieService::AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB)
 {
   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;
+
+  // if it's a non-session cookie and hasn't just been read from the db, write it out.
+  if (aWriteToDB && !aCookie->IsSession() && mStmtInsert) {
+    // use our cached sqlite "insert" statement
+    mozStorageStatementScoper scoper(mStmtInsert);
+
+    nsresult rv = bindCookieParameters(mStmtInsert, aCookie);
+    NS_ENSURE_SUCCESS(rv, PR_TRUE);
+
+    PRBool hasResult;
+    rv = mStmtInsert->ExecuteStep(&hasResult);
+    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "db insert failed!");
+  }
 
   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();
+    if (data->oldestID > iter.current->CreationID()) {
+      data->oldestID = iter.current->CreationID();
       data->iter = iter;
     }
   }
   return PL_DHASH_NEXT;
 }
 
 void
 nsCookieService::FindOldestCookie(nsEnumerationData &aData)
--- a/netwerk/cookie/src/nsCookieService.h
+++ b/netwerk/cookie/src/nsCookieService.h
@@ -45,33 +45,31 @@
 #include "nsICookieManager2.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 
 #include "nsCookie.h"
 #include "nsString.h"
 #include "nsTHashtable.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;
+class mozIStorageConnection;
+class mozIStorageStatement;
 
 // hash entry class
 class nsCookieEntry : public PLDHashEntryHdr
 {
   public:
     // Hash methods
     typedef const char* KeyType;
     typedef const char* KeyTypePointer;
@@ -163,59 +161,56 @@ class nsCookieService : public nsICookie
 
     nsCookieService();
     virtual ~nsCookieService();
     static nsCookieService*       GetSingleton();
     nsresult                      Init();
 
   protected:
     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
+    nsresult                      InitDB();
+    nsresult                      CreateTable();
+    nsresult                      ImportCookies();
     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);
+    PRBool                        SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, PRInt64 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                          AddInternal(nsCookie *aCookie, PRInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);
     void                          RemoveCookieFromList(nsListIter &aIter);
-    PRBool                        AddCookieToList(nsCookie *aCookie);
+    PRBool                        AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB = PR_TRUE);
     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                 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);
     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);
+    static PRBool                 GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime, nsCookieStatus aStatus);
     void                          RemoveAllFromMemory();
-    void                          RemoveExpiredCookies(nsInt64 aCurrentTime);
+    void                          RemoveExpiredCookies(PRInt64 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);
-
   protected:
     // cached members
-    nsCOMPtr<nsIFile>             mCookieFile;
+    nsCOMPtr<mozIStorageConnection> mDBConn;
+    nsCOMPtr<mozIStorageStatement> mStmtInsert;
+    nsCOMPtr<mozIStorageStatement> mStmtDelete;
     nsCOMPtr<nsIObserverService>  mObserverService;
     nsCOMPtr<nsICookieConsent>    mP3PService;
     nsCOMPtr<nsICookiePermission> mPermissionService;
 
     // impl members
-    nsCOMPtr<nsITimer>            mWriteTimer;
     nsTHashtable<nsCookieEntry>   mHostTable;
     PRUint32                      mCookieCount;
-    PRPackedBool                  mCookieChanged;
     PRPackedBool                  mCookieIconVisible;
 
     // cached prefs
     PRUint8                       mCookiesPermissions;   // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, P3P}
     PRUint16                      mMaxNumberOfCookies;
     PRUint16                      mMaxCookiesPerHost;
 
     // private static member, used to cache a ptr to nsCookieService,