tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimeZone.h (12671B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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/. */
      6 
      7 #ifndef builtin_temporal_TimeZone_h
      8 #define builtin_temporal_TimeZone_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/Attributes.h"
     12 #include "mozilla/Maybe.h"
     13 
     14 #include <array>
     15 #include <stddef.h>
     16 #include <stdint.h>
     17 
     18 #include "builtin/temporal/TemporalTypes.h"
     19 #include "js/RootingAPI.h"
     20 #include "js/TypeDecls.h"
     21 #include "js/Value.h"
     22 #include "vm/JSObject.h"
     23 #include "vm/NativeObject.h"
     24 #include "vm/StringType.h"
     25 
     26 class JS_PUBLIC_API JSTracer;
     27 struct JSClassOps;
     28 
     29 namespace mozilla::intl {
     30 class TimeZone;
     31 }
     32 
     33 namespace js::temporal {
     34 
     35 class TimeZoneObject : public NativeObject {
     36 public:
     37  static const JSClass class_;
     38 
     39  static constexpr uint32_t IDENTIFIER_SLOT = 0;
     40  static constexpr uint32_t PRIMARY_IDENTIFIER_SLOT = 1;
     41  static constexpr uint32_t OFFSET_MINUTES_SLOT = 2;
     42  static constexpr uint32_t INTL_TIMEZONE_SLOT = 3;
     43  static constexpr uint32_t SLOT_COUNT = 4;
     44 
     45  // Estimated memory use for intl::TimeZone (see IcuMemoryUsage).
     46  static constexpr size_t EstimatedMemoryUse = 6840;
     47 
     48  bool isOffset() const { return getFixedSlot(OFFSET_MINUTES_SLOT).isInt32(); }
     49 
     50  JSLinearString* identifier() const {
     51    return &getFixedSlot(IDENTIFIER_SLOT).toString()->asLinear();
     52  }
     53 
     54  JSLinearString* primaryIdentifier() const {
     55    MOZ_ASSERT(!isOffset());
     56    return &getFixedSlot(PRIMARY_IDENTIFIER_SLOT).toString()->asLinear();
     57  }
     58 
     59  int32_t offsetMinutes() const {
     60    MOZ_ASSERT(isOffset());
     61    return getFixedSlot(OFFSET_MINUTES_SLOT).toInt32();
     62  }
     63 
     64  mozilla::intl::TimeZone* getTimeZone() const {
     65    const auto& slot = getFixedSlot(INTL_TIMEZONE_SLOT);
     66    if (slot.isUndefined()) {
     67      return nullptr;
     68    }
     69    return static_cast<mozilla::intl::TimeZone*>(slot.toPrivate());
     70  }
     71 
     72  void setTimeZone(mozilla::intl::TimeZone* timeZone) {
     73    setFixedSlot(INTL_TIMEZONE_SLOT, JS::PrivateValue(timeZone));
     74  }
     75 
     76 private:
     77  static const JSClassOps classOps_;
     78 
     79  static void finalize(JS::GCContext* gcx, JSObject* obj);
     80 };
     81 
     82 } /* namespace js::temporal */
     83 
     84 namespace js::temporal {
     85 
     86 /**
     87 * Temporal time zones are either available named time zones or offset time
     88 * zones.
     89 *
     90 * The identifier of an available named time zones is an available named
     91 * time zone identifier, which is either a primary time zone identifier or a
     92 * non-primary time zone identifier.
     93 *
     94 * The identifier of an offset time zone is an offset time zone identifier.
     95 *
     96 * Temporal methods always return the normalized format of a time zone
     97 * identifier. Available named time zone identifier are always in normalized
     98 * format.
     99 *
    100 * Examples of valid available time zone identifiers in normalized format:
    101 * - "UTC" (primary identifier)
    102 * - "Etc/UTC" (non-primary identifier)
    103 * - "America/New_York" (primary identifier)
    104 * - "+00:00"
    105 *
    106 * Examples of valid available time zone identifiers in non-normalized format:
    107 * - "+00"
    108 * - "-00:00"
    109 *
    110 * Examples of invalid available time zone identifiers:
    111 * - "utc" (wrong case)
    112 * - "+00:00:00" (sub-minute precision)
    113 * - "+00:00:01" (sub-minute precision)
    114 *
    115 * The following two implementation approaches are possible:
    116 *
    117 * 1. Represent time zones as JSStrings. Additionally keep a mapping from
    118 *    JSString to `mozilla::intl::TimeZone` to avoid repeatedly creating new
    119 *    `mozilla::intl::TimeZone` for time zone operations. Offset string time
    120 *    zones have to be special cased, because they don't use
    121 *    `mozilla::intl::TimeZone`. Either detect offset strings by checking the
    122 *    time zone identifier or store offset strings as the offset in minutes
    123 *    value to avoid reparsing the offset string again and again.
    124 * 2. Represent time zones as objects which hold `mozilla::intl::TimeZone` in
    125 *    an internal slot.
    126 *
    127 * Option 2 is a bit easier to implement, so we use this approach for now.
    128 */
    129 class MOZ_STACK_CLASS TimeZoneValue final {
    130  TimeZoneObject* object_ = nullptr;
    131 
    132 public:
    133  /**
    134   * Default initialize this TimeZoneValue.
    135   */
    136  TimeZoneValue() = default;
    137 
    138  /**
    139   * Initialize this TimeZoneValue with a time zone object.
    140   */
    141  explicit TimeZoneValue(TimeZoneObject* timeZone) : object_(timeZone) {
    142    MOZ_ASSERT(object_);
    143  }
    144 
    145  /**
    146   * Initialize this TimeZoneValue from a slot Value.
    147   */
    148  explicit TimeZoneValue(const JS::Value& value)
    149      : object_(&value.toObject().as<TimeZoneObject>()) {}
    150 
    151  /**
    152   * Return true if this TimeZoneValue is not null.
    153   */
    154  explicit operator bool() const { return !!object_; }
    155 
    156  /**
    157   * Return true if this TimeZoneValue is an offset time zone.
    158   */
    159  bool isOffset() const {
    160    MOZ_ASSERT(object_);
    161    return object_->isOffset();
    162  }
    163 
    164  /**
    165   * Return the offset of an offset time zone.
    166   */
    167  auto offsetMinutes() const {
    168    MOZ_ASSERT(object_);
    169    return object_->offsetMinutes();
    170  }
    171 
    172  /**
    173   * Return the time zone identifier.
    174   */
    175  auto* identifier() const {
    176    MOZ_ASSERT(object_);
    177    return object_->identifier();
    178  }
    179 
    180  /**
    181   * Return the primary time zone identifier of a named time zone.
    182   */
    183  auto* primaryIdentifier() const {
    184    MOZ_ASSERT(object_);
    185    return object_->primaryIdentifier();
    186  }
    187 
    188  /**
    189   * Return the time zone implementation.
    190   */
    191  auto* getTimeZone() const {
    192    MOZ_ASSERT(object_);
    193    return object_->getTimeZone();
    194  }
    195 
    196  /**
    197   * Return the underlying TimeZoneObject.
    198   */
    199  auto* toTimeZoneObject() const {
    200    MOZ_ASSERT(object_);
    201    return object_;
    202  }
    203 
    204  /**
    205   * Return the slot Value representation of this TimeZoneValue.
    206   */
    207  JS::Value toSlotValue() const {
    208    MOZ_ASSERT(object_);
    209    return JS::ObjectValue(*object_);
    210  }
    211 
    212  // Helper methods for (Mutable)WrappedPtrOperations.
    213  auto address() { return &object_; }
    214  auto address() const { return &object_; }
    215 
    216  // Trace implementation.
    217  void trace(JSTracer* trc);
    218 };
    219 
    220 class PossibleEpochNanoseconds final {
    221  // GetPossibleEpochNanoseconds can return up-to two elements.
    222  static constexpr size_t MaxLength = 2;
    223 
    224  std::array<EpochNanoseconds, MaxLength> array_ = {};
    225  size_t length_ = 0;
    226 
    227  void append(const EpochNanoseconds& epochNs) { array_[length_++] = epochNs; }
    228 
    229 public:
    230  PossibleEpochNanoseconds() = default;
    231 
    232  explicit PossibleEpochNanoseconds(const EpochNanoseconds& epochNs) {
    233    append(epochNs);
    234  }
    235 
    236  explicit PossibleEpochNanoseconds(const EpochNanoseconds& earlier,
    237                                    const EpochNanoseconds& later) {
    238    MOZ_ASSERT(earlier <= later);
    239    append(earlier);
    240    append(later);
    241  }
    242 
    243  size_t length() const { return length_; }
    244  bool empty() const { return length_ == 0; }
    245 
    246  const auto& operator[](size_t i) const { return array_[i]; }
    247 
    248  auto begin() const { return array_.begin(); }
    249  auto end() const { return array_.begin() + length_; }
    250 
    251  const auto& front() const {
    252    MOZ_ASSERT(length_ > 0);
    253    return array_[0];
    254  }
    255  const auto& back() const {
    256    MOZ_ASSERT(length_ > 0);
    257    return array_[length_ - 1];
    258  }
    259 };
    260 
    261 struct ParsedTimeZone;
    262 enum class TemporalDisambiguation;
    263 
    264 /**
    265 * Create a new |TimeZoneObject| whose identifier is |identifier| and whose
    266 * primary identifier is |primaryIdentifier|.
    267 */
    268 TimeZoneObject* CreateTimeZoneObject(
    269    JSContext* cx, JS::Handle<JSLinearString*> identifier,
    270    JS::Handle<JSLinearString*> primaryIdentifier);
    271 
    272 /**
    273 * SystemTimeZoneIdentifier ( )
    274 */
    275 JSLinearString* ComputeSystemTimeZoneIdentifier(JSContext* cx);
    276 
    277 /**
    278 * SystemTimeZoneIdentifier ( )
    279 */
    280 JSLinearString* SystemTimeZoneIdentifier(JSContext* cx);
    281 
    282 /**
    283 * SystemTimeZoneIdentifier ( )
    284 */
    285 bool SystemTimeZone(JSContext* cx, JS::MutableHandle<TimeZoneValue> result);
    286 
    287 /**
    288 * ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike )
    289 */
    290 bool ToTemporalTimeZone(JSContext* cx,
    291                        JS::Handle<JS::Value> temporalTimeZoneLike,
    292                        JS::MutableHandle<TimeZoneValue> result);
    293 
    294 /**
    295 * ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike )
    296 */
    297 bool ToTemporalTimeZone(JSContext* cx, JS::Handle<ParsedTimeZone> string,
    298                        JS::MutableHandle<TimeZoneValue> result);
    299 
    300 /**
    301 * Verifies that the given string is a valid time zone name. If it is a valid
    302 * time zone name, returns the canonicalized time zone name. Canonicalization
    303 * resolves link names to their target time zones.
    304 */
    305 JSLinearString* ToValidCanonicalTimeZoneIdentifier(
    306    JSContext* cx, JS::Handle<JSString*> timeZone);
    307 
    308 /**
    309 * TimeZoneEquals ( one, two )
    310 */
    311 bool TimeZoneEquals(const TimeZoneValue& one, const TimeZoneValue& two);
    312 
    313 /**
    314 * GetISODateTimeFor ( timeZone, epochNs )
    315 */
    316 ISODateTime GetISODateTimeFor(const EpochNanoseconds& epochNs,
    317                              int64_t offsetNanoseconds);
    318 
    319 /**
    320 * GetISODateTimeFor ( timeZone, epochNs )
    321 */
    322 bool GetISODateTimeFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
    323                       const EpochNanoseconds& epochNs, ISODateTime* result);
    324 
    325 /**
    326 * GetEpochNanosecondsFor ( timeZone, isoDateTime, disambiguation )
    327 */
    328 bool GetEpochNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
    329                            const ISODateTime& isoDateTime,
    330                            TemporalDisambiguation disambiguation,
    331                            EpochNanoseconds* result);
    332 
    333 /**
    334 * GetOffsetNanosecondsFor ( timeZone, epochNs )
    335 */
    336 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
    337                             const EpochNanoseconds& epochNs,
    338                             int64_t* offsetNanoseconds);
    339 
    340 /**
    341 * GetPossibleEpochNanoseconds ( timeZone, isoDateTime )
    342 */
    343 bool GetPossibleEpochNanoseconds(JSContext* cx,
    344                                 JS::Handle<TimeZoneValue> timeZone,
    345                                 const ISODateTime& isoDateTime,
    346                                 PossibleEpochNanoseconds* result);
    347 
    348 /**
    349 * DisambiguatePossibleEpochNanoseconds ( possibleEpochNs, timeZone,
    350 * isoDateTime, disambiguation )
    351 */
    352 bool DisambiguatePossibleEpochNanoseconds(
    353    JSContext* cx, const PossibleEpochNanoseconds& possibleEpochNs,
    354    JS::Handle<TimeZoneValue> timeZone, const ISODateTime& isoDateTime,
    355    TemporalDisambiguation disambiguation, EpochNanoseconds* result);
    356 
    357 /**
    358 * GetNamedTimeZoneNextTransition ( timeZoneIdentifier, epochNanoseconds )
    359 */
    360 bool GetNamedTimeZoneNextTransition(JSContext* cx,
    361                                    JS::Handle<TimeZoneValue> timeZone,
    362                                    const EpochNanoseconds& epochNanoseconds,
    363                                    mozilla::Maybe<EpochNanoseconds>* result);
    364 
    365 /**
    366 * GetNamedTimeZonePreviousTransition ( timeZoneIdentifier, epochNanoseconds )
    367 */
    368 bool GetNamedTimeZonePreviousTransition(
    369    JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
    370    const EpochNanoseconds& epochNanoseconds,
    371    mozilla::Maybe<EpochNanoseconds>* result);
    372 
    373 /**
    374 * GetStartOfDay ( timeZone, isoDate )
    375 */
    376 bool GetStartOfDay(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
    377                   const ISODate& isoDate, EpochNanoseconds* result);
    378 
    379 // Helper for MutableWrappedPtrOperations.
    380 bool WrapTimeZoneValueObject(JSContext* cx,
    381                             JS::MutableHandle<TimeZoneObject*> timeZone);
    382 
    383 } /* namespace js::temporal */
    384 
    385 namespace js {
    386 
    387 template <typename Wrapper>
    388 class WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> {
    389  const auto& container() const {
    390    return static_cast<const Wrapper*>(this)->get();
    391  }
    392 
    393 public:
    394  explicit operator bool() const { return !!container(); }
    395 
    396  bool isOffset() const { return container().isOffset(); }
    397 
    398  auto offsetMinutes() const { return container().offsetMinutes(); }
    399 
    400  auto* identifier() const { return container().identifier(); }
    401 
    402  auto* primaryIdentifier() const { return container().primaryIdentifier(); }
    403 
    404  auto* getTimeZone() const { return container().getTimeZone(); }
    405 
    406  JS::Value toSlotValue() const { return container().toSlotValue(); }
    407 };
    408 
    409 template <typename Wrapper>
    410 class MutableWrappedPtrOperations<temporal::TimeZoneValue, Wrapper>
    411    : public WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> {
    412  auto& container() { return static_cast<Wrapper*>(this)->get(); }
    413 
    414 public:
    415  /**
    416   * Wrap the time zone value into the current compartment.
    417   */
    418  bool wrap(JSContext* cx) {
    419    MOZ_ASSERT(container());
    420    auto mh = JS::MutableHandle<temporal::TimeZoneObject*>::fromMarkedLocation(
    421        container().address());
    422    return temporal::WrapTimeZoneValueObject(cx, mh);
    423  }
    424 };
    425 
    426 } /* namespace js */
    427 
    428 #endif /* builtin_temporal_TimeZone_h */