tor-browser

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

SingleLineTextInputTypes.cpp (8225B)


      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 "mozilla/dom/SingleLineTextInputTypes.h"
      8 
      9 #include "HTMLSplitOnSpacesTokenizer.h"
     10 #include "mozilla/TextUtils.h"
     11 #include "mozilla/dom/BindingDeclarations.h"
     12 #include "mozilla/dom/HTMLInputElement.h"
     13 #include "nsCRTGlue.h"
     14 #include "nsContentUtils.h"
     15 #include "nsIIOService.h"
     16 #include "nsNetCID.h"
     17 #include "nsNetUtil.h"
     18 
     19 using namespace mozilla;
     20 using namespace mozilla::dom;
     21 
     22 bool SingleLineTextInputTypeBase::IsMutable() const {
     23  return !mInputElement->IsDisabledOrReadOnly();
     24 }
     25 
     26 bool SingleLineTextInputTypeBase::IsTooLong() const {
     27  int32_t maxLength = mInputElement->MaxLength();
     28 
     29  // Maxlength of -1 means attribute isn't set or parsing error.
     30  if (maxLength == -1) {
     31    return false;
     32  }
     33 
     34  int32_t textLength = mInputElement->InputTextLength(CallerType::System);
     35 
     36  return textLength > maxLength;
     37 }
     38 
     39 bool SingleLineTextInputTypeBase::IsTooShort() const {
     40  int32_t minLength = mInputElement->MinLength();
     41 
     42  // Minlength of -1 means attribute isn't set or parsing error.
     43  if (minLength == -1) {
     44    return false;
     45  }
     46 
     47  int32_t textLength = mInputElement->InputTextLength(CallerType::System);
     48 
     49  return textLength && textLength < minLength;
     50 }
     51 
     52 bool SingleLineTextInputTypeBase::IsValueMissing() const {
     53  if (!mInputElement->IsRequired()) {
     54    return false;
     55  }
     56 
     57  if (!IsMutable()) {
     58    return false;
     59  }
     60 
     61  return IsValueEmpty();
     62 }
     63 
     64 Maybe<bool> SingleLineTextInputTypeBase::HasPatternMismatch() const {
     65  if (!mInputElement->HasPatternAttribute()) {
     66    return Some(false);
     67  }
     68 
     69  nsAutoString pattern;
     70  if (!mInputElement->GetAttr(nsGkAtoms::pattern, pattern)) {
     71    return Some(false);
     72  }
     73 
     74  nsAutoString value;
     75  GetNonFileValueInternal(value);
     76 
     77  if (value.IsEmpty()) {
     78    return Some(false);
     79  }
     80 
     81  Document* doc = mInputElement->OwnerDoc();
     82  Maybe<bool> result = nsContentUtils::IsPatternMatching(
     83      value, std::move(pattern), doc,
     84      mInputElement->HasAttr(nsGkAtoms::multiple));
     85  return result ? Some(!*result) : Nothing();
     86 }
     87 
     88 /* input type=url */
     89 
     90 bool URLInputType::HasTypeMismatch() const {
     91  nsAutoString value;
     92  GetNonFileValueInternal(value);
     93 
     94  if (value.IsEmpty()) {
     95    return false;
     96  }
     97 
     98  /**
     99   * TODO:
    100   * The URL is not checked as the HTML5 specifications want it to be because
    101   * there is no code to check for a valid URI/IRI according to 3986 and 3987
    102   * RFC's at the moment, see bug 561586.
    103   *
    104   * RFC 3987 (IRI) implementation: bug 42899
    105   *
    106   * HTML5 specifications:
    107   * http://dev.w3.org/html5/spec/infrastructure.html#valid-url
    108   */
    109  nsCOMPtr<nsIIOService> ioService = do_GetIOService();
    110  nsCOMPtr<nsIURI> uri;
    111 
    112  return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr,
    113                                         nullptr, getter_AddRefs(uri)));
    114 }
    115 
    116 nsresult URLInputType::GetTypeMismatchMessage(nsAString& aMessage) {
    117  return nsContentUtils::GetMaybeLocalizedString(
    118      nsContentUtils::eDOM_PROPERTIES, "FormValidationInvalidURL",
    119      mInputElement->OwnerDoc(), aMessage);
    120 }
    121 
    122 /* input type=email */
    123 
    124 bool EmailInputType::HasTypeMismatch() const {
    125  nsAutoString value;
    126  GetNonFileValueInternal(value);
    127 
    128  if (value.IsEmpty()) {
    129    return false;
    130  }
    131 
    132  return mInputElement->HasAttr(nsGkAtoms::multiple)
    133             ? !IsValidEmailAddressList(value)
    134             : !IsValidEmailAddress(value);
    135 }
    136 
    137 bool EmailInputType::HasBadInput() const {
    138  // With regards to suffering from bad input the spec says that only the
    139  // punycode conversion works, so we don't care whether the email address is
    140  // valid or not here. (If the email address is invalid then we will be
    141  // suffering from a type mismatch.)
    142  nsAutoString value;
    143  nsAutoCString unused;
    144  uint32_t unused2;
    145  GetNonFileValueInternal(value);
    146  HTMLSplitOnSpacesTokenizer tokenizer(value, ',');
    147  while (tokenizer.hasMoreTokens()) {
    148    if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) {
    149      return true;
    150    }
    151  }
    152  return false;
    153 }
    154 
    155 nsresult EmailInputType::GetTypeMismatchMessage(nsAString& aMessage) {
    156  return nsContentUtils::GetMaybeLocalizedString(
    157      nsContentUtils::eDOM_PROPERTIES, "FormValidationInvalidEmail",
    158      mInputElement->OwnerDoc(), aMessage);
    159 }
    160 
    161 nsresult EmailInputType::GetBadInputMessage(nsAString& aMessage) {
    162  return nsContentUtils::GetMaybeLocalizedString(
    163      nsContentUtils::eDOM_PROPERTIES, "FormValidationInvalidEmail",
    164      mInputElement->OwnerDoc(), aMessage);
    165 }
    166 
    167 /* static */
    168 bool EmailInputType::IsValidEmailAddressList(const nsAString& aValue) {
    169  HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
    170 
    171  while (tokenizer.hasMoreTokens()) {
    172    if (!IsValidEmailAddress(tokenizer.nextToken())) {
    173      return false;
    174    }
    175  }
    176 
    177  return !tokenizer.separatorAfterCurrentToken();
    178 }
    179 
    180 /* static */
    181 bool EmailInputType::IsValidEmailAddress(const nsAString& aValue) {
    182  nsAutoString trimmed(aValue);
    183  trimmed.Trim(" \n\r\t\f");
    184 
    185  // Email addresses can't be empty and can't end with a '.' or '-'.
    186  if (trimmed.IsEmpty() || trimmed.Last() == '.' || trimmed.Last() == '-') {
    187    return false;
    188  }
    189 
    190  uint32_t atPos;
    191  nsAutoCString value;
    192  if (!PunycodeEncodeEmailAddress(trimmed, value, &atPos) ||
    193      atPos == (uint32_t)kNotFound || atPos == 0 ||
    194      atPos == value.Length() - 1) {
    195    // Could not encode, or "@" was not found, or it was at the start or end
    196    // of the input - in all cases, not a valid email address.
    197    return false;
    198  }
    199 
    200  uint32_t length = value.Length();
    201  uint32_t i = 0;
    202 
    203  // Parsing the username.
    204  for (; i < atPos; ++i) {
    205    char16_t c = value[i];
    206 
    207    // The username characters have to be in this list to be valid.
    208    if (!(IsAsciiAlpha(c) || IsAsciiDigit(c) || c == '.' || c == '!' ||
    209          c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' ||
    210          c == '*' || c == '+' || c == '-' || c == '/' || c == '=' ||
    211          c == '?' || c == '^' || c == '_' || c == '`' || c == '{' ||
    212          c == '|' || c == '}' || c == '~')) {
    213      return false;
    214    }
    215  }
    216 
    217  // Skip the '@'.
    218  ++i;
    219 
    220  // The domain name can't begin with a dot or a dash.
    221  if (value[i] == '.' || value[i] == '-') {
    222    return false;
    223  }
    224 
    225  // Parsing the domain name.
    226  for (; i < length; ++i) {
    227    char16_t c = value[i];
    228 
    229    if (c == '.') {
    230      // A dot can't follow a dot or a dash.
    231      if (value[i - 1] == '.' || value[i - 1] == '-') {
    232        return false;
    233      }
    234    } else if (c == '-') {
    235      // A dash can't follow a dot.
    236      if (value[i - 1] == '.') {
    237        return false;
    238      }
    239    } else if (!(IsAsciiAlpha(c) || IsAsciiDigit(c) || c == '-')) {
    240      // The domain characters have to be in this list to be valid.
    241      return false;
    242    }
    243  }
    244 
    245  return true;
    246 }
    247 
    248 /* static */
    249 bool EmailInputType::PunycodeEncodeEmailAddress(const nsAString& aEmail,
    250                                                nsAutoCString& aEncodedEmail,
    251                                                uint32_t* aIndexOfAt) {
    252  nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail);
    253  *aIndexOfAt = (uint32_t)value.FindChar('@');
    254 
    255  if (*aIndexOfAt == (uint32_t)kNotFound || *aIndexOfAt == value.Length() - 1) {
    256    aEncodedEmail = value;
    257    return true;
    258  }
    259 
    260  uint32_t indexOfDomain = *aIndexOfAt + 1;
    261 
    262  const nsDependentCSubstring domain = Substring(value, indexOfDomain);
    263  nsAutoCString domainACE;
    264  NS_DomainToASCII(domain, domainACE);
    265 
    266  // NS_DomainToASCII does not check length (removed in bug 1788115), so we
    267  // check for that limit here as required by the spec:
    268  // https://html.spec.whatwg.org/#valid-e-mail-address
    269  nsCCharSeparatedTokenizer tokenizer(domainACE, '.');
    270  while (tokenizer.hasMoreTokens()) {
    271    // XXX should check each token for not starting or ending with hyphen;
    272    // https://bugzilla.mozilla.org/show_bug.cgi?id=1890466
    273    if (tokenizer.nextToken().Length() > 63) {
    274      return false;
    275    }
    276  }
    277 
    278  value.Replace(indexOfDomain, domain.Length(), domainACE);
    279 
    280  aEncodedEmail = value;
    281  return true;
    282 }