tor-browser

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

WindowFeatures.cpp (8242B)


      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 #include "WindowFeatures.h"
      8 
      9 #include "nsContentUtils.h"        // nsContentUtils
     10 #include "nsDependentSubstring.h"  // Substring
     11 #include "nsINode.h"               // IsSpaceCharacter
     12 #include "nsReadableUtils.h"       // ToLowerCase
     13 
     14 using mozilla::dom::IsSpaceCharacter;
     15 using mozilla::dom::WindowFeatures;
     16 
     17 #ifdef DEBUG
     18 /* static */
     19 bool WindowFeatures::IsLowerCase(const char* text) {
     20  nsAutoCString before(text);
     21  nsAutoCString after;
     22  ToLowerCase(before, after);
     23  return before == after;
     24 }
     25 #endif
     26 
     27 static bool IsFeatureSeparator(char aChar) {
     28  // https://html.spec.whatwg.org/multipage/window-object.html#feature-separator
     29  // A code point is a feature separator if it is ASCII whitespace, U+003D (=),
     30  // or U+002C (,).
     31  return IsSpaceCharacter(aChar) || aChar == '=' || aChar == ',';
     32 }
     33 
     34 template <class IterT, class CondT>
     35 void AdvanceWhile(IterT& aPosition, const IterT& aEnd, CondT aCondition) {
     36  // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
     37  //
     38  // Step 2. While `position` doesn’t point past the end of `input` and the
     39  // code point at `position` within `input` meets the condition condition:
     40  while (aCondition(*aPosition) && aPosition < aEnd) {
     41    // Step 2.1. Append that code point to the end of `result`.
     42    // (done by caller)
     43 
     44    // Step 2.2. Advance `position` by 1.
     45    ++aPosition;
     46  }
     47 }
     48 
     49 template <class IterT, class CondT>
     50 nsTDependentSubstring<char> CollectSequence(IterT& aPosition, const IterT& aEnd,
     51                                            CondT aCondition) {
     52  // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
     53  // To collect a sequence of code points meeting a condition `condition` from
     54  // a string `input`, given a position variable `position` tracking the
     55  // position of the calling algorithm within `input`:
     56 
     57  // Step 1. Let `result` be the empty string.
     58  auto start = aPosition;
     59 
     60  // Step 2.
     61  AdvanceWhile(aPosition, aEnd, aCondition);
     62 
     63  // Step 3. Return `result`.
     64  return Substring(start, aPosition);
     65 }
     66 
     67 static void NormalizeName(nsAutoCString& aName) {
     68  // https://html.spec.whatwg.org/multipage/window-object.html#normalizing-the-feature-name
     69  if (aName == "screenx") {
     70    aName = "left";
     71  } else if (aName == "screeny") {
     72    aName = "top";
     73  } else if (aName == "innerwidth") {
     74    aName = "width";
     75  } else if (aName == "innerheight") {
     76    aName = "height";
     77  }
     78 }
     79 
     80 /* static */
     81 int32_t WindowFeatures::ParseIntegerWithFallback(const nsCString& aValue) {
     82  // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
     83  //
     84  // Step 3. Let `parsed` be the result of parsing value as an integer.
     85  nsContentUtils::ParseHTMLIntegerResultFlags parseResult;
     86  int32_t parsed = nsContentUtils::ParseHTMLInteger(aValue, &parseResult);
     87 
     88  // Step 4. If `parsed` is an error, then set it to 0.
     89  //
     90  // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
     91  //
     92  // eParseHTMLInteger_NonStandard is not tested given:
     93  //   * Step 4 allows leading whitespaces
     94  //   * Step 6 allows a plus sign
     95  //   * Step 8 does not disallow leading zeros
     96  //   * Steps 6 and 9 allow `-0`
     97  //
     98  // eParseHTMLInteger_DidNotConsumeAllInput is not tested given:
     99  //   * Step 8 collects digits and ignores remaining part
    100  //
    101  if (parseResult & nsContentUtils::eParseHTMLInteger_Error) {
    102    parsed = 0;
    103  }
    104 
    105  return parsed;
    106 }
    107 
    108 /* static */
    109 bool WindowFeatures::ParseBool(const nsCString& aValue) {
    110  // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
    111  // To parse a boolean feature given a string `value`:
    112 
    113  // Step 1. If `value` is the empty string, then return true.
    114  if (aValue.IsEmpty()) {
    115    return true;
    116  }
    117 
    118  // Step 2. If `value` is "yes", then return true.
    119  if (aValue == "yes") {
    120    return true;
    121  }
    122 
    123  // Step 3. If `value` is "true", then return
    124  if (aValue == "true") {
    125    return true;
    126  }
    127 
    128  // Steps 4-5.
    129  int32_t parsed = ParseIntegerWithFallback(aValue);
    130 
    131  // Step 6. Return false if `parsed` is 0, and true otherwise.
    132  return parsed != 0;
    133 }
    134 
    135 bool WindowFeatures::Tokenize(const nsACString& aFeatures) {
    136  // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-tokenize
    137  // To tokenize the `features` argument:
    138 
    139  // Step 1. Let `tokenizedFeatures` be a new ordered map.
    140  // (implicit)
    141 
    142  // Step 2. Let `position` point at the first code point of features.
    143  auto position = aFeatures.BeginReading();
    144 
    145  // Step 3. While `position` is not past the end of `features`:
    146  auto end = aFeatures.EndReading();
    147  while (position < end) {
    148    // Step 3.1. Let `name` be the empty string.
    149    // (implicit)
    150 
    151    // Step 3.2. Let `value` be the empty string.
    152    nsAutoCString value;
    153 
    154    // Step 3.3. Collect a sequence of code points that are feature separators
    155    // from `features` given `position`. This skips past leading separators
    156    // before the name.
    157    //
    158    // NOTE: Do not collect given this value is unused.
    159    AdvanceWhile(position, end, IsFeatureSeparator);
    160 
    161    // Step 3.4. Collect a sequence of code points that are not feature
    162    // separators from `features` given `position`. Set `name` to the collected
    163    // characters, converted to ASCII lowercase.
    164    nsAutoCString name(CollectSequence(
    165        position, end, [](char c) { return !IsFeatureSeparator(c); }));
    166    ToLowerCase(name);
    167 
    168    // Step 3.5. Set `name` to the result of normalizing the feature name
    169    // `name`.
    170    NormalizeName(name);
    171 
    172    // Step 3.6. While `position` is not past the end of `features` and the
    173    // code point at `position` in features is not U+003D (=):
    174    //
    175    // Step 3.6.1. If the code point at `position` in features is U+002C (,),
    176    // or if it is not a feature separator, then break.
    177    //
    178    // Step 3.6.2. Advance `position` by 1.
    179    //
    180    // NOTE: This skips to the first U+003D (=) but does not skip past a U+002C
    181    //       (,) or a non-separator.
    182    //
    183    // The above means skip all whitespaces.
    184    AdvanceWhile(position, end, [](char c) { return IsSpaceCharacter(c); });
    185 
    186    // Step 3.7. If the code point at `position` in `features` is a feature
    187    // separator:
    188    if (position < end && IsFeatureSeparator(*position)) {
    189      // Step 3.7.1. While `position` is not past the end of `features` and the
    190      // code point at `position` in `features` is a feature separator:
    191      //
    192      // Step 3.7.1.1. If the code point at `position` in `features` is
    193      // U+002C (,), then break.
    194      //
    195      // Step 3.7.1.2. Advance `position` by 1.
    196      //
    197      // NOTE: This skips to the first non-separator but does not skip past a
    198      // U+002C (,).
    199      AdvanceWhile(position, end,
    200                   [](char c) { return IsFeatureSeparator(c) && c != ','; });
    201 
    202      // Step 3.7.2. Collect a sequence of code points that are not feature
    203      // separators code points from `features` given `position`. Set `value` to
    204      // the collected code points, converted to ASCII lowercase.
    205      value = CollectSequence(position, end,
    206                              [](char c) { return !IsFeatureSeparator(c); });
    207      ToLowerCase(value);
    208    }
    209 
    210    // Step 3.8. If `name` is not the empty string, then set
    211    // `tokenizedFeatures[name]` to `value`.
    212    if (!name.IsEmpty()) {
    213      if (!tokenizedFeatures_.put(name, value)) {
    214        return false;
    215      }
    216    }
    217  }
    218 
    219  // Step 4. Return `tokenizedFeatures`.
    220  return true;
    221 }
    222 
    223 void WindowFeatures::Stringify(nsAutoCString& aOutput) {
    224  MOZ_ASSERT(aOutput.IsEmpty());
    225 
    226  for (auto r = tokenizedFeatures_.all(); !r.empty(); r.popFront()) {
    227    if (!aOutput.IsEmpty()) {
    228      aOutput.Append(',');
    229    }
    230 
    231    const nsCString& name = r.front().key();
    232    const nsCString& value = r.front().value();
    233 
    234    aOutput.Append(name);
    235 
    236    if (!value.IsEmpty()) {
    237      aOutput.Append('=');
    238      aOutput.Append(value);
    239    }
    240  }
    241 }