1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CookieStorage.h"
8 #include "mozilla/Encoding.h"
9 #include "mozilla/dom/ToJSValue.h"
10 #include "mozilla/glean/GleanMetrics.h"
11 #include "mozilla/StaticPrefs_network.h"
12 #include "nsIURLParser.h"
13 #include "nsURLHelper.h"
19 /******************************************************************************
22 ******************************************************************************/
24 // This is a counter that keeps track of the last used creation time, each time
25 // we create a new Cookie. This is nominally the time (in microseconds) the
26 // cookie was created, but is guaranteed to be monotonically increasing for
27 // cookies added at runtime after the database has been read in. This is
28 // necessary to enforce ordering among cookies whose creation times would
29 // otherwise overlap, since it's possible two cookies may be created at the
30 // same time, or that the system clock isn't monotonic.
31 static int64_t gLastCreationTime
;
33 int64_t Cookie::GenerateUniqueCreationTime(int64_t aCreationTime
) {
34 // Check if the creation time given to us is greater than the running maximum
35 // (it should always be monotonically increasing).
36 if (aCreationTime
> gLastCreationTime
) {
37 gLastCreationTime
= aCreationTime
;
42 return ++gLastCreationTime
;
45 already_AddRefed
<Cookie
> Cookie::Create(
46 const CookieStruct
& aCookieData
,
47 const OriginAttributes
& aOriginAttributes
) {
48 RefPtr
<Cookie
> cookie
=
49 Cookie::FromCookieStruct(aCookieData
, aOriginAttributes
);
51 // If the creationTime given to us is higher than the running maximum,
52 // update our maximum.
53 if (cookie
->mData
.creationTime() > gLastCreationTime
) {
54 gLastCreationTime
= cookie
->mData
.creationTime();
57 return cookie
.forget();
60 already_AddRefed
<Cookie
> Cookie::FromCookieStruct(
61 const CookieStruct
& aCookieData
,
62 const OriginAttributes
& aOriginAttributes
) {
63 RefPtr
<Cookie
> cookie
= new Cookie(aCookieData
, aOriginAttributes
);
65 // Ensure mValue contains a valid UTF-8 sequence. Otherwise XPConnect will
66 // truncate the string after the first invalid octet.
67 UTF_8_ENCODING
->DecodeWithoutBOMHandling(aCookieData
.value(),
68 cookie
->mData
.value());
70 // If sameSite/rawSameSite values aren't sensible reset to Default
71 // cf. 5.4.7 in draft-ietf-httpbis-rfc6265bis-09
72 if (!Cookie::ValidateSameSite(cookie
->mData
)) {
73 cookie
->mData
.sameSite() = nsICookie::SAMESITE_LAX
;
74 cookie
->mData
.rawSameSite() = nsICookie::SAMESITE_NONE
;
77 return cookie
.forget();
80 already_AddRefed
<Cookie
> Cookie::CreateValidated(
81 const CookieStruct
& aCookieData
,
82 const OriginAttributes
& aOriginAttributes
) {
83 if (!StaticPrefs::network_cookie_fixup_on_db_load()) {
84 return Cookie::Create(aCookieData
, aOriginAttributes
);
87 RefPtr
<Cookie
> cookie
=
88 Cookie::FromCookieStruct(aCookieData
, aOriginAttributes
);
90 int64_t currentTimeInUsec
= PR_Now();
91 // Assert that the last creation time is not higher than the current time.
92 // The 10000 wiggle room accounts for the fact that calling
93 // GenerateUniqueCreationTime might go over the value of PR_Now(), but we'd
94 // most likely not add 10000 cookies in a row.
95 MOZ_ASSERT(gLastCreationTime
< currentTimeInUsec
+ 10000,
96 "Last creation time must not be higher than NOW");
98 // If the creationTime given to us is higher than the current time then
99 // update the creation time to now.
100 if (cookie
->mData
.creationTime() > currentTimeInUsec
) {
101 uint64_t diffInSeconds
=
102 (cookie
->mData
.creationTime() - currentTimeInUsec
) / PR_USEC_PER_SEC
;
103 mozilla::glean::networking::cookie_creation_fixup_diff
.AccumulateSamples(
105 glean::networking::cookie_timestamp_fixed_count
.Get("creationTime"_ns
)
108 cookie
->mData
.creationTime() =
109 GenerateUniqueCreationTime(currentTimeInUsec
);
112 if (cookie
->mData
.lastAccessed() > currentTimeInUsec
) {
113 uint64_t diffInSeconds
=
114 (cookie
->mData
.lastAccessed() - currentTimeInUsec
) / PR_USEC_PER_SEC
;
115 mozilla::glean::networking::cookie_access_fixup_diff
.AccumulateSamples(
117 glean::networking::cookie_timestamp_fixed_count
.Get("lastAccessed"_ns
)
120 cookie
->mData
.lastAccessed() = currentTimeInUsec
;
123 return cookie
.forget();
126 size_t Cookie::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const {
127 return aMallocSizeOf(this) +
128 mData
.name().SizeOfExcludingThisIfUnshared(MallocSizeOf
) +
129 mData
.value().SizeOfExcludingThisIfUnshared(MallocSizeOf
) +
130 mData
.host().SizeOfExcludingThisIfUnshared(MallocSizeOf
) +
131 mData
.path().SizeOfExcludingThisIfUnshared(MallocSizeOf
) +
132 mFilePathCache
.SizeOfExcludingThisIfUnshared(MallocSizeOf
);
135 bool Cookie::IsStale() const {
136 int64_t currentTimeInUsec
= PR_Now();
138 return currentTimeInUsec
- LastAccessed() >
139 StaticPrefs::network_cookie_staleThreshold() * PR_USEC_PER_SEC
;
142 /******************************************************************************
145 ******************************************************************************/
148 NS_IMETHODIMP
Cookie::GetName(nsACString
& aName
) {
152 NS_IMETHODIMP
Cookie::GetValue(nsACString
& aValue
) {
156 NS_IMETHODIMP
Cookie::GetHost(nsACString
& aHost
) {
160 NS_IMETHODIMP
Cookie::GetRawHost(nsACString
& aHost
) {
164 NS_IMETHODIMP
Cookie::GetPath(nsACString
& aPath
) {
168 NS_IMETHODIMP
Cookie::GetExpiry(int64_t* aExpiry
) {
172 NS_IMETHODIMP
Cookie::GetIsSession(bool* aIsSession
) {
173 *aIsSession
= IsSession();
176 NS_IMETHODIMP
Cookie::GetIsDomain(bool* aIsDomain
) {
177 *aIsDomain
= IsDomain();
180 NS_IMETHODIMP
Cookie::GetIsSecure(bool* aIsSecure
) {
181 *aIsSecure
= IsSecure();
184 NS_IMETHODIMP
Cookie::GetIsHttpOnly(bool* aHttpOnly
) {
185 *aHttpOnly
= IsHttpOnly();
188 NS_IMETHODIMP
Cookie::GetCreationTime(int64_t* aCreation
) {
189 *aCreation
= CreationTime();
192 NS_IMETHODIMP
Cookie::GetLastAccessed(int64_t* aTime
) {
193 *aTime
= LastAccessed();
196 NS_IMETHODIMP
Cookie::GetSameSite(int32_t* aSameSite
) {
197 if (StaticPrefs::network_cookie_sameSite_laxByDefault()) {
198 *aSameSite
= SameSite();
200 *aSameSite
= RawSameSite();
204 NS_IMETHODIMP
Cookie::GetSchemeMap(nsICookie::schemeType
* aSchemeMap
) {
205 *aSchemeMap
= static_cast<nsICookie::schemeType
>(SchemeMap());
210 Cookie::GetOriginAttributes(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aVal
) {
211 if (NS_WARN_IF(!ToJSValue(aCx
, mOriginAttributes
, aVal
))) {
212 return NS_ERROR_FAILURE
;
217 const Cookie
& Cookie::AsCookie() { return *this; }
219 const nsCString
& Cookie::GetFilePath() {
220 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
222 if (Path().IsEmpty()) {
223 // If we don't have a path, just return the (empty) file path cache.
224 return mFilePathCache
;
226 if (!mFilePathCache
.IsEmpty()) {
227 // If we've computed the answer before, just return it.
228 return mFilePathCache
;
231 nsIURLParser
* parser
= net_GetStdURLParser();
232 NS_ENSURE_TRUE(parser
, mFilePathCache
);
234 int32_t pathLen
= Path().Length();
235 int32_t filepathLen
= 0;
236 uint32_t filepathPos
= 0;
238 nsresult rv
= parser
->ParsePath(PromiseFlatCString(Path()).get(), pathLen
,
239 &filepathPos
, &filepathLen
, nullptr,
240 nullptr, // don't care about query
241 nullptr, nullptr); // don't care about ref
242 NS_ENSURE_SUCCESS(rv
, mFilePathCache
);
244 mFilePathCache
= Substring(Path(), filepathPos
, filepathLen
);
246 return mFilePathCache
;
249 // compatibility method, for use with the legacy nsICookie interface.
250 // here, expires == 0 denotes a session cookie.
252 Cookie::GetExpires(uint64_t* aExpires
) {
256 *aExpires
= Expiry() > 0 ? Expiry() : 1;
262 bool Cookie::ValidateSameSite(const CookieStruct
& aCookieData
) {
263 // For proper migration towards a laxByDefault world,
264 // sameSite is initialized to LAX even though the server
265 // has never sent it.
266 if (aCookieData
.rawSameSite() == aCookieData
.sameSite()) {
267 return aCookieData
.rawSameSite() >= nsICookie::SAMESITE_NONE
&&
268 aCookieData
.rawSameSite() <= nsICookie::SAMESITE_STRICT
;
270 return aCookieData
.rawSameSite() == nsICookie::SAMESITE_NONE
&&
271 aCookieData
.sameSite() == nsICookie::SAMESITE_LAX
;
274 already_AddRefed
<Cookie
> Cookie::Clone() const {
275 return Create(mData
, OriginAttributesRef());
278 NS_IMPL_ISUPPORTS(Cookie
, nsICookie
)
281 } // namespace mozilla