Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "vm/DateTime.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <time.h> |
michael@0 | 10 | |
michael@0 | 11 | #include "jsutil.h" |
michael@0 | 12 | |
michael@0 | 13 | using mozilla::UnspecifiedNaN; |
michael@0 | 14 | |
michael@0 | 15 | static bool |
michael@0 | 16 | ComputeLocalTime(time_t local, struct tm *ptm) |
michael@0 | 17 | { |
michael@0 | 18 | #ifdef HAVE_LOCALTIME_R |
michael@0 | 19 | return localtime_r(&local, ptm); |
michael@0 | 20 | #else |
michael@0 | 21 | struct tm *otm = localtime(&local); |
michael@0 | 22 | if (!otm) |
michael@0 | 23 | return false; |
michael@0 | 24 | *ptm = *otm; |
michael@0 | 25 | return true; |
michael@0 | 26 | #endif |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | static bool |
michael@0 | 30 | ComputeUTCTime(time_t t, struct tm *ptm) |
michael@0 | 31 | { |
michael@0 | 32 | #ifdef HAVE_GMTIME_R |
michael@0 | 33 | return gmtime_r(&t, ptm); |
michael@0 | 34 | #else |
michael@0 | 35 | struct tm *otm = gmtime(&t); |
michael@0 | 36 | if (!otm) |
michael@0 | 37 | return false; |
michael@0 | 38 | *ptm = *otm; |
michael@0 | 39 | return true; |
michael@0 | 40 | #endif |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | /* |
michael@0 | 44 | * Compute the offset in seconds from the current UTC time to the current local |
michael@0 | 45 | * standard time (i.e. not including any offset due to DST). |
michael@0 | 46 | * |
michael@0 | 47 | * Examples: |
michael@0 | 48 | * |
michael@0 | 49 | * Suppose we are in California, USA on January 1, 2013 at 04:00 PST (UTC-8, no |
michael@0 | 50 | * DST in effect), corresponding to 12:00 UTC. This function would then return |
michael@0 | 51 | * -8 * SecondsPerHour, or -28800. |
michael@0 | 52 | * |
michael@0 | 53 | * Or suppose we are in Berlin, Germany on July 1, 2013 at 17:00 CEST (UTC+2, |
michael@0 | 54 | * DST in effect), corresponding to 15:00 UTC. This function would then return |
michael@0 | 55 | * +1 * SecondsPerHour, or +3600. |
michael@0 | 56 | */ |
michael@0 | 57 | static int32_t |
michael@0 | 58 | UTCToLocalStandardOffsetSeconds() |
michael@0 | 59 | { |
michael@0 | 60 | using js::SecondsPerDay; |
michael@0 | 61 | using js::SecondsPerHour; |
michael@0 | 62 | using js::SecondsPerMinute; |
michael@0 | 63 | |
michael@0 | 64 | #if defined(XP_WIN) |
michael@0 | 65 | // Windows doesn't follow POSIX: updates to the TZ environment variable are |
michael@0 | 66 | // not reflected immediately on that platform as they are on other systems |
michael@0 | 67 | // without this call. |
michael@0 | 68 | _tzset(); |
michael@0 | 69 | #endif |
michael@0 | 70 | |
michael@0 | 71 | // Get the current time. |
michael@0 | 72 | time_t currentMaybeWithDST = time(nullptr); |
michael@0 | 73 | if (currentMaybeWithDST == time_t(-1)) |
michael@0 | 74 | return 0; |
michael@0 | 75 | |
michael@0 | 76 | // Break down the current time into its (locally-valued, maybe with DST) |
michael@0 | 77 | // components. |
michael@0 | 78 | struct tm local; |
michael@0 | 79 | if (!ComputeLocalTime(currentMaybeWithDST, &local)) |
michael@0 | 80 | return 0; |
michael@0 | 81 | |
michael@0 | 82 | // Compute a |time_t| corresponding to |local| interpreted without DST. |
michael@0 | 83 | time_t currentNoDST; |
michael@0 | 84 | if (local.tm_isdst == 0) { |
michael@0 | 85 | // If |local| wasn't DST, we can use the same time. |
michael@0 | 86 | currentNoDST = currentMaybeWithDST; |
michael@0 | 87 | } else { |
michael@0 | 88 | // If |local| respected DST, we need a time broken down into components |
michael@0 | 89 | // ignoring DST. Turn off DST in the broken-down time. |
michael@0 | 90 | local.tm_isdst = 0; |
michael@0 | 91 | |
michael@0 | 92 | // Compute a |time_t t| corresponding to the broken-down time with DST |
michael@0 | 93 | // off. This has boundary-condition issues (for about the duration of |
michael@0 | 94 | // a DST offset) near the time a location moves to a different time |
michael@0 | 95 | // zone. But 1) errors will be transient; 2) locations rarely change |
michael@0 | 96 | // time zone; and 3) in the absence of an API that provides the time |
michael@0 | 97 | // zone offset directly, this may be the best we can do. |
michael@0 | 98 | currentNoDST = mktime(&local); |
michael@0 | 99 | if (currentNoDST == time_t(-1)) |
michael@0 | 100 | return 0; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | // Break down the time corresponding to the no-DST |local| into UTC-based |
michael@0 | 104 | // components. |
michael@0 | 105 | struct tm utc; |
michael@0 | 106 | if (!ComputeUTCTime(currentNoDST, &utc)) |
michael@0 | 107 | return 0; |
michael@0 | 108 | |
michael@0 | 109 | // Finally, compare the seconds-based components of the local non-DST |
michael@0 | 110 | // representation and the UTC representation to determine the actual |
michael@0 | 111 | // difference. |
michael@0 | 112 | int utc_secs = utc.tm_hour * SecondsPerHour + utc.tm_min * SecondsPerMinute; |
michael@0 | 113 | int local_secs = local.tm_hour * SecondsPerHour + local.tm_min * SecondsPerMinute; |
michael@0 | 114 | |
michael@0 | 115 | // Same-day? Just subtract the seconds counts. |
michael@0 | 116 | if (utc.tm_mday == local.tm_mday) |
michael@0 | 117 | return local_secs - utc_secs; |
michael@0 | 118 | |
michael@0 | 119 | // If we have more UTC seconds, move local seconds into the UTC seconds' |
michael@0 | 120 | // frame of reference and then subtract. |
michael@0 | 121 | if (utc_secs > local_secs) |
michael@0 | 122 | return (SecondsPerDay + local_secs) - utc_secs; |
michael@0 | 123 | |
michael@0 | 124 | // Otherwise we have more local seconds, so move the UTC seconds into the |
michael@0 | 125 | // local seconds' frame of reference and then subtract. |
michael@0 | 126 | return local_secs - (utc_secs + SecondsPerDay); |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | void |
michael@0 | 130 | js::DateTimeInfo::updateTimeZoneAdjustment() |
michael@0 | 131 | { |
michael@0 | 132 | /* |
michael@0 | 133 | * The difference between local standard time and UTC will never change for |
michael@0 | 134 | * a given time zone. |
michael@0 | 135 | */ |
michael@0 | 136 | utcToLocalStandardOffsetSeconds = UTCToLocalStandardOffsetSeconds(); |
michael@0 | 137 | |
michael@0 | 138 | double newTZA = utcToLocalStandardOffsetSeconds * msPerSecond; |
michael@0 | 139 | if (newTZA == localTZA_) |
michael@0 | 140 | return; |
michael@0 | 141 | |
michael@0 | 142 | localTZA_ = newTZA; |
michael@0 | 143 | |
michael@0 | 144 | /* |
michael@0 | 145 | * The initial range values are carefully chosen to result in a cache miss |
michael@0 | 146 | * on first use given the range of possible values. Be careful to keep |
michael@0 | 147 | * these values and the caching algorithm in sync! |
michael@0 | 148 | */ |
michael@0 | 149 | offsetMilliseconds = 0; |
michael@0 | 150 | rangeStartSeconds = rangeEndSeconds = INT64_MIN; |
michael@0 | 151 | oldOffsetMilliseconds = 0; |
michael@0 | 152 | oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN; |
michael@0 | 153 | |
michael@0 | 154 | sanityCheck(); |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | /* |
michael@0 | 158 | * Since getDSTOffsetMilliseconds guarantees that all times seen will be |
michael@0 | 159 | * positive, we can initialize the range at construction time with large |
michael@0 | 160 | * negative numbers to ensure the first computation is always a cache miss and |
michael@0 | 161 | * doesn't return a bogus offset. |
michael@0 | 162 | */ |
michael@0 | 163 | js::DateTimeInfo::DateTimeInfo() |
michael@0 | 164 | { |
michael@0 | 165 | // Set to a totally impossible TZA so that the comparison above will fail |
michael@0 | 166 | // and all fields will be properly initialized. |
michael@0 | 167 | localTZA_ = UnspecifiedNaN<double>(); |
michael@0 | 168 | updateTimeZoneAdjustment(); |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | int64_t |
michael@0 | 172 | js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds) |
michael@0 | 173 | { |
michael@0 | 174 | MOZ_ASSERT(utcSeconds >= 0); |
michael@0 | 175 | MOZ_ASSERT(utcSeconds <= MaxUnixTimeT); |
michael@0 | 176 | |
michael@0 | 177 | #if defined(XP_WIN) |
michael@0 | 178 | // Windows does not follow POSIX. Updates to the TZ environment variable |
michael@0 | 179 | // are not reflected immediately on that platform as they are on UNIX |
michael@0 | 180 | // systems without this call. |
michael@0 | 181 | _tzset(); |
michael@0 | 182 | #endif |
michael@0 | 183 | |
michael@0 | 184 | struct tm tm; |
michael@0 | 185 | if (!ComputeLocalTime(static_cast<time_t>(utcSeconds), &tm)) |
michael@0 | 186 | return 0; |
michael@0 | 187 | |
michael@0 | 188 | int32_t dayoff = int32_t((utcSeconds + utcToLocalStandardOffsetSeconds) % SecondsPerDay); |
michael@0 | 189 | int32_t tmoff = tm.tm_sec + (tm.tm_min * SecondsPerMinute) + (tm.tm_hour * SecondsPerHour); |
michael@0 | 190 | |
michael@0 | 191 | int32_t diff = tmoff - dayoff; |
michael@0 | 192 | |
michael@0 | 193 | if (diff < 0) |
michael@0 | 194 | diff += SecondsPerDay; |
michael@0 | 195 | |
michael@0 | 196 | return diff * msPerSecond; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | int64_t |
michael@0 | 200 | js::DateTimeInfo::getDSTOffsetMilliseconds(int64_t utcMilliseconds) |
michael@0 | 201 | { |
michael@0 | 202 | sanityCheck(); |
michael@0 | 203 | |
michael@0 | 204 | int64_t utcSeconds = utcMilliseconds / msPerSecond; |
michael@0 | 205 | |
michael@0 | 206 | if (utcSeconds > MaxUnixTimeT) { |
michael@0 | 207 | utcSeconds = MaxUnixTimeT; |
michael@0 | 208 | } else if (utcSeconds < 0) { |
michael@0 | 209 | /* Go ahead a day to make localtime work (does not work with 0). */ |
michael@0 | 210 | utcSeconds = SecondsPerDay; |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | /* |
michael@0 | 214 | * NB: Be aware of the initial range values when making changes to this |
michael@0 | 215 | * code: the first call to this method, with those initial range |
michael@0 | 216 | * values, must result in a cache miss. |
michael@0 | 217 | */ |
michael@0 | 218 | |
michael@0 | 219 | if (rangeStartSeconds <= utcSeconds && utcSeconds <= rangeEndSeconds) |
michael@0 | 220 | return offsetMilliseconds; |
michael@0 | 221 | |
michael@0 | 222 | if (oldRangeStartSeconds <= utcSeconds && utcSeconds <= oldRangeEndSeconds) |
michael@0 | 223 | return oldOffsetMilliseconds; |
michael@0 | 224 | |
michael@0 | 225 | oldOffsetMilliseconds = offsetMilliseconds; |
michael@0 | 226 | oldRangeStartSeconds = rangeStartSeconds; |
michael@0 | 227 | oldRangeEndSeconds = rangeEndSeconds; |
michael@0 | 228 | |
michael@0 | 229 | if (rangeStartSeconds <= utcSeconds) { |
michael@0 | 230 | int64_t newEndSeconds = Min(rangeEndSeconds + RangeExpansionAmount, MaxUnixTimeT); |
michael@0 | 231 | if (newEndSeconds >= utcSeconds) { |
michael@0 | 232 | int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds); |
michael@0 | 233 | if (endOffsetMilliseconds == offsetMilliseconds) { |
michael@0 | 234 | rangeEndSeconds = newEndSeconds; |
michael@0 | 235 | return offsetMilliseconds; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | offsetMilliseconds = computeDSTOffsetMilliseconds(utcSeconds); |
michael@0 | 239 | if (offsetMilliseconds == endOffsetMilliseconds) { |
michael@0 | 240 | rangeStartSeconds = utcSeconds; |
michael@0 | 241 | rangeEndSeconds = newEndSeconds; |
michael@0 | 242 | } else { |
michael@0 | 243 | rangeEndSeconds = utcSeconds; |
michael@0 | 244 | } |
michael@0 | 245 | return offsetMilliseconds; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | offsetMilliseconds = computeDSTOffsetMilliseconds(utcSeconds); |
michael@0 | 249 | rangeStartSeconds = rangeEndSeconds = utcSeconds; |
michael@0 | 250 | return offsetMilliseconds; |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | int64_t newStartSeconds = Max<int64_t>(rangeStartSeconds - RangeExpansionAmount, 0); |
michael@0 | 254 | if (newStartSeconds <= utcSeconds) { |
michael@0 | 255 | int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds); |
michael@0 | 256 | if (startOffsetMilliseconds == offsetMilliseconds) { |
michael@0 | 257 | rangeStartSeconds = newStartSeconds; |
michael@0 | 258 | return offsetMilliseconds; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | offsetMilliseconds = computeDSTOffsetMilliseconds(utcSeconds); |
michael@0 | 262 | if (offsetMilliseconds == startOffsetMilliseconds) { |
michael@0 | 263 | rangeStartSeconds = newStartSeconds; |
michael@0 | 264 | rangeEndSeconds = utcSeconds; |
michael@0 | 265 | } else { |
michael@0 | 266 | rangeStartSeconds = utcSeconds; |
michael@0 | 267 | } |
michael@0 | 268 | return offsetMilliseconds; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | rangeStartSeconds = rangeEndSeconds = utcSeconds; |
michael@0 | 272 | offsetMilliseconds = computeDSTOffsetMilliseconds(utcSeconds); |
michael@0 | 273 | return offsetMilliseconds; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | void |
michael@0 | 277 | js::DateTimeInfo::sanityCheck() |
michael@0 | 278 | { |
michael@0 | 279 | MOZ_ASSERT(rangeStartSeconds <= rangeEndSeconds); |
michael@0 | 280 | MOZ_ASSERT_IF(rangeStartSeconds == INT64_MIN, rangeEndSeconds == INT64_MIN); |
michael@0 | 281 | MOZ_ASSERT_IF(rangeEndSeconds == INT64_MIN, rangeStartSeconds == INT64_MIN); |
michael@0 | 282 | MOZ_ASSERT_IF(rangeStartSeconds != INT64_MIN, |
michael@0 | 283 | rangeStartSeconds >= 0 && rangeEndSeconds >= 0); |
michael@0 | 284 | MOZ_ASSERT_IF(rangeStartSeconds != INT64_MIN, |
michael@0 | 285 | rangeStartSeconds <= MaxUnixTimeT && rangeEndSeconds <= MaxUnixTimeT); |
michael@0 | 286 | } |