js/src/vm/DateTime.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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.

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

mercurial