tor-browser

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

HTMLInputElement.cpp (256798B)


      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/HTMLInputElement.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "HTMLDataListElement.h"
     12 #include "HTMLFormSubmissionConstants.h"
     13 #include "mozilla/AsyncEventDispatcher.h"
     14 #include "mozilla/BasePrincipal.h"
     15 #include "mozilla/Components.h"
     16 #include "mozilla/ContentEvents.h"
     17 #include "mozilla/DebugOnly.h"
     18 #include "mozilla/EventDispatcher.h"
     19 #include "mozilla/EventStateManager.h"
     20 #include "mozilla/MappedDeclarationsBuilder.h"
     21 #include "mozilla/Maybe.h"
     22 #include "mozilla/MouseEvents.h"
     23 #include "mozilla/PresShell.h"
     24 #include "mozilla/PresState.h"
     25 #include "mozilla/ServoCSSParser.h"
     26 #include "mozilla/ServoComputedData.h"
     27 #include "mozilla/StaticPrefs_dom.h"
     28 #include "mozilla/StaticPrefs_signon.h"
     29 #include "mozilla/TextControlState.h"
     30 #include "mozilla/TextEditor.h"
     31 #include "mozilla/TextEvents.h"
     32 #include "mozilla/TextUtils.h"
     33 #include "mozilla/TouchEvents.h"
     34 #include "mozilla/Try.h"
     35 #include "mozilla/dom/AutocompleteInfoBinding.h"
     36 #include "mozilla/dom/BlobImpl.h"
     37 #include "mozilla/dom/CustomEvent.h"
     38 #include "mozilla/dom/Directory.h"
     39 #include "mozilla/dom/Document.h"
     40 #include "mozilla/dom/DocumentInlines.h"
     41 #include "mozilla/dom/DocumentOrShadowRoot.h"
     42 #include "mozilla/dom/ElementBinding.h"
     43 #include "mozilla/dom/FileSystemUtils.h"
     44 #include "mozilla/dom/FormData.h"
     45 #include "mozilla/dom/GetFilesHelper.h"
     46 #include "mozilla/dom/HTMLDataListElement.h"
     47 #include "mozilla/dom/HTMLOptionElement.h"
     48 #include "mozilla/dom/InputType.h"
     49 #include "mozilla/dom/MouseEvent.h"
     50 #include "mozilla/dom/NumericInputTypes.h"
     51 #include "mozilla/dom/ProgressEvent.h"
     52 #include "mozilla/dom/UnionTypes.h"
     53 #include "mozilla/dom/UserActivation.h"
     54 #include "mozilla/dom/WheelEventBinding.h"
     55 #include "mozilla/dom/WindowContext.h"
     56 #include "mozilla/dom/WindowGlobalChild.h"
     57 #include "mozilla/glean/DomMetrics.h"
     58 #include "nsAttrValueInlines.h"
     59 #include "nsAttrValueOrString.h"
     60 #include "nsBaseCommandController.h"
     61 #include "nsCRTGlue.h"
     62 #include "nsColorControlFrame.h"
     63 #include "nsError.h"
     64 #include "nsFileControlFrame.h"
     65 #include "nsFocusManager.h"
     66 #include "nsGkAtoms.h"
     67 #include "nsIEditor.h"
     68 #include "nsIFilePicker.h"
     69 #include "nsIFormControl.h"
     70 #include "nsIFormFillController.h"
     71 #include "nsIFrame.h"
     72 #include "nsIMutationObserver.h"
     73 #include "nsIPromptCollection.h"
     74 #include "nsIStringBundle.h"
     75 #include "nsLayoutUtils.h"
     76 #include "nsLinebreakConverter.h"  //to strip out carriage returns
     77 #include "nsNetUtil.h"
     78 #include "nsNumberControlFrame.h"
     79 #include "nsPIDOMWindow.h"
     80 #include "nsPresContext.h"
     81 #include "nsQueryObject.h"
     82 #include "nsRangeFrame.h"
     83 #include "nsReadableUtils.h"
     84 #include "nsRepeatService.h"
     85 #include "nsSearchControlFrame.h"
     86 #include "nsStyleConsts.h"
     87 #include "nsUnicharUtils.h"
     88 #include "nsVariant.h"
     89 
     90 // input type=radio
     91 #include "mozilla/dom/RadioGroupContainer.h"
     92 
     93 // input type=file
     94 #include "mozilla/dom/File.h"
     95 #include "mozilla/dom/FileList.h"
     96 #include "mozilla/dom/FileSystem.h"
     97 #include "mozilla/dom/FileSystemEntry.h"
     98 #include "nsDirectoryServiceDefs.h"
     99 #include "nsIContentPrefService2.h"
    100 #include "nsIFile.h"
    101 #include "nsIMIMEService.h"
    102 #include "nsIObserverService.h"
    103 
    104 // input type=image
    105 
    106 #include "HTMLSplitOnSpacesTokenizer.h"
    107 #include "imgRequestProxy.h"
    108 #include "mozAutoDocUpdate.h"
    109 #include "mozilla/LookAndFeel.h"
    110 #include "mozilla/Preferences.h"
    111 #include "mozilla/dom/DirectionalityUtils.h"
    112 #include "nsContentCreatorFunctions.h"
    113 #include "nsContentUtils.h"
    114 #include "nsFrameSelection.h"
    115 #include "nsIColorPicker.h"
    116 #include "nsIMIMEInfo.h"
    117 #include "nsIStringEnumerator.h"
    118 #include "nsImageLoadingContent.h"
    119 #include "nsXULControllers.h"
    120 
    121 // input type=date
    122 #include "js/Date.h"
    123 
    124 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
    125 
    126 // XXX align=left, hspace, vspace, border? other nav4 attrs
    127 
    128 namespace mozilla::dom {
    129 
    130 // First bits are needed for the control type.
    131 #define NS_OUTER_ACTIVATE_EVENT (1 << 9)
    132 #define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
    133 // (1 << 11 is unused)
    134 #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
    135 #define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
    136 #define NS_IN_SUBMIT_CLICK (1 << 15)
    137 #define NS_CONTROL_TYPE(bits)                                              \
    138  ((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE |        \
    139              NS_ORIGINAL_INDETERMINATE_VALUE | NS_PRE_HANDLE_BLUR_EVENT | \
    140              NS_IN_SUBMIT_CLICK))
    141 
    142 // whether textfields should be selected once focused:
    143 //  -1: no, 1: yes, 0: uninitialized
    144 static int32_t gSelectTextFieldOnFocus;
    145 UploadLastDir* HTMLInputElement::gUploadLastDir;
    146 
    147 static constexpr nsAttrValue::EnumTableEntry kInputTypeTable[] = {
    148    {"button", FormControlType::InputButton},
    149    {"checkbox", FormControlType::InputCheckbox},
    150    {"color", FormControlType::InputColor},
    151    {"date", FormControlType::InputDate},
    152    {"datetime-local", FormControlType::InputDatetimeLocal},
    153    {"email", FormControlType::InputEmail},
    154    {"file", FormControlType::InputFile},
    155    {"hidden", FormControlType::InputHidden},
    156    {"reset", FormControlType::InputReset},
    157    {"image", FormControlType::InputImage},
    158    {"month", FormControlType::InputMonth},
    159    {"number", FormControlType::InputNumber},
    160    {"password", FormControlType::InputPassword},
    161    {"radio", FormControlType::InputRadio},
    162    {"range", FormControlType::InputRange},
    163    {"search", FormControlType::InputSearch},
    164    {"submit", FormControlType::InputSubmit},
    165    {"tel", FormControlType::InputTel},
    166    {"time", FormControlType::InputTime},
    167    {"url", FormControlType::InputUrl},
    168    {"week", FormControlType::InputWeek},
    169    // "text" must be last for ParseAttribute to work right.  If you add things
    170    // before it, please update kInputDefaultType.
    171    {"text", FormControlType::InputText},
    172 };
    173 
    174 // Default type is 'text'.
    175 static constexpr const nsAttrValue::EnumTableEntry* kInputDefaultType =
    176    &kInputTypeTable[std::size(kInputTypeTable) - 1];
    177 
    178 static constexpr nsAttrValue::EnumTableEntry kCaptureTable[] = {
    179    {"user", nsIFilePicker::captureUser},
    180    {"environment", nsIFilePicker::captureEnv},
    181    {"", nsIFilePicker::captureDefault},
    182 };
    183 
    184 static constexpr const nsAttrValue::EnumTableEntry* kCaptureDefault =
    185    &kCaptureTable[2];
    186 
    187 using namespace blink;
    188 
    189 constexpr Decimal HTMLInputElement::kStepScaleFactorDate(86400000_d);
    190 constexpr Decimal HTMLInputElement::kStepScaleFactorNumberRange(1_d);
    191 constexpr Decimal HTMLInputElement::kStepScaleFactorTime(1000_d);
    192 constexpr Decimal HTMLInputElement::kStepScaleFactorMonth(1_d);
    193 constexpr Decimal HTMLInputElement::kStepScaleFactorWeek(7 * 86400000_d);
    194 constexpr Decimal HTMLInputElement::kDefaultStepBase(0_d);
    195 constexpr Decimal HTMLInputElement::kDefaultStepBaseWeek(-259200000_d);
    196 constexpr Decimal HTMLInputElement::kDefaultStep(1_d);
    197 constexpr Decimal HTMLInputElement::kDefaultStepTime(60_d);
    198 constexpr Decimal HTMLInputElement::kStepAny(0_d);
    199 
    200 const double HTMLInputElement::kMinimumYear = 1;
    201 const double HTMLInputElement::kMaximumYear = 275760;
    202 const double HTMLInputElement::kMaximumWeekInMaximumYear = 37;
    203 const double HTMLInputElement::kMaximumDayInMaximumYear = 13;
    204 const double HTMLInputElement::kMaximumMonthInMaximumYear = 9;
    205 const double HTMLInputElement::kMaximumWeekInYear = 53;
    206 const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000;
    207 
    208 // An helper class for the dispatching of the 'change' event.
    209 // This class is used when the FilePicker finished its task (or when files and
    210 // directories are set by some chrome/test only method).
    211 // The task of this class is to postpone the dispatching of 'change' and 'input'
    212 // events at the end of the exploration of the directories.
    213 class DispatchChangeEventCallback final : public GetFilesCallback {
    214 public:
    215  explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
    216      : mInputElement(aInputElement) {
    217    MOZ_ASSERT(aInputElement);
    218  }
    219 
    220  virtual void Callback(
    221      nsresult aStatus,
    222      const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
    223    if (!mInputElement->GetOwnerGlobal()) {
    224      return;
    225    }
    226 
    227    nsTArray<OwningFileOrDirectory> array;
    228    for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) {
    229      OwningFileOrDirectory* element = array.AppendElement();
    230      RefPtr<File> file =
    231          File::Create(mInputElement->GetOwnerGlobal(), aBlobImpls[i]);
    232      if (NS_WARN_IF(!file)) {
    233        return;
    234      }
    235 
    236      element->SetAsFile() = file;
    237    }
    238 
    239    mInputElement->SetFilesOrDirectories(array, true);
    240    (void)NS_WARN_IF(NS_FAILED(DispatchEvents()));
    241  }
    242 
    243  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    244  nsresult DispatchEvents() {
    245    RefPtr<HTMLInputElement> inputElement(mInputElement);
    246    nsresult rv = nsContentUtils::DispatchInputEvent(inputElement);
    247    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
    248    mInputElement->SetUserInteracted(true);
    249    rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
    250                                              mInputElement, u"change"_ns,
    251                                              CanBubble::eYes, Cancelable::eNo);
    252 
    253    return rv;
    254  }
    255 
    256 private:
    257  RefPtr<HTMLInputElement> mInputElement;
    258 };
    259 
    260 struct HTMLInputElement::FileData {
    261  /**
    262   * The value of the input if it is a file input. This is the list of files or
    263   * directories DOM objects used when uploading a file. It is vital that this
    264   * is kept separate from mValue so that it won't be possible to 'leak' the
    265   * value from a text-input to a file-input. Additionally, the logic for this
    266   * value is kept as simple as possible to avoid accidental errors where the
    267   * wrong filename is used.  Therefor the list of filenames is always owned by
    268   * this member, never by the frame. Whenever the frame wants to change the
    269   * filename it has to call SetFilesOrDirectories to update this member.
    270   */
    271  nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
    272 
    273  RefPtr<GetFilesHelper> mGetFilesRecursiveHelper;
    274  RefPtr<GetFilesHelper> mGetFilesNonRecursiveHelper;
    275 
    276  /**
    277   * Hack for bug 1086684: Stash the .value when we're a file picker.
    278   */
    279  nsString mFirstFilePath;
    280 
    281  RefPtr<FileList> mFileList;
    282  Sequence<RefPtr<FileSystemEntry>> mEntries;
    283 
    284  nsString mStaticDocFileList;
    285 
    286  void ClearGetFilesHelpers() {
    287    if (mGetFilesRecursiveHelper) {
    288      mGetFilesRecursiveHelper->Unlink();
    289      mGetFilesRecursiveHelper = nullptr;
    290    }
    291 
    292    if (mGetFilesNonRecursiveHelper) {
    293      mGetFilesNonRecursiveHelper->Unlink();
    294      mGetFilesNonRecursiveHelper = nullptr;
    295    }
    296  }
    297 
    298  // Cycle Collection support.
    299  void Traverse(nsCycleCollectionTraversalCallback& cb) {
    300    FileData* tmp = this;
    301    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
    302    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
    303    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries)
    304    if (mGetFilesRecursiveHelper) {
    305      mGetFilesRecursiveHelper->Traverse(cb);
    306    }
    307 
    308    if (mGetFilesNonRecursiveHelper) {
    309      mGetFilesNonRecursiveHelper->Traverse(cb);
    310    }
    311  }
    312 
    313  void Unlink() {
    314    FileData* tmp = this;
    315    NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
    316    NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
    317    NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries)
    318    ClearGetFilesHelpers();
    319  }
    320 };
    321 
    322 HTMLInputElement::nsFilePickerShownCallback::nsFilePickerShownCallback(
    323    HTMLInputElement* aInput, nsIFilePicker* aFilePicker)
    324    : mFilePicker(aFilePicker), mInput(aInput) {}
    325 
    326 NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
    327 
    328 NS_IMETHODIMP
    329 UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason) {
    330  nsCOMPtr<nsIFile> localFile;
    331  nsAutoString prefStr;
    332 
    333  if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
    334    Preferences::GetString("dom.input.fallbackUploadDir", prefStr);
    335  }
    336 
    337  if (prefStr.IsEmpty() && mResult) {
    338    nsCOMPtr<nsIVariant> pref;
    339    mResult->GetValue(getter_AddRefs(pref));
    340    pref->GetAsAString(prefStr);
    341  }
    342 
    343  if (!prefStr.IsEmpty()) {
    344    nsresult rv = NS_NewLocalFile(prefStr, getter_AddRefs(localFile));
    345    (void)NS_WARN_IF(NS_FAILED(rv));
    346  }
    347 
    348  if (localFile) {
    349    mFilePicker->SetDisplayDirectory(localFile);
    350  } else {
    351    // If no custom directory was set through the pref, default to
    352    // "desktop" directory for each platform.
    353    mFilePicker->SetDisplaySpecialDirectory(
    354        NS_LITERAL_STRING_FROM_CSTRING(NS_OS_DESKTOP_DIR));
    355  }
    356 
    357  mFilePicker->Open(mFpCallback);
    358  return NS_OK;
    359 }
    360 
    361 NS_IMETHODIMP
    362 UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref) {
    363  mResult = pref;
    364  return NS_OK;
    365 }
    366 
    367 NS_IMETHODIMP
    368 UploadLastDir::ContentPrefCallback::HandleError(nsresult error) {
    369  // HandleCompletion is always called (even with HandleError was called),
    370  // so we don't need to do anything special here.
    371  return NS_OK;
    372 }
    373 
    374 namespace {
    375 
    376 /**
    377 * This may return nullptr if the DOM File's implementation of
    378 * File::mozFullPathInternal does not successfully return a non-empty
    379 * string that is a valid path. This can happen on Firefox OS, for example,
    380 * where the file picker can create Blobs.
    381 */
    382 static already_AddRefed<nsIFile> LastUsedDirectory(
    383    const OwningFileOrDirectory& aData) {
    384  if (aData.IsFile()) {
    385    nsAutoString path;
    386    ErrorResult error;
    387    aData.GetAsFile()->GetMozFullPathInternal(path, error);
    388    if (error.Failed() || path.IsEmpty()) {
    389      error.SuppressException();
    390      return nullptr;
    391    }
    392 
    393    nsCOMPtr<nsIFile> localFile;
    394    nsresult rv = NS_NewLocalFile(path, getter_AddRefs(localFile));
    395    if (NS_WARN_IF(NS_FAILED(rv))) {
    396      return nullptr;
    397    }
    398 
    399    nsCOMPtr<nsIFile> parentFile;
    400    rv = localFile->GetParent(getter_AddRefs(parentFile));
    401    if (NS_WARN_IF(NS_FAILED(rv))) {
    402      return nullptr;
    403    }
    404 
    405    return parentFile.forget();
    406  }
    407 
    408  MOZ_ASSERT(aData.IsDirectory());
    409 
    410  nsCOMPtr<nsIFile> localFile = aData.GetAsDirectory()->GetInternalNsIFile();
    411  MOZ_ASSERT(localFile);
    412 
    413  return localFile.forget();
    414 }
    415 
    416 void GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
    417                               nsAString& aName) {
    418  if (aData.IsFile()) {
    419    aData.GetAsFile()->GetName(aName);
    420  } else {
    421    MOZ_ASSERT(aData.IsDirectory());
    422    ErrorResult rv;
    423    aData.GetAsDirectory()->GetName(aName, rv);
    424    if (NS_WARN_IF(rv.Failed())) {
    425      rv.SuppressException();
    426    }
    427  }
    428 }
    429 
    430 void GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
    431                               nsAString& aPath, ErrorResult& aRv) {
    432  if (aData.IsFile()) {
    433    aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
    434  } else {
    435    MOZ_ASSERT(aData.IsDirectory());
    436    aData.GetAsDirectory()->GetFullRealPath(aPath);
    437  }
    438 }
    439 
    440 }  // namespace
    441 
    442 NS_IMETHODIMP
    443 HTMLInputElement::nsFilePickerShownCallback::Done(
    444    nsIFilePicker::ResultCode aResult) {
    445  mInput->PickerClosed();
    446 
    447  if (aResult == nsIFilePicker::returnCancel) {
    448    RefPtr<HTMLInputElement> inputElement(mInput);
    449    return nsContentUtils::DispatchTrustedEvent(
    450        inputElement->OwnerDoc(), inputElement, u"cancel"_ns, CanBubble::eYes,
    451        Cancelable::eNo);
    452  }
    453 
    454  mInput->OwnerDoc()->NotifyUserGestureActivation();
    455 
    456  nsIFilePicker::Mode mode;
    457  mFilePicker->GetMode(&mode);
    458 
    459  // Collect new selected filenames
    460  nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
    461  if (mode == nsIFilePicker::modeOpenMultiple) {
    462    nsCOMPtr<nsISimpleEnumerator> iter;
    463    nsresult rv =
    464        mFilePicker->GetDomFileOrDirectoryEnumerator(getter_AddRefs(iter));
    465    NS_ENSURE_SUCCESS(rv, rv);
    466 
    467    if (!iter) {
    468      return NS_OK;
    469    }
    470 
    471    nsCOMPtr<nsISupports> tmp;
    472    bool hasMore = true;
    473 
    474    while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
    475      iter->GetNext(getter_AddRefs(tmp));
    476      RefPtr<Blob> domBlob = do_QueryObject(tmp);
    477      MOZ_ASSERT(domBlob,
    478                 "Null file object from FilePicker's file enumerator?");
    479      if (!domBlob) {
    480        continue;
    481      }
    482 
    483      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
    484      element->SetAsFile() = domBlob->ToFile();
    485    }
    486  } else {
    487    MOZ_ASSERT(mode == nsIFilePicker::modeOpen ||
    488               mode == nsIFilePicker::modeGetFolder);
    489    nsCOMPtr<nsISupports> tmp;
    490    nsresult rv = mFilePicker->GetDomFileOrDirectory(getter_AddRefs(tmp));
    491    NS_ENSURE_SUCCESS(rv, rv);
    492 
    493    if (!tmp) {
    494      return NS_OK;
    495    }
    496 
    497    // Show a prompt to get user confirmation before allowing folder access.
    498    // This is to prevent sites from tricking the user into uploading files.
    499    // See Bug 1338637.
    500    if (mode == nsIFilePicker::modeGetFolder) {
    501      nsCOMPtr<nsIPromptCollection> prompter =
    502          do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
    503      if (!prompter) {
    504        return NS_ERROR_NOT_AVAILABLE;
    505      }
    506 
    507      bool confirmed = false;
    508      BrowsingContext* bc = mInput->OwnerDoc()->GetBrowsingContext();
    509 
    510      // Get directory name
    511      RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
    512      nsAutoString directoryName;
    513      ErrorResult error;
    514      directory->GetName(directoryName, error);
    515      if (NS_WARN_IF(error.Failed())) {
    516        return error.StealNSResult();
    517      }
    518 
    519      rv = prompter->ConfirmFolderUpload(bc, directoryName, &confirmed);
    520      NS_ENSURE_SUCCESS(rv, rv);
    521      if (!confirmed) {
    522        // User aborted upload
    523        return NS_OK;
    524      }
    525    }
    526 
    527    RefPtr<Blob> blob = do_QueryObject(tmp);
    528    if (blob) {
    529      RefPtr<File> file = blob->ToFile();
    530      MOZ_ASSERT(file);
    531 
    532      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
    533      element->SetAsFile() = file;
    534    } else if (tmp) {
    535      RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
    536      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
    537      element->SetAsDirectory() = directory;
    538    }
    539  }
    540 
    541  if (newFilesOrDirectories.IsEmpty()) {
    542    return NS_OK;
    543  }
    544 
    545  // Store the last used directory using the content pref service:
    546  nsCOMPtr<nsIFile> lastUsedDir = LastUsedDirectory(newFilesOrDirectories[0]);
    547 
    548  if (lastUsedDir) {
    549    HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(mInput->OwnerDoc(),
    550                                                             lastUsedDir);
    551  }
    552 
    553  // The text control frame (if there is one) isn't going to send a change
    554  // event because it will think this is done by a script.
    555  // So, we can safely send one by ourself.
    556  mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
    557 
    558  // mInput(HTMLInputElement) has no scriptGlobalObject, don't create
    559  // DispatchChangeEventCallback
    560  if (!mInput->GetOwnerGlobal()) {
    561    return NS_OK;
    562  }
    563  RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
    564      new DispatchChangeEventCallback(mInput);
    565 
    566  if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
    567      mInput->HasAttr(nsGkAtoms::webkitdirectory)) {
    568 #ifdef MOZ_WIDGET_ANDROID
    569    // Android 13 or later cannot enumerate files into user directory due to
    570    // no permission. So we store file list into file picker.
    571    FallibleTArray<RefPtr<BlobImpl>> filesInWebKitDirectory;
    572 
    573    nsCOMPtr<nsISimpleEnumerator> iter;
    574    if (NS_SUCCEEDED(
    575            mFilePicker->GetDomFilesInWebKitDirectory(getter_AddRefs(iter))) &&
    576        iter) {
    577      nsCOMPtr<nsISupports> supports;
    578 
    579      bool loop = true;
    580      while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
    581        iter->GetNext(getter_AddRefs(supports));
    582        if (supports) {
    583          RefPtr<BlobImpl> file = static_cast<File*>(supports.get())->Impl();
    584          MOZ_ASSERT(file);
    585          if (!filesInWebKitDirectory.AppendElement(file, fallible)) {
    586            return nsresult::NS_ERROR_OUT_OF_MEMORY;
    587          }
    588        }
    589      }
    590    }
    591 
    592    if (!filesInWebKitDirectory.IsEmpty()) {
    593      dispatchChangeEventCallback->Callback(NS_OK, filesInWebKitDirectory);
    594      return NS_OK;
    595    }
    596 #endif
    597 
    598    ErrorResult error;
    599    GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
    600    if (NS_WARN_IF(error.Failed())) {
    601      return error.StealNSResult();
    602    }
    603 
    604    helper->AddCallback(dispatchChangeEventCallback);
    605    return NS_OK;
    606  }
    607 
    608  return dispatchChangeEventCallback->DispatchEvents();
    609 }
    610 
    611 NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
    612                  nsIFilePickerShownCallback)
    613 
    614 class nsColorPickerShownCallback final : public nsIColorPickerShownCallback {
    615  ~nsColorPickerShownCallback() = default;
    616 
    617 public:
    618  nsColorPickerShownCallback(HTMLInputElement* aInput,
    619                             nsIColorPicker* aColorPicker)
    620      : mInput(aInput), mColorPicker(aColorPicker), mValueChanged(false) {}
    621 
    622  NS_DECL_ISUPPORTS
    623 
    624  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    625  NS_IMETHOD Update(const nsAString& aColor) override;
    626  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    627  NS_IMETHOD Done(const nsAString& aColor) override;
    628 
    629 private:
    630  /**
    631   * Updates the internals of the object using aColor as the new value.
    632   * If aTrustedUpdate is true, it will consider that aColor is a new value.
    633   * Otherwise, it will check that aColor is different from the current value.
    634   */
    635  MOZ_CAN_RUN_SCRIPT
    636  nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate);
    637 
    638  RefPtr<HTMLInputElement> mInput;
    639  nsCOMPtr<nsIColorPicker> mColorPicker;
    640  bool mValueChanged;
    641 };
    642 
    643 nsresult nsColorPickerShownCallback::UpdateInternal(const nsAString& aColor,
    644                                                    bool aTrustedUpdate) {
    645  bool valueChanged = false;
    646  nsAutoString oldValue;
    647  if (aTrustedUpdate) {
    648    mInput->OwnerDoc()->NotifyUserGestureActivation();
    649    valueChanged = true;
    650  } else {
    651    mInput->GetValue(oldValue, CallerType::System);
    652  }
    653 
    654  mInput->SetValue(aColor, CallerType::System, IgnoreErrors());
    655 
    656  if (!aTrustedUpdate) {
    657    nsAutoString newValue;
    658    mInput->GetValue(newValue, CallerType::System);
    659    if (!oldValue.Equals(newValue)) {
    660      valueChanged = true;
    661    }
    662  }
    663 
    664  if (!valueChanged) {
    665    return NS_OK;
    666  }
    667 
    668  mValueChanged = true;
    669  RefPtr<HTMLInputElement> input(mInput);
    670  DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(input);
    671  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    672                       "Failed to dispatch input event");
    673  return NS_OK;
    674 }
    675 
    676 NS_IMETHODIMP
    677 nsColorPickerShownCallback::Update(const nsAString& aColor) {
    678  return UpdateInternal(aColor, true);
    679 }
    680 
    681 NS_IMETHODIMP
    682 nsColorPickerShownCallback::Done(const nsAString& aColor) {
    683  /**
    684   * When Done() is called, we might be at the end of a serie of Update() calls
    685   * in which case mValueChanged is set to true and a change event will have to
    686   * be fired but we might also be in a one shot Done() call situation in which
    687   * case we should fire a change event iif the value actually changed.
    688   * UpdateInternal(bool) is taking care of that logic for us.
    689   */
    690  nsresult rv = NS_OK;
    691 
    692  mInput->PickerClosed();
    693 
    694  if (!aColor.IsEmpty()) {
    695    UpdateInternal(aColor, false);
    696  }
    697 
    698  if (mValueChanged) {
    699    mInput->SetUserInteracted(true);
    700    rv = nsContentUtils::DispatchTrustedEvent(
    701        mInput->OwnerDoc(), static_cast<Element*>(mInput.get()), u"change"_ns,
    702        CanBubble::eYes, Cancelable::eNo);
    703  }
    704 
    705  return rv;
    706 }
    707 
    708 NS_IMPL_ISUPPORTS(nsColorPickerShownCallback, nsIColorPickerShownCallback)
    709 
    710 static bool IsPickerBlocked(Document* aDoc) {
    711  if (aDoc->ConsumeTransientUserGestureActivation()) {
    712    return false;
    713  }
    714 
    715  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
    716                                  nsContentUtils::eDOM_PROPERTIES,
    717                                  "InputPickerBlockedNoUserActivation");
    718  return true;
    719 }
    720 
    721 /**
    722 * Parse a CSS color string and convert it to the target colorspace if it
    723 * succeeds.
    724 * https://html.spec.whatwg.org/#update-a-color-well-control-color
    725 *
    726 * @param aValue the string to be parsed
    727 * @return the parsed result as a HTML compatible form
    728 */
    729 static Maybe<StyleAbsoluteColor> MaybeComputeColor(Document* aDocument,
    730                                                   const nsAString& aValue) {
    731  // A few steps are ignored given we don't support alpha and colorspace. See
    732  // bug 1919718.
    733  return ServoCSSParser::ComputeColorWellControlColor(
    734      aDocument->EnsureStyleSet().RawData(), NS_ConvertUTF16toUTF8(aValue),
    735      StyleColorSpace::Srgb);
    736 }
    737 
    738 /**
    739 * https://html.spec.whatwg.org/#serialize-a-color-well-control-color
    740 * https://drafts.csswg.org/css-color/#color-serialization-html-compatible-serialization-is-requested
    741 *
    742 * @param aColor The parsed color
    743 * @param aResult The result in the form of #ffffff.
    744 */
    745 static void SerializeColorForHTMLCompatibility(const StyleAbsoluteColor& aColor,
    746                                               nsAString& aResult) {
    747  // Raw StyleAbsoluteColor can have floats outside of 0-1 range e.g. when
    748  // display-p3 color is converted to srgb, and ToColor guarantees to fit the
    749  // values within the range.
    750  nscolor color = aColor.ToColor();
    751  aResult.Truncate();
    752  aResult.AppendPrintf("#%02x%02x%02x", NS_GET_R(color), NS_GET_G(color),
    753                       NS_GET_B(color));
    754 }
    755 
    756 nsTArray<nsString> HTMLInputElement::GetColorsFromList() {
    757  RefPtr<HTMLDataListElement> dataList = GetList();
    758  if (!dataList) {
    759    return {};
    760  }
    761 
    762  nsTArray<nsString> colors;
    763 
    764  RefPtr<nsContentList> options = dataList->Options();
    765  uint32_t length = options->Length(true);
    766  for (uint32_t i = 0; i < length; ++i) {
    767    auto* option = HTMLOptionElement::FromNodeOrNull(options->Item(i, false));
    768    if (!option) {
    769      continue;
    770    }
    771 
    772    nsAutoString value;
    773    option->GetValue(value);
    774    // https://html.spec.whatwg.org/#update-a-color-well-control-color
    775    // https://html.spec.whatwg.org/#serialize-a-color-well-control-color
    776    if (Maybe<StyleAbsoluteColor> result =
    777            MaybeComputeColor(OwnerDoc(), value)) {
    778      // Serialization step 6: If htmlCompatible is true, then do so with
    779      // HTML-compatible serialization requested.
    780      SerializeColorForHTMLCompatibility(*result, value);
    781      colors.AppendElement(value);
    782    }
    783  }
    784 
    785  return colors;
    786 }
    787 
    788 nsresult HTMLInputElement::InitColorPicker() {
    789  MOZ_ASSERT(IsMutable());
    790 
    791  if (mPickerRunning) {
    792    NS_WARNING("Just one nsIColorPicker is allowed");
    793    return NS_ERROR_FAILURE;
    794  }
    795 
    796  nsCOMPtr<Document> doc = OwnerDoc();
    797 
    798  RefPtr<BrowsingContext> bc = doc->GetBrowsingContext();
    799  if (!bc) {
    800    return NS_ERROR_FAILURE;
    801  }
    802 
    803  if (IsPickerBlocked(doc)) {
    804    return NS_OK;
    805  }
    806 
    807  // NOTE(krosylight): Android doesn't support HTML widgets. We can modify
    808  // GeckoView to handle MozOpenColorPicker and let it keep using its current
    809  // picker, but for now this is ok.
    810 #ifndef ANDROID
    811  if (StaticPrefs::dom_forms_html_color_picker_enabled()) {
    812    OpenColorPicker();
    813    return NS_OK;
    814  }
    815 #endif
    816 
    817  // Get Loc title
    818  nsAutoString title;
    819  nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
    820                                     "ColorPicker", title);
    821 
    822  nsCOMPtr<nsIColorPicker> colorPicker =
    823      do_CreateInstance("@mozilla.org/colorpicker;1");
    824  if (!colorPicker) {
    825    return NS_ERROR_FAILURE;
    826  }
    827 
    828  nsAutoString initialValue;
    829  GetNonFileValueInternal(initialValue);
    830  nsTArray<nsString> colors = GetColorsFromList();
    831  nsresult rv = colorPicker->Init(bc, title, initialValue, colors);
    832  NS_ENSURE_SUCCESS(rv, rv);
    833 
    834  nsCOMPtr<nsIColorPickerShownCallback> callback =
    835      new nsColorPickerShownCallback(this, colorPicker);
    836 
    837  rv = colorPicker->Open(callback);
    838  if (NS_SUCCEEDED(rv)) {
    839    mPickerRunning = true;
    840    SetOpenState(true);
    841  }
    842 
    843  return rv;
    844 }
    845 
    846 nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
    847  MOZ_ASSERT(IsMutable());
    848 
    849  if (mPickerRunning) {
    850    NS_WARNING("Just one nsIFilePicker is allowed");
    851    return NS_ERROR_FAILURE;
    852  }
    853 
    854  // Get parent nsPIDOMWindow object.
    855  nsCOMPtr<Document> doc = OwnerDoc();
    856 
    857  RefPtr<BrowsingContext> bc = doc->GetBrowsingContext();
    858  if (!bc) {
    859    return NS_ERROR_FAILURE;
    860  }
    861 
    862  if (IsPickerBlocked(doc)) {
    863    return NS_OK;
    864  }
    865 
    866  // Get Loc title
    867  nsAutoString title;
    868  nsAutoString okButtonLabel;
    869  if (aType == FILE_PICKER_DIRECTORY) {
    870    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
    871                                            "DirectoryUpload", doc, title);
    872 
    873    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
    874                                            "DirectoryPickerOkButtonLabel", doc,
    875                                            okButtonLabel);
    876  } else {
    877    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
    878                                            "FileUpload", doc, title);
    879  }
    880 
    881  nsCOMPtr<nsIFilePicker> filePicker =
    882      do_CreateInstance("@mozilla.org/filepicker;1");
    883  if (!filePicker) return NS_ERROR_FAILURE;
    884 
    885  nsIFilePicker::Mode mode;
    886 
    887  if (aType == FILE_PICKER_DIRECTORY) {
    888    mode = nsIFilePicker::modeGetFolder;
    889  } else if (HasAttr(nsGkAtoms::multiple)) {
    890    mode = nsIFilePicker::modeOpenMultiple;
    891  } else {
    892    mode = nsIFilePicker::modeOpen;
    893  }
    894 
    895  nsresult rv = filePicker->Init(bc, title, mode);
    896  NS_ENSURE_SUCCESS(rv, rv);
    897 
    898  if (!okButtonLabel.IsEmpty()) {
    899    filePicker->SetOkButtonLabel(okButtonLabel);
    900  }
    901 
    902  // Native directory pickers ignore file type filters, so we don't spend
    903  // cycles adding them for FILE_PICKER_DIRECTORY.
    904  if (HasAttr(nsGkAtoms::accept) && aType != FILE_PICKER_DIRECTORY) {
    905    SetFilePickerFiltersFromAccept(filePicker);
    906 
    907    if (StaticPrefs::dom_capture_enabled()) {
    908      if (const nsAttrValue* captureVal = GetParsedAttr(nsGkAtoms::capture)) {
    909        filePicker->SetCapture(static_cast<nsIFilePicker::CaptureTarget>(
    910            captureVal->GetEnumValue()));
    911      }
    912    }
    913  } else {
    914    filePicker->AppendFilters(nsIFilePicker::filterAll);
    915  }
    916 
    917  // Set default directory and filename
    918  nsAutoString defaultName;
    919 
    920  const nsTArray<OwningFileOrDirectory>& oldFiles =
    921      GetFilesOrDirectoriesInternal();
    922 
    923  nsCOMPtr<nsIFilePickerShownCallback> callback =
    924      new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
    925 
    926  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    927  if (obs) {
    928    // Used by WebDriver BiDi to emit input.fileDialogOpened whenever an input
    929    // type=file opens a file picker.
    930    obs->NotifyObservers(ToSupports(this), "file-input-picker-opening",
    931                         nullptr);
    932  }
    933 
    934  if (!oldFiles.IsEmpty() && aType != FILE_PICKER_DIRECTORY) {
    935    nsAutoString path;
    936 
    937    nsCOMPtr<nsIFile> parentFile = LastUsedDirectory(oldFiles[0]);
    938    if (parentFile) {
    939      filePicker->SetDisplayDirectory(parentFile);
    940    }
    941 
    942    // Unfortunately nsIFilePicker doesn't allow multiple files to be
    943    // default-selected, so only select something by default if exactly
    944    // one file was selected before.
    945    if (oldFiles.Length() == 1) {
    946      nsAutoString leafName;
    947      GetDOMFileOrDirectoryName(oldFiles[0], leafName);
    948 
    949      if (!leafName.IsEmpty()) {
    950        filePicker->SetDefaultString(leafName);
    951      }
    952    }
    953 
    954    rv = filePicker->Open(callback);
    955    if (NS_SUCCEEDED(rv)) {
    956      mPickerRunning = true;
    957      SetOpenState(true);
    958    }
    959 
    960    return rv;
    961  }
    962 
    963  HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(
    964      doc, filePicker, callback);
    965  mPickerRunning = true;
    966  SetOpenState(true);
    967  return NS_OK;
    968 }
    969 
    970 #define CPS_PREF_NAME u"browser.upload.lastDir"_ns
    971 
    972 NS_IMPL_ISUPPORTS(UploadLastDir, nsIObserver, nsISupportsWeakReference)
    973 
    974 void HTMLInputElement::InitUploadLastDir() {
    975  gUploadLastDir = new UploadLastDir();
    976  NS_ADDREF(gUploadLastDir);
    977 
    978  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
    979  if (observerService && gUploadLastDir) {
    980    observerService->AddObserver(gUploadLastDir,
    981                                 "browser:purge-session-history", true);
    982  }
    983 }
    984 
    985 void HTMLInputElement::DestroyUploadLastDir() { NS_IF_RELEASE(gUploadLastDir); }
    986 
    987 nsresult UploadLastDir::FetchDirectoryAndDisplayPicker(
    988    Document* aDoc, nsIFilePicker* aFilePicker,
    989    nsIFilePickerShownCallback* aFpCallback) {
    990  MOZ_ASSERT(aDoc, "aDoc is null");
    991  MOZ_ASSERT(aFilePicker, "aFilePicker is null");
    992  MOZ_ASSERT(aFpCallback, "aFpCallback is null");
    993 
    994  nsIURI* docURI = aDoc->GetDocumentURI();
    995  MOZ_ASSERT(docURI, "docURI is null");
    996 
    997  nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
    998  nsCOMPtr<nsIContentPrefCallback2> prefCallback =
    999      new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
   1000 
   1001  // Attempt to get the CPS, if it's not present we'll fallback to use the
   1002  // Desktop folder
   1003  nsCOMPtr<nsIContentPrefService2> contentPrefService =
   1004      do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
   1005  if (!contentPrefService) {
   1006    prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
   1007    return NS_OK;
   1008  }
   1009 
   1010  nsAutoCString cstrSpec;
   1011  docURI->GetSpec(cstrSpec);
   1012  NS_ConvertUTF8toUTF16 spec(cstrSpec);
   1013 
   1014  contentPrefService->GetByDomainAndName(spec, CPS_PREF_NAME, loadContext,
   1015                                         prefCallback);
   1016  return NS_OK;
   1017 }
   1018 
   1019 nsresult UploadLastDir::StoreLastUsedDirectory(Document* aDoc, nsIFile* aDir) {
   1020  MOZ_ASSERT(aDoc, "aDoc is null");
   1021  if (!aDir) {
   1022    return NS_OK;
   1023  }
   1024 
   1025  nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
   1026  MOZ_ASSERT(docURI, "docURI is null");
   1027 
   1028  // Attempt to get the CPS, if it's not present we'll just return
   1029  nsCOMPtr<nsIContentPrefService2> contentPrefService =
   1030      do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
   1031  if (!contentPrefService) return NS_ERROR_NOT_AVAILABLE;
   1032 
   1033  nsAutoCString cstrSpec;
   1034  docURI->GetSpec(cstrSpec);
   1035  NS_ConvertUTF8toUTF16 spec(cstrSpec);
   1036 
   1037  // Find the parent of aFile, and store it
   1038  nsString unicodePath;
   1039  aDir->GetPath(unicodePath);
   1040  if (unicodePath.IsEmpty())  // nothing to do
   1041    return NS_OK;
   1042  RefPtr<nsVariantCC> prefValue = new nsVariantCC();
   1043  prefValue->SetAsAString(unicodePath);
   1044 
   1045  // Use the document's current load context to ensure that the content pref
   1046  // service doesn't persistently store this directory for this domain if the
   1047  // user is using private browsing:
   1048  nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
   1049  return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext,
   1050                                 nullptr);
   1051 }
   1052 
   1053 NS_IMETHODIMP
   1054 UploadLastDir::Observe(nsISupports* aSubject, char const* aTopic,
   1055                       char16_t const* aData) {
   1056  if (strcmp(aTopic, "browser:purge-session-history") == 0) {
   1057    nsCOMPtr<nsIContentPrefService2> contentPrefService =
   1058        do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
   1059    if (contentPrefService)
   1060      contentPrefService->RemoveByName(CPS_PREF_NAME, nullptr, nullptr);
   1061  }
   1062  return NS_OK;
   1063 }
   1064 
   1065 #ifdef ACCESSIBILITY
   1066 // Helper method
   1067 static nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
   1068                                          EventMessage aEventMessage);
   1069 #endif
   1070 
   1071 //
   1072 // construction, destruction
   1073 //
   1074 
   1075 HTMLInputElement::HTMLInputElement(already_AddRefed<dom::NodeInfo>&& aNodeInfo,
   1076                                   FromParser aFromParser, FromClone aFromClone)
   1077    : TextControlElement(std::move(aNodeInfo), aFromParser,
   1078                         FormControlType(kInputDefaultType->value)),
   1079      mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
   1080      mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
   1081      mDisabledChanged(false),
   1082      mValueChanged(false),
   1083      mUserInteracted(false),
   1084      mLastValueChangeWasInteractive(false),
   1085      mCheckedChanged(false),
   1086      mChecked(false),
   1087      mShouldInitChecked(false),
   1088      mDoneCreating(aFromParser == NOT_FROM_PARSER &&
   1089                    aFromClone == FromClone::No),
   1090      mInInternalActivate(false),
   1091      mCheckedIsToggled(false),
   1092      mIndeterminate(false),
   1093      mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT),
   1094      mHasRange(false),
   1095      mIsDraggingRange(false),
   1096      mNumberControlSpinnerIsSpinning(false),
   1097      mNumberControlSpinnerSpinsUp(false),
   1098      mPickerRunning(false),
   1099      mIsPreviewEnabled(false),
   1100      mHasBeenTypePassword(false),
   1101      mHasPatternAttribute(false),
   1102      mRadioGroupContainer(nullptr) {
   1103  // If size is above 512, mozjemalloc allocates 1kB, see
   1104  // memory/build/mozjemalloc.cpp
   1105  static_assert(sizeof(HTMLInputElement) <= 512,
   1106                "Keep the size of HTMLInputElement under 512 to avoid "
   1107                "performance regression!");
   1108 
   1109  // We are in a type=text but we create TextControlState lazily.
   1110  mInputData.mState = nullptr;
   1111 
   1112  void* memory = mInputTypeMem;
   1113  mInputType = InputType::Create(this, mType, memory);
   1114 
   1115  if (!gUploadLastDir) HTMLInputElement::InitUploadLastDir();
   1116 
   1117  // Set up our default state.  By default we're enabled (since we're a control
   1118  // type that can be disabled but not actually disabled right now), optional,
   1119  // read-write, and valid. Also by default we don't have to show validity UI
   1120  // and so forth.
   1121  AddStatesSilently(ElementState::ENABLED | ElementState::OPTIONAL_ |
   1122                    ElementState::VALID | ElementState::VALUE_EMPTY |
   1123                    ElementState::READWRITE);
   1124  RemoveStatesSilently(ElementState::READONLY);
   1125  UpdateApzAwareFlag();
   1126 }
   1127 
   1128 HTMLInputElement::~HTMLInputElement() {
   1129  if (mNumberControlSpinnerIsSpinning) {
   1130    StopNumberControlSpinnerSpin(eDisallowDispatchingEvents);
   1131  }
   1132  nsImageLoadingContent::Destroy();
   1133  FreeData();
   1134 }
   1135 
   1136 void HTMLInputElement::FreeData() {
   1137  if (!IsSingleLineTextControl(false)) {
   1138    free(mInputData.mValue);
   1139    mInputData.mValue = nullptr;
   1140  } else if (mInputData.mState) {
   1141    // XXX Passing nullptr to UnbindFromFrame doesn't do anything!
   1142    UnbindFromFrame(nullptr);
   1143    mInputData.mState->Destroy();
   1144    mInputData.mState = nullptr;
   1145  }
   1146 
   1147  if (mInputType) {
   1148    mInputType->DropReference();
   1149    mInputType = nullptr;
   1150  }
   1151 }
   1152 
   1153 void HTMLInputElement::EnsureEditorState() {
   1154  MOZ_ASSERT(IsSingleLineTextControl(false));
   1155  if (!mInputData.mState) {
   1156    mInputData.mState = TextControlState::Construct(this);
   1157  }
   1158 }
   1159 
   1160 TextControlState* HTMLInputElement::GetEditorState() const {
   1161  if (!IsSingleLineTextControl(false)) {
   1162    return nullptr;
   1163  }
   1164 
   1165  // We've postponed allocating TextControlState, doing that in a const
   1166  // method is fine.
   1167  const_cast<HTMLInputElement*>(this)->EnsureEditorState();
   1168 
   1169  MOZ_ASSERT(mInputData.mState,
   1170             "Single line text controls need to have a state"
   1171             " associated with them");
   1172 
   1173  return mInputData.mState;
   1174 }
   1175 
   1176 // nsISupports
   1177 
   1178 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLInputElement)
   1179 
   1180 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
   1181                                                  TextControlElement)
   1182  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
   1183  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   1184  if (tmp->IsSingleLineTextControl(false) && tmp->mInputData.mState) {
   1185    tmp->mInputData.mState->Traverse(cb);
   1186  }
   1187 
   1188  if (tmp->mFileData) {
   1189    tmp->mFileData->Traverse(cb);
   1190  }
   1191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1192 
   1193 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
   1194                                                TextControlElement)
   1195  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
   1196  NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   1197  if (tmp->IsSingleLineTextControl(false) && tmp->mInputData.mState) {
   1198    tmp->mInputData.mState->Unlink();
   1199  }
   1200 
   1201  if (tmp->mFileData) {
   1202    tmp->mFileData->Unlink();
   1203  }
   1204  // XXX should unlink more?
   1205 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1206 
   1207 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLInputElement,
   1208                                             TextControlElement,
   1209                                             imgINotificationObserver,
   1210                                             nsIImageLoadingContent,
   1211                                             nsIConstraintValidation)
   1212 
   1213 // nsINode
   1214 
   1215 nsresult HTMLInputElement::Clone(dom::NodeInfo* aNodeInfo,
   1216                                 nsINode** aResult) const {
   1217  *aResult = nullptr;
   1218 
   1219  RefPtr<HTMLInputElement> it = new (aNodeInfo->NodeInfoManager())
   1220      HTMLInputElement(do_AddRef(aNodeInfo), NOT_FROM_PARSER, FromClone::Yes);
   1221 
   1222  nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it);
   1223  NS_ENSURE_SUCCESS(rv, rv);
   1224 
   1225  switch (GetValueMode()) {
   1226    case VALUE_MODE_VALUE:
   1227      if (mValueChanged) {
   1228        // We don't have our default value anymore.  Set our value on
   1229        // the clone.
   1230        nsAutoString value;
   1231        GetNonFileValueInternal(value);
   1232        // SetValueInternal handles setting the VALUE_CHANGED bit for us
   1233        if (NS_WARN_IF(
   1234                NS_FAILED(rv = it->SetValueInternal(
   1235                              value, {ValueSetterOption::SetValueChanged})))) {
   1236          return rv;
   1237        }
   1238      }
   1239      break;
   1240    case VALUE_MODE_FILENAME:
   1241      if (it->OwnerDoc()->IsStaticDocument()) {
   1242        // We're going to be used in print preview.  Since the doc is static
   1243        // we can just grab the pretty string and use it as wallpaper
   1244        GetDisplayFileName(it->mFileData->mStaticDocFileList);
   1245      } else {
   1246        it->mFileData->ClearGetFilesHelpers();
   1247        it->mFileData->mFilesOrDirectories.Clear();
   1248        it->mFileData->mFilesOrDirectories.AppendElements(
   1249            mFileData->mFilesOrDirectories);
   1250      }
   1251      break;
   1252    case VALUE_MODE_DEFAULT_ON:
   1253    case VALUE_MODE_DEFAULT:
   1254      break;
   1255  }
   1256 
   1257  if (mCheckedChanged) {
   1258    // We no longer have our original checked state.  Set our
   1259    // checked state on the clone.
   1260    it->DoSetChecked(mChecked, /* aNotify */ false,
   1261                     /* aSetValueChanged */ true);
   1262    // Then tell DoneCreatingElement() not to overwrite:
   1263    it->mShouldInitChecked = false;
   1264  }
   1265 
   1266  it->mIndeterminate = mIndeterminate;
   1267 
   1268  it->DoneCreatingElement();
   1269 
   1270  it->SetLastValueChangeWasInteractive(mLastValueChangeWasInteractive);
   1271  it.forget(aResult);
   1272  return NS_OK;
   1273 }
   1274 
   1275 void HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
   1276                                     const nsAttrValue* aValue, bool aNotify) {
   1277  if (aNameSpaceID == kNameSpaceID_None) {
   1278    if (aNotify && aName == nsGkAtoms::disabled) {
   1279      mDisabledChanged = true;
   1280    }
   1281 
   1282    // When name or type changes, radio should be removed from radio group.
   1283    // If we are not done creating the radio, we also should not do it.
   1284    if (mType == FormControlType::InputRadio) {
   1285      if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
   1286          (mForm || mDoneCreating)) {
   1287        RemoveFromRadioGroup();
   1288      } else if (aName == nsGkAtoms::required) {
   1289        auto* container = GetCurrentRadioGroupContainer();
   1290 
   1291        if (container && ((aValue && !HasAttr(aNameSpaceID, aName)) ||
   1292                          (!aValue && HasAttr(aNameSpaceID, aName)))) {
   1293          nsAutoString name;
   1294          GetAttr(nsGkAtoms::name, name);
   1295          container->RadioRequiredWillChange(name, !!aValue);
   1296        }
   1297      }
   1298    }
   1299 
   1300    if (aName == nsGkAtoms::webkitdirectory) {
   1301      glean::dom::webkit_directory_used
   1302          .EnumGet(glean::dom::WebkitDirectoryUsedLabel::eTrue)
   1303          .Add();
   1304    }
   1305  }
   1306 
   1307  return nsGenericHTMLFormControlElementWithState::BeforeSetAttr(
   1308      aNameSpaceID, aName, aValue, aNotify);
   1309 }
   1310 
   1311 void HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
   1312                                    const nsAttrValue* aValue,
   1313                                    const nsAttrValue* aOldValue,
   1314                                    nsIPrincipal* aSubjectPrincipal,
   1315                                    bool aNotify) {
   1316  if (aNameSpaceID == kNameSpaceID_None) {
   1317    bool needValidityUpdate = false;
   1318    if (aName == nsGkAtoms::src) {
   1319      nsAttrValueOrString value(aValue);
   1320      mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
   1321          this, value.String(), aSubjectPrincipal);
   1322      if (aNotify && mType == FormControlType::InputImage) {
   1323        if (aValue) {
   1324          // Mark channel as urgent-start before load image if the image load is
   1325          // initiated by a user interaction.
   1326          mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
   1327 
   1328          LoadImage(value.String(), true, aNotify, eImageLoadType_Normal,
   1329                    mSrcTriggeringPrincipal);
   1330        } else {
   1331          // Null value means the attr got unset; drop the image
   1332          CancelImageRequests(aNotify);
   1333        }
   1334      }
   1335    }
   1336 
   1337    if (aName == nsGkAtoms::value) {
   1338      // If the element has a value in value mode, the value content attribute
   1339      // is the default value. So if the elements value didn't change from the
   1340      // default, we have to re-set it.
   1341      if (!mValueChanged && GetValueMode() == VALUE_MODE_VALUE) {
   1342        SetDefaultValueAsValue();
   1343      } else if (GetValueMode() == VALUE_MODE_DEFAULT) {
   1344        ResetDirFormAssociatedElement(this, aNotify, HasDirAuto());
   1345      }
   1346      // GetStepBase() depends on the `value` attribute if `min` is not present,
   1347      // even if the value doesn't change.
   1348      UpdateStepMismatchValidityState();
   1349      needValidityUpdate = true;
   1350    }
   1351 
   1352    // Checked must be set no matter what type of control it is, since
   1353    // mChecked must reflect the new value
   1354    if (aName == nsGkAtoms::checked) {
   1355      if (IsRadioOrCheckbox()) {
   1356        SetStates(ElementState::DEFAULT, !!aValue, aNotify);
   1357      }
   1358      if (!mCheckedChanged) {
   1359        // Delay setting checked if we are creating this element (wait
   1360        // until everything is set)
   1361        if (!mDoneCreating) {
   1362          mShouldInitChecked = true;
   1363        } else {
   1364          DoSetChecked(!!aValue, aNotify, /* aSetValueChanged */ false);
   1365        }
   1366      }
   1367      needValidityUpdate = true;
   1368    }
   1369 
   1370    if (aName == nsGkAtoms::type) {
   1371      FormControlType newType;
   1372      if (!aValue) {
   1373        // We're now a text input.
   1374        newType = FormControlType(kInputDefaultType->value);
   1375      } else {
   1376        newType = FormControlType(aValue->GetEnumValue());
   1377      }
   1378      if (newType != mType) {
   1379        HandleTypeChange(newType, aNotify);
   1380        needValidityUpdate = true;
   1381      }
   1382    }
   1383 
   1384    // When name or type changes, radio should be added to radio group.
   1385    // If we are not done creating the radio, we also should not do it.
   1386    if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
   1387        mType == FormControlType::InputRadio && (mForm || mDoneCreating)) {
   1388      AddToRadioGroup();
   1389      UpdateValueMissingValidityStateForRadio(false);
   1390      needValidityUpdate = true;
   1391    }
   1392 
   1393    if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
   1394        aName == nsGkAtoms::readonly) {
   1395      if (aName == nsGkAtoms::disabled) {
   1396        // This *has* to be called *before* validity state check because
   1397        // UpdateBarredFromConstraintValidation and
   1398        // UpdateValueMissingValidityState depend on our disabled state.
   1399        UpdateDisabledState(aNotify);
   1400      }
   1401 
   1402      if (aName == nsGkAtoms::required && DoesRequiredApply()) {
   1403        // This *has* to be called *before* UpdateValueMissingValidityState
   1404        // because UpdateValueMissingValidityState depends on our required
   1405        // state.
   1406        UpdateRequiredState(!!aValue, aNotify);
   1407      }
   1408 
   1409      if (aName == nsGkAtoms::readonly && !!aValue != !!aOldValue) {
   1410        UpdateReadOnlyState(aNotify);
   1411      }
   1412 
   1413      UpdateValueMissingValidityState();
   1414 
   1415      // This *has* to be called *after* validity has changed.
   1416      if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
   1417        UpdateBarredFromConstraintValidation();
   1418      }
   1419      needValidityUpdate = true;
   1420    } else if (aName == nsGkAtoms::maxlength) {
   1421      UpdateTooLongValidityState();
   1422      needValidityUpdate = true;
   1423    } else if (aName == nsGkAtoms::minlength) {
   1424      UpdateTooShortValidityState();
   1425      needValidityUpdate = true;
   1426    } else if (aName == nsGkAtoms::pattern) {
   1427      // Although pattern attribute only applies to single line text controls,
   1428      // we set this flag for all input types to save having to check the type
   1429      // here.
   1430      mHasPatternAttribute = !!aValue;
   1431 
   1432      if (mDoneCreating) {
   1433        UpdatePatternMismatchValidityState();
   1434      }
   1435      needValidityUpdate = true;
   1436    } else if (aName == nsGkAtoms::multiple) {
   1437      UpdateTypeMismatchValidityState();
   1438      needValidityUpdate = true;
   1439    } else if (aName == nsGkAtoms::max) {
   1440      UpdateHasRange(aNotify);
   1441      mInputType->MinMaxStepAttrChanged();
   1442      // Validity state must be updated *after* the UpdateValueDueToAttrChange
   1443      // call above or else the following assert will not be valid.
   1444      // We don't assert the state of underflow during creation since
   1445      // DoneCreatingElement sanitizes.
   1446      UpdateRangeOverflowValidityState();
   1447      needValidityUpdate = true;
   1448      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
   1449                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
   1450                 "HTML5 spec does not allow underflow for type=range");
   1451    } else if (aName == nsGkAtoms::min) {
   1452      UpdateHasRange(aNotify);
   1453      mInputType->MinMaxStepAttrChanged();
   1454      // See corresponding @max comment
   1455      UpdateRangeUnderflowValidityState();
   1456      UpdateStepMismatchValidityState();
   1457      needValidityUpdate = true;
   1458      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
   1459                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
   1460                 "HTML5 spec does not allow underflow for type=range");
   1461    } else if (aName == nsGkAtoms::step) {
   1462      mInputType->MinMaxStepAttrChanged();
   1463      // See corresponding @max comment
   1464      UpdateStepMismatchValidityState();
   1465      needValidityUpdate = true;
   1466      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
   1467                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
   1468                 "HTML5 spec does not allow underflow for type=range");
   1469    } else if (aName == nsGkAtoms::dir && aValue &&
   1470               aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
   1471      ResetDirFormAssociatedElement(this, aNotify, true);
   1472    } else if (aName == nsGkAtoms::lang) {
   1473      // FIXME(emilio, bug 1651070): This doesn't account for lang changes on
   1474      // ancestors.
   1475      if (mType == FormControlType::InputNumber) {
   1476        // The validity of our value may have changed based on the locale.
   1477        UpdateValidityState();
   1478        needValidityUpdate = true;
   1479      }
   1480    } else if (aName == nsGkAtoms::autocomplete) {
   1481      // Clear the cached @autocomplete attribute and autocompleteInfo state.
   1482      mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
   1483      mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
   1484    } else if (aName == nsGkAtoms::placeholder) {
   1485      // Full addition / removals of the attribute reconstruct right now.
   1486      if (nsTextControlFrame* f = do_QueryFrame(GetPrimaryFrame())) {
   1487        f->PlaceholderChanged(aOldValue, aValue);
   1488      }
   1489      UpdatePlaceholderShownState();
   1490      needValidityUpdate = true;
   1491    }
   1492 
   1493    if (CreatesDateTimeWidget()) {
   1494      if (aName == nsGkAtoms::value || aName == nsGkAtoms::readonly ||
   1495          aName == nsGkAtoms::tabindex || aName == nsGkAtoms::required ||
   1496          aName == nsGkAtoms::disabled) {
   1497        // If original target is this and not the inner text control, we should
   1498        // pass the focus to the inner text control.
   1499        if (Element* dateTimeBoxElement = GetDateTimeBoxElement()) {
   1500          AsyncEventDispatcher::RunDOMEventWhenSafe(
   1501              *dateTimeBoxElement,
   1502              aName == nsGkAtoms::value ? u"MozDateTimeValueChanged"_ns
   1503                                        : u"MozDateTimeAttributeChanged"_ns,
   1504              CanBubble::eNo, ChromeOnlyDispatch::eNo);
   1505        }
   1506      }
   1507    }
   1508    if (needValidityUpdate) {
   1509      UpdateValidityElementStates(aNotify);
   1510    }
   1511  }
   1512 
   1513  return nsGenericHTMLFormControlElementWithState::AfterSetAttr(
   1514      aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
   1515 }
   1516 
   1517 void HTMLInputElement::BeforeSetForm(HTMLFormElement* aForm, bool aBindToTree) {
   1518  // No need to remove from radio group if we are just binding to tree.
   1519  if (mType == FormControlType::InputRadio && !aBindToTree) {
   1520    RemoveFromRadioGroup();
   1521  }
   1522 
   1523  // Dispatch event when <input> @form is set
   1524  if (!aBindToTree) {
   1525    MaybeDispatchLoginManagerEvents(aForm);
   1526  }
   1527 }
   1528 
   1529 void HTMLInputElement::AfterClearForm(bool aUnbindOrDelete) {
   1530  MOZ_ASSERT(!mForm);
   1531 
   1532  // Do not add back to radio group if we are releasing or unbinding from tree.
   1533  if (mType == FormControlType::InputRadio && !aUnbindOrDelete &&
   1534      !GetCurrentRadioGroupContainer()) {
   1535    AddToRadioGroup();
   1536    UpdateValueMissingValidityStateForRadio(false);
   1537  }
   1538 }
   1539 
   1540 void HTMLInputElement::ResultForDialogSubmit(nsAString& aResult) {
   1541  if (mType == FormControlType::InputImage) {
   1542    // Get a property set by the frame to find out where it was clicked.
   1543    const auto* lastClickedPoint =
   1544        static_cast<CSSIntPoint*>(GetProperty(nsGkAtoms::imageClickedPoint));
   1545    int32_t x, y;
   1546    if (lastClickedPoint) {
   1547      x = lastClickedPoint->x;
   1548      y = lastClickedPoint->y;
   1549    } else {
   1550      x = y = 0;
   1551    }
   1552    aResult.AppendInt(x);
   1553    aResult.AppendLiteral(",");
   1554    aResult.AppendInt(y);
   1555  } else {
   1556    GetAttr(nsGkAtoms::value, aResult);
   1557  }
   1558 }
   1559 
   1560 void HTMLInputElement::GetAutocomplete(nsAString& aValue) {
   1561  if (!DoesAutocompleteApply()) {
   1562    return;
   1563  }
   1564 
   1565  aValue.Truncate();
   1566  const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
   1567 
   1568  mAutocompleteAttrState = nsContentUtils::SerializeAutocompleteAttribute(
   1569      attributeVal, aValue, mAutocompleteAttrState);
   1570 }
   1571 
   1572 void HTMLInputElement::GetAutocompleteInfo(Nullable<AutocompleteInfo>& aInfo) {
   1573  if (!DoesAutocompleteApply()) {
   1574    aInfo.SetNull();
   1575    return;
   1576  }
   1577 
   1578  const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
   1579  mAutocompleteInfoState = nsContentUtils::SerializeAutocompleteAttribute(
   1580      attributeVal, aInfo.SetValue(), mAutocompleteInfoState, true);
   1581 }
   1582 
   1583 void HTMLInputElement::GetCapture(nsAString& aValue) {
   1584  GetEnumAttr(nsGkAtoms::capture, kCaptureDefault->tag, aValue);
   1585 }
   1586 
   1587 void HTMLInputElement::GetFormEnctype(nsAString& aValue) {
   1588  GetEnumAttr(nsGkAtoms::formenctype, "", kFormDefaultEnctype->tag, aValue);
   1589 }
   1590 
   1591 void HTMLInputElement::GetFormMethod(nsAString& aValue) {
   1592  GetEnumAttr(nsGkAtoms::formmethod, "", kFormDefaultMethod->tag, aValue);
   1593 }
   1594 
   1595 void HTMLInputElement::GetType(nsAString& aValue) const {
   1596  GetEnumAttr(nsGkAtoms::type, kInputDefaultType->tag, aValue);
   1597 }
   1598 
   1599 int32_t HTMLInputElement::TabIndexDefault() { return 0; }
   1600 
   1601 uint32_t HTMLInputElement::Height() {
   1602  if (mType != FormControlType::InputImage) {
   1603    return 0;
   1604  }
   1605  return GetWidthHeightForImage().height;
   1606 }
   1607 
   1608 void HTMLInputElement::SetIndeterminateInternal(bool aValue,
   1609                                                bool aShouldInvalidate) {
   1610  mIndeterminate = aValue;
   1611  if (mType != FormControlType::InputCheckbox) {
   1612    return;
   1613  }
   1614 
   1615  SetStates(ElementState::INDETERMINATE, aValue);
   1616 
   1617  if (aShouldInvalidate) {
   1618    // Repaint the frame
   1619    if (nsIFrame* frame = GetPrimaryFrame()) {
   1620      frame->InvalidateFrameSubtree();
   1621    }
   1622  }
   1623 }
   1624 
   1625 void HTMLInputElement::SetIndeterminate(bool aValue) {
   1626  SetIndeterminateInternal(aValue, true);
   1627 }
   1628 
   1629 uint32_t HTMLInputElement::Width() {
   1630  if (mType != FormControlType::InputImage) {
   1631    return 0;
   1632  }
   1633  return GetWidthHeightForImage().width;
   1634 }
   1635 
   1636 bool HTMLInputElement::SanitizesOnValueGetter() const {
   1637  // Don't return non-sanitized value for datetime types, email, or number.
   1638  return mType == FormControlType::InputEmail ||
   1639         mType == FormControlType::InputNumber || IsDateTimeInputType(mType);
   1640 }
   1641 
   1642 void HTMLInputElement::GetValue(nsAString& aValue, CallerType aCallerType) {
   1643  GetValueInternal(aValue, aCallerType);
   1644 
   1645  // In the case where we need to sanitize an input value without affecting
   1646  // the displayed user's input, we instead sanitize only on .value accesses.
   1647  // For the more general case of input elements displaying text that isn't
   1648  // their current value, see bug 805049.
   1649  if (SanitizesOnValueGetter()) {
   1650    SanitizeValue(aValue, SanitizationKind::ForValueGetter);
   1651  }
   1652 }
   1653 
   1654 void HTMLInputElement::GetValueInternal(nsAString& aValue,
   1655                                        CallerType aCallerType) const {
   1656  if (mType != FormControlType::InputFile) {
   1657    GetNonFileValueInternal(aValue);
   1658    return;
   1659  }
   1660 
   1661  if (aCallerType == CallerType::System) {
   1662    aValue.Assign(mFileData->mFirstFilePath);
   1663    return;
   1664  }
   1665 
   1666  if (mFileData->mFilesOrDirectories.IsEmpty()) {
   1667    aValue.Truncate();
   1668    return;
   1669  }
   1670 
   1671  nsAutoString file;
   1672  GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], file);
   1673  if (file.IsEmpty()) {
   1674    aValue.Truncate();
   1675    return;
   1676  }
   1677 
   1678  aValue.AssignLiteral("C:\\fakepath\\");
   1679  aValue.Append(file);
   1680 }
   1681 
   1682 void HTMLInputElement::GetNonFileValueInternal(nsAString& aValue) const {
   1683  switch (GetValueMode()) {
   1684    case VALUE_MODE_VALUE:
   1685      if (IsSingleLineTextControl(false)) {
   1686        if (mInputData.mState) {
   1687          mInputData.mState->GetValue(aValue, /* aForDisplay = */ false);
   1688        } else {
   1689          // Value hasn't been set yet.
   1690          aValue.Truncate();
   1691        }
   1692      } else if (!aValue.Assign(mInputData.mValue, fallible)) {
   1693        aValue.Truncate();
   1694      }
   1695      return;
   1696 
   1697    case VALUE_MODE_FILENAME:
   1698      MOZ_ASSERT_UNREACHABLE("Someone screwed up here");
   1699      // We'll just return empty string if someone does screw up.
   1700      aValue.Truncate();
   1701      return;
   1702 
   1703    case VALUE_MODE_DEFAULT:
   1704      // Treat defaultValue as value.
   1705      GetAttr(nsGkAtoms::value, aValue);
   1706      return;
   1707 
   1708    case VALUE_MODE_DEFAULT_ON:
   1709      // Treat default value as value and returns "on" if no value.
   1710      if (!GetAttr(nsGkAtoms::value, aValue)) {
   1711        aValue.AssignLiteral("on");
   1712      }
   1713      return;
   1714  }
   1715 }
   1716 
   1717 void HTMLInputElement::ClearFiles(bool aSetValueChanged) {
   1718  nsTArray<OwningFileOrDirectory> data;
   1719  SetFilesOrDirectories(data, aSetValueChanged);
   1720 }
   1721 
   1722 int32_t HTMLInputElement::MonthsSinceJan1970(uint32_t aYear,
   1723                                             uint32_t aMonth) const {
   1724  return (aYear - 1970) * 12 + aMonth - 1;
   1725 }
   1726 
   1727 /* static */
   1728 Decimal HTMLInputElement::StringToDecimal(const nsAString& aValue) {
   1729  auto d = nsContentUtils::ParseHTMLFloatingPointNumber(aValue);
   1730  return d ? Decimal::fromDouble(*d) : Decimal::nan();
   1731 }
   1732 
   1733 Decimal HTMLInputElement::GetValueAsDecimal() const {
   1734  nsAutoString stringValue;
   1735  GetNonFileValueInternal(stringValue);
   1736  Decimal result = mInputType->ConvertStringToNumber(stringValue).mResult;
   1737  return result.isFinite() ? result : Decimal::nan();
   1738 }
   1739 
   1740 void HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
   1741                                ErrorResult& aRv) {
   1742  // check security.  Note that setting the value to the empty string is always
   1743  // OK and gives pages a way to clear a file input if necessary.
   1744  if (mType == FormControlType::InputFile) {
   1745    if (!aValue.IsEmpty()) {
   1746      if (aCallerType != CallerType::System) {
   1747        // setting the value of a "FILE" input widget requires
   1748        // chrome privilege
   1749        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1750        return;
   1751      }
   1752      Sequence<nsString> list;
   1753      if (!list.AppendElement(aValue, fallible)) {
   1754        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1755        return;
   1756      }
   1757 
   1758      MozSetFileNameArray(list, aRv);
   1759      return;
   1760    }
   1761    ClearFiles(true);
   1762  } else {
   1763    if (MayFireChangeOnBlur()) {
   1764      // If the value has been set by a script, we basically want to keep the
   1765      // current change event state. If the element is ready to fire a change
   1766      // event, we should keep it that way. Otherwise, we should make sure the
   1767      // element will not fire any event because of the script interaction.
   1768      //
   1769      // NOTE: this is currently quite expensive work (too much string
   1770      // manipulation). We should probably optimize that.
   1771      nsAutoString currentValue;
   1772      GetNonFileValueInternal(currentValue);
   1773 
   1774      nsresult rv = SetValueInternal(
   1775          aValue, &currentValue,
   1776          {ValueSetterOption::ByContentAPI, ValueSetterOption::SetValueChanged,
   1777           ValueSetterOption::MoveCursorToEndIfValueChanged});
   1778      if (NS_FAILED(rv)) {
   1779        aRv.Throw(rv);
   1780        return;
   1781      }
   1782 
   1783      if (mFocusedValue.Equals(currentValue)) {
   1784        GetValue(mFocusedValue, aCallerType);
   1785      }
   1786    } else {
   1787      nsresult rv = SetValueInternal(
   1788          aValue,
   1789          {ValueSetterOption::ByContentAPI, ValueSetterOption::SetValueChanged,
   1790           ValueSetterOption::MoveCursorToEndIfValueChanged});
   1791      if (NS_FAILED(rv)) {
   1792        aRv.Throw(rv);
   1793        return;
   1794      }
   1795    }
   1796  }
   1797 }
   1798 
   1799 HTMLDataListElement* HTMLInputElement::GetList() const {
   1800  nsAutoString dataListId;
   1801  GetAttr(nsGkAtoms::list, dataListId);
   1802  if (dataListId.IsEmpty()) {
   1803    return nullptr;
   1804  }
   1805 
   1806  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
   1807  if (!docOrShadow) {
   1808    return nullptr;
   1809  }
   1810 
   1811  return HTMLDataListElement::FromNodeOrNull(
   1812      docOrShadow->GetElementById(dataListId));
   1813 }
   1814 
   1815 void HTMLInputElement::SetValue(Decimal aValue, CallerType aCallerType) {
   1816  MOZ_ASSERT(!aValue.isInfinity(), "aValue must not be Infinity!");
   1817 
   1818  if (aValue.isNaN()) {
   1819    SetValue(u""_ns, aCallerType, IgnoreErrors());
   1820    return;
   1821  }
   1822 
   1823  nsAutoString value;
   1824  mInputType->ConvertNumberToString(aValue, InputType::Localized::No, value);
   1825  SetValue(value, aCallerType, IgnoreErrors());
   1826 }
   1827 
   1828 void HTMLInputElement::GetValueAsDate(JSContext* aCx,
   1829                                      JS::MutableHandle<JSObject*> aObject,
   1830                                      ErrorResult& aRv) {
   1831  aObject.set(nullptr);
   1832  if (!IsDateTimeInputType(mType)) {
   1833    return;
   1834  }
   1835 
   1836  Maybe<JS::ClippedTime> time;
   1837 
   1838  switch (mType) {
   1839    case FormControlType::InputDate: {
   1840      uint32_t year, month, day;
   1841      nsAutoString value;
   1842      GetNonFileValueInternal(value);
   1843      if (!ParseDate(value, &year, &month, &day)) {
   1844        return;
   1845      }
   1846 
   1847      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day)));
   1848      break;
   1849    }
   1850    case FormControlType::InputTime: {
   1851      uint32_t millisecond;
   1852      nsAutoString value;
   1853      GetNonFileValueInternal(value);
   1854      if (!ParseTime(value, &millisecond)) {
   1855        return;
   1856      }
   1857 
   1858      time.emplace(JS::TimeClip(millisecond));
   1859      MOZ_ASSERT(time->toDouble() == millisecond,
   1860                 "HTML times are restricted to the day after the epoch and "
   1861                 "never clip");
   1862      break;
   1863    }
   1864    case FormControlType::InputMonth: {
   1865      uint32_t year, month;
   1866      nsAutoString value;
   1867      GetNonFileValueInternal(value);
   1868      if (!ParseMonth(value, &year, &month)) {
   1869        return;
   1870      }
   1871 
   1872      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, 1)));
   1873      break;
   1874    }
   1875    case FormControlType::InputWeek: {
   1876      uint32_t year, week;
   1877      nsAutoString value;
   1878      GetNonFileValueInternal(value);
   1879      if (!ParseWeek(value, &year, &week)) {
   1880        return;
   1881      }
   1882 
   1883      double days = DaysSinceEpochFromWeek(year, week);
   1884      time.emplace(JS::TimeClip(days * kMsPerDay));
   1885 
   1886      break;
   1887    }
   1888    case FormControlType::InputDatetimeLocal: {
   1889      uint32_t year, month, day, timeInMs;
   1890      nsAutoString value;
   1891      GetNonFileValueInternal(value);
   1892      if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) {
   1893        return;
   1894      }
   1895 
   1896      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day, timeInMs)));
   1897      break;
   1898    }
   1899    default:
   1900      break;
   1901  }
   1902 
   1903  if (time) {
   1904    aObject.set(JS::NewDateObject(aCx, *time));
   1905    if (!aObject) {
   1906      aRv.NoteJSContextException(aCx);
   1907    }
   1908    return;
   1909  }
   1910 
   1911  MOZ_ASSERT(false, "Unrecognized input type");
   1912  aRv.Throw(NS_ERROR_UNEXPECTED);
   1913 }
   1914 
   1915 void HTMLInputElement::SetValueAsDate(JSContext* aCx,
   1916                                      JS::Handle<JSObject*> aObj,
   1917                                      ErrorResult& aRv) {
   1918  if (!IsDateTimeInputType(mType)) {
   1919    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1920    return;
   1921  }
   1922 
   1923  if (aObj) {
   1924    bool isDate;
   1925    if (!JS::ObjectIsDate(aCx, aObj, &isDate)) {
   1926      aRv.NoteJSContextException(aCx);
   1927      return;
   1928    }
   1929    if (!isDate) {
   1930      aRv.ThrowTypeError("Value being assigned is not a date.");
   1931      return;
   1932    }
   1933  }
   1934 
   1935  double milliseconds;
   1936  if (aObj) {
   1937    if (!js::DateGetMsecSinceEpoch(aCx, aObj, &milliseconds)) {
   1938      aRv.NoteJSContextException(aCx);
   1939      return;
   1940    }
   1941  } else {
   1942    milliseconds = UnspecifiedNaN<double>();
   1943  }
   1944 
   1945  // At this point we know we're not a file input, so we can just pass "not
   1946  // system" as the caller type, since the caller type only matters in the file
   1947  // input case.
   1948  if (std::isnan(milliseconds)) {
   1949    SetValue(u""_ns, CallerType::NonSystem, aRv);
   1950    return;
   1951  }
   1952 
   1953  if (mType != FormControlType::InputMonth) {
   1954    SetValue(Decimal::fromDouble(milliseconds), CallerType::NonSystem);
   1955    return;
   1956  }
   1957 
   1958  // type=month expects the value to be number of months.
   1959  double year = JS::YearFromTime(milliseconds);
   1960  double month = JS::MonthFromTime(milliseconds);
   1961 
   1962  if (std::isnan(year) || std::isnan(month)) {
   1963    SetValue(u""_ns, CallerType::NonSystem, aRv);
   1964    return;
   1965  }
   1966 
   1967  int32_t months = MonthsSinceJan1970(year, month + 1);
   1968  SetValue(Decimal(int32_t(months)), CallerType::NonSystem);
   1969 }
   1970 
   1971 void HTMLInputElement::SetValueAsNumber(double aValueAsNumber,
   1972                                        ErrorResult& aRv) {
   1973  if (std::isinf(aValueAsNumber)) {
   1974    aRv.ThrowTypeError("Value being assigned is infinite.");
   1975    return;
   1976  }
   1977 
   1978  if (!DoesValueAsNumberApply()) {
   1979    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1980    return;
   1981  }
   1982 
   1983  // At this point we know we're not a file input, so we can just pass "not
   1984  // system" as the caller type, since the caller type only matters in the file
   1985  // input case.
   1986  SetValue(Decimal::fromDouble(aValueAsNumber), CallerType::NonSystem);
   1987 }
   1988 
   1989 Decimal HTMLInputElement::GetMinimum() const {
   1990  MOZ_ASSERT(
   1991      DoesValueAsNumberApply(),
   1992      "GetMinimum() should only be used for types that allow .valueAsNumber");
   1993 
   1994  // Only type=range has a default minimum
   1995  Decimal defaultMinimum =
   1996      mType == FormControlType::InputRange ? Decimal(0) : Decimal::nan();
   1997 
   1998  if (!HasAttr(nsGkAtoms::min)) {
   1999    return defaultMinimum;
   2000  }
   2001 
   2002  nsAutoString minStr;
   2003  GetAttr(nsGkAtoms::min, minStr);
   2004 
   2005  Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
   2006  return min.isFinite() ? min : defaultMinimum;
   2007 }
   2008 
   2009 Decimal HTMLInputElement::GetMaximum() const {
   2010  MOZ_ASSERT(
   2011      DoesValueAsNumberApply(),
   2012      "GetMaximum() should only be used for types that allow .valueAsNumber");
   2013 
   2014  // Only type=range has a default maximum
   2015  Decimal defaultMaximum =
   2016      mType == FormControlType::InputRange ? Decimal(100) : Decimal::nan();
   2017 
   2018  if (!HasAttr(nsGkAtoms::max)) {
   2019    return defaultMaximum;
   2020  }
   2021 
   2022  nsAutoString maxStr;
   2023  GetAttr(nsGkAtoms::max, maxStr);
   2024 
   2025  Decimal max = mInputType->ConvertStringToNumber(maxStr).mResult;
   2026  return max.isFinite() ? max : defaultMaximum;
   2027 }
   2028 
   2029 Decimal HTMLInputElement::GetStepBase() const {
   2030  MOZ_ASSERT(IsDateTimeInputType(mType) ||
   2031                 mType == FormControlType::InputNumber ||
   2032                 mType == FormControlType::InputRange,
   2033             "Check that kDefaultStepBase is correct for this new type");
   2034  // Do NOT use GetMinimum here - the spec says to use "the min content
   2035  // attribute", not "the minimum".
   2036  nsAutoString minStr;
   2037  if (GetAttr(nsGkAtoms::min, minStr)) {
   2038    Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
   2039    if (min.isFinite()) {
   2040      return min;
   2041    }
   2042  }
   2043 
   2044  // If @min is not a double, we should use @value.
   2045  nsAutoString valueStr;
   2046  if (GetAttr(nsGkAtoms::value, valueStr)) {
   2047    Decimal value = mInputType->ConvertStringToNumber(valueStr).mResult;
   2048    if (value.isFinite()) {
   2049      return value;
   2050    }
   2051  }
   2052 
   2053  if (mType == FormControlType::InputWeek) {
   2054    return kDefaultStepBaseWeek;
   2055  }
   2056 
   2057  return kDefaultStepBase;
   2058 }
   2059 
   2060 void HTMLInputElement::GetColor(InputPickerColor& aValue) {
   2061  MOZ_ASSERT(mType == FormControlType::InputColor,
   2062             "getColor is only for type=color.");
   2063 
   2064  nsAutoString value;
   2065  GetValue(value, CallerType::System);
   2066 
   2067  StyleAbsoluteColor color =
   2068      MaybeComputeColor(OwnerDoc(), value).valueOr(StyleAbsoluteColor::BLACK);
   2069  aValue.mComponent1 = color.components._0;
   2070  aValue.mComponent2 = color.components._1;
   2071  aValue.mComponent3 = color.components._2;
   2072  // aValue.mAlpha = color.alpha;
   2073  // aValue.mColorSpace = mColorSpace;
   2074 }
   2075 
   2076 void HTMLInputElement::SetUserInputColor(const InputPickerColor& aValue) {
   2077  MOZ_ASSERT(mType == FormControlType::InputColor,
   2078             "setUserInputColor is only for type=color.");
   2079 
   2080  // TODO(krosylight): We should ultimately get a helper method where the compat
   2081  // serialization happens only conditionally
   2082  nsAutoString serialized;
   2083  SerializeColorForHTMLCompatibility(
   2084      StyleAbsoluteColor{
   2085          .components =
   2086              StyleColorComponents{
   2087                  ._0 = aValue.mComponent1,
   2088                  ._1 = aValue.mComponent2,
   2089                  ._2 = aValue.mComponent3,
   2090              },
   2091          .alpha = 1,
   2092          .color_space = StyleColorSpace::Srgb,
   2093      },
   2094      serialized);
   2095 
   2096  // (We are either Chrome/UA but the principal doesn't matter for color inputs)
   2097  SetUserInput(serialized, *NodePrincipal());
   2098 }
   2099 
   2100 Decimal HTMLInputElement::GetValueIfStepped(int32_t aStep,
   2101                                            StepCallerType aCallerType,
   2102                                            ErrorResult& aRv) {
   2103  constexpr auto kNaN = Decimal::nan();
   2104  if (!DoStepDownStepUpApply()) {
   2105    aRv.ThrowInvalidStateError("Step doesn't apply to this input type");
   2106    return kNaN;
   2107  }
   2108 
   2109  Decimal stepBase = GetStepBase();
   2110  Decimal step = GetStep();
   2111  if (step == kStepAny) {
   2112    if (aCallerType != StepCallerType::ForUserEvent) {
   2113      aRv.ThrowInvalidStateError("Can't step an input with step=\"any\"");
   2114      return kNaN;
   2115    }
   2116    // Allow the spin buttons and up/down arrow keys to do something sensible:
   2117    step = GetDefaultStep();
   2118  }
   2119 
   2120  Decimal minimum = GetMinimum();
   2121  Decimal maximum = GetMaximum();
   2122 
   2123  if (!maximum.isNaN()) {
   2124    // "max - (max - stepBase) % step" is the nearest valid value to max.
   2125    maximum = maximum - NS_floorModulo(maximum - stepBase, step);
   2126    if (!minimum.isNaN()) {
   2127      if (minimum > maximum) {
   2128        // Either the minimum was greater than the maximum prior to our
   2129        // adjustment to align maximum on a step, or else (if we adjusted
   2130        // maximum) there is no valid step between minimum and the unadjusted
   2131        // maximum.
   2132        return kNaN;
   2133      }
   2134    }
   2135  }
   2136 
   2137  Decimal value = GetValueAsDecimal();
   2138  bool valueWasNaN = false;
   2139  if (value.isNaN()) {
   2140    value = Decimal(0);
   2141    valueWasNaN = true;
   2142  }
   2143  Decimal valueBeforeStepping = value;
   2144 
   2145  Decimal deltaFromStep = NS_floorModulo(value - stepBase, step);
   2146 
   2147  if (deltaFromStep != Decimal(0)) {
   2148    if (aStep > 0) {
   2149      value += step - deltaFromStep;       // partial step
   2150      value += step * Decimal(aStep - 1);  // then remaining steps
   2151    } else if (aStep < 0) {
   2152      value -= deltaFromStep;              // partial step
   2153      value += step * Decimal(aStep + 1);  // then remaining steps
   2154    }
   2155  } else {
   2156    value += step * Decimal(aStep);
   2157  }
   2158 
   2159  if (value < minimum) {
   2160    value = minimum;
   2161    deltaFromStep = NS_floorModulo(value - stepBase, step);
   2162    if (deltaFromStep != Decimal(0)) {
   2163      value += step - deltaFromStep;
   2164    }
   2165  }
   2166  if (value > maximum) {
   2167    value = maximum;
   2168    deltaFromStep = NS_floorModulo(value - stepBase, step);
   2169    if (deltaFromStep != Decimal(0)) {
   2170      value -= deltaFromStep;
   2171    }
   2172  }
   2173 
   2174  if (!valueWasNaN &&  // value="", resulting in us using "0"
   2175      ((aStep > 0 && value < valueBeforeStepping) ||
   2176       (aStep < 0 && value > valueBeforeStepping))) {
   2177    // We don't want step-up to effectively step down, or step-down to
   2178    // effectively step up, so return;
   2179    return kNaN;
   2180  }
   2181 
   2182  return value;
   2183 }
   2184 
   2185 void HTMLInputElement::ApplyStep(int32_t aStep, ErrorResult& aRv) {
   2186  Decimal nextStep = GetValueIfStepped(aStep, StepCallerType::ForScript, aRv);
   2187  if (aRv.Failed() || !nextStep.isFinite()) {
   2188    return;
   2189  }
   2190  // We know we're not a file input, so the caller type does not matter; just
   2191  // pass "not system" to be safe.
   2192  SetValue(nextStep, CallerType::NonSystem);
   2193 }
   2194 
   2195 bool HTMLInputElement::IsDateTimeInputType(FormControlType aType) {
   2196  switch (aType) {
   2197    case FormControlType::InputDate:
   2198    case FormControlType::InputTime:
   2199    case FormControlType::InputMonth:
   2200    case FormControlType::InputWeek:
   2201    case FormControlType::InputDatetimeLocal:
   2202      return true;
   2203    default:
   2204      return false;
   2205  }
   2206 }
   2207 
   2208 void HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
   2209                                           ErrorResult& aRv) {
   2210  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2211    return;
   2212  }
   2213 
   2214  const nsTArray<OwningFileOrDirectory>& filesOrDirs =
   2215      GetFilesOrDirectoriesInternal();
   2216  for (uint32_t i = 0; i < filesOrDirs.Length(); i++) {
   2217    nsAutoString str;
   2218    GetDOMFileOrDirectoryPath(filesOrDirs[i], str, aRv);
   2219    if (NS_WARN_IF(aRv.Failed())) {
   2220      return;
   2221    }
   2222 
   2223    aArray.AppendElement(str);
   2224  }
   2225 }
   2226 
   2227 void HTMLInputElement::MozSetFileArray(
   2228    const Sequence<OwningNonNull<File>>& aFiles) {
   2229  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2230    return;
   2231  }
   2232 
   2233  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   2234  MOZ_ASSERT(global);
   2235  if (!global) {
   2236    return;
   2237  }
   2238 
   2239  nsTArray<OwningFileOrDirectory> files;
   2240  for (uint32_t i = 0; i < aFiles.Length(); ++i) {
   2241    RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
   2242    if (NS_WARN_IF(!file)) {
   2243      return;
   2244    }
   2245 
   2246    OwningFileOrDirectory* element = files.AppendElement();
   2247    element->SetAsFile() = file;
   2248  }
   2249 
   2250  SetFilesOrDirectories(files, true);
   2251 }
   2252 
   2253 void HTMLInputElement::MozSetFileNameArray(const Sequence<nsString>& aFileNames,
   2254                                           ErrorResult& aRv) {
   2255  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2256    return;
   2257  }
   2258 
   2259  if (XRE_IsContentProcess()) {
   2260    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   2261    return;
   2262  }
   2263 
   2264  nsTArray<OwningFileOrDirectory> files;
   2265  for (uint32_t i = 0; i < aFileNames.Length(); ++i) {
   2266    nsCOMPtr<nsIFile> file;
   2267 
   2268    if (StringBeginsWith(aFileNames[i], u"file:"_ns,
   2269                         nsASCIICaseInsensitiveStringComparator)) {
   2270      // Converts the URL string into the corresponding nsIFile if possible
   2271      // A local file will be created if the URL string begins with file://
   2272      (void)NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(aFileNames[i]),
   2273                                  getter_AddRefs(file));
   2274    }
   2275 
   2276    if (!file) {
   2277      // this is no "file://", try as local file
   2278      (void)NS_NewLocalFile(aFileNames[i], getter_AddRefs(file));
   2279    }
   2280 
   2281    if (!file) {
   2282      continue;  // Not much we can do if the file doesn't exist
   2283    }
   2284 
   2285    nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   2286    if (!global) {
   2287      aRv.Throw(NS_ERROR_FAILURE);
   2288      return;
   2289    }
   2290 
   2291    RefPtr<File> domFile = File::CreateFromFile(global, file);
   2292    if (NS_WARN_IF(!domFile)) {
   2293      aRv.Throw(NS_ERROR_FAILURE);
   2294      return;
   2295    }
   2296 
   2297    OwningFileOrDirectory* element = files.AppendElement();
   2298    element->SetAsFile() = domFile;
   2299  }
   2300 
   2301  SetFilesOrDirectories(files, true);
   2302 }
   2303 
   2304 void HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
   2305                                       ErrorResult& aRv) {
   2306  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2307    return;
   2308  }
   2309 
   2310  nsCOMPtr<nsIFile> file;
   2311  aRv = NS_NewLocalFile(aDirectoryPath, getter_AddRefs(file));
   2312  if (NS_WARN_IF(aRv.Failed())) {
   2313    return;
   2314  }
   2315 
   2316  nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   2317  if (NS_WARN_IF(!window)) {
   2318    aRv.Throw(NS_ERROR_FAILURE);
   2319    return;
   2320  }
   2321 
   2322  RefPtr<Directory> directory = Directory::Create(window->AsGlobal(), file);
   2323  MOZ_ASSERT(directory);
   2324 
   2325  nsTArray<OwningFileOrDirectory> array;
   2326  OwningFileOrDirectory* element = array.AppendElement();
   2327  element->SetAsDirectory() = directory;
   2328 
   2329  SetFilesOrDirectories(array, true);
   2330 }
   2331 
   2332 void HTMLInputElement::GetDateTimeInputBoxValue(DateTimeValue& aValue) {
   2333  if (NS_WARN_IF(!IsDateTimeInputType(mType)) || !mDateTimeInputBoxValue) {
   2334    return;
   2335  }
   2336 
   2337  aValue = *mDateTimeInputBoxValue;
   2338 }
   2339 
   2340 Element* HTMLInputElement::GetDateTimeBoxElement() {
   2341  if (!GetShadowRoot()) {
   2342    return nullptr;
   2343  }
   2344 
   2345  // The datetimebox <div> is the only child of the UA Widget Shadow Root
   2346  // if it is present.
   2347  MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
   2348  MOZ_ASSERT(1 >= GetShadowRoot()->GetChildCount());
   2349  if (nsIContent* inputAreaContent = GetShadowRoot()->GetFirstChild()) {
   2350    return inputAreaContent->AsElement();
   2351  }
   2352 
   2353  return nullptr;
   2354 }
   2355 
   2356 void HTMLInputElement::OpenDateTimePicker(const DateTimeValue& aInitialValue) {
   2357  if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
   2358    return;
   2359  }
   2360 
   2361  mDateTimeInputBoxValue = MakeUnique<DateTimeValue>(aInitialValue);
   2362  nsContentUtils::DispatchChromeEvent(OwnerDoc(), this,
   2363                                      u"MozOpenDateTimePicker"_ns,
   2364                                      CanBubble::eYes, Cancelable::eYes);
   2365 }
   2366 
   2367 void HTMLInputElement::CloseDateTimePicker() {
   2368  if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
   2369    return;
   2370  }
   2371 
   2372  nsContentUtils::DispatchChromeEvent(OwnerDoc(), this,
   2373                                      u"MozCloseDateTimePicker"_ns,
   2374                                      CanBubble::eYes, Cancelable::eYes);
   2375 }
   2376 
   2377 void HTMLInputElement::SetOpenState(bool aIsOpen) {
   2378  SetStates(ElementState::OPEN, aIsOpen);
   2379 }
   2380 
   2381 void HTMLInputElement::OpenColorPicker() {
   2382  if (NS_WARN_IF(mType != FormControlType::InputColor)) {
   2383    return;
   2384  }
   2385 
   2386  nsContentUtils::DispatchChromeEvent(OwnerDoc(), this,
   2387                                      u"MozOpenColorPicker"_ns, CanBubble::eYes,
   2388                                      Cancelable::eYes);
   2389 }
   2390 
   2391 void HTMLInputElement::SetFocusState(bool aIsFocused) {
   2392  if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
   2393    return;
   2394  }
   2395  SetStates(ElementState::FOCUS | ElementState::FOCUSRING, aIsFocused);
   2396 }
   2397 
   2398 void HTMLInputElement::UpdateValidityState() {
   2399  if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
   2400    return;
   2401  }
   2402 
   2403  // For now, datetime input box call this function only when the value may
   2404  // become valid/invalid. For other validity states, they will be updated when
   2405  // .value is actually changed.
   2406  UpdateBadInputValidityState();
   2407  UpdateValidityElementStates(true);
   2408 }
   2409 
   2410 bool HTMLInputElement::MozIsTextField(bool aExcludePassword) {
   2411  // TODO: temporary until bug 888320 is fixed.
   2412  //
   2413  // FIXME: Historically we never returned true for `number`, we should consider
   2414  // changing that now that it is similar to other inputs.
   2415  if (IsDateTimeInputType(mType) || mType == FormControlType::InputNumber) {
   2416    return false;
   2417  }
   2418 
   2419  return IsSingleLineTextControl(aExcludePassword);
   2420 }
   2421 
   2422 void HTMLInputElement::SetUserInput(const nsAString& aValue,
   2423                                    nsIPrincipal& aSubjectPrincipal) {
   2424  AutoHandlingUserInputStatePusher inputStatePusher(true);
   2425 
   2426  if (mType == FormControlType::InputFile &&
   2427      !aSubjectPrincipal.IsSystemPrincipal()) {
   2428    return;
   2429  }
   2430 
   2431  if (mType == FormControlType::InputFile) {
   2432    Sequence<nsString> list;
   2433    if (!list.AppendElement(aValue, fallible)) {
   2434      return;
   2435    }
   2436 
   2437    MozSetFileNameArray(list, IgnoreErrors());
   2438    return;
   2439  }
   2440 
   2441  bool isInputEventDispatchedByTextControlState =
   2442      GetValueMode() == VALUE_MODE_VALUE && IsSingleLineTextControl(false);
   2443 
   2444  nsresult rv = SetValueInternal(
   2445      aValue,
   2446      {ValueSetterOption::BySetUserInputAPI, ValueSetterOption::SetValueChanged,
   2447       ValueSetterOption::MoveCursorToEndIfValueChanged});
   2448  NS_ENSURE_SUCCESS_VOID(rv);
   2449 
   2450  if (!isInputEventDispatchedByTextControlState) {
   2451    DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
   2452    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   2453                         "Failed to dispatch input event");
   2454  }
   2455 
   2456  // If this element is not currently focused, it won't receive a change event
   2457  // for this update through the normal channels. So fire a change event
   2458  // immediately, instead.
   2459  if (CreatesDateTimeWidget() || !ShouldBlur(this)) {
   2460    FireChangeEventIfNeeded();
   2461  }
   2462 }
   2463 
   2464 nsIEditor* HTMLInputElement::GetEditorForBindings() {
   2465  if (!GetPrimaryFrame()) {
   2466    // Ensure we construct frames (and thus an editor) if needed.
   2467    GetPrimaryFrame(FlushType::Frames);
   2468  }
   2469  return GetTextEditorFromState();
   2470 }
   2471 
   2472 bool HTMLInputElement::HasEditor() const { return !!GetExtantTextEditor(); }
   2473 
   2474 TextEditor* HTMLInputElement::GetTextEditorFromState() {
   2475  TextControlState* state = GetEditorState();
   2476  if (state) {
   2477    return state->GetTextEditor();
   2478  }
   2479  return nullptr;
   2480 }
   2481 
   2482 TextEditor* HTMLInputElement::GetTextEditor() {
   2483  return GetTextEditorFromState();
   2484 }
   2485 
   2486 TextEditor* HTMLInputElement::GetExtantTextEditor() const {
   2487  const TextControlState* const state = GetEditorState();
   2488  if (!state) {
   2489    return nullptr;
   2490  }
   2491  return state->GetExtantTextEditor();
   2492 }
   2493 
   2494 nsISelectionController* HTMLInputElement::GetSelectionController() {
   2495  TextControlState* state = GetEditorState();
   2496  if (state) {
   2497    return state->GetSelectionController();
   2498  }
   2499  return nullptr;
   2500 }
   2501 
   2502 nsFrameSelection* HTMLInputElement::GetIndependentFrameSelection() const {
   2503  TextControlState* state = GetEditorState();
   2504  if (state) {
   2505    return state->GetIndependentFrameSelection();
   2506  }
   2507  return nullptr;
   2508 }
   2509 
   2510 nsresult HTMLInputElement::BindToFrame(nsTextControlFrame* aFrame) {
   2511  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   2512  TextControlState* state = GetEditorState();
   2513  if (state) {
   2514    return state->BindToFrame(aFrame);
   2515  }
   2516  return NS_ERROR_FAILURE;
   2517 }
   2518 
   2519 void HTMLInputElement::UnbindFromFrame(nsTextControlFrame* aFrame) {
   2520  TextControlState* state = GetEditorState();
   2521  if (state && aFrame) {
   2522    state->UnbindFromFrame(aFrame);
   2523  }
   2524 }
   2525 
   2526 nsresult HTMLInputElement::CreateEditor() {
   2527  TextControlState* state = GetEditorState();
   2528  if (state) {
   2529    return state->PrepareEditor();
   2530  }
   2531  return NS_ERROR_FAILURE;
   2532 }
   2533 
   2534 void HTMLInputElement::SetPreviewValue(const nsAString& aValue) {
   2535  TextControlState* state = GetEditorState();
   2536  if (state) {
   2537    state->SetPreviewText(aValue, true);
   2538  }
   2539 }
   2540 
   2541 void HTMLInputElement::GetPreviewValue(nsAString& aValue) {
   2542  TextControlState* state = GetEditorState();
   2543  if (state) {
   2544    state->GetPreviewText(aValue);
   2545  }
   2546 }
   2547 
   2548 void HTMLInputElement::EnablePreview() {
   2549  if (mIsPreviewEnabled) {
   2550    return;
   2551  }
   2552 
   2553  mIsPreviewEnabled = true;
   2554  // Reconstruct the frame to append an anonymous preview node
   2555  nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0},
   2556                                  nsChangeHint_ReconstructFrame);
   2557 }
   2558 
   2559 bool HTMLInputElement::IsPreviewEnabled() { return mIsPreviewEnabled; }
   2560 
   2561 void HTMLInputElement::GetDisplayFileName(nsAString& aValue) const {
   2562  MOZ_ASSERT(mFileData);
   2563 
   2564  if (OwnerDoc()->IsStaticDocument()) {
   2565    aValue = mFileData->mStaticDocFileList;
   2566    return;
   2567  }
   2568 
   2569  if (mFileData->mFilesOrDirectories.Length() == 1) {
   2570    GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], aValue);
   2571    return;
   2572  }
   2573 
   2574  nsAutoString value;
   2575 
   2576  if (mFileData->mFilesOrDirectories.IsEmpty()) {
   2577    if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
   2578        HasAttr(nsGkAtoms::webkitdirectory)) {
   2579      nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
   2580                                              "NoDirSelected", OwnerDoc(),
   2581                                              value);
   2582    } else if (HasAttr(nsGkAtoms::multiple)) {
   2583      nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
   2584                                              "NoFilesSelected", OwnerDoc(),
   2585                                              value);
   2586    } else {
   2587      nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
   2588                                              "NoFileSelected", OwnerDoc(),
   2589                                              value);
   2590    }
   2591  } else {
   2592    nsString count;
   2593    count.AppendInt(int(mFileData->mFilesOrDirectories.Length()));
   2594 
   2595    nsContentUtils::FormatMaybeLocalizedString(
   2596        value, nsContentUtils::eFORMS_PROPERTIES, "XFilesSelected", OwnerDoc(),
   2597        count);
   2598  }
   2599 
   2600  aValue = value;
   2601 }
   2602 
   2603 const nsTArray<OwningFileOrDirectory>&
   2604 HTMLInputElement::GetFilesOrDirectoriesInternal() const {
   2605  return mFileData->mFilesOrDirectories;
   2606 }
   2607 
   2608 void HTMLInputElement::SetFilesOrDirectories(
   2609    const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
   2610    bool aSetValueChanged) {
   2611  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2612    return;
   2613  }
   2614 
   2615  MOZ_ASSERT(mFileData);
   2616 
   2617  mFileData->ClearGetFilesHelpers();
   2618 
   2619  if (StaticPrefs::dom_webkitBlink_filesystem_enabled()) {
   2620    HTMLInputElement_Binding::ClearCachedWebkitEntriesValue(this);
   2621    mFileData->mEntries.Clear();
   2622  }
   2623 
   2624  mFileData->mFilesOrDirectories.Clear();
   2625  mFileData->mFilesOrDirectories.AppendElements(aFilesOrDirectories);
   2626 
   2627  AfterSetFilesOrDirectories(aSetValueChanged);
   2628 }
   2629 
   2630 void HTMLInputElement::SetFiles(FileList* aFiles, bool aSetValueChanged) {
   2631  MOZ_ASSERT(mFileData);
   2632 
   2633  mFileData->mFilesOrDirectories.Clear();
   2634  mFileData->ClearGetFilesHelpers();
   2635 
   2636  if (StaticPrefs::dom_webkitBlink_filesystem_enabled()) {
   2637    HTMLInputElement_Binding::ClearCachedWebkitEntriesValue(this);
   2638    mFileData->mEntries.Clear();
   2639  }
   2640 
   2641  if (aFiles) {
   2642    uint32_t listLength = aFiles->Length();
   2643    for (uint32_t i = 0; i < listLength; i++) {
   2644      OwningFileOrDirectory* element =
   2645          mFileData->mFilesOrDirectories.AppendElement();
   2646      element->SetAsFile() = aFiles->Item(i);
   2647    }
   2648  }
   2649 
   2650  AfterSetFilesOrDirectories(aSetValueChanged);
   2651 }
   2652 
   2653 // This method is used for testing only.
   2654 void HTMLInputElement::MozSetDndFilesAndDirectories(
   2655    const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories) {
   2656  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   2657    return;
   2658  }
   2659 
   2660  SetFilesOrDirectories(aFilesOrDirectories, true);
   2661 
   2662  if (StaticPrefs::dom_webkitBlink_filesystem_enabled()) {
   2663    UpdateEntries(aFilesOrDirectories);
   2664  }
   2665 
   2666  RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
   2667      new DispatchChangeEventCallback(this);
   2668 
   2669  if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
   2670      HasAttr(nsGkAtoms::webkitdirectory)) {
   2671    ErrorResult rv;
   2672    GetFilesHelper* helper =
   2673        GetOrCreateGetFilesHelper(true /* recursionFlag */, rv);
   2674    if (NS_WARN_IF(rv.Failed())) {
   2675      rv.SuppressException();
   2676      return;
   2677    }
   2678 
   2679    helper->AddCallback(dispatchChangeEventCallback);
   2680  } else {
   2681    dispatchChangeEventCallback->DispatchEvents();
   2682  }
   2683 }
   2684 
   2685 void HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged) {
   2686  // No need to flush here, if there's no frame at this point we
   2687  // don't need to force creation of one just to tell it about this
   2688  // new value.  We just want the display to update as needed.
   2689  if (nsFileControlFrame* f = do_QueryFrame(GetPrimaryFrame())) {
   2690    f->SelectedFilesUpdated();
   2691  }
   2692 
   2693  // Grab the full path here for any chrome callers who access our .value via a
   2694  // CPOW. This path won't be called from a CPOW meaning the potential sync IPC
   2695  // call under GetMozFullPath won't be rejected for not being urgent.
   2696  if (mFileData->mFilesOrDirectories.IsEmpty()) {
   2697    mFileData->mFirstFilePath.Truncate();
   2698  } else {
   2699    ErrorResult rv;
   2700    GetDOMFileOrDirectoryPath(mFileData->mFilesOrDirectories[0],
   2701                              mFileData->mFirstFilePath, rv);
   2702    if (NS_WARN_IF(rv.Failed())) {
   2703      rv.SuppressException();
   2704    }
   2705  }
   2706 
   2707  // Null out |mFileData->mFileList| to return a new file list when asked for.
   2708  // Don't clear it since the file list might come from the user via SetFiles.
   2709  if (mFileData->mFileList) {
   2710    mFileData->mFileList = nullptr;
   2711  }
   2712 
   2713  if (aSetValueChanged) {
   2714    SetValueChanged(true);
   2715  }
   2716 
   2717  UpdateAllValidityStates(true);
   2718 }
   2719 
   2720 void HTMLInputElement::FireChangeEventIfNeeded() {
   2721  if (!MayFireChangeOnBlur()) {
   2722    return;
   2723  }
   2724 
   2725  // We're not exposing the GetValue return value anywhere here, so it's safe to
   2726  // claim to be a system caller.
   2727  nsAutoString value;
   2728  GetValue(value, CallerType::System);
   2729 
   2730  // NOTE(emilio): Per spec we should not set this if we don't fire the change
   2731  // event, but that seems like a bug. Using mValueChanged seems reasonable to
   2732  // keep the expected behavior while
   2733  // https://github.com/whatwg/html/issues/10013 is resolved.
   2734  if (mValueChanged) {
   2735    SetUserInteracted(true);
   2736  }
   2737  if (mFocusedValue.Equals(value)) {
   2738    return;
   2739  }
   2740  // Dispatch the change event.
   2741  mFocusedValue = value;
   2742  nsContentUtils::DispatchTrustedEvent(
   2743      OwnerDoc(), static_cast<nsIContent*>(this), u"change"_ns, CanBubble::eYes,
   2744      Cancelable::eNo);
   2745 }
   2746 
   2747 FileList* HTMLInputElement::GetFiles() {
   2748  if (mType != FormControlType::InputFile) {
   2749    return nullptr;
   2750  }
   2751 
   2752  if (!mFileData->mFileList) {
   2753    mFileData->mFileList = new FileList(static_cast<nsIContent*>(this));
   2754    for (const OwningFileOrDirectory& item : GetFilesOrDirectoriesInternal()) {
   2755      if (item.IsFile()) {
   2756        mFileData->mFileList->Append(item.GetAsFile());
   2757      }
   2758    }
   2759  }
   2760 
   2761  return mFileData->mFileList;
   2762 }
   2763 
   2764 void HTMLInputElement::SetFiles(FileList* aFiles) {
   2765  if (mType != FormControlType::InputFile || !aFiles) {
   2766    return;
   2767  }
   2768 
   2769  // Update |mFileData->mFilesOrDirectories|
   2770  SetFiles(aFiles, true);
   2771 
   2772  MOZ_ASSERT(!mFileData->mFileList, "Should've cleared the existing file list");
   2773 
   2774  // Update |mFileData->mFileList| without copy
   2775  mFileData->mFileList = aFiles;
   2776 }
   2777 
   2778 /* static */
   2779 void HTMLInputElement::HandleNumberControlSpin(void* aData) {
   2780  RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(aData);
   2781 
   2782  NS_ASSERTION(input->mNumberControlSpinnerIsSpinning,
   2783               "Should have called nsRepeatService::Stop()");
   2784 
   2785  nsNumberControlFrame* numberControlFrame =
   2786      do_QueryFrame(input->GetPrimaryFrame());
   2787  if (input->mType != FormControlType::InputNumber || !numberControlFrame) {
   2788    // Type has changed (and possibly our frame type hasn't been updated yet)
   2789    // or else we've lost our frame. Either way, stop the timer and don't do
   2790    // anything else.
   2791    input->StopNumberControlSpinnerSpin();
   2792  } else {
   2793    input->StepNumberControlForUserEvent(
   2794        input->mNumberControlSpinnerSpinsUp ? 1 : -1);
   2795  }
   2796 }
   2797 
   2798 nsresult HTMLInputElement::SetValueInternal(
   2799    const nsAString& aValue, const nsAString* aOldValue,
   2800    const ValueSetterOptions& aOptions) {
   2801  MOZ_ASSERT(GetValueMode() != VALUE_MODE_FILENAME,
   2802             "Don't call SetValueInternal for file inputs");
   2803 
   2804  // We want to remember if the SetValueInternal() call is being made for a XUL
   2805  // element.  We do that by looking at the parent node here, and if that node
   2806  // is a XUL node, we consider our control a XUL control. XUL controls preserve
   2807  // edit history across value setters.
   2808  //
   2809  // TODO(emilio): Rather than doing this maybe add an attribute instead and
   2810  // read it only on chrome docs or something? That'd allow front-end code to
   2811  // move away from xul without weird side-effects.
   2812  const bool forcePreserveUndoHistory = mParent && mParent->IsXULElement();
   2813 
   2814  switch (GetValueMode()) {
   2815    case VALUE_MODE_VALUE: {
   2816      // At the moment, only single line text control have to sanitize their
   2817      // value Because we have to create a new string for that, we should
   2818      // prevent doing it if it's useless.
   2819      nsAutoString value(aValue);
   2820 
   2821      if (mDoneCreating &&
   2822          !(mType == FormControlType::InputNumber &&
   2823            aOptions.contains(ValueSetterOption::BySetUserInputAPI))) {
   2824        // When the value of a number input is set by a script, we need to make
   2825        // sure the value is a valid floating-point number.
   2826        // https://html.spec.whatwg.org/#valid-floating-point-number
   2827        // When it's set by a user, however, we need to be more permissive, so
   2828        // we don't sanitize its value here. See bug 1839572.
   2829        SanitizeValue(value, SanitizationKind::ForValueSetter);
   2830      }
   2831      // else DoneCreatingElement calls us again once mDoneCreating is true
   2832 
   2833      const bool setValueChanged =
   2834          aOptions.contains(ValueSetterOption::SetValueChanged);
   2835      if (setValueChanged) {
   2836        SetValueChanged(true);
   2837      }
   2838 
   2839      if (IsSingleLineTextControl(false)) {
   2840        // Note that if aOptions includes
   2841        // ValueSetterOption::BySetUserInputAPI, "input" event is automatically
   2842        // dispatched by TextControlState::SetValue(). If you'd change condition
   2843        // of calling this method, you need to maintain SetUserInput() too. FYI:
   2844        // After calling SetValue(), the input type might have been
   2845        //      modified so that mInputData may not store TextControlState.
   2846        EnsureEditorState();
   2847        if (!mInputData.mState->SetValue(
   2848                value, aOldValue,
   2849                forcePreserveUndoHistory
   2850                    ? aOptions + ValueSetterOption::PreserveUndoHistory
   2851                    : aOptions)) {
   2852          return NS_ERROR_OUT_OF_MEMORY;
   2853        }
   2854        // If the caller won't dispatch "input" event via
   2855        // nsContentUtils::DispatchInputEvent(), we need to modify
   2856        // validationMessage value here.
   2857        //
   2858        // FIXME(emilio): ValueSetterOption::ByInternalAPI is not supposed to
   2859        // change state, but maybe we could run this too?
   2860        if (aOptions.contains(ValueSetterOption::ByContentAPI)) {
   2861          MaybeUpdateAllValidityStates(!mDoneCreating);
   2862        }
   2863      } else {
   2864        free(mInputData.mValue);
   2865        mInputData.mValue = ToNewUnicode(value);
   2866        if (setValueChanged) {
   2867          SetValueChanged(true);
   2868        }
   2869        if (mType == FormControlType::InputRange) {
   2870          nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   2871          if (frame) {
   2872            frame->UpdateForValueChange();
   2873          }
   2874        } else if (CreatesDateTimeWidget() &&
   2875                   !aOptions.contains(ValueSetterOption::BySetUserInputAPI)) {
   2876          if (Element* dateTimeBoxElement = GetDateTimeBoxElement()) {
   2877            AsyncEventDispatcher::RunDOMEventWhenSafe(
   2878                *dateTimeBoxElement, u"MozDateTimeValueChanged"_ns,
   2879                CanBubble::eNo, ChromeOnlyDispatch::eNo);
   2880          }
   2881        }
   2882        if (mDoneCreating) {
   2883          OnValueChanged(ValueChangeKind::Internal, value.IsEmpty(), &value);
   2884        }
   2885        // else DoneCreatingElement calls us again once mDoneCreating is true
   2886      }
   2887 
   2888      if (mType == FormControlType::InputColor) {
   2889        // Update color frame, to reflect color changes
   2890        nsColorControlFrame* colorControlFrame =
   2891            do_QueryFrame(GetPrimaryFrame());
   2892        if (colorControlFrame) {
   2893          colorControlFrame->UpdateColor();
   2894        }
   2895      }
   2896      return NS_OK;
   2897    }
   2898 
   2899    case VALUE_MODE_DEFAULT:
   2900    case VALUE_MODE_DEFAULT_ON:
   2901      // If the value of a hidden input was changed, we mark it changed so that
   2902      // we will know we need to save / restore the value.  Yes, we are
   2903      // overloading the meaning of ValueChanged just a teensy bit to save a
   2904      // measly byte of storage space in HTMLInputElement.  Yes, you are free to
   2905      // make a new flag, NEED_TO_SAVE_VALUE, at such time as mBitField becomes
   2906      // a 16-bit value.
   2907      if (mType == FormControlType::InputHidden) {
   2908        SetValueChanged(true);
   2909      }
   2910 
   2911      // Make sure to keep track of the last value change not being interactive,
   2912      // just in case this used to be another kind of editable input before.
   2913      // Note that a checked change _could_ really be interactive, but we don't
   2914      // keep track of that elsewhere so seems fine to just do this.
   2915      SetLastValueChangeWasInteractive(false);
   2916 
   2917      // Treat value == defaultValue for other input elements.
   2918      return nsGenericHTMLFormControlElementWithState::SetAttr(
   2919          kNameSpaceID_None, nsGkAtoms::value, aValue, true);
   2920 
   2921    case VALUE_MODE_FILENAME:
   2922      return NS_ERROR_UNEXPECTED;
   2923  }
   2924 
   2925  // This return statement is required for some compilers.
   2926  return NS_OK;
   2927 }
   2928 
   2929 void HTMLInputElement::SetValueChanged(bool aValueChanged) {
   2930  if (mValueChanged == aValueChanged) {
   2931    return;
   2932  }
   2933  mValueChanged = aValueChanged;
   2934  UpdateTooLongValidityState();
   2935  UpdateTooShortValidityState();
   2936  UpdateValidityElementStates(true);
   2937 }
   2938 
   2939 void HTMLInputElement::SetLastValueChangeWasInteractive(bool aWasInteractive) {
   2940  if (aWasInteractive == mLastValueChangeWasInteractive) {
   2941    return;
   2942  }
   2943  mLastValueChangeWasInteractive = aWasInteractive;
   2944  const bool wasValid = IsValid();
   2945  UpdateTooLongValidityState();
   2946  UpdateTooShortValidityState();
   2947  if (wasValid != IsValid()) {
   2948    UpdateValidityElementStates(true);
   2949  }
   2950 }
   2951 
   2952 void HTMLInputElement::SetCheckedChanged(bool aCheckedChanged) {
   2953  if (mType == FormControlType::InputRadio) {
   2954    if (mCheckedChanged != aCheckedChanged) {
   2955      VisitGroup(
   2956          [aCheckedChanged](HTMLInputElement* aRadio) {
   2957            aRadio->SetCheckedChangedInternal(aCheckedChanged);
   2958            return true;
   2959          },
   2960          false);
   2961    }
   2962  } else {
   2963    SetCheckedChangedInternal(aCheckedChanged);
   2964  }
   2965 }
   2966 
   2967 void HTMLInputElement::SetCheckedChangedInternal(bool aCheckedChanged) {
   2968  if (mCheckedChanged == aCheckedChanged) {
   2969    return;
   2970  }
   2971  mCheckedChanged = aCheckedChanged;
   2972  UpdateValidityElementStates(true);
   2973 }
   2974 
   2975 void HTMLInputElement::SetChecked(bool aChecked) {
   2976  DoSetChecked(aChecked, /* aNotify */ true, /* aSetValueChanged */ true);
   2977 }
   2978 
   2979 void HTMLInputElement::DoSetChecked(bool aChecked, bool aNotify,
   2980                                    bool aSetValueChanged,
   2981                                    bool aUpdateOtherElement) {
   2982  // If the user or JS attempts to set checked, whether it actually changes the
   2983  // value or not, we say the value was changed so that defaultValue don't
   2984  // affect it no more.
   2985  if (aSetValueChanged) {
   2986    SetCheckedChanged(true);
   2987  }
   2988 
   2989  // Don't do anything if we're not changing whether it's checked (it would
   2990  // screw up state actually, especially when you are setting radio button to
   2991  // false)
   2992  if (mChecked == aChecked) {
   2993    return;
   2994  }
   2995 
   2996  // Set checked
   2997  if (mType != FormControlType::InputRadio) {
   2998    SetCheckedInternal(aChecked, aNotify);
   2999    return;
   3000  }
   3001 
   3002  // For radio button, we need to do some extra fun stuff
   3003  if (aChecked) {
   3004    RadioSetChecked(aNotify, aUpdateOtherElement);
   3005    return;
   3006  }
   3007 
   3008  if (auto* container = GetCurrentRadioGroupContainer()) {
   3009    nsAutoString name;
   3010    GetAttr(nsGkAtoms::name, name);
   3011    container->SetCurrentRadioButton(name, nullptr);
   3012  }
   3013  // SetCheckedInternal is going to ask all radios to update their
   3014  // validity state. We have to be sure the radio group container knows
   3015  // the currently selected radio.
   3016  SetCheckedInternal(false, aNotify);
   3017 }
   3018 
   3019 void HTMLInputElement::RadioSetChecked(bool aNotify, bool aUpdateOtherElement) {
   3020  if (aUpdateOtherElement) {
   3021    // It’s possible for multiple radio input to have their checkedness set to
   3022    // true, so we need to deselect all of them.
   3023    VisitGroup([](HTMLInputElement* aRadio) {
   3024      aRadio->SetCheckedInternal(false, true);
   3025      return true;
   3026    });
   3027  }
   3028 
   3029  // Let the group know that we are now the One True Radio Button
   3030  if (auto* container = GetCurrentRadioGroupContainer()) {
   3031    nsAutoString name;
   3032    GetAttr(nsGkAtoms::name, name);
   3033    container->SetCurrentRadioButton(name, this);
   3034  }
   3035 
   3036  // SetCheckedInternal is going to ask all radios to update their
   3037  // validity state.
   3038  SetCheckedInternal(true, aNotify);
   3039 }
   3040 
   3041 RadioGroupContainer* HTMLInputElement::GetCurrentRadioGroupContainer() const {
   3042  NS_ASSERTION(
   3043      mType == FormControlType::InputRadio,
   3044      "GetRadioGroupContainer should only be called when type='radio'");
   3045  return mRadioGroupContainer;
   3046 }
   3047 
   3048 RadioGroupContainer* HTMLInputElement::FindTreeRadioGroupContainer() const {
   3049  nsAutoString name;
   3050  GetAttr(nsGkAtoms::name, name);
   3051 
   3052  if (name.IsEmpty()) {
   3053    return nullptr;
   3054  }
   3055  if (mForm) {
   3056    return &mForm->OwnedRadioGroupContainer();
   3057  }
   3058  if (IsInNativeAnonymousSubtree()) {
   3059    return nullptr;
   3060  }
   3061  if (Document* doc = GetUncomposedDoc()) {
   3062    return &doc->OwnedRadioGroupContainer();
   3063  }
   3064  return &static_cast<FragmentOrElement*>(SubtreeRoot())
   3065              ->OwnedRadioGroupContainer();
   3066 }
   3067 
   3068 void HTMLInputElement::DisconnectRadioGroupContainer() {
   3069  mRadioGroupContainer = nullptr;
   3070 }
   3071 
   3072 HTMLInputElement* HTMLInputElement::GetSelectedRadioButton() const {
   3073  auto* container = GetCurrentRadioGroupContainer();
   3074  if (!container) {
   3075    return nullptr;
   3076  }
   3077 
   3078  nsAutoString name;
   3079  GetAttr(nsGkAtoms::name, name);
   3080 
   3081  return container->GetCurrentRadioButton(name);
   3082 }
   3083 
   3084 void HTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext) {
   3085  if (!mForm) {
   3086    // Nothing to do here.
   3087    return;
   3088  }
   3089 
   3090  RefPtr<PresShell> presShell = aPresContext->GetPresShell();
   3091  if (!presShell) {
   3092    return;
   3093  }
   3094 
   3095  // Get the default submit element
   3096  if (RefPtr<nsGenericHTMLFormElement> submitContent =
   3097          mForm->GetDefaultSubmitElement()) {
   3098    WidgetPointerEvent event(true, ePointerClick, nullptr);
   3099    event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
   3100    // pointerId definition in Pointer Events:
   3101    // > The pointerId value of -1 MUST be reserved and used to indicate events
   3102    // > that were generated by something other than a pointing device.
   3103    event.pointerId = -1;
   3104    nsEventStatus status = nsEventStatus_eIgnore;
   3105    presShell->HandleDOMEventWithTarget(submitContent, &event, &status);
   3106  } else if (!mForm->ImplicitSubmissionIsDisabled()) {
   3107    // If there's only one text control, just submit the form
   3108    // Hold strong ref across the event
   3109    RefPtr<dom::HTMLFormElement> form(mForm);
   3110    form->MaybeSubmit(nullptr);
   3111  }
   3112 }
   3113 
   3114 void HTMLInputElement::UpdateCheckedState(bool aNotify) {
   3115  SetStates(ElementState::CHECKED, IsRadioOrCheckbox() && mChecked, aNotify);
   3116 }
   3117 
   3118 void HTMLInputElement::UpdateIndeterminateState(bool aNotify) {
   3119  bool indeterminate = [&] {
   3120    if (mType == FormControlType::InputCheckbox) {
   3121      return mIndeterminate;
   3122    }
   3123    if (mType == FormControlType::InputRadio) {
   3124      return !mChecked && !GetSelectedRadioButton();
   3125    }
   3126    return false;
   3127  }();
   3128  SetStates(ElementState::INDETERMINATE, indeterminate, aNotify);
   3129 }
   3130 
   3131 void HTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify) {
   3132  // Set the value
   3133  mChecked = aChecked;
   3134 
   3135  if (IsRadioOrCheckbox()) {
   3136    SetStates(ElementState::CHECKED, aChecked, aNotify);
   3137  }
   3138 
   3139  // No need to update element state, since we're about to call
   3140  // UpdateState anyway.
   3141  UpdateAllValidityStatesButNotElementState();
   3142  UpdateIndeterminateState(aNotify);
   3143  UpdateValidityElementStates(aNotify);
   3144 
   3145  // Notify all radios in the group that value has changed, this is to let
   3146  // radios to have the chance to update its states, e.g., :indeterminate.
   3147  if (mType == FormControlType::InputRadio) {
   3148    UpdateRadioGroupState();
   3149  }
   3150 }
   3151 
   3152 #if !defined(ANDROID) && !defined(XP_MACOSX)
   3153 bool HTMLInputElement::IsNodeApzAwareInternal() const {
   3154  // Tell APZC we may handle mouse wheel event and do preventDefault when input
   3155  // type is number.
   3156  return mType == FormControlType::InputNumber ||
   3157         mType == FormControlType::InputRange ||
   3158         nsINode::IsNodeApzAwareInternal();
   3159 }
   3160 #endif
   3161 
   3162 bool HTMLInputElement::IsInteractiveHTMLContent() const {
   3163  return mType != FormControlType::InputHidden ||
   3164         nsGenericHTMLFormControlElementWithState::IsInteractiveHTMLContent();
   3165 }
   3166 
   3167 void HTMLInputElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
   3168  nsImageLoadingContent::AsyncEventRunning(aEvent);
   3169 }
   3170 
   3171 void HTMLInputElement::Select() {
   3172  if (!IsSingleLineTextControl(false)) {
   3173    return;
   3174  }
   3175 
   3176  TextControlState* state = GetEditorState();
   3177  MOZ_ASSERT(state, "Single line text controls are expected to have a state");
   3178 
   3179  if (FocusState() != FocusTristate::eUnfocusable) {
   3180    RefPtr<nsFrameSelection> fs = state->GetIndependentFrameSelection();
   3181    if (fs && fs->MouseDownRecorded()) {
   3182      // This means that we're being called while the frame selection has a
   3183      // mouse down event recorded to adjust the caret during the mouse up
   3184      // event. We are probably called from the focus event handler.  We should
   3185      // override the delayed caret data in this case to ensure that this
   3186      // select() call takes effect.
   3187      fs->SetDelayedCaretData(nullptr);
   3188    }
   3189 
   3190    if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   3191      fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
   3192 
   3193      // A focus event handler may change the type attribute, which will destroy
   3194      // the previous state object.
   3195      state = GetEditorState();
   3196      if (!state) {
   3197        return;
   3198      }
   3199    }
   3200  }
   3201 
   3202  // Directly call TextControlState::SetSelectionRange because
   3203  // HTMLInputElement::SetSelectionRange only applies to fewer types
   3204  state->SetSelectionRange(0, UINT32_MAX, Optional<nsAString>(), IgnoreErrors(),
   3205                           TextControlState::ScrollAfterSelection::No);
   3206 }
   3207 
   3208 void HTMLInputElement::SelectAll() {
   3209  // FIXME(emilio): Should we try to call Select(), which will avoid flushing?
   3210  if (nsTextControlFrame* tf =
   3211          do_QueryFrame(GetPrimaryFrame(FlushType::Frames))) {
   3212    tf->SelectAll();
   3213  }
   3214 }
   3215 
   3216 bool HTMLInputElement::NeedToInitializeEditorForEvent(
   3217    EventChainPreVisitor& aVisitor) const {
   3218  // We only need to initialize the editor for single line input controls
   3219  // because they are lazily initialized.  We don't need to initialize the
   3220  // control for certain types of events, because we know that those events are
   3221  // safe to be handled without the editor being initialized.  These events
   3222  // include: mousein/move/out, overflow/underflow, and void events. Void events
   3223  // are dispatched frequently by async keyboard scrolling to focused elements,
   3224  // so it's important to handle them to prevent excessive DOM mutations.
   3225  if (!IsSingleLineTextControl(false)) {
   3226    return false;
   3227  }
   3228 
   3229  switch (aVisitor.mEvent->mMessage) {
   3230    case eVoidEvent:
   3231    case eMouseMove:
   3232    case eMouseEnterIntoWidget:
   3233    case eMouseExitFromWidget:
   3234    case eMouseOver:
   3235    case eMouseOut:
   3236    case eScrollPortUnderflow:
   3237    case eScrollPortOverflow:
   3238      return false;
   3239    default:
   3240      return true;
   3241  }
   3242 }
   3243 
   3244 bool HTMLInputElement::IsDisabledForEvents(WidgetEvent* aEvent) {
   3245  return IsElementDisabledForEvents(aEvent, GetPrimaryFrame());
   3246 }
   3247 
   3248 bool HTMLInputElement::CheckActivationBehaviorPreconditions(
   3249    EventChainVisitor& aVisitor) const {
   3250  // Track whether we're in the outermost Dispatch invocation that will
   3251  // cause activation of the input.  That is, if we're a click event, or a
   3252  // DOMActivate that was dispatched directly, this will be set, but if
   3253  // we're a DOMActivate dispatched from click handling, it will not be set.
   3254  WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
   3255  bool outerActivateEvent =
   3256      (mouseEvent && mouseEvent->IsLeftClickEvent()) ||
   3257      (aVisitor.mEvent->mMessage == eLegacyDOMActivate && !mInInternalActivate);
   3258  if (outerActivateEvent) {
   3259    aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
   3260  }
   3261  return outerActivateEvent;
   3262 }
   3263 
   3264 void HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
   3265  // Do not process any DOM events if the element is disabled
   3266  aVisitor.mCanHandle = false;
   3267  if (IsDisabledForEvents(aVisitor.mEvent)) {
   3268    return;
   3269  }
   3270 
   3271  // Initialize the editor if needed.
   3272  if (NeedToInitializeEditorForEvent(aVisitor)) {
   3273    if (nsTextControlFrame* tcf = do_QueryFrame(GetPrimaryFrame())) {
   3274      tcf->EnsureEditorInitialized();
   3275    }
   3276  }
   3277 
   3278  if (CheckActivationBehaviorPreconditions(aVisitor)) {
   3279    aVisitor.mWantsActivationBehavior = true;
   3280  }
   3281 
   3282  // We must cache type because mType may change during JS event (bug 2369)
   3283  aVisitor.mItemFlags |= uint8_t(mType);
   3284 
   3285  if (aVisitor.mEvent->mMessage == eFocus && aVisitor.mEvent->IsTrusted() &&
   3286      MayFireChangeOnBlur() &&
   3287      // StartRangeThumbDrag already set mFocusedValue on 'mousedown' before
   3288      // we get the 'focus' event.
   3289      !mIsDraggingRange) {
   3290    GetValue(mFocusedValue, CallerType::System);
   3291  }
   3292 
   3293  // Fire onchange (if necessary), before we do the blur, bug 357684.
   3294  if (aVisitor.mEvent->mMessage == eBlur) {
   3295    // We set NS_PRE_HANDLE_BLUR_EVENT here and handle it in PreHandleEvent to
   3296    // prevent breaking event target chain creation.
   3297    aVisitor.mWantsPreHandleEvent = true;
   3298    aVisitor.mItemFlags |= NS_PRE_HANDLE_BLUR_EVENT;
   3299  }
   3300 
   3301  if (mType == FormControlType::InputRange &&
   3302      (aVisitor.mEvent->mMessage == eFocus ||
   3303       aVisitor.mEvent->mMessage == eBlur)) {
   3304    // We handle focus here.
   3305    // FIXME(emilio): Why is this needed? If it is it should be moved to
   3306    // nsRangeFrame::ElementStateChanged.
   3307    if (nsIFrame* frame = GetPrimaryFrame()) {
   3308      frame->InvalidateFrameSubtree();
   3309    }
   3310  }
   3311 
   3312  if (mType == FormControlType::InputNumber && aVisitor.mEvent->IsTrusted()) {
   3313    if (mNumberControlSpinnerIsSpinning) {
   3314      // If the timer is running the user has depressed the mouse on one of the
   3315      // spin buttons. If the mouse exits the button we either want to reverse
   3316      // the direction of spin if it has moved over the other button, or else
   3317      // we want to end the spin. We do this here (rather than in
   3318      // PostHandleEvent) because we don't want to let content preventDefault()
   3319      // the end of the spin.
   3320      if (aVisitor.mEvent->mMessage == eMouseMove) {
   3321        // Be aggressive about stopping the spin:
   3322        bool stopSpin = true;
   3323        nsNumberControlFrame* numberControlFrame =
   3324            do_QueryFrame(GetPrimaryFrame());
   3325        if (numberControlFrame) {
   3326          bool oldNumberControlSpinTimerSpinsUpValue =
   3327              mNumberControlSpinnerSpinsUp;
   3328          switch (numberControlFrame->GetSpinButtonForPointerEvent(
   3329              aVisitor.mEvent->AsMouseEvent())) {
   3330            case nsNumberControlFrame::eSpinButtonUp:
   3331              mNumberControlSpinnerSpinsUp = true;
   3332              stopSpin = false;
   3333              break;
   3334            case nsNumberControlFrame::eSpinButtonDown:
   3335              mNumberControlSpinnerSpinsUp = false;
   3336              stopSpin = false;
   3337              break;
   3338          }
   3339          if (mNumberControlSpinnerSpinsUp !=
   3340              oldNumberControlSpinTimerSpinsUpValue) {
   3341            nsNumberControlFrame* numberControlFrame =
   3342                do_QueryFrame(GetPrimaryFrame());
   3343            if (numberControlFrame) {
   3344              numberControlFrame->SpinnerStateChanged();
   3345            }
   3346          }
   3347        }
   3348        if (stopSpin) {
   3349          StopNumberControlSpinnerSpin();
   3350        }
   3351      } else if (aVisitor.mEvent->mMessage == eMouseUp) {
   3352        StopNumberControlSpinnerSpin();
   3353      }
   3354    }
   3355 
   3356    if (StaticPrefs::dom_input_number_and_range_modified_by_mousewheel() &&
   3357        aVisitor.mEvent->mMessage == eWheel) {
   3358      aVisitor.mMaybeUncancelable = false;
   3359    }
   3360  }
   3361 
   3362  nsGenericHTMLFormControlElementWithState::GetEventTargetParent(aVisitor);
   3363 }
   3364 
   3365 void HTMLInputElement::LegacyPreActivationBehavior(
   3366    EventChainVisitor& aVisitor) {
   3367  //
   3368  // Web pages expect the value of a radio button or checkbox to be set
   3369  // *before* onclick and DOMActivate fire, and they expect that if they set
   3370  // the value explicitly during onclick or DOMActivate it will not be toggled
   3371  // or any such nonsense.
   3372  // In order to support that (bug 57137 and 58460 are examples) we toggle
   3373  // the checked attribute *first*, and then fire onclick.  If the user
   3374  // returns false, we reset the control to the old checked value.  Otherwise,
   3375  // we dispatch DOMActivate.  If DOMActivate is cancelled, we also reset
   3376  // the control to the old checked value.  We need to keep track of whether
   3377  // we've already toggled the state from onclick since the user could
   3378  // explicitly dispatch DOMActivate on the element.
   3379  //
   3380  // These are compatibility hacks and are defined as legacy-pre-activation
   3381  // and legacy-canceled-activation behavior in HTML.
   3382  //
   3383 
   3384  // Assert mType didn't change after GetEventTargetParent
   3385  MOZ_ASSERT(NS_CONTROL_TYPE(aVisitor.mItemFlags) == uint8_t(mType));
   3386 
   3387  bool originalCheckedValue = false;
   3388  mCheckedIsToggled = false;
   3389 
   3390  if (mType == FormControlType::InputCheckbox) {
   3391    if (mIndeterminate) {
   3392      // indeterminate is always set to FALSE when the checkbox is toggled
   3393      SetIndeterminateInternal(false, false);
   3394      aVisitor.mItemFlags |= NS_ORIGINAL_INDETERMINATE_VALUE;
   3395    }
   3396 
   3397    originalCheckedValue = Checked();
   3398    DoSetChecked(!originalCheckedValue, /* aNotify */ true,
   3399                 /* aSetValueChanged */ true);
   3400    mCheckedIsToggled = true;
   3401 
   3402    if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
   3403      aVisitor.mEventStatus = nsEventStatus_eConsumeDoDefault;
   3404    }
   3405  } else if (mType == FormControlType::InputRadio) {
   3406    HTMLInputElement* selectedRadioButton = GetSelectedRadioButton();
   3407    aVisitor.mItemData = static_cast<Element*>(selectedRadioButton);
   3408 
   3409    originalCheckedValue = Checked();
   3410    if (!originalCheckedValue) {
   3411      DoSetChecked(/* aValue */ true, /* aNotify */ true,
   3412                   /* aSetValueChanged */ true);
   3413      mCheckedIsToggled = true;
   3414    }
   3415 
   3416    if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
   3417      aVisitor.mEventStatus = nsEventStatus_eConsumeDoDefault;
   3418    }
   3419  }
   3420 
   3421  if (originalCheckedValue) {
   3422    aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
   3423  }
   3424 
   3425  // out-of-spec legacy pre-activation behavior needed because of bug 1803805.
   3426  // XXXedgar: We exclude the radio type because `mItemData` is already used to
   3427  // store the originally selected radio button above.
   3428  if (mForm && mType != FormControlType::InputRadio) {
   3429    aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
   3430    aVisitor.mItemData = static_cast<Element*>(mForm);
   3431    // tell the form that we are about to enter a click handler.
   3432    // that means that if there are scripted submissions, the
   3433    // latest one will be deferred until after the exit point of the
   3434    // handler.
   3435    mForm->OnSubmitClickBegin();
   3436 
   3437    if ((mType == FormControlType::InputSubmit ||
   3438         mType == FormControlType::InputImage) &&
   3439        aVisitor.mDOMEvent) {
   3440      if (auto* mouseEvent = aVisitor.mDOMEvent->AsMouseEvent()) {
   3441        const CSSIntPoint pt = RoundedToInt(mouseEvent->OffsetPoint());
   3442        if (auto* imageClickedPoint = static_cast<CSSIntPoint*>(
   3443                GetProperty(nsGkAtoms::imageClickedPoint))) {
   3444          // Ensures that a dispatched event's clicked point is not the default
   3445          // value.
   3446          *imageClickedPoint = pt;
   3447        }
   3448      }
   3449    }
   3450  }
   3451 }
   3452 
   3453 void HTMLInputElement::MaybeDispatchWillBlur(EventChainVisitor& aVisitor) {
   3454  if (!CreatesDateTimeWidget() || !aVisitor.mEvent->IsTrusted()) {
   3455    return;
   3456  }
   3457  RefPtr<Element> dateTimeBoxElement = GetDateTimeBoxElement();
   3458  if (!dateTimeBoxElement) {
   3459    return;
   3460  }
   3461  AutoJSAPI jsapi;
   3462  if (NS_WARN_IF(!jsapi.Init(GetOwnerGlobal()))) {
   3463    return;
   3464  }
   3465  if (!aVisitor.mDOMEvent) {
   3466    RefPtr<Event> event = EventDispatcher::CreateEvent(
   3467        aVisitor.mEvent->mOriginalTarget, aVisitor.mPresContext,
   3468        aVisitor.mEvent, u""_ns);
   3469    event.swap(aVisitor.mDOMEvent);
   3470  }
   3471  JS::Rooted<JS::Value> detail(jsapi.cx(), JS::NullHandleValue);
   3472  if (NS_WARN_IF(!ToJSValue(jsapi.cx(), aVisitor.mDOMEvent, &detail))) {
   3473    return;
   3474  }
   3475  // Event is dispatched to closed-shadow tree and doesn't bubble.
   3476  RefPtr<CustomEvent> event =
   3477      NS_NewDOMCustomEvent(OwnerDoc(), aVisitor.mPresContext, nullptr);
   3478  event->InitCustomEvent(jsapi.cx(), u"MozDateTimeWillBlur"_ns,
   3479                         /* CanBubble */ false,
   3480                         /* Cancelable */ false, detail);
   3481  event->SetTrusted(true);
   3482  dateTimeBoxElement->DispatchEvent(*event);
   3483 }
   3484 
   3485 nsresult HTMLInputElement::PreHandleEvent(EventChainVisitor& aVisitor) {
   3486  if (aVisitor.mItemFlags & NS_PRE_HANDLE_BLUR_EVENT) {
   3487    MOZ_ASSERT(aVisitor.mEvent->mMessage == eBlur);
   3488    // TODO(emilio): This should probably happen only if the event is trusted?
   3489    FireChangeEventIfNeeded();
   3490    MaybeDispatchWillBlur(aVisitor);
   3491  }
   3492  return nsGenericHTMLFormControlElementWithState::PreHandleEvent(aVisitor);
   3493 }
   3494 
   3495 void HTMLInputElement::StartRangeThumbDrag(WidgetGUIEvent* aEvent) {
   3496  nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
   3497  if (!rangeFrame) {
   3498    return;
   3499  }
   3500 
   3501  mIsDraggingRange = true;
   3502  mRangeThumbDragStartValue = GetValueAsDecimal();
   3503  // Don't use CaptureFlags::RetargetToElement, as that breaks pseudo-class
   3504  // styling of the thumb.
   3505  PresShell::SetCapturingContent(this, CaptureFlags::IgnoreAllowedState);
   3506 
   3507  // Before we change the value, record the current value so that we'll
   3508  // correctly send a 'change' event if appropriate. We need to do this here
   3509  // because the 'focus' event is handled after the 'mousedown' event that
   3510  // we're being called for (i.e. too late to update mFocusedValue, since we'll
   3511  // have changed it by then).
   3512  GetValue(mFocusedValue, CallerType::System);
   3513 
   3514  SetValueOfRangeForUserEvent(rangeFrame->GetValueAtEventPoint(aEvent),
   3515                              SnapToTickMarks::Yes);
   3516 }
   3517 
   3518 void HTMLInputElement::FinishRangeThumbDrag(WidgetGUIEvent* aEvent) {
   3519  MOZ_ASSERT(mIsDraggingRange);
   3520 
   3521  if (PresShell::GetCapturingContent() == this) {
   3522    PresShell::ReleaseCapturingContent();
   3523  }
   3524  if (aEvent) {
   3525    nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
   3526    SetValueOfRangeForUserEvent(rangeFrame->GetValueAtEventPoint(aEvent),
   3527                                SnapToTickMarks::Yes);
   3528  }
   3529  mIsDraggingRange = false;
   3530  FireChangeEventIfNeeded();
   3531 }
   3532 
   3533 void HTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent) {
   3534  MOZ_ASSERT(mIsDraggingRange);
   3535 
   3536  mIsDraggingRange = false;
   3537  if (PresShell::GetCapturingContent() == this) {
   3538    PresShell::ReleaseCapturingContent();
   3539  }
   3540  if (aIsForUserEvent) {
   3541    SetValueOfRangeForUserEvent(mRangeThumbDragStartValue,
   3542                                SnapToTickMarks::Yes);
   3543  } else {
   3544    // Don't dispatch an 'input' event - at least not using
   3545    // DispatchTrustedEvent.
   3546    // TODO: decide what we should do here - bug 851782.
   3547    nsAutoString val;
   3548    mInputType->ConvertNumberToString(mRangeThumbDragStartValue,
   3549                                      InputType::Localized::No, val);
   3550    // TODO: What should we do if SetValueInternal fails?  (The allocation
   3551    // is small, so we should be fine here.)
   3552    SetValueInternal(val, {ValueSetterOption::BySetUserInputAPI,
   3553                           ValueSetterOption::SetValueChanged});
   3554    if (nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame())) {
   3555      frame->UpdateForValueChange();
   3556    }
   3557    DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
   3558    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   3559                         "Failed to dispatch input event");
   3560  }
   3561 }
   3562 
   3563 void HTMLInputElement::SetValueOfRangeForUserEvent(
   3564    Decimal aValue, SnapToTickMarks aSnapToTickMarks) {
   3565  MOZ_ASSERT(aValue.isFinite());
   3566  if (aSnapToTickMarks == SnapToTickMarks::Yes) {
   3567    MaybeSnapToTickMark(aValue);
   3568  }
   3569 
   3570  Decimal oldValue = GetValueAsDecimal();
   3571 
   3572  nsAutoString val;
   3573  mInputType->ConvertNumberToString(aValue, InputType::Localized::No, val);
   3574  // TODO: What should we do if SetValueInternal fails?  (The allocation
   3575  // is small, so we should be fine here.)
   3576  SetValueInternal(val, {ValueSetterOption::BySetUserInputAPI,
   3577                         ValueSetterOption::SetValueChanged});
   3578  if (nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame())) {
   3579    frame->UpdateForValueChange();
   3580  }
   3581 
   3582  if (GetValueAsDecimal() != oldValue) {
   3583    DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
   3584    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   3585                         "Failed to dispatch input event");
   3586  }
   3587 }
   3588 
   3589 void HTMLInputElement::StartNumberControlSpinnerSpin() {
   3590  MOZ_ASSERT(!mNumberControlSpinnerIsSpinning);
   3591 
   3592  mNumberControlSpinnerIsSpinning = true;
   3593 
   3594  nsRepeatService::GetInstance()->Start(
   3595      HandleNumberControlSpin, this, OwnerDoc(), "HandleNumberControlSpin"_ns);
   3596 
   3597  // Capture the mouse so that we can tell if the pointer moves from one
   3598  // spin button to the other, or to some other element:
   3599  PresShell::SetCapturingContent(this, CaptureFlags::IgnoreAllowedState);
   3600 
   3601  nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame());
   3602  if (numberControlFrame) {
   3603    numberControlFrame->SpinnerStateChanged();
   3604  }
   3605 }
   3606 
   3607 void HTMLInputElement::StopNumberControlSpinnerSpin(SpinnerStopState aState) {
   3608  if (mNumberControlSpinnerIsSpinning) {
   3609    if (PresShell::GetCapturingContent() == this) {
   3610      PresShell::ReleaseCapturingContent();
   3611    }
   3612 
   3613    nsRepeatService::GetInstance()->Stop(HandleNumberControlSpin, this);
   3614 
   3615    mNumberControlSpinnerIsSpinning = false;
   3616 
   3617    if (aState == eAllowDispatchingEvents) {
   3618      FireChangeEventIfNeeded();
   3619    }
   3620 
   3621    nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame());
   3622    if (numberControlFrame) {
   3623      MOZ_ASSERT(aState == eAllowDispatchingEvents,
   3624                 "Shouldn't have primary frame for the element when we're not "
   3625                 "allowed to dispatch events to it anymore.");
   3626      numberControlFrame->SpinnerStateChanged();
   3627    }
   3628  }
   3629 }
   3630 
   3631 void HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection) {
   3632  // We can't use GetValidityState here because the validity state is not set
   3633  // if the user hasn't previously taken an action to set or change the value,
   3634  // according to the specs.
   3635  if (HasBadInput()) {
   3636    // If the user has typed a value into the control and inadvertently made a
   3637    // mistake (e.g. put a thousand separator at the wrong point) we do not
   3638    // want to wipe out what they typed if they try to increment/decrement the
   3639    // value. Better is to highlight the value as being invalid so that they
   3640    // can correct what they typed.
   3641    // We only do this if there actually is a value typed in by/displayed to
   3642    // the user. (IsValid() can return false if the 'required' attribute is
   3643    // set and the value is the empty string.)
   3644    if (!IsValueEmpty()) {
   3645      // We pass 'true' for SetUserInteracted because we need the UI to update
   3646      // _now_ or the user will wonder why the step behavior isn't functioning.
   3647      SetUserInteracted(true);
   3648      return;
   3649    }
   3650  }
   3651 
   3652  Decimal newValue = GetValueIfStepped(aDirection, StepCallerType::ForUserEvent,
   3653                                       IgnoreErrors());
   3654  if (!newValue.isFinite()) {
   3655    return;  // value should not or will not change
   3656  }
   3657 
   3658  nsAutoString newVal;
   3659  mInputType->ConvertNumberToString(newValue, InputType::Localized::No, newVal);
   3660  // TODO: What should we do if SetValueInternal fails?  (The allocation
   3661  // is small, so we should be fine here.)
   3662  SetValueInternal(newVal, {ValueSetterOption::BySetUserInputAPI,
   3663                            ValueSetterOption::SetValueChanged});
   3664 }
   3665 
   3666 static bool SelectTextFieldOnFocus() {
   3667  if (!gSelectTextFieldOnFocus) {
   3668    int32_t selectTextfieldsOnKeyFocus = -1;
   3669    nsresult rv =
   3670        LookAndFeel::GetInt(LookAndFeel::IntID::SelectTextfieldsOnKeyFocus,
   3671                            &selectTextfieldsOnKeyFocus);
   3672    if (NS_FAILED(rv)) {
   3673      gSelectTextFieldOnFocus = -1;
   3674    } else {
   3675      gSelectTextFieldOnFocus = selectTextfieldsOnKeyFocus != 0 ? 1 : -1;
   3676    }
   3677  }
   3678 
   3679  return gSelectTextFieldOnFocus == 1;
   3680 }
   3681 
   3682 bool HTMLInputElement::ShouldPreventDOMActivateDispatch(
   3683    EventTarget* aOriginalTarget) {
   3684  /*
   3685   * For the moment, there is only one situation where we actually want to
   3686   * prevent firing a DOMActivate event:
   3687   *  - we are a <input type='file'> that just got a click event,
   3688   *  - the event was targeted to our button which should have sent a
   3689   *    DOMActivate event.
   3690   */
   3691 
   3692  if (mType != FormControlType::InputFile) {
   3693    return false;
   3694  }
   3695 
   3696  Element* target = Element::FromEventTargetOrNull(aOriginalTarget);
   3697  if (!target) {
   3698    return false;
   3699  }
   3700 
   3701  return target->GetParent() == this &&
   3702         target->IsRootOfNativeAnonymousSubtree() &&
   3703         target->IsHTMLElement(nsGkAtoms::button);
   3704 }
   3705 
   3706 nsresult HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor) {
   3707  // Open a file picker when we receive a click on a <input type='file'>, or
   3708  // open a color picker when we receive a click on a <input type='color'>.
   3709  // A click is handled if it's the left mouse button.
   3710  // We do not prevent non-trusted click because authors can already use
   3711  // .click(). However, the pickers will check and consume user activation.
   3712  WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
   3713  if (!(mouseEvent && mouseEvent->IsLeftClickEvent())) {
   3714    return NS_OK;
   3715  }
   3716  if (mType == FormControlType::InputFile) {
   3717    // If the user clicked on the "Choose folder..." button we open the
   3718    // directory picker, else we open the file picker.
   3719    FilePickerType type = FILE_PICKER_FILE;
   3720    nsIContent* target =
   3721        nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
   3722    if (target && target->FindFirstNonChromeOnlyAccessContent() == this &&
   3723        StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
   3724        HasAttr(nsGkAtoms::webkitdirectory)) {
   3725      type = FILE_PICKER_DIRECTORY;
   3726    }
   3727    return InitFilePicker(type);
   3728  }
   3729  if (mType == FormControlType::InputColor) {
   3730    return InitColorPicker();
   3731  }
   3732 
   3733  return NS_OK;
   3734 }
   3735 
   3736 /**
   3737 * Return true if the input event should be ignored because of its modifiers.
   3738 * Control is treated specially, since sometimes we ignore it, and sometimes
   3739 * we don't (for webcompat reasons).
   3740 */
   3741 static bool IgnoreInputEventWithModifier(const WidgetInputEvent& aEvent,
   3742                                         bool ignoreControl) {
   3743  return (ignoreControl && aEvent.IsControl()) ||
   3744         aEvent.IsAltGraph()
   3745 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   3746         // Meta key is the Windows Logo key on Windows and Linux which may
   3747         // assign some special meaning for the events while it's pressed.
   3748         // On the other hand, it's a normal modifier in macOS and Android.
   3749         // Therefore, We should ignore it only in Win/Linux.
   3750         || aEvent.IsMeta()
   3751 #endif
   3752         || aEvent.IsFn();
   3753 }
   3754 
   3755 bool HTMLInputElement::StepsInputValue(
   3756    const WidgetKeyboardEvent& aEvent) const {
   3757  if (mType != FormControlType::InputNumber) {
   3758    return false;
   3759  }
   3760  if (aEvent.mMessage != eKeyPress) {
   3761    return false;
   3762  }
   3763  if (!aEvent.IsTrusted()) {
   3764    return false;
   3765  }
   3766  if (aEvent.mKeyCode != NS_VK_UP && aEvent.mKeyCode != NS_VK_DOWN) {
   3767    return false;
   3768  }
   3769  if (IgnoreInputEventWithModifier(aEvent, false)) {
   3770    return false;
   3771  }
   3772  if (aEvent.DefaultPrevented()) {
   3773    return false;
   3774  }
   3775  if (!IsMutable()) {
   3776    return false;
   3777  }
   3778  return true;
   3779 }
   3780 
   3781 static bool ActivatesWithKeyboard(FormControlType aType, uint32_t aKeyCode) {
   3782  switch (aType) {
   3783    case FormControlType::InputCheckbox:
   3784    case FormControlType::InputRadio:
   3785      // Checkbox and Radio try to submit on Enter press
   3786      return aKeyCode != NS_VK_RETURN;
   3787    case FormControlType::InputButton:
   3788    case FormControlType::InputReset:
   3789    case FormControlType::InputSubmit:
   3790    case FormControlType::InputFile:
   3791    case FormControlType::InputImage:  // Bug 34418
   3792    case FormControlType::InputColor:
   3793      return true;
   3794    default:
   3795      return false;
   3796  }
   3797 }
   3798 
   3799 nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
   3800  if (aVisitor.mEvent->mMessage == eBlur) {
   3801    if (mIsDraggingRange) {
   3802      FinishRangeThumbDrag();
   3803    } else if (mNumberControlSpinnerIsSpinning) {
   3804      StopNumberControlSpinnerSpin();
   3805    }
   3806  }
   3807 
   3808  nsresult rv = NS_OK;
   3809  auto oldType = FormControlType(NS_CONTROL_TYPE(aVisitor.mItemFlags));
   3810 
   3811  // Ideally we would make the default action for click and space just dispatch
   3812  // DOMActivate, and the default action for DOMActivate flip the checkbox/
   3813  // radio state and fire onchange.  However, for backwards compatibility, we
   3814  // need to flip the state before firing click, and we need to fire click
   3815  // when space is pressed.  So, we just nest the firing of DOMActivate inside
   3816  // the click event handling, and allow cancellation of DOMActivate to cancel
   3817  // the click.
   3818  if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault &&
   3819      !IsSingleLineTextControl(true) && mType != FormControlType::InputNumber) {
   3820    WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
   3821    if (mouseEvent && mouseEvent->IsLeftClickEvent() &&
   3822        OwnerDoc()->MayHaveDOMActivateListeners() &&
   3823        !ShouldPreventDOMActivateDispatch(aVisitor.mEvent->mOriginalTarget)) {
   3824      // DOMActive event should be trusted since the activation is actually
   3825      // occurred even if the cause is an untrusted click event.
   3826      InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
   3827      actEvent.mDetail = 1;
   3828 
   3829      if (RefPtr<PresShell> presShell =
   3830              aVisitor.mPresContext ? aVisitor.mPresContext->GetPresShell()
   3831                                    : nullptr) {
   3832        nsEventStatus status = nsEventStatus_eIgnore;
   3833        mInInternalActivate = true;
   3834        rv = presShell->HandleDOMEventWithTarget(this, &actEvent, &status);
   3835        mInInternalActivate = false;
   3836 
   3837        // If activate is cancelled, we must do the same as when click is
   3838        // cancelled (revert the checkbox to its original value).
   3839        if (status == nsEventStatus_eConsumeNoDefault) {
   3840          aVisitor.mEventStatus = status;
   3841        }
   3842      }
   3843    }
   3844  }
   3845 
   3846  bool preventDefault =
   3847      aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault;
   3848  if (IsDisabled() && oldType != FormControlType::InputCheckbox &&
   3849      oldType != FormControlType::InputRadio) {
   3850    // Behave as if defaultPrevented when the element becomes disabled by event
   3851    // listeners. Checkboxes and radio buttons should still process clicks for
   3852    // web compat. See:
   3853    // https://html.spec.whatwg.org/multipage/input.html#the-input-element:activation-behaviour
   3854    preventDefault = true;
   3855  }
   3856 
   3857  if (NS_SUCCEEDED(rv)) {
   3858    WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
   3859    if (keyEvent && StepsInputValue(*keyEvent)) {
   3860      StepNumberControlForUserEvent(keyEvent->mKeyCode == NS_VK_UP ? 1 : -1);
   3861      FireChangeEventIfNeeded();
   3862      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3863    } else if (!preventDefault) {
   3864      if (keyEvent && ActivatesWithKeyboard(mType, keyEvent->mKeyCode) &&
   3865          keyEvent->IsTrusted()) {
   3866        // We maybe dispatch a synthesized click for keyboard activation.
   3867        HandleKeyboardActivation(aVisitor);
   3868      }
   3869 
   3870      switch (aVisitor.mEvent->mMessage) {
   3871        case eFocus: {
   3872          // see if we should select the contents of the textbox. This happens
   3873          // for text and password fields when the field was focused by the
   3874          // keyboard or a navigation, the platform allows it, and it wasn't
   3875          // just because we raised a window.
   3876          //
   3877          // While it'd usually make sense, we don't do this for JS callers
   3878          // because it causes some compat issues, see bug 1712724 for example.
   3879          nsFocusManager* fm = nsFocusManager::GetFocusManager();
   3880          if (fm && IsSingleLineTextControl(false) &&
   3881              !aVisitor.mEvent->AsFocusEvent()->mFromRaise &&
   3882              SelectTextFieldOnFocus()) {
   3883            if (Document* document = GetComposedDoc()) {
   3884              uint32_t lastFocusMethod =
   3885                  fm->GetLastFocusMethod(document->GetWindow());
   3886              const bool shouldSelectAllOnFocus = [&] {
   3887                if (lastFocusMethod & nsIFocusManager::FLAG_BYMOVEFOCUS) {
   3888                  return true;
   3889                }
   3890                if (lastFocusMethod & nsIFocusManager::FLAG_BYJS) {
   3891                  return false;
   3892                }
   3893                return bool(lastFocusMethod & nsIFocusManager::FLAG_BYKEY);
   3894              }();
   3895              if (shouldSelectAllOnFocus) {
   3896                SelectAll();
   3897              }
   3898            }
   3899          }
   3900          break;
   3901        }
   3902 
   3903        case eKeyDown: {
   3904          // For compatibility with the other browsers, we should active this
   3905          // element at least when a checkbox or a radio button.
   3906          // TODO: Investigate which elements are activated by space key in the
   3907          //       other browsers.
   3908          if (aVisitor.mPresContext && keyEvent->IsTrusted() && !IsDisabled() &&
   3909              keyEvent->ShouldWorkAsSpaceKey() &&
   3910              (mType == FormControlType::InputCheckbox ||
   3911               mType == FormControlType::InputRadio)) {
   3912            EventStateManager::SetActiveManager(
   3913                aVisitor.mPresContext->EventStateManager(), this);
   3914          }
   3915 
   3916          if (keyEvent->mKeyCode == NS_VK_ESCAPE && keyEvent->IsTrusted() &&
   3917              !keyEvent->DefaultPrevented() && !keyEvent->mIsComposing &&
   3918              mType == FormControlType::InputSearch &&
   3919              StaticPrefs::dom_forms_search_esc() && !IsDisabledOrReadOnly() &&
   3920              !IsValueEmpty()) {
   3921            // WebKit and Blink both also do this on keydown, see:
   3922            //   https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/forms/search_input_type.cc;l=82;drc=04f1f437aaefbd3bb4e0cdb5911c1ea1e3eb3557;bpv=1;bpt=1
   3923            //   https://searchfox.org/wubkat/rev/717f9adc97dd16bf639d27addbe0faf420f7dfce/Source/WebCore/html/SearchInputType.cpp#145
   3924            SetUserInput(EmptyString(), *NodePrincipal());
   3925            aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3926          }
   3927          break;
   3928        }
   3929 
   3930        case eKeyPress: {
   3931          if (mType == FormControlType::InputRadio && keyEvent->IsTrusted() &&
   3932              !keyEvent->IsAlt() && !keyEvent->IsControl() &&
   3933              !keyEvent->IsMeta()) {
   3934            // Radio button navigation needs to check visibility, so flush
   3935            // to ensure visibility is up to date.
   3936            if (Document* doc = GetComposedDoc()) {
   3937              doc->FlushPendingNotifications(
   3938                  FlushType::EnsurePresShellInitAndFrames);
   3939            }
   3940            rv = MaybeHandleRadioButtonNavigation(aVisitor, keyEvent->mKeyCode);
   3941          }
   3942 
   3943          /*
   3944           * For some input types, if the user hits enter, the form is
   3945           * submitted.
   3946           *
   3947           * Bug 99920, bug 109463 and bug 147850:
   3948           * (a) if there is a submit control in the form, click the first
   3949           *     submit control in the form.
   3950           * (b) if there is just one text control in the form, submit by
   3951           *     sending a submit event directly to the form
   3952           * (c) if there is more than one text input and no submit buttons, do
   3953           *     not submit, period.
   3954           */
   3955 
   3956          if (keyEvent->mKeyCode == NS_VK_RETURN && keyEvent->IsTrusted() &&
   3957              (IsSingleLineTextControl(false, mType) ||
   3958               IsDateTimeInputType(mType) ||
   3959               mType == FormControlType::InputCheckbox ||
   3960               mType == FormControlType::InputRadio)) {
   3961            if (IsSingleLineTextControl(false, mType) ||
   3962                IsDateTimeInputType(mType)) {
   3963              FireChangeEventIfNeeded();
   3964            }
   3965 
   3966            if (aVisitor.mPresContext) {
   3967              MaybeSubmitForm(aVisitor.mPresContext);
   3968            }
   3969          }
   3970 
   3971          if (mType == FormControlType::InputRange && keyEvent->IsTrusted() &&
   3972              !keyEvent->IsAlt() && !keyEvent->IsControl() &&
   3973              !keyEvent->IsMeta() &&
   3974              (keyEvent->mKeyCode == NS_VK_LEFT ||
   3975               keyEvent->mKeyCode == NS_VK_RIGHT ||
   3976               keyEvent->mKeyCode == NS_VK_UP ||
   3977               keyEvent->mKeyCode == NS_VK_DOWN ||
   3978               keyEvent->mKeyCode == NS_VK_PAGE_UP ||
   3979               keyEvent->mKeyCode == NS_VK_PAGE_DOWN ||
   3980               keyEvent->mKeyCode == NS_VK_HOME ||
   3981               keyEvent->mKeyCode == NS_VK_END)) {
   3982            Decimal minimum = GetMinimum();
   3983            Decimal maximum = GetMaximum();
   3984            MOZ_ASSERT(minimum.isFinite() && maximum.isFinite());
   3985            if (minimum < maximum) {  // else the value is locked to the minimum
   3986              Decimal value = GetValueAsDecimal();
   3987              Decimal step = GetStep();
   3988              if (step == kStepAny) {
   3989                step = GetDefaultStep();
   3990              }
   3991              MOZ_ASSERT(value.isFinite() && step.isFinite());
   3992              Decimal newValue;
   3993              switch (keyEvent->mKeyCode) {
   3994                case NS_VK_LEFT:
   3995                  newValue = value +
   3996                             (GetComputedDirectionality() == Directionality::Rtl
   3997                                  ? step
   3998                                  : -step);
   3999                  break;
   4000                case NS_VK_RIGHT:
   4001                  newValue = value +
   4002                             (GetComputedDirectionality() == Directionality::Rtl
   4003                                  ? -step
   4004                                  : step);
   4005                  break;
   4006                case NS_VK_UP:
   4007                  // Even for horizontal range, "up" means "increase"
   4008                  newValue = value + step;
   4009                  break;
   4010                case NS_VK_DOWN:
   4011                  // Even for horizontal range, "down" means "decrease"
   4012                  newValue = value - step;
   4013                  break;
   4014                case NS_VK_HOME:
   4015                  newValue = minimum;
   4016                  break;
   4017                case NS_VK_END:
   4018                  newValue = maximum;
   4019                  break;
   4020                case NS_VK_PAGE_UP:
   4021                  // For PgUp/PgDn we jump 10% of the total range, unless step
   4022                  // requires us to jump more.
   4023                  newValue =
   4024                      value + std::max(step, (maximum - minimum) / Decimal(10));
   4025                  break;
   4026                case NS_VK_PAGE_DOWN:
   4027                  newValue =
   4028                      value - std::max(step, (maximum - minimum) / Decimal(10));
   4029                  break;
   4030              }
   4031              SetValueOfRangeForUserEvent(newValue);
   4032              FireChangeEventIfNeeded();
   4033              aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   4034            }
   4035          }
   4036        } break;  // eKeyPress
   4037 
   4038        case eMouseDown:
   4039        case eMouseUp:
   4040        case eMouseDoubleClick: {
   4041          // cancel all of these events for buttons
   4042          // XXXsmaug Why?
   4043          WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
   4044          if (mouseEvent->mButton == MouseButton::eMiddle ||
   4045              mouseEvent->mButton == MouseButton::eSecondary) {
   4046            if (mType == FormControlType::InputButton ||
   4047                mType == FormControlType::InputReset ||
   4048                mType == FormControlType::InputSubmit) {
   4049              if (aVisitor.mDOMEvent) {
   4050                aVisitor.mDOMEvent->StopPropagation();
   4051              } else {
   4052                rv = NS_ERROR_FAILURE;
   4053              }
   4054            }
   4055          }
   4056          if (mType == FormControlType::InputNumber &&
   4057              aVisitor.mEvent->IsTrusted()) {
   4058            if (mouseEvent->mButton == MouseButton::ePrimary &&
   4059                !IgnoreInputEventWithModifier(*mouseEvent, false)) {
   4060              nsNumberControlFrame* numberControlFrame =
   4061                  do_QueryFrame(GetPrimaryFrame());
   4062              if (numberControlFrame) {
   4063                if (aVisitor.mEvent->mMessage == eMouseDown && IsMutable()) {
   4064                  switch (numberControlFrame->GetSpinButtonForPointerEvent(
   4065                      aVisitor.mEvent->AsMouseEvent())) {
   4066                    case nsNumberControlFrame::eSpinButtonUp:
   4067                      StepNumberControlForUserEvent(1);
   4068                      mNumberControlSpinnerSpinsUp = true;
   4069                      StartNumberControlSpinnerSpin();
   4070                      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   4071                      break;
   4072                    case nsNumberControlFrame::eSpinButtonDown:
   4073                      StepNumberControlForUserEvent(-1);
   4074                      mNumberControlSpinnerSpinsUp = false;
   4075                      StartNumberControlSpinnerSpin();
   4076                      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   4077                      break;
   4078                  }
   4079                }
   4080              }
   4081            }
   4082            if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
   4083              // We didn't handle this to step up/down. Whatever this was, be
   4084              // aggressive about stopping the spin. (And don't set
   4085              // nsEventStatus_eConsumeNoDefault after doing so, since that
   4086              // might prevent, say, the context menu from opening.)
   4087              StopNumberControlSpinnerSpin();
   4088            }
   4089          }
   4090          break;
   4091        }
   4092        case eWheel: {
   4093          if (StaticPrefs::
   4094                  dom_input_number_and_range_modified_by_mousewheel()) {
   4095            // Handle wheel events as increasing / decreasing the input
   4096            // element's value when it's focused and it's type is number or
   4097            // range.
   4098            WidgetWheelEvent* wheelEvent = aVisitor.mEvent->AsWheelEvent();
   4099            if (!aVisitor.mEvent->DefaultPrevented() &&
   4100                aVisitor.mEvent->IsTrusted() && IsMutable() && wheelEvent &&
   4101                wheelEvent->mDeltaY != 0 &&
   4102                wheelEvent->mDeltaMode != WheelEvent_Binding::DOM_DELTA_PIXEL) {
   4103              if (mType == FormControlType::InputNumber) {
   4104                if (nsFocusManager::GetFocusedElementStatic() == this) {
   4105                  StepNumberControlForUserEvent(wheelEvent->mDeltaY > 0 ? -1
   4106                                                                        : 1);
   4107                  FireChangeEventIfNeeded();
   4108                  aVisitor.mEvent->PreventDefault();
   4109                }
   4110              } else if (mType == FormControlType::InputRange &&
   4111                         nsFocusManager::GetFocusedElementStatic() == this &&
   4112                         GetMinimum() < GetMaximum()) {
   4113                Decimal value = GetValueAsDecimal();
   4114                Decimal step = GetStep();
   4115                if (step == kStepAny) {
   4116                  step = GetDefaultStep();
   4117                }
   4118                MOZ_ASSERT(value.isFinite() && step.isFinite());
   4119                SetValueOfRangeForUserEvent(
   4120                    wheelEvent->mDeltaY < 0 ? value + step : value - step);
   4121                FireChangeEventIfNeeded();
   4122                aVisitor.mEvent->PreventDefault();
   4123              }
   4124            }
   4125          }
   4126          break;
   4127        }
   4128        case ePointerClick: {
   4129          if (!aVisitor.mEvent->DefaultPrevented() &&
   4130              aVisitor.mEvent->IsTrusted() &&
   4131              aVisitor.mEvent->AsMouseEvent()->mButton ==
   4132                  MouseButton::ePrimary) {
   4133            // TODO(emilio): Handling this should ideally not move focus.
   4134            if (mType == FormControlType::InputSearch) {
   4135              if (nsSearchControlFrame* searchControlFrame =
   4136                      do_QueryFrame(GetPrimaryFrame())) {
   4137                Element* clearButton = searchControlFrame->GetButton();
   4138                if (clearButton &&
   4139                    aVisitor.mEvent->mOriginalTarget == clearButton) {
   4140                  SetUserInput(EmptyString(),
   4141                               *nsContentUtils::GetSystemPrincipal());
   4142                }
   4143              }
   4144            } else if (mType == FormControlType::InputPassword) {
   4145              if (nsTextControlFrame* textControlFrame =
   4146                      do_QueryFrame(GetPrimaryFrame())) {
   4147                auto* reveal = textControlFrame->GetButton();
   4148                if (reveal && aVisitor.mEvent->mOriginalTarget == reveal) {
   4149                  SetRevealPassword(!RevealPassword());
   4150                }
   4151              }
   4152            }
   4153          }
   4154          break;
   4155        }
   4156        default:
   4157          break;
   4158      }
   4159 
   4160      // Bug 1459231: Temporarily needed till links respect activation target,
   4161      // then also remove NS_OUTER_ACTIVATE_EVENT. The appropriate
   4162      // behavior/model for links is still under discussion (see
   4163      // https://github.com/whatwg/html/issues/1576). For now, we aim for
   4164      // consistency with other browsers.
   4165      if (aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT) {
   4166        switch (mType) {
   4167          case FormControlType::InputReset:
   4168          case FormControlType::InputSubmit:
   4169          case FormControlType::InputImage:
   4170            if (mForm) {
   4171              aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   4172            }
   4173            break;
   4174          case FormControlType::InputCheckbox:
   4175          case FormControlType::InputRadio:
   4176            aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   4177            break;
   4178          default:
   4179            break;
   4180        }
   4181      }
   4182    }
   4183  }  // if
   4184 
   4185  if (NS_SUCCEEDED(rv) && mType == FormControlType::InputRange) {
   4186    PostHandleEventForRangeThumb(aVisitor);
   4187  }
   4188 
   4189  if (!preventDefault) {
   4190    MOZ_TRY(MaybeInitPickers(aVisitor));
   4191  }
   4192  return NS_OK;
   4193 }
   4194 
   4195 void EndSubmitClick(EventChainPostVisitor& aVisitor) {
   4196  if (aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) {
   4197    nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mItemData));
   4198    RefPtr<HTMLFormElement> form = HTMLFormElement::FromNodeOrNull(content);
   4199    // Tell the form that we are about to exit a click handler,
   4200    // so the form knows not to defer subsequent submissions.
   4201    // The pending ones that were created during the handler
   4202    // will be flushed or forgotten.
   4203    form->OnSubmitClickEnd();
   4204    // tell the form to flush a possible pending submission.
   4205    // the reason is that the script returned false (the event was
   4206    // not ignored) so if there is a stored submission, it needs to
   4207    // be submitted immediately.
   4208    form->FlushPendingSubmission();
   4209  }
   4210 }
   4211 
   4212 void HTMLInputElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
   4213  auto oldType = FormControlType(NS_CONTROL_TYPE(aVisitor.mItemFlags));
   4214 
   4215  auto endSubmit = MakeScopeExit([&] { EndSubmitClick(aVisitor); });
   4216 
   4217  if (IsDisabled() && oldType != FormControlType::InputCheckbox &&
   4218      oldType != FormControlType::InputRadio) {
   4219    // Behave as if defaultPrevented when the element becomes disabled by event
   4220    // listeners. Checkboxes and radio buttons should still process clicks for
   4221    // web compat. See:
   4222    // https://html.spec.whatwg.org/multipage/input.html#the-input-element:activation-behaviour
   4223    return;
   4224  }
   4225 
   4226  // https://html.spec.whatwg.org/#checkbox-state-(type=checkbox):input-activation-behavior
   4227  // If element is connected, fire input and change event
   4228  if (mCheckedIsToggled && IsInComposedDoc()) {
   4229    SetUserInteracted(true);
   4230 
   4231    // Fire input event and then change event.
   4232    DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
   4233    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   4234                         "Failed to dispatch input event");
   4235 
   4236    // FIXME: Why is this different than every other change event?
   4237    nsContentUtils::DispatchTrustedEvent<WidgetEvent>(
   4238        OwnerDoc(), static_cast<Element*>(this), eFormChange, CanBubble::eYes,
   4239        Cancelable::eNo);
   4240 #ifdef ACCESSIBILITY
   4241    // Fire an event to notify accessibility
   4242    if (mType == FormControlType::InputCheckbox) {
   4243      if (nsContentUtils::MayHaveFormCheckboxStateChangeListeners()) {
   4244        FireEventForAccessibility(this, eFormCheckboxStateChange);
   4245      }
   4246    } else if (nsContentUtils::MayHaveFormRadioStateChangeListeners()) {
   4247      FireEventForAccessibility(this, eFormRadioStateChange);
   4248      // Fire event for the previous selected radio.
   4249      nsCOMPtr<nsIContent> content = do_QueryInterface(aVisitor.mItemData);
   4250      if (auto* previous = HTMLInputElement::FromNodeOrNull(content)) {
   4251        FireEventForAccessibility(previous, eFormRadioStateChange);
   4252      }
   4253    }
   4254 #endif
   4255  }
   4256 
   4257  switch (mType) {
   4258    case FormControlType::InputReset:
   4259    case FormControlType::InputSubmit:
   4260    case FormControlType::InputImage:
   4261      if (mForm) {
   4262        // Hold a strong ref while dispatching
   4263        RefPtr<HTMLFormElement> form(mForm);
   4264        if (mType == FormControlType::InputReset) {
   4265          form->MaybeReset(this);
   4266        } else {
   4267          form->MaybeSubmit(this);
   4268        }
   4269        aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   4270        return;
   4271      }
   4272      break;
   4273 
   4274    default:
   4275      break;
   4276  }  // switch
   4277  if (IsButtonControl()) {
   4278    nsCOMPtr<Element> eventTarget =
   4279        do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
   4280    HandlePopoverTargetAction(eventTarget);
   4281  }
   4282 }
   4283 
   4284 void HTMLInputElement::LegacyCanceledActivationBehavior(
   4285    EventChainPostVisitor& aVisitor) {
   4286  bool originalCheckedValue =
   4287      !!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
   4288  auto oldType = FormControlType(NS_CONTROL_TYPE(aVisitor.mItemFlags));
   4289 
   4290  if (mCheckedIsToggled) {
   4291    // if it was canceled and a radio button, then set the old
   4292    // selected btn to TRUE. if it is a checkbox then set it to its
   4293    // original value (legacy-canceled-activation)
   4294    if (oldType == FormControlType::InputRadio) {
   4295      nsCOMPtr<nsIContent> content = do_QueryInterface(aVisitor.mItemData);
   4296      HTMLInputElement* selectedRadioButton =
   4297          HTMLInputElement::FromNodeOrNull(content);
   4298      if (selectedRadioButton) {
   4299        selectedRadioButton->SetChecked(true);
   4300      }
   4301      // If there was no checked radio button or this one is no longer a
   4302      // radio button we must reset it back to false to cancel the action.
   4303      // See how the web of hack grows?
   4304      if (!selectedRadioButton || mType != FormControlType::InputRadio) {
   4305        DoSetChecked(/* aValue */ false, /* aNotify */ true,
   4306                     /* aSetValueChanged */ true);
   4307      }
   4308    } else if (oldType == FormControlType::InputCheckbox) {
   4309      bool originalIndeterminateValue =
   4310          !!(aVisitor.mItemFlags & NS_ORIGINAL_INDETERMINATE_VALUE);
   4311      SetIndeterminateInternal(originalIndeterminateValue, false);
   4312      DoSetChecked(originalCheckedValue, /* aNotify */ true,
   4313                   /* aSetValueChanged */ true);
   4314    }
   4315  }
   4316 
   4317  // Relevant for bug 242494: submit button with "submit(); return false;"
   4318  EndSubmitClick(aVisitor);
   4319 }
   4320 
   4321 enum class RadioButtonMove { Back, Forward, None };
   4322 nsresult HTMLInputElement::MaybeHandleRadioButtonNavigation(
   4323    EventChainPostVisitor& aVisitor, uint32_t aKeyCode) {
   4324  auto move = [&] {
   4325    switch (aKeyCode) {
   4326      case NS_VK_UP:
   4327        return RadioButtonMove::Back;
   4328      case NS_VK_DOWN:
   4329        return RadioButtonMove::Forward;
   4330      case NS_VK_LEFT:
   4331      case NS_VK_RIGHT: {
   4332        const bool isRtl = GetComputedDirectionality() == Directionality::Rtl;
   4333        return isRtl == (aKeyCode == NS_VK_LEFT) ? RadioButtonMove::Forward
   4334                                                 : RadioButtonMove::Back;
   4335      }
   4336    }
   4337    return RadioButtonMove::None;
   4338  }();
   4339  if (move == RadioButtonMove::None) {
   4340    return NS_OK;
   4341  }
   4342  // Arrow key pressed, focus+select prev/next radio button
   4343  RefPtr<HTMLInputElement> selectedRadioButton;
   4344  if (auto* container = GetCurrentRadioGroupContainer()) {
   4345    nsAutoString name;
   4346    GetAttr(nsGkAtoms::name, name);
   4347    container->GetNextRadioButton(name, move == RadioButtonMove::Back, this,
   4348                                  getter_AddRefs(selectedRadioButton));
   4349  }
   4350  if (!selectedRadioButton) {
   4351    return NS_OK;
   4352  }
   4353  FocusOptions options;
   4354  ErrorResult error;
   4355  selectedRadioButton->Focus(options, CallerType::System, error);
   4356  if (error.Failed()) {
   4357    return error.StealNSResult();
   4358  }
   4359  nsresult rv = DispatchSimulatedClick(
   4360      selectedRadioButton, aVisitor.mEvent->IsTrusted(), aVisitor.mPresContext);
   4361  if (NS_SUCCEEDED(rv)) {
   4362    aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   4363  }
   4364  return rv;
   4365 }
   4366 
   4367 void HTMLInputElement::PostHandleEventForRangeThumb(
   4368    EventChainPostVisitor& aVisitor) {
   4369  MOZ_ASSERT(mType == FormControlType::InputRange);
   4370 
   4371  if (nsEventStatus_eConsumeNoDefault == aVisitor.mEventStatus ||
   4372      !(aVisitor.mEvent->mClass == eMouseEventClass ||
   4373        aVisitor.mEvent->mClass == eTouchEventClass ||
   4374        aVisitor.mEvent->mClass == eKeyboardEventClass)) {
   4375    return;
   4376  }
   4377 
   4378  nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
   4379  if (!rangeFrame && mIsDraggingRange) {
   4380    CancelRangeThumbDrag();
   4381    return;
   4382  }
   4383 
   4384  switch (aVisitor.mEvent->mMessage) {
   4385    case eMouseDown:
   4386    case eTouchStart: {
   4387      if (mIsDraggingRange) {
   4388        break;
   4389      }
   4390      if (PresShell::GetCapturingContent()) {
   4391        break;  // don't start drag if someone else is already capturing
   4392      }
   4393      WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
   4394      if (IgnoreInputEventWithModifier(*inputEvent, true)) {
   4395        break;  // ignore
   4396      }
   4397      if (aVisitor.mEvent->mMessage == eMouseDown) {
   4398        if (aVisitor.mEvent->AsMouseEvent()->mButtons ==
   4399            MouseButtonsFlag::ePrimaryFlag) {
   4400          StartRangeThumbDrag(inputEvent);
   4401        } else if (mIsDraggingRange) {
   4402          CancelRangeThumbDrag();
   4403        }
   4404      } else {
   4405        if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) {
   4406          StartRangeThumbDrag(inputEvent);
   4407        } else if (mIsDraggingRange) {
   4408          CancelRangeThumbDrag();
   4409        }
   4410      }
   4411      aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   4412    } break;
   4413 
   4414    case eMouseMove:
   4415    case eTouchMove:
   4416      if (!mIsDraggingRange) {
   4417        break;
   4418      }
   4419      if (PresShell::GetCapturingContent() != this) {
   4420        // Someone else grabbed capture.
   4421        CancelRangeThumbDrag();
   4422        break;
   4423      }
   4424      SetValueOfRangeForUserEvent(
   4425          rangeFrame->GetValueAtEventPoint(aVisitor.mEvent->AsInputEvent()),
   4426          SnapToTickMarks::Yes);
   4427      aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   4428      break;
   4429 
   4430    case eMouseUp:
   4431    case eTouchEnd:
   4432      if (!mIsDraggingRange) {
   4433        break;
   4434      }
   4435      // We don't check to see whether we are the capturing content here and
   4436      // call CancelRangeThumbDrag() if that is the case. We just finish off
   4437      // the drag and set our final value (unless someone has called
   4438      // preventDefault() and prevents us getting here).
   4439      FinishRangeThumbDrag(aVisitor.mEvent->AsInputEvent());
   4440      aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   4441      break;
   4442 
   4443    case eKeyPress:
   4444      if (mIsDraggingRange &&
   4445          aVisitor.mEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
   4446        CancelRangeThumbDrag();
   4447      }
   4448      break;
   4449 
   4450    case eTouchCancel:
   4451      if (mIsDraggingRange) {
   4452        CancelRangeThumbDrag();
   4453      }
   4454      break;
   4455 
   4456    default:
   4457      break;
   4458  }
   4459 }
   4460 
   4461 void HTMLInputElement::MaybeLoadImage() {
   4462  // Our base URI may have changed; claim that our URI changed, and the
   4463  // nsImageLoadingContent will decide whether a new image load is warranted.
   4464  nsAutoString uri;
   4465  if (mType == FormControlType::InputImage && GetAttr(nsGkAtoms::src, uri) &&
   4466      (NS_FAILED(LoadImage(uri, false, true, eImageLoadType_Normal,
   4467                           mSrcTriggeringPrincipal)) ||
   4468       !LoadingEnabled())) {
   4469    CancelImageRequests(true);
   4470  }
   4471 }
   4472 
   4473 nsresult HTMLInputElement::BindToTree(BindContext& aContext, nsINode& aParent) {
   4474  // If we are currently bound to a disconnected subtree root, remove
   4475  // ourselves from it first.
   4476  if (!mForm && mType == FormControlType::InputRadio) {
   4477    RemoveFromRadioGroup();
   4478  }
   4479 
   4480  nsresult rv =
   4481      nsGenericHTMLFormControlElementWithState::BindToTree(aContext, aParent);
   4482  NS_ENSURE_SUCCESS(rv, rv);
   4483 
   4484  nsImageLoadingContent::BindToTree(aContext, aParent);
   4485 
   4486  if (mType == FormControlType::InputImage) {
   4487    // Our base URI may have changed; claim that our URI changed, and the
   4488    // nsImageLoadingContent will decide whether a new image load is warranted.
   4489    if (HasAttr(nsGkAtoms::src)) {
   4490      // Mark channel as urgent-start before load image if the image load is
   4491      // initaiated by a user interaction.
   4492      mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
   4493 
   4494      nsContentUtils::AddScriptRunner(
   4495          NewRunnableMethod("dom::HTMLInputElement::MaybeLoadImage", this,
   4496                            &HTMLInputElement::MaybeLoadImage));
   4497    }
   4498  }
   4499 
   4500  // Add radio to document if we don't have a form already (if we do it's
   4501  // already been added into that group)
   4502  if (!mForm && mType == FormControlType::InputRadio) {
   4503    AddToRadioGroup();
   4504  }
   4505 
   4506  // Set direction based on value if dir=auto
   4507  ResetDirFormAssociatedElement(this, false, HasDirAuto());
   4508 
   4509  // An element can't suffer from value missing if it is not in a document.
   4510  // We have to check if we suffer from that as we are now in a document.
   4511  UpdateValueMissingValidityState();
   4512 
   4513  // If there is a disabled fieldset in the parent chain, the element is now
   4514  // barred from constraint validation and can't suffer from value missing
   4515  // (call done before).
   4516  UpdateBarredFromConstraintValidation();
   4517 
   4518  // And now make sure our state is up to date
   4519  UpdateValidityElementStates(true);
   4520 
   4521  if (CreatesDateTimeWidget() && IsInComposedDoc()) {
   4522    // Construct Shadow Root so web content can be hidden in the DOM.
   4523    AttachAndSetUAShadowRoot(NotifyUAWidgetSetup::Yes, DelegatesFocus::Yes);
   4524  }
   4525 
   4526  MaybeDispatchLoginManagerEvents(mForm);
   4527 
   4528  return rv;
   4529 }
   4530 
   4531 void HTMLInputElement::MaybeDispatchLoginManagerEvents(HTMLFormElement* aForm) {
   4532  // Don't disptach the event if the <input> is disconnected
   4533  // or belongs to a disconnected form
   4534  if (!IsInComposedDoc()) {
   4535    return;
   4536  }
   4537 
   4538  nsString eventType;
   4539  EventTarget* target = nullptr;
   4540 
   4541  if (mType == FormControlType::InputPassword) {
   4542    // Don't fire another event if we have a pending event.
   4543    if (aForm && aForm->mHasPendingPasswordEvent) {
   4544      return;
   4545    }
   4546 
   4547    // TODO(Bug 1864404): Use one event for formless and form inputs.
   4548    eventType = aForm ? u"DOMFormHasPassword"_ns : u"DOMInputPasswordAdded"_ns;
   4549 
   4550    if (aForm) {
   4551      target = aForm;
   4552      aForm->mHasPendingPasswordEvent = true;
   4553    } else {
   4554      target = this;
   4555    }
   4556 
   4557  } else if (mType == FormControlType::InputEmail ||
   4558             mType == FormControlType::InputText) {
   4559    // Don't fire a username event if:
   4560    // - we have a pending event
   4561    // - username only forms are not supported
   4562    // fire event if we have a username field without a form with the
   4563    // autcomplete value of username
   4564 
   4565    if (!StaticPrefs::signon_usernameOnlyForm_enabled()) {
   4566      return;
   4567    }
   4568 
   4569    if (aForm) {
   4570      if (aForm->mHasPendingPossibleUsernameEvent) {
   4571        return;
   4572      }
   4573      aForm->mHasPendingPossibleUsernameEvent = true;
   4574      target = aForm;
   4575    } else {
   4576      nsAutoString autocompleteValue;
   4577      GetAutocomplete(autocompleteValue);
   4578      if (!autocompleteValue.EqualsASCII("username")) {
   4579        return;
   4580      }
   4581      target = GetComposedDoc();
   4582    }
   4583    eventType = u"DOMPossibleUsernameInputAdded"_ns;
   4584  } else {
   4585    return;
   4586  }
   4587 
   4588  RefPtr<AsyncEventDispatcher> dispatcher = new AsyncEventDispatcher(
   4589      target, eventType, CanBubble::eYes, ChromeOnlyDispatch::eYes);
   4590  dispatcher->PostDOMEvent();
   4591 }
   4592 
   4593 void HTMLInputElement::UnbindFromTree(UnbindContext& aContext) {
   4594  if (mType == FormControlType::InputPassword) {
   4595    MaybeFireInputPasswordRemoved();
   4596  }
   4597 
   4598  // If we have a form and are unbound from it,
   4599  // nsGenericHTMLFormControlElementWithState::UnbindFromTree() will unset the
   4600  // form and that takes care of form's WillRemove so we just have to take care
   4601  // of the case where we're removing from the document and we don't
   4602  // have a form
   4603  if (!mForm && mType == FormControlType::InputRadio) {
   4604    RemoveFromRadioGroup();
   4605  }
   4606 
   4607  if (CreatesDateTimeWidget() && IsInComposedDoc()) {
   4608    NotifyUAWidgetTeardown();
   4609  }
   4610 
   4611  nsImageLoadingContent::UnbindFromTree();
   4612  nsGenericHTMLFormControlElementWithState::UnbindFromTree(aContext);
   4613 
   4614  // If we are contained within a disconnected subtree, attempt to add
   4615  // ourselves to the subtree root's radio group.
   4616  if (!mForm && mType == FormControlType::InputRadio) {
   4617    AddToRadioGroup();
   4618  }
   4619 
   4620  // GetCurrentDoc is returning nullptr so we can update the value
   4621  // missing validity state to reflect we are no longer into a doc.
   4622  UpdateValueMissingValidityState();
   4623  // We might be no longer disabled because of parent chain changed.
   4624  UpdateBarredFromConstraintValidation();
   4625  // And now make sure our state is up to date
   4626  UpdateValidityElementStates(false);
   4627 }
   4628 
   4629 /**
   4630 * @param aType InputElementTypes
   4631 * @return true, iff SetRangeText applies to aType as specified at
   4632 * https://html.spec.whatwg.org/#concept-input-apply.
   4633 */
   4634 static bool SetRangeTextApplies(FormControlType aType) {
   4635  return aType == FormControlType::InputText ||
   4636         aType == FormControlType::InputSearch ||
   4637         aType == FormControlType::InputUrl ||
   4638         aType == FormControlType::InputTel ||
   4639         aType == FormControlType::InputPassword;
   4640 }
   4641 
   4642 void HTMLInputElement::HandleTypeChange(FormControlType aNewType,
   4643                                        bool aNotify) {
   4644  FormControlType oldType = mType;
   4645  MOZ_ASSERT(oldType != aNewType);
   4646 
   4647  mHasBeenTypePassword =
   4648      mHasBeenTypePassword || aNewType == FormControlType::InputPassword;
   4649 
   4650  if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
   4651    // Input element can represent very different kinds of UIs, and we may
   4652    // need to flush styling even when focusing the already focused input
   4653    // element.
   4654    fm->NeedsFlushBeforeEventHandling(this);
   4655  }
   4656 
   4657  if (oldType == FormControlType::InputPassword &&
   4658      State().HasState(ElementState::REVEALED)) {
   4659    // Modify the state directly to avoid dispatching events.
   4660    RemoveStates(ElementState::REVEALED, aNotify);
   4661  }
   4662 
   4663  if (aNewType == FormControlType::InputFile ||
   4664      oldType == FormControlType::InputFile) {
   4665    if (aNewType == FormControlType::InputFile) {
   4666      mFileData.reset(new FileData());
   4667    } else {
   4668      mFileData->Unlink();
   4669      mFileData = nullptr;
   4670    }
   4671  }
   4672 
   4673  if (oldType == FormControlType::InputRange && mIsDraggingRange) {
   4674    CancelRangeThumbDrag(false);
   4675  }
   4676 
   4677  const ValueModeType oldValueMode = GetValueMode();
   4678  nsAutoString oldValue;
   4679  if (oldValueMode == VALUE_MODE_VALUE) {
   4680    // Doesn't matter what caller type we pass here, since we know we're not a
   4681    // file input anyway.
   4682    GetValue(oldValue, CallerType::NonSystem);
   4683  }
   4684 
   4685  TextControlState::SelectionProperties sp;
   4686 
   4687  if (IsSingleLineTextControl(false) && mInputData.mState) {
   4688    mInputData.mState->SyncUpSelectionPropertiesBeforeDestruction();
   4689    sp = mInputData.mState->GetSelectionProperties();
   4690  }
   4691 
   4692  // We already have a copy of the value, lets free it and changes the type.
   4693  FreeData();
   4694  mType = aNewType;
   4695  void* memory = mInputTypeMem;
   4696  mInputType = InputType::Create(this, mType, memory);
   4697 
   4698  if (IsSingleLineTextControl()) {
   4699    mInputData.mState = TextControlState::Construct(this);
   4700    if (!sp.IsDefault()) {
   4701      mInputData.mState->SetSelectionProperties(sp);
   4702    }
   4703  }
   4704 
   4705  // Whether placeholder applies might have changed.
   4706  UpdatePlaceholderShownState();
   4707  // Whether readonly applies might have changed.
   4708  UpdateReadOnlyState(aNotify);
   4709  UpdateCheckedState(aNotify);
   4710  UpdateIndeterminateState(aNotify);
   4711  const bool isDefault = IsRadioOrCheckbox()
   4712                             ? DefaultChecked()
   4713                             : (mForm && mForm->IsDefaultSubmitElement(this));
   4714  SetStates(ElementState::DEFAULT, isDefault, aNotify);
   4715 
   4716  // https://html.spec.whatwg.org/#input-type-change
   4717  switch (GetValueMode()) {
   4718    case VALUE_MODE_DEFAULT:
   4719    case VALUE_MODE_DEFAULT_ON:
   4720      // 1. If the previous state of the element's type attribute put the value
   4721      //    IDL attribute in the value mode, and the element's value is not the
   4722      //    empty string, and the new state of the element's type attribute puts
   4723      //    the value IDL attribute in either the default mode or the default/on
   4724      //    mode, then set the element's value content attribute to the
   4725      //    element's value.
   4726      if (oldValueMode == VALUE_MODE_VALUE && !oldValue.IsEmpty()) {
   4727        SetAttr(kNameSpaceID_None, nsGkAtoms::value, oldValue, true);
   4728      }
   4729      break;
   4730    case VALUE_MODE_VALUE: {
   4731      ValueSetterOptions options{ValueSetterOption::ByInternalAPI};
   4732      if (!SetRangeTextApplies(oldType) && SetRangeTextApplies(mType)) {
   4733        options +=
   4734            ValueSetterOption::MoveCursorToBeginSetSelectionDirectionForward;
   4735      }
   4736      if (oldValueMode != VALUE_MODE_VALUE) {
   4737        // 2. Otherwise, if the previous state of the element's type attribute
   4738        //    put the value IDL attribute in any mode other than the value
   4739        //    mode, and the new state of the element's type attribute puts the
   4740        //    value IDL attribute in the value mode, then set the value of the
   4741        //    element to the value of the value content attribute, if there is
   4742        //    one, or the empty string otherwise, and then set the control's
   4743        //    dirty value flag to false.
   4744        nsAutoString value;
   4745        GetAttr(nsGkAtoms::value, value);
   4746        SetValueInternal(value, options);
   4747        SetValueChanged(false);
   4748      } else if (mValueChanged) {
   4749        // We're both in the "value" mode state, we need to make no change per
   4750        // spec, but due to how we store the value internally we need to call
   4751        // SetValueInternal, if our value had changed at all.
   4752        // TODO: What should we do if SetValueInternal fails?  (The allocation
   4753        // may potentially be big, but most likely we've failed to allocate
   4754        // before the type change.)
   4755        SetValueInternal(oldValue, options);
   4756      } else {
   4757        // The value dirty flag is not set, so our value is based on our default
   4758        // value. But our default value might be dependent on the type. Make
   4759        // sure to set it so that state is consistent.
   4760        SetDefaultValueAsValue();
   4761      }
   4762      break;
   4763    }
   4764    case VALUE_MODE_FILENAME:
   4765    default:
   4766      // 3. Otherwise, if the previous state of the element's type attribute
   4767      //    put the value IDL attribute in any mode other than the filename
   4768      //    mode, and the new state of the element's type attribute puts the
   4769      //    value IDL attribute in the filename mode, then set the value of the
   4770      //    element to the empty string.
   4771      //
   4772      // Setting the attribute to the empty string is basically calling
   4773      // ClearFiles, but there can't be any files.
   4774      break;
   4775  }
   4776 
   4777  // Updating mFocusedValue in consequence:
   4778  // If the new type fires a change event on blur, but the previous type
   4779  // doesn't, we should set mFocusedValue to the current value.
   4780  // Otherwise, if the new type doesn't fire a change event on blur, but the
   4781  // previous type does, we should clear out mFocusedValue.
   4782  if (MayFireChangeOnBlur(mType) && !MayFireChangeOnBlur(oldType)) {
   4783    GetValue(mFocusedValue, CallerType::System);
   4784  } else if (!IsSingleLineTextControl(false, mType) &&
   4785             IsSingleLineTextControl(false, oldType)) {
   4786    mFocusedValue.Truncate();
   4787  }
   4788 
   4789  // Update or clear our required states since we may have changed from a
   4790  // required input type to a non-required input type or viceversa.
   4791  if (DoesRequiredApply()) {
   4792    const bool isRequired = HasAttr(nsGkAtoms::required);
   4793    UpdateRequiredState(isRequired, aNotify);
   4794  } else {
   4795    RemoveStates(ElementState::REQUIRED_STATES, aNotify);
   4796  }
   4797 
   4798  UpdateHasRange(aNotify);
   4799 
   4800  // Update validity states, but not element state.  We'll update
   4801  // element state later, as part of this attribute change.
   4802  UpdateAllValidityStatesButNotElementState();
   4803 
   4804  UpdateApzAwareFlag();
   4805 
   4806  UpdateBarredFromConstraintValidation();
   4807 
   4808  // Changing type might change auto directionality of this or the assigned slot
   4809  const bool autoDirAssociated = IsAutoDirectionalityAssociated(mType);
   4810  if (IsAutoDirectionalityAssociated(oldType) != autoDirAssociated) {
   4811    ResetDirFormAssociatedElement(this, aNotify, true);
   4812  }
   4813  // Special case for <input type=tel> as specified in
   4814  // https://html.spec.whatwg.org/multipage/dom.html#the-directionality
   4815  if (!HasDirAuto() && (oldType == FormControlType::InputTel ||
   4816                        mType == FormControlType::InputTel)) {
   4817    RecomputeDirectionality(this, aNotify);
   4818  }
   4819 
   4820  if (oldType == FormControlType::InputImage ||
   4821      mType == FormControlType::InputImage) {
   4822    if (oldType == FormControlType::InputImage) {
   4823      // We're no longer an image input.  Cancel our image requests, if we have
   4824      // any.
   4825      CancelImageRequests(aNotify);
   4826      RemoveStates(ElementState::BROKEN, aNotify);
   4827    } else {
   4828      // We just got switched to be an image input; we should see whether we
   4829      // have an image to load;
   4830      bool hasSrc = false;
   4831      if (aNotify) {
   4832        nsAutoString src;
   4833        if ((hasSrc = GetAttr(nsGkAtoms::src, src))) {
   4834          // Mark channel as urgent-start before load image if the image load is
   4835          // initiated by a user interaction.
   4836          mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
   4837 
   4838          LoadImage(src, false, aNotify, eImageLoadType_Normal,
   4839                    mSrcTriggeringPrincipal);
   4840        }
   4841      } else {
   4842        hasSrc = HasAttr(nsGkAtoms::src);
   4843      }
   4844      if (!hasSrc) {
   4845        AddStates(ElementState::BROKEN, aNotify);
   4846      }
   4847    }
   4848    // We should update our mapped attribute mapping function.
   4849    if (mAttrs.HasAttrs() && !mAttrs.IsPendingMappedAttributeEvaluation()) {
   4850      mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation();
   4851      if (auto* doc = GetComposedDoc()) {
   4852        doc->ScheduleForPresAttrEvaluation(this);
   4853      }
   4854    }
   4855  }
   4856 
   4857  MaybeDispatchLoginManagerEvents(mForm);
   4858 
   4859  if (IsInComposedDoc()) {
   4860    if (CreatesDateTimeWidget(oldType)) {
   4861      if (!CreatesDateTimeWidget()) {
   4862        // Switch away from date/time type.
   4863        NotifyUAWidgetTeardown();
   4864      } else {
   4865        // Switch between date and time.
   4866        NotifyUAWidgetSetupOrChange();
   4867      }
   4868    } else if (CreatesDateTimeWidget()) {
   4869      // Switch to date/time type.
   4870      AttachAndSetUAShadowRoot(NotifyUAWidgetSetup::Yes, DelegatesFocus::Yes);
   4871    }
   4872    // If we're becoming a text control and have focus, make sure to show focus
   4873    // rings.
   4874    if (State().HasState(ElementState::FOCUS) && IsSingleLineTextControl() &&
   4875        !IsSingleLineTextControl(/* aExcludePassword = */ false, oldType)) {
   4876      AddStates(ElementState::FOCUSRING);
   4877    }
   4878  }
   4879 }
   4880 
   4881 void HTMLInputElement::MaybeSnapToTickMark(Decimal& aValue) {
   4882  nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
   4883  if (!rangeFrame) {
   4884    return;
   4885  }
   4886  auto tickMark = rangeFrame->NearestTickMark(aValue);
   4887  if (tickMark.isNaN()) {
   4888    return;
   4889  }
   4890  auto rangeFrameSize = CSSPixel::FromAppUnits(rangeFrame->GetSize());
   4891  CSSCoord rangeTrackLength;
   4892  if (rangeFrame->IsHorizontal()) {
   4893    rangeTrackLength = rangeFrameSize.width;
   4894  } else {
   4895    rangeTrackLength = rangeFrameSize.height;
   4896  }
   4897  auto stepBase = GetStepBase();
   4898  auto distanceToTickMark =
   4899      rangeTrackLength * float(rangeFrame->GetDoubleAsFractionOfRange(
   4900                             stepBase + (tickMark - aValue).abs()));
   4901  const CSSCoord magnetEffectRange(
   4902      StaticPrefs::dom_range_element_magnet_effect_threshold());
   4903  if (distanceToTickMark <= magnetEffectRange) {
   4904    aValue = tickMark;
   4905  }
   4906 }
   4907 
   4908 void HTMLInputElement::SanitizeValue(nsAString& aValue,
   4909                                     SanitizationKind aKind) const {
   4910  NS_ASSERTION(mDoneCreating, "The element creation should be finished!");
   4911 
   4912  switch (mType) {
   4913    case FormControlType::InputText:
   4914    case FormControlType::InputSearch:
   4915    case FormControlType::InputTel:
   4916    case FormControlType::InputPassword: {
   4917      aValue.StripCRLF();
   4918    } break;
   4919    case FormControlType::InputEmail: {
   4920      aValue.StripCRLF();
   4921      aValue = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
   4922          aValue);
   4923 
   4924      if (Multiple() && !aValue.IsEmpty()) {
   4925        nsAutoString oldValue(aValue);
   4926        HTMLSplitOnSpacesTokenizer tokenizer(oldValue, ',');
   4927        aValue.Truncate(0);
   4928        aValue.Append(tokenizer.nextToken());
   4929        while (tokenizer.hasMoreTokens() ||
   4930               tokenizer.separatorAfterCurrentToken()) {
   4931          aValue.Append(',');
   4932          aValue.Append(tokenizer.nextToken());
   4933        }
   4934      }
   4935    } break;
   4936    case FormControlType::InputUrl: {
   4937      aValue.StripCRLF();
   4938 
   4939      aValue = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
   4940          aValue);
   4941    } break;
   4942    case FormControlType::InputNumber: {
   4943      auto result =
   4944          aKind == SanitizationKind::ForValueSetter
   4945              ? InputType::StringToNumberResult{StringToDecimal(aValue)}
   4946              : mInputType->ConvertStringToNumber(aValue);
   4947      if (!result.mResult.isFinite()) {
   4948        aValue.Truncate();
   4949        return;
   4950      }
   4951      switch (aKind) {
   4952        case SanitizationKind::ForValueGetter: {
   4953          // If the default non-localized algorithm parses the value, then we're
   4954          // done, don't un-localize it, to avoid precision loss, and to
   4955          // preserve scientific notation as well for example.
   4956          if (!result.mLocalized) {
   4957            return;
   4958          }
   4959          // For the <input type=number> value getter, we return the unlocalized
   4960          // value if it doesn't parse as StringToDecimal, for compat with other
   4961          // browsers.
   4962          aValue.AssignASCII(result.mResult.toString().c_str());
   4963          break;
   4964        }
   4965        case SanitizationKind::ForDisplay:
   4966        case SanitizationKind::ForValueSetter: {
   4967          // We localize as needed, but if both the localized and unlocalized
   4968          // version parse with the generic parser, we just use the unlocalized
   4969          // one, to preserve the input as much as possible.
   4970          //
   4971          // FIXME(emilio, bug 1622808): Localization should ideally be more
   4972          // input-preserving.
   4973          nsString localizedValue;
   4974          mInputType->ConvertNumberToString(
   4975              result.mResult, InputType::Localized::Yes, localizedValue);
   4976          if (!StringToDecimal(localizedValue).isFinite()) {
   4977            aValue = std::move(localizedValue);
   4978          }
   4979          break;
   4980        }
   4981      }
   4982      break;
   4983    }
   4984    case FormControlType::InputRange: {
   4985      Decimal minimum = GetMinimum();
   4986      Decimal maximum = GetMaximum();
   4987      MOZ_ASSERT(minimum.isFinite() && maximum.isFinite(),
   4988                 "type=range should have a default maximum/minimum");
   4989 
   4990      // We use this to avoid modifying the string unnecessarily, since that
   4991      // may introduce rounding. This is set to true only if the value we
   4992      // parse out from aValue needs to be sanitized.
   4993      bool needSanitization = false;
   4994 
   4995      Decimal value = mInputType->ConvertStringToNumber(aValue).mResult;
   4996      if (!value.isFinite()) {
   4997        needSanitization = true;
   4998        // Set value to midway between minimum and maximum.
   4999        value = maximum <= minimum ? minimum
   5000                                   : minimum + (maximum - minimum) / Decimal(2);
   5001      } else if (value < minimum || maximum < minimum) {
   5002        needSanitization = true;
   5003        value = minimum;
   5004      } else if (value > maximum) {
   5005        needSanitization = true;
   5006        value = maximum;
   5007      }
   5008 
   5009      Decimal step = GetStep();
   5010      if (step != kStepAny) {
   5011        Decimal stepBase = GetStepBase();
   5012        // There could be rounding issues below when dealing with fractional
   5013        // numbers, but let's ignore that until ECMAScript supplies us with a
   5014        // decimal number type.
   5015        Decimal deltaToStep = NS_floorModulo(value - stepBase, step);
   5016        if (deltaToStep != Decimal(0)) {
   5017          // "suffering from a step mismatch"
   5018          // Round the element's value to the nearest number for which the
   5019          // element would not suffer from a step mismatch, and which is
   5020          // greater than or equal to the minimum, and, if the maximum is not
   5021          // less than the minimum, which is less than or equal to the
   5022          // maximum, if there is a number that matches these constraints:
   5023          MOZ_ASSERT(deltaToStep > Decimal(0),
   5024                     "stepBelow/stepAbove will be wrong");
   5025          Decimal stepBelow = value - deltaToStep;
   5026          Decimal stepAbove = value - deltaToStep + step;
   5027          Decimal halfStep = step / Decimal(2);
   5028          bool stepAboveIsClosest = (stepAbove - value) <= halfStep;
   5029          bool stepAboveInRange = stepAbove >= minimum && stepAbove <= maximum;
   5030          bool stepBelowInRange = stepBelow >= minimum && stepBelow <= maximum;
   5031 
   5032          if ((stepAboveIsClosest || !stepBelowInRange) && stepAboveInRange) {
   5033            needSanitization = true;
   5034            value = stepAbove;
   5035          } else if ((!stepAboveIsClosest || !stepAboveInRange) &&
   5036                     stepBelowInRange) {
   5037            needSanitization = true;
   5038            value = stepBelow;
   5039          }
   5040        }
   5041      }
   5042 
   5043      if (needSanitization) {
   5044        aValue.AssignASCII(value.toString().c_str());
   5045      }
   5046    } break;
   5047    case FormControlType::InputDate: {
   5048      if (!aValue.IsEmpty() && !IsValidDate(aValue)) {
   5049        aValue.Truncate();
   5050      }
   5051    } break;
   5052    case FormControlType::InputTime: {
   5053      if (!aValue.IsEmpty() && !IsValidTime(aValue)) {
   5054        aValue.Truncate();
   5055      }
   5056    } break;
   5057    case FormControlType::InputMonth: {
   5058      if (!aValue.IsEmpty() && !IsValidMonth(aValue)) {
   5059        aValue.Truncate();
   5060      }
   5061    } break;
   5062    case FormControlType::InputWeek: {
   5063      if (!aValue.IsEmpty() && !IsValidWeek(aValue)) {
   5064        aValue.Truncate();
   5065      }
   5066    } break;
   5067    case FormControlType::InputDatetimeLocal: {
   5068      if (!aValue.IsEmpty() && !IsValidDateTimeLocal(aValue)) {
   5069        aValue.Truncate();
   5070      } else {
   5071        NormalizeDateTimeLocal(aValue);
   5072      }
   5073    } break;
   5074    case FormControlType::InputColor: {
   5075      // https://html.spec.whatwg.org/#update-a-color-well-control-color
   5076      // https://html.spec.whatwg.org/#serialize-a-color-well-control-color
   5077      StyleAbsoluteColor color = MaybeComputeColor(OwnerDoc(), aValue)
   5078                                     .valueOr(StyleAbsoluteColor::BLACK);
   5079      // Serialization step 6: If htmlCompatible is true, then do so with
   5080      // HTML-compatible serialization requested.
   5081      SerializeColorForHTMLCompatibility(color, aValue);
   5082      break;
   5083    }
   5084    default:
   5085      break;
   5086  }
   5087 }
   5088 
   5089 Maybe<nscolor> HTMLInputElement::ParseSimpleColor(const nsAString& aColor) {
   5090  // Input color string should be 7 length (i.e. a string representing a valid
   5091  // simple color)
   5092  if (aColor.Length() != 7 || aColor.First() != '#') {
   5093    return {};
   5094  }
   5095 
   5096  const nsAString& withoutHash = StringTail(aColor, 6);
   5097  nscolor color;
   5098  if (!NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) {
   5099    return {};
   5100  }
   5101 
   5102  return Some(color);
   5103 }
   5104 
   5105 bool HTMLInputElement::IsLeapYear(uint32_t aYear) const {
   5106  if ((aYear % 4 == 0 && aYear % 100 != 0) || (aYear % 400 == 0)) {
   5107    return true;
   5108  }
   5109  return false;
   5110 }
   5111 
   5112 uint32_t HTMLInputElement::DayOfWeek(uint32_t aYear, uint32_t aMonth,
   5113                                     uint32_t aDay, bool isoWeek) const {
   5114  MOZ_ASSERT(1 <= aMonth && aMonth <= 12, "month is in 1..12");
   5115  MOZ_ASSERT(1 <= aDay && aDay <= 31, "day is in 1..31");
   5116 
   5117  // Tomohiko Sakamoto algorithm.
   5118  int monthTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
   5119  aYear -= aMonth < 3;
   5120 
   5121  uint32_t day = (aYear + aYear / 4 - aYear / 100 + aYear / 400 +
   5122                  monthTable[aMonth - 1] + aDay) %
   5123                 7;
   5124 
   5125  if (isoWeek) {
   5126    return ((day + 6) % 7) + 1;
   5127  }
   5128 
   5129  return day;
   5130 }
   5131 
   5132 uint32_t HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const {
   5133  int day = DayOfWeek(aYear, 1, 1, true);  // January 1.
   5134  // A year starting on Thursday or a leap year starting on Wednesday has 53
   5135  // weeks. All other years have 52 weeks.
   5136  return day == 4 || (day == 3 && IsLeapYear(aYear)) ? kMaximumWeekInYear
   5137                                                     : kMaximumWeekInYear - 1;
   5138 }
   5139 
   5140 bool HTMLInputElement::IsValidWeek(const nsAString& aValue) const {
   5141  uint32_t year, week;
   5142  return ParseWeek(aValue, &year, &week);
   5143 }
   5144 
   5145 bool HTMLInputElement::IsValidMonth(const nsAString& aValue) const {
   5146  uint32_t year, month;
   5147  return ParseMonth(aValue, &year, &month);
   5148 }
   5149 
   5150 bool HTMLInputElement::IsValidDate(const nsAString& aValue) const {
   5151  uint32_t year, month, day;
   5152  return ParseDate(aValue, &year, &month, &day);
   5153 }
   5154 
   5155 bool HTMLInputElement::IsValidDateTimeLocal(const nsAString& aValue) const {
   5156  uint32_t year, month, day, time;
   5157  return ParseDateTimeLocal(aValue, &year, &month, &day, &time);
   5158 }
   5159 
   5160 bool HTMLInputElement::ParseYear(const nsAString& aValue,
   5161                                 uint32_t* aYear) const {
   5162  if (aValue.Length() < 4) {
   5163    return false;
   5164  }
   5165 
   5166  return DigitSubStringToNumber(aValue, 0, aValue.Length(), aYear) &&
   5167         *aYear > 0;
   5168 }
   5169 
   5170 bool HTMLInputElement::ParseMonth(const nsAString& aValue, uint32_t* aYear,
   5171                                  uint32_t* aMonth) const {
   5172  // Parse the year, month values out a string formatted as 'yyyy-mm'.
   5173  if (aValue.Length() < 7) {
   5174    return false;
   5175  }
   5176 
   5177  uint32_t endOfYearOffset = aValue.Length() - 3;
   5178  if (aValue[endOfYearOffset] != '-') {
   5179    return false;
   5180  }
   5181 
   5182  const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
   5183  if (!ParseYear(yearStr, aYear)) {
   5184    return false;
   5185  }
   5186 
   5187  return DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) &&
   5188         *aMonth > 0 && *aMonth <= 12;
   5189 }
   5190 
   5191 bool HTMLInputElement::ParseWeek(const nsAString& aValue, uint32_t* aYear,
   5192                                 uint32_t* aWeek) const {
   5193  // Parse the year, month values out a string formatted as 'yyyy-Www'.
   5194  if (aValue.Length() < 8) {
   5195    return false;
   5196  }
   5197 
   5198  uint32_t endOfYearOffset = aValue.Length() - 4;
   5199  if (aValue[endOfYearOffset] != '-') {
   5200    return false;
   5201  }
   5202 
   5203  if (aValue[endOfYearOffset + 1] != 'W') {
   5204    return false;
   5205  }
   5206 
   5207  const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
   5208  if (!ParseYear(yearStr, aYear)) {
   5209    return false;
   5210  }
   5211 
   5212  return DigitSubStringToNumber(aValue, endOfYearOffset + 2, 2, aWeek) &&
   5213         *aWeek > 0 && *aWeek <= MaximumWeekInYear(*aYear);
   5214 }
   5215 
   5216 bool HTMLInputElement::ParseDate(const nsAString& aValue, uint32_t* aYear,
   5217                                 uint32_t* aMonth, uint32_t* aDay) const {
   5218  /*
   5219   * Parse the year, month, day values out a date string formatted as
   5220   * yyyy-mm-dd. -The year must be 4 or more digits long, and year > 0 -The
   5221   * month must be exactly 2 digits long, and 01 <= month <= 12 -The day must be
   5222   * exactly 2 digit long, and 01 <= day <= maxday Where maxday is the number of
   5223   * days in the month 'month' and year 'year'
   5224   */
   5225  if (aValue.Length() < 10) {
   5226    return false;
   5227  }
   5228 
   5229  uint32_t endOfMonthOffset = aValue.Length() - 3;
   5230  if (aValue[endOfMonthOffset] != '-') {
   5231    return false;
   5232  }
   5233 
   5234  const nsAString& yearMonthStr = Substring(aValue, 0, endOfMonthOffset);
   5235  if (!ParseMonth(yearMonthStr, aYear, aMonth)) {
   5236    return false;
   5237  }
   5238 
   5239  return DigitSubStringToNumber(aValue, endOfMonthOffset + 1, 2, aDay) &&
   5240         *aDay > 0 && *aDay <= NumberOfDaysInMonth(*aMonth, *aYear);
   5241 }
   5242 
   5243 bool HTMLInputElement::ParseDateTimeLocal(const nsAString& aValue,
   5244                                          uint32_t* aYear, uint32_t* aMonth,
   5245                                          uint32_t* aDay,
   5246                                          uint32_t* aTime) const {
   5247  // Parse the year, month, day and time values out a string formatted as
   5248  // 'yyyy-mm-ddThh:mm[:ss.s] or 'yyyy-mm-dd hh:mm[:ss.s]', where fractions of
   5249  // seconds can be 1 to 3 digits.
   5250  // The minimum length allowed is 16, which is of the form 'yyyy-mm-ddThh:mm'
   5251  // or 'yyyy-mm-dd hh:mm'.
   5252  if (aValue.Length() < 16) {
   5253    return false;
   5254  }
   5255 
   5256  int32_t sepIndex = aValue.FindChar('T');
   5257  if (sepIndex == -1) {
   5258    sepIndex = aValue.FindChar(' ');
   5259 
   5260    if (sepIndex == -1) {
   5261      return false;
   5262    }
   5263  }
   5264 
   5265  const nsAString& dateStr = Substring(aValue, 0, sepIndex);
   5266  if (!ParseDate(dateStr, aYear, aMonth, aDay)) {
   5267    return false;
   5268  }
   5269 
   5270  const nsAString& timeStr =
   5271      Substring(aValue, sepIndex + 1, aValue.Length() - sepIndex + 1);
   5272  if (!ParseTime(timeStr, aTime)) {
   5273    return false;
   5274  }
   5275 
   5276  return true;
   5277 }
   5278 
   5279 void HTMLInputElement::NormalizeDateTimeLocal(nsAString& aValue) const {
   5280  if (aValue.IsEmpty()) {
   5281    return;
   5282  }
   5283 
   5284  // Use 'T' as the separator between date string and time string.
   5285  int32_t sepIndex = aValue.FindChar(' ');
   5286  if (sepIndex != -1) {
   5287    aValue.ReplaceLiteral(sepIndex, 1, u"T");
   5288  } else {
   5289    sepIndex = aValue.FindChar('T');
   5290  }
   5291 
   5292  // Time expressed as the shortest possible string, which is hh:mm.
   5293  if ((aValue.Length() - sepIndex) == 6) {
   5294    return;
   5295  }
   5296 
   5297  // Fractions of seconds part is optional, ommit it if it's 0.
   5298  if ((aValue.Length() - sepIndex) > 9) {
   5299    const uint32_t millisecSepIndex = sepIndex + 9;
   5300    uint32_t milliseconds;
   5301    if (!DigitSubStringToNumber(aValue, millisecSepIndex + 1,
   5302                                aValue.Length() - (millisecSepIndex + 1),
   5303                                &milliseconds)) {
   5304      return;
   5305    }
   5306 
   5307    if (milliseconds != 0) {
   5308      return;
   5309    }
   5310 
   5311    aValue.Cut(millisecSepIndex, aValue.Length() - millisecSepIndex);
   5312  }
   5313 
   5314  // Seconds part is optional, ommit it if it's 0.
   5315  const uint32_t secondSepIndex = sepIndex + 6;
   5316  uint32_t seconds;
   5317  if (!DigitSubStringToNumber(aValue, secondSepIndex + 1,
   5318                              aValue.Length() - (secondSepIndex + 1),
   5319                              &seconds)) {
   5320    return;
   5321  }
   5322 
   5323  if (seconds != 0) {
   5324    return;
   5325  }
   5326 
   5327  aValue.Cut(secondSepIndex, aValue.Length() - secondSepIndex);
   5328 }
   5329 
   5330 double HTMLInputElement::DaysSinceEpochFromWeek(uint32_t aYear,
   5331                                                uint32_t aWeek) const {
   5332  double days = JS::DayFromYear(aYear) + (aWeek - 1) * 7;
   5333  uint32_t dayOneIsoWeekday = DayOfWeek(aYear, 1, 1, true);
   5334 
   5335  // If day one of that year is on/before Thursday, we should subtract the
   5336  // days that belong to last year in our first week, otherwise, our first
   5337  // days belong to last year's last week, and we should add those days
   5338  // back.
   5339  if (dayOneIsoWeekday <= 4) {
   5340    days -= (dayOneIsoWeekday - 1);
   5341  } else {
   5342    days += (7 - dayOneIsoWeekday + 1);
   5343  }
   5344 
   5345  return days;
   5346 }
   5347 
   5348 uint32_t HTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth,
   5349                                               uint32_t aYear) const {
   5350  /*
   5351   * Returns the number of days in a month.
   5352   * Months that are |longMonths| always have 31 days.
   5353   * Months that are not |longMonths| have 30 days except February (month 2).
   5354   * February has 29 days during leap years which are years that are divisible
   5355   * by 400. or divisible by 100 and 4. February has 28 days otherwise.
   5356   */
   5357 
   5358  static const bool longMonths[] = {true, false, true,  false, true,  false,
   5359                                    true, true,  false, true,  false, true};
   5360  MOZ_ASSERT(aMonth <= 12 && aMonth > 0);
   5361 
   5362  if (longMonths[aMonth - 1]) {
   5363    return 31;
   5364  }
   5365 
   5366  if (aMonth != 2) {
   5367    return 30;
   5368  }
   5369 
   5370  return IsLeapYear(aYear) ? 29 : 28;
   5371 }
   5372 
   5373 /* static */
   5374 bool HTMLInputElement::DigitSubStringToNumber(const nsAString& aStr,
   5375                                              uint32_t aStart, uint32_t aLen,
   5376                                              uint32_t* aRetVal) {
   5377  MOZ_ASSERT(aStr.Length() > (aStart + aLen - 1));
   5378 
   5379  for (uint32_t offset = 0; offset < aLen; ++offset) {
   5380    if (!IsAsciiDigit(aStr[aStart + offset])) {
   5381      return false;
   5382    }
   5383  }
   5384 
   5385  nsresult ec;
   5386  *aRetVal = static_cast<uint32_t>(
   5387      PromiseFlatString(Substring(aStr, aStart, aLen)).ToInteger(&ec));
   5388 
   5389  return NS_SUCCEEDED(ec);
   5390 }
   5391 
   5392 bool HTMLInputElement::IsValidTime(const nsAString& aValue) const {
   5393  return ParseTime(aValue, nullptr);
   5394 }
   5395 
   5396 /* static */
   5397 bool HTMLInputElement::ParseTime(const nsAString& aValue, uint32_t* aResult) {
   5398  /* The string must have the following parts:
   5399   * - HOURS: two digits, value being in [0, 23];
   5400   * - Colon (:);
   5401   * - MINUTES: two digits, value being in [0, 59];
   5402   * - Optional:
   5403   *   - Colon (:);
   5404   *   - SECONDS: two digits, value being in [0, 59];
   5405   *   - Optional:
   5406   *     - DOT (.);
   5407   *     - FRACTIONAL SECONDS: one to three digits, no value range.
   5408   */
   5409 
   5410  // The following format is the shorter one allowed: "HH:MM".
   5411  if (aValue.Length() < 5) {
   5412    return false;
   5413  }
   5414 
   5415  uint32_t hours;
   5416  if (!DigitSubStringToNumber(aValue, 0, 2, &hours) || hours > 23) {
   5417    return false;
   5418  }
   5419 
   5420  // Hours/minutes separator.
   5421  if (aValue[2] != ':') {
   5422    return false;
   5423  }
   5424 
   5425  uint32_t minutes;
   5426  if (!DigitSubStringToNumber(aValue, 3, 2, &minutes) || minutes > 59) {
   5427    return false;
   5428  }
   5429 
   5430  if (aValue.Length() == 5) {
   5431    if (aResult) {
   5432      *aResult = ((hours * 60) + minutes) * 60000;
   5433    }
   5434    return true;
   5435  }
   5436 
   5437  // The following format is the next shorter one: "HH:MM:SS".
   5438  if (aValue.Length() < 8 || aValue[5] != ':') {
   5439    return false;
   5440  }
   5441 
   5442  uint32_t seconds;
   5443  if (!DigitSubStringToNumber(aValue, 6, 2, &seconds) || seconds > 59) {
   5444    return false;
   5445  }
   5446 
   5447  if (aValue.Length() == 8) {
   5448    if (aResult) {
   5449      *aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000;
   5450    }
   5451    return true;
   5452  }
   5453 
   5454  // The string must follow this format now: "HH:MM:SS.{s,ss,sss}".
   5455  // There can be 1 to 3 digits for the fractions of seconds.
   5456  if (aValue.Length() == 9 || aValue.Length() > 12 || aValue[8] != '.') {
   5457    return false;
   5458  }
   5459 
   5460  uint32_t fractionsSeconds;
   5461  if (!DigitSubStringToNumber(aValue, 9, aValue.Length() - 9,
   5462                              &fractionsSeconds)) {
   5463    return false;
   5464  }
   5465 
   5466  if (aResult) {
   5467    *aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000 +
   5468               // NOTE: there is 10.0 instead of 10 and static_cast<int> because
   5469               // some old [and stupid] compilers can't just do the right thing.
   5470               fractionsSeconds *
   5471                   pow(10.0, static_cast<int>(3 - (aValue.Length() - 9)));
   5472  }
   5473 
   5474  return true;
   5475 }
   5476 
   5477 /* static */
   5478 bool HTMLInputElement::IsDateTimeTypeSupported(
   5479    FormControlType aDateTimeInputType) {
   5480  switch (aDateTimeInputType) {
   5481    case FormControlType::InputDate:
   5482    case FormControlType::InputTime:
   5483    case FormControlType::InputDatetimeLocal:
   5484      return true;
   5485    case FormControlType::InputMonth:
   5486    case FormControlType::InputWeek:
   5487      return StaticPrefs::dom_forms_datetime_others();
   5488    default:
   5489      return false;
   5490  }
   5491 }
   5492 
   5493 void HTMLInputElement::GetLastInteractiveValue(nsAString& aValue) {
   5494  if (mLastValueChangeWasInteractive) {
   5495    return GetValue(aValue, CallerType::System);
   5496  }
   5497  if (TextControlState* state = GetEditorState()) {
   5498    return aValue.Assign(
   5499        state->LastInteractiveValueIfLastChangeWasNonInteractive());
   5500  }
   5501  aValue.Truncate();
   5502 }
   5503 
   5504 bool HTMLInputElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
   5505                                      const nsAString& aValue,
   5506                                      nsIPrincipal* aMaybeScriptedPrincipal,
   5507                                      nsAttrValue& aResult) {
   5508  static_assert(
   5509      FormControlType(kInputDefaultType->value) == FormControlType::InputText,
   5510      "Someone forgot to update kInputDefaultType when adding a new "
   5511      "input type.");
   5512  static_assert(
   5513      FormControlType(kInputTypeTable[std::size(kInputTypeTable) - 1].value) ==
   5514          FormControlType::InputText,
   5515      "Last entry in the table must be the \"text\" entry");
   5516 
   5517  if (aNamespaceID == kNameSpaceID_None) {
   5518    if (aAttribute == nsGkAtoms::type) {
   5519      aResult.ParseEnumValue(aValue, kInputTypeTable, false, kInputDefaultType);
   5520      auto newType = FormControlType(aResult.GetEnumValue());
   5521      if (IsDateTimeInputType(newType) && !IsDateTimeTypeSupported(newType)) {
   5522        // There's no public way to set an nsAttrValue to an enum value, but we
   5523        // can just re-parse with a table that doesn't have any types other than
   5524        // "text" in it.
   5525        MOZ_ASSERT(&Span(kInputTypeTable).Last<1>()[0] == kInputDefaultType);
   5526        aResult.ParseEnumValue(aValue, Span(kInputTypeTable).Last<1>(), false,
   5527                               kInputDefaultType);
   5528      }
   5529 
   5530      return true;
   5531    }
   5532    if (aAttribute == nsGkAtoms::width) {
   5533      return aResult.ParseHTMLDimension(aValue);
   5534    }
   5535    if (aAttribute == nsGkAtoms::height) {
   5536      return aResult.ParseHTMLDimension(aValue);
   5537    }
   5538    if (aAttribute == nsGkAtoms::maxlength) {
   5539      return aResult.ParseNonNegativeIntValue(aValue);
   5540    }
   5541    if (aAttribute == nsGkAtoms::minlength) {
   5542      return aResult.ParseNonNegativeIntValue(aValue);
   5543    }
   5544    if (aAttribute == nsGkAtoms::size) {
   5545      return aResult.ParsePositiveIntValue(aValue);
   5546    }
   5547    if (aAttribute == nsGkAtoms::align) {
   5548      return ParseAlignValue(aValue, aResult);
   5549    }
   5550    if (aAttribute == nsGkAtoms::formmethod) {
   5551      return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
   5552    }
   5553    if (aAttribute == nsGkAtoms::formenctype) {
   5554      return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
   5555    }
   5556    if (aAttribute == nsGkAtoms::autocomplete) {
   5557      aResult.ParseAtomArray(aValue);
   5558      return true;
   5559    }
   5560    if (aAttribute == nsGkAtoms::capture) {
   5561      return aResult.ParseEnumValue(aValue, kCaptureTable, false,
   5562                                    kCaptureDefault);
   5563    }
   5564    if (ParseImageAttribute(aAttribute, aValue, aResult)) {
   5565      // We have to call |ParseImageAttribute| unconditionally since we
   5566      // don't know if we're going to have a type="image" attribute yet,
   5567      // (or could have it set dynamically in the future).  See bug
   5568      // 214077.
   5569      return true;
   5570    }
   5571  }
   5572 
   5573  return TextControlElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
   5574                                            aMaybeScriptedPrincipal, aResult);
   5575 }
   5576 
   5577 void HTMLInputElement::ImageInputMapAttributesIntoRule(
   5578    MappedDeclarationsBuilder& aBuilder) {
   5579  nsGenericHTMLFormControlElementWithState::MapImageBorderAttributeInto(
   5580      aBuilder);
   5581  nsGenericHTMLFormControlElementWithState::MapImageMarginAttributeInto(
   5582      aBuilder);
   5583  nsGenericHTMLFormControlElementWithState::MapImageSizeAttributesInto(
   5584      aBuilder, MapAspectRatio::Yes);
   5585  // Images treat align as "float"
   5586  nsGenericHTMLFormControlElementWithState::MapImageAlignAttributeInto(
   5587      aBuilder);
   5588  nsGenericHTMLFormControlElementWithState::MapCommonAttributesInto(aBuilder);
   5589 }
   5590 
   5591 nsChangeHint HTMLInputElement::GetAttributeChangeHint(
   5592    const nsAtom* aAttribute, AttrModType aModType) const {
   5593  nsChangeHint retval =
   5594      nsGenericHTMLFormControlElementWithState::GetAttributeChangeHint(
   5595          aAttribute, aModType);
   5596 
   5597  const bool isAdditionOrRemoval = IsAdditionOrRemoval(aModType);
   5598  const bool reconstruct = [&] {
   5599    if (aAttribute == nsGkAtoms::type) {
   5600      return true;
   5601    }
   5602 
   5603    if (PlaceholderApplies() && aAttribute == nsGkAtoms::placeholder &&
   5604        isAdditionOrRemoval) {
   5605      // We need to re-create our placeholder text.
   5606      return true;
   5607    }
   5608 
   5609    if (mType == FormControlType::InputFile &&
   5610        aAttribute == nsGkAtoms::webkitdirectory) {
   5611      // The presence or absence of the 'directory' attribute determines what
   5612      // value we show in the file label when empty, via GetDisplayFileName.
   5613      return true;
   5614    }
   5615 
   5616    if (mType == FormControlType::InputImage && isAdditionOrRemoval &&
   5617        (aAttribute == nsGkAtoms::alt || aAttribute == nsGkAtoms::value)) {
   5618      // We might need to rebuild our alt text.  Just go ahead and
   5619      // reconstruct our frame.  This should be quite rare..
   5620      return true;
   5621    }
   5622    return false;
   5623  }();
   5624 
   5625  if (reconstruct) {
   5626    retval |= nsChangeHint_ReconstructFrame;
   5627  } else if (aAttribute == nsGkAtoms::value) {
   5628    retval |= NS_STYLE_HINT_REFLOW;
   5629  } else if (aAttribute == nsGkAtoms::size && IsSingleLineTextControl(false)) {
   5630    retval |= NS_STYLE_HINT_REFLOW;
   5631  }
   5632 
   5633  return retval;
   5634 }
   5635 
   5636 NS_IMETHODIMP_(bool)
   5637 HTMLInputElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   5638  static const MappedAttributeEntry attributes[] = {
   5639      {nsGkAtoms::align},
   5640      {nullptr},
   5641  };
   5642 
   5643  static const MappedAttributeEntry* const map[] = {
   5644      attributes,
   5645      sCommonAttributeMap,
   5646      sImageMarginSizeAttributeMap,
   5647      sImageBorderAttributeMap,
   5648  };
   5649 
   5650  return FindAttributeDependence(aAttribute, map);
   5651 }
   5652 
   5653 nsMapRuleToAttributesFunc HTMLInputElement::GetAttributeMappingFunction()
   5654    const {
   5655  // GetAttributeChangeHint guarantees that changes to mType will trigger a
   5656  // reframe, and we update the mapping function in our mapped attrs when our
   5657  // type changes, so it's safe to condition our attribute mapping function on
   5658  // mType.
   5659  if (mType == FormControlType::InputImage) {
   5660    return &ImageInputMapAttributesIntoRule;
   5661  }
   5662 
   5663  return &MapCommonAttributesInto;
   5664 }
   5665 
   5666 // Directory picking methods:
   5667 
   5668 already_AddRefed<Promise> HTMLInputElement::GetFilesAndDirectories(
   5669    ErrorResult& aRv) {
   5670  if (mType != FormControlType::InputFile) {
   5671    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5672    return nullptr;
   5673  }
   5674 
   5675  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   5676  MOZ_ASSERT(global);
   5677  if (!global) {
   5678    return nullptr;
   5679  }
   5680 
   5681  RefPtr<Promise> p = Promise::Create(global, aRv);
   5682  if (aRv.Failed()) {
   5683    return nullptr;
   5684  }
   5685 
   5686  const nsTArray<OwningFileOrDirectory>& filesAndDirs =
   5687      GetFilesOrDirectoriesInternal();
   5688 
   5689  Sequence<OwningFileOrDirectory> filesAndDirsSeq;
   5690 
   5691  if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), fallible)) {
   5692    p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
   5693    return p.forget();
   5694  }
   5695 
   5696  for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
   5697    if (filesAndDirs[i].IsDirectory()) {
   5698      RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
   5699 
   5700      // In future we could refactor SetFilePickerFiltersFromAccept to return a
   5701      // semicolon separated list of file extensions and include that in the
   5702      // filter string passed here.
   5703      directory->SetContentFilters(u"filter-out-sensitive"_ns);
   5704      filesAndDirsSeq[i].SetAsDirectory() = directory;
   5705    } else {
   5706      MOZ_ASSERT(filesAndDirs[i].IsFile());
   5707 
   5708      // This file was directly selected by the user, so don't filter it.
   5709      filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
   5710    }
   5711  }
   5712 
   5713  p->MaybeResolve(filesAndDirsSeq);
   5714  return p.forget();
   5715 }
   5716 
   5717 // Controllers Methods
   5718 
   5719 nsIControllers* HTMLInputElement::GetControllers(ErrorResult& aRv) {
   5720  // XXX: what about type "file"?
   5721  if (IsSingleLineTextControl(false)) {
   5722    if (!mControllers) {
   5723      mControllers = new nsXULControllers();
   5724      if (!mControllers) {
   5725        aRv.Throw(NS_ERROR_FAILURE);
   5726        return nullptr;
   5727      }
   5728 
   5729      RefPtr<nsBaseCommandController> commandController =
   5730          nsBaseCommandController::CreateEditorController();
   5731      if (!commandController) {
   5732        aRv.Throw(NS_ERROR_FAILURE);
   5733        return nullptr;
   5734      }
   5735 
   5736      mControllers->AppendController(commandController);
   5737 
   5738      commandController = nsBaseCommandController::CreateEditingController();
   5739      if (!commandController) {
   5740        aRv.Throw(NS_ERROR_FAILURE);
   5741        return nullptr;
   5742      }
   5743 
   5744      mControllers->AppendController(commandController);
   5745    }
   5746  }
   5747 
   5748  return GetExtantControllers();
   5749 }
   5750 
   5751 nsresult HTMLInputElement::GetControllers(nsIControllers** aResult) {
   5752  NS_ENSURE_ARG_POINTER(aResult);
   5753 
   5754  ErrorResult rv;
   5755  RefPtr<nsIControllers> controller = GetControllers(rv);
   5756  controller.forget(aResult);
   5757  return rv.StealNSResult();
   5758 }
   5759 
   5760 int32_t HTMLInputElement::InputTextLength(CallerType aCallerType) {
   5761  nsAutoString val;
   5762  GetValue(val, aCallerType);
   5763  return val.Length();
   5764 }
   5765 
   5766 void HTMLInputElement::SetSelectionRange(uint32_t aSelectionStart,
   5767                                         uint32_t aSelectionEnd,
   5768                                         const Optional<nsAString>& aDirection,
   5769                                         ErrorResult& aRv) {
   5770  if (!SupportsTextSelection()) {
   5771    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5772    return;
   5773  }
   5774 
   5775  TextControlState* state = GetEditorState();
   5776  MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
   5777  state->SetSelectionRange(aSelectionStart, aSelectionEnd, aDirection, aRv);
   5778 }
   5779 
   5780 void HTMLInputElement::SetRangeText(const nsAString& aReplacement,
   5781                                    ErrorResult& aRv) {
   5782  if (!SupportsTextSelection()) {
   5783    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5784    return;
   5785  }
   5786 
   5787  TextControlState* state = GetEditorState();
   5788  MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
   5789  state->SetRangeText(aReplacement, aRv);
   5790 }
   5791 
   5792 void HTMLInputElement::SetRangeText(const nsAString& aReplacement,
   5793                                    uint32_t aStart, uint32_t aEnd,
   5794                                    SelectionMode aSelectMode,
   5795                                    ErrorResult& aRv) {
   5796  if (!SupportsTextSelection()) {
   5797    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5798    return;
   5799  }
   5800 
   5801  TextControlState* state = GetEditorState();
   5802  MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
   5803  state->SetRangeText(aReplacement, aStart, aEnd, aSelectMode, aRv);
   5804 }
   5805 
   5806 void HTMLInputElement::GetValueFromSetRangeText(nsAString& aValue) {
   5807  GetNonFileValueInternal(aValue);
   5808 }
   5809 
   5810 nsresult HTMLInputElement::SetValueFromSetRangeText(const nsAString& aValue) {
   5811  return SetValueInternal(aValue, {ValueSetterOption::ByContentAPI,
   5812                                   ValueSetterOption::BySetRangeTextAPI,
   5813                                   ValueSetterOption::SetValueChanged});
   5814 }
   5815 
   5816 Nullable<uint32_t> HTMLInputElement::GetSelectionStart(ErrorResult& aRv) {
   5817  if (!SupportsTextSelection()) {
   5818    return Nullable<uint32_t>();
   5819  }
   5820 
   5821  uint32_t selStart = GetSelectionStartIgnoringType(aRv);
   5822  if (aRv.Failed()) {
   5823    return Nullable<uint32_t>();
   5824  }
   5825 
   5826  return Nullable<uint32_t>(selStart);
   5827 }
   5828 
   5829 uint32_t HTMLInputElement::GetSelectionStartIgnoringType(ErrorResult& aRv) {
   5830  uint32_t selEnd = 0, selStart = 0;
   5831  GetSelectionRange(&selStart, &selEnd, aRv);
   5832  return selStart;
   5833 }
   5834 
   5835 void HTMLInputElement::SetSelectionStart(
   5836    const Nullable<uint32_t>& aSelectionStart, ErrorResult& aRv) {
   5837  if (!SupportsTextSelection()) {
   5838    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5839    return;
   5840  }
   5841 
   5842  TextControlState* state = GetEditorState();
   5843  MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
   5844  state->SetSelectionStart(aSelectionStart, aRv);
   5845 }
   5846 
   5847 Nullable<uint32_t> HTMLInputElement::GetSelectionEnd(ErrorResult& aRv) {
   5848  if (!SupportsTextSelection()) {
   5849    return Nullable<uint32_t>();
   5850  }
   5851 
   5852  uint32_t selEnd = GetSelectionEndIgnoringType(aRv);
   5853  if (aRv.Failed()) {
   5854    return Nullable<uint32_t>();
   5855  }
   5856 
   5857  return Nullable<uint32_t>(selEnd);
   5858 }
   5859 
   5860 uint32_t HTMLInputElement::GetSelectionEndIgnoringType(ErrorResult& aRv) {
   5861  uint32_t selEnd = 0, selStart = 0;
   5862  GetSelectionRange(&selStart, &selEnd, aRv);
   5863  return selEnd;
   5864 }
   5865 
   5866 void HTMLInputElement::SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd,
   5867                                       ErrorResult& aRv) {
   5868  if (!SupportsTextSelection()) {
   5869    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5870    return;
   5871  }
   5872 
   5873  TextControlState* state = GetEditorState();
   5874  MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
   5875  state->SetSelectionEnd(aSelectionEnd, aRv);
   5876 }
   5877 
   5878 void HTMLInputElement::GetSelectionRange(uint32_t* aSelectionStart,
   5879                                         uint32_t* aSelectionEnd,
   5880                                         ErrorResult& aRv) {
   5881  TextControlState* state = GetEditorState();
   5882  if (!state) {
   5883    // Not a text control.
   5884    aRv.Throw(NS_ERROR_UNEXPECTED);
   5885    return;
   5886  }
   5887 
   5888  state->GetSelectionRange(aSelectionStart, aSelectionEnd, aRv);
   5889 }
   5890 
   5891 void HTMLInputElement::GetSelectionDirection(nsAString& aDirection,
   5892                                             ErrorResult& aRv) {
   5893  if (!SupportsTextSelection()) {
   5894    aDirection.SetIsVoid(true);
   5895    return;
   5896  }
   5897 
   5898  TextControlState* state = GetEditorState();
   5899  MOZ_ASSERT(state, "SupportsTextSelection came back true!");
   5900  state->GetSelectionDirectionString(aDirection, aRv);
   5901 }
   5902 
   5903 void HTMLInputElement::SetSelectionDirection(const nsAString& aDirection,
   5904                                             ErrorResult& aRv) {
   5905  if (!SupportsTextSelection()) {
   5906    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   5907    return;
   5908  }
   5909 
   5910  TextControlState* state = GetEditorState();
   5911  MOZ_ASSERT(state, "SupportsTextSelection came back true!");
   5912  state->SetSelectionDirection(aDirection, aRv);
   5913 }
   5914 
   5915 // https://html.spec.whatwg.org/multipage/input.html#dom-input-showpicker
   5916 void HTMLInputElement::ShowPicker(ErrorResult& aRv) {
   5917  // Step 1. If this is not mutable, then throw an "InvalidStateError"
   5918  // DOMException.
   5919  if (!IsMutable()) {
   5920    return aRv.ThrowInvalidStateError(
   5921        "This input is either disabled or readonly.");
   5922  }
   5923 
   5924  // Step 2. If this's relevant settings object's origin is not same origin with
   5925  // this's relevant settings object's top-level origin, and this's type
   5926  // attribute is not in the File Upload state or Color state, then throw a
   5927  // "SecurityError" DOMException.
   5928  if (mType != FormControlType::InputFile &&
   5929      mType != FormControlType::InputColor) {
   5930    nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   5931    WindowGlobalChild* windowGlobalChild =
   5932        window ? window->GetWindowGlobalChild() : nullptr;
   5933    if (!windowGlobalChild || !windowGlobalChild->SameOriginWithTop()) {
   5934      return aRv.ThrowSecurityError(
   5935          "Call was blocked because the current origin isn't same-origin with "
   5936          "top.");
   5937    }
   5938  }
   5939 
   5940  // Step 3. If this's relevant global object does not have transient
   5941  // activation, then throw a "NotAllowedError" DOMException.
   5942  if (!OwnerDoc()->HasValidTransientUserGestureActivation()) {
   5943    return aRv.ThrowNotAllowedError(
   5944        "Call was blocked due to lack of user activation.");
   5945  }
   5946 
   5947  // Step 4. Show the picker, if applicable, for this.
   5948  //
   5949  // https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable
   5950  // To show the picker, if applicable for an input element element:
   5951 
   5952  // Step 1. Assert: element's relevant global object has transient activation.
   5953  // Step 2. If element is not mutable, then return.
   5954  // (See above.)
   5955 
   5956  // Step 3. Consume user activation given element's relevant global object.
   5957  // InitFilePicker() and InitColorPicker() consume it themselves,
   5958  // so only consume in this function if not those.
   5959 
   5960  // Step 5. If element's type attribute is in the File Upload state, then run
   5961  // these steps in parallel:
   5962  if (mType == FormControlType::InputFile) {
   5963    FilePickerType type = FILE_PICKER_FILE;
   5964    if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
   5965        HasAttr(nsGkAtoms::webkitdirectory)) {
   5966      type = FILE_PICKER_DIRECTORY;
   5967    }
   5968    InitFilePicker(type);
   5969    return;
   5970  }
   5971 
   5972  // Step 6. Otherwise, the user agent should show any relevant user interface
   5973  // for selecting a value for element, in the way it normally would when the
   5974  // user interacts with the control
   5975 
   5976  // Step 6 for color
   5977  if (mType == FormControlType::InputColor) {
   5978    InitColorPicker();
   5979    return;
   5980  }
   5981 
   5982  // See Step 3.
   5983  OwnerDoc()->ConsumeTransientUserGestureActivation();
   5984 
   5985  if (!IsInComposedDoc()) {
   5986    return;
   5987  }
   5988 
   5989  // Step 6 for date and time types
   5990  if (IsDateTimeTypeSupported(mType)) {
   5991    if (CreatesDateTimeWidget()) {
   5992      if (RefPtr<Element> dateTimeBoxElement = GetDateTimeBoxElement()) {
   5993        // Event is dispatched to closed-shadow tree and doesn't bubble.
   5994        RefPtr<Document> doc = OwnerDoc();
   5995        nsContentUtils::DispatchTrustedEvent(doc, dateTimeBoxElement,
   5996                                             u"MozDateTimeShowPickerForJS"_ns,
   5997                                             CanBubble::eNo, Cancelable::eNo);
   5998      }
   5999    } else {
   6000      DateTimeValue value;
   6001      GetDateTimeInputBoxValue(value);
   6002      OpenDateTimePicker(value);
   6003    }
   6004    return;
   6005  }
   6006 
   6007  // Step 6 for input elements with a suggestions source element.
   6008  // I.e. show the autocomplete dropdown based on the list attribute.
   6009  // XXX Form-fill support on android is bug 1535985.
   6010  if (StaticPrefs::dom_input_showPicker_datalist_enabled() &&
   6011      IsSingleLineTextControl(true) && GetList()) {
   6012    if (nsCOMPtr<nsIFormFillController> controller =
   6013            do_GetService("@mozilla.org/satchel/form-fill-controller;1")) {
   6014      controller->SetControlledElement(this);
   6015      controller->ShowPopup();
   6016    }
   6017  }
   6018 }
   6019 
   6020 #ifdef ACCESSIBILITY
   6021 /*static*/ nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
   6022                                              EventMessage aEventMessage) {
   6023  Element* element = static_cast<Element*>(aTarget);
   6024  return nsContentUtils::DispatchTrustedEvent<WidgetEvent>(
   6025      element->OwnerDoc(), element, aEventMessage, CanBubble::eYes,
   6026      Cancelable::eYes);
   6027 }
   6028 #endif
   6029 
   6030 void HTMLInputElement::UpdateApzAwareFlag() {
   6031 #if !defined(ANDROID) && !defined(XP_MACOSX)
   6032  if (mType == FormControlType::InputNumber ||
   6033      mType == FormControlType::InputRange) {
   6034    SetMayBeApzAware();
   6035  }
   6036 #endif
   6037 }
   6038 
   6039 nsresult HTMLInputElement::SetDefaultValueAsValue() {
   6040  NS_ASSERTION(GetValueMode() == VALUE_MODE_VALUE,
   6041               "GetValueMode() should return VALUE_MODE_VALUE!");
   6042 
   6043  // The element has a content attribute value different from it's value when
   6044  // it's in the value mode value.
   6045  nsAutoString resetVal;
   6046  GetDefaultValue(resetVal);
   6047 
   6048  // SetValueInternal is going to sanitize the value.
   6049  // TODO(mbrodesser): sanitizing will only happen if `mDoneCreating` is true.
   6050  return SetValueInternal(resetVal, ValueSetterOption::ByInternalAPI);
   6051 }
   6052 
   6053 NS_IMETHODIMP
   6054 HTMLInputElement::Reset() {
   6055  // We should be able to reset all dirty flags regardless of the type.
   6056  SetCheckedChanged(false);
   6057  SetValueChanged(false);
   6058  SetLastValueChangeWasInteractive(false);
   6059  SetUserInteracted(false);
   6060 
   6061  switch (GetValueMode()) {
   6062    case VALUE_MODE_VALUE: {
   6063      nsresult result = SetDefaultValueAsValue();
   6064      if (CreatesDateTimeWidget()) {
   6065        // mFocusedValue has to be set here, so that `FireChangeEventIfNeeded`
   6066        // can fire a change event if necessary.
   6067        GetValue(mFocusedValue, CallerType::System);
   6068      }
   6069      return result;
   6070    }
   6071    case VALUE_MODE_DEFAULT_ON:
   6072      DoSetChecked(DefaultChecked(), /* aNotify */ true,
   6073                   /* aSetValueChanged */ false);
   6074      return NS_OK;
   6075    case VALUE_MODE_FILENAME:
   6076      ClearFiles(false);
   6077      return NS_OK;
   6078    case VALUE_MODE_DEFAULT:
   6079    default:
   6080      return NS_OK;
   6081  }
   6082 }
   6083 
   6084 NS_IMETHODIMP
   6085 HTMLInputElement::SubmitNamesValues(FormData* aFormData) {
   6086  // For type=reset, and type=button, we just never submit, period.
   6087  // For type=image and type=button, we only submit if we were the button
   6088  // pressed
   6089  // For type=radio and type=checkbox, we only submit if checked=true
   6090  if (mType == FormControlType::InputReset ||
   6091      mType == FormControlType::InputButton ||
   6092      ((mType == FormControlType::InputSubmit ||
   6093        mType == FormControlType::InputImage) &&
   6094       aFormData->GetSubmitterElement() != this) ||
   6095      ((mType == FormControlType::InputRadio ||
   6096        mType == FormControlType::InputCheckbox) &&
   6097       !mChecked)) {
   6098    return NS_OK;
   6099  }
   6100 
   6101  // Get the name
   6102  nsAutoString name;
   6103  GetAttr(nsGkAtoms::name, name);
   6104 
   6105  // Submit .x, .y for input type=image
   6106  if (mType == FormControlType::InputImage) {
   6107    // Get a property set by the frame to find out where it was clicked.
   6108    const auto* lastClickedPoint =
   6109        static_cast<CSSIntPoint*>(GetProperty(nsGkAtoms::imageClickedPoint));
   6110    int32_t x, y;
   6111    if (lastClickedPoint) {
   6112      // Convert the values to strings for submission
   6113      x = lastClickedPoint->x;
   6114      y = lastClickedPoint->y;
   6115    } else {
   6116      x = y = 0;
   6117    }
   6118 
   6119    nsAutoString xVal, yVal;
   6120    xVal.AppendInt(x);
   6121    yVal.AppendInt(y);
   6122 
   6123    if (!name.IsEmpty()) {
   6124      aFormData->AddNameValuePair(name + u".x"_ns, xVal);
   6125      aFormData->AddNameValuePair(name + u".y"_ns, yVal);
   6126    } else {
   6127      // If the Image Element has no name, simply return x and y
   6128      // to Nav and IE compatibility.
   6129      aFormData->AddNameValuePair(u"x"_ns, xVal);
   6130      aFormData->AddNameValuePair(u"y"_ns, yVal);
   6131    }
   6132 
   6133    return NS_OK;
   6134  }
   6135 
   6136  // If name not there, don't submit
   6137  if (name.IsEmpty()) {
   6138    return NS_OK;
   6139  }
   6140 
   6141  //
   6142  // Submit file if its input type=file and this encoding method accepts files
   6143  //
   6144  if (mType == FormControlType::InputFile) {
   6145    // Submit files
   6146 
   6147    const nsTArray<OwningFileOrDirectory>& files =
   6148        GetFilesOrDirectoriesInternal();
   6149 
   6150    if (files.IsEmpty()) {
   6151      NS_ENSURE_STATE(GetOwnerGlobal());
   6152      ErrorResult rv;
   6153      RefPtr<Blob> blob = Blob::CreateStringBlob(
   6154          GetOwnerGlobal(), ""_ns, u"application/octet-stream"_ns);
   6155      RefPtr<File> file = blob->ToFile(u""_ns, rv);
   6156 
   6157      if (!rv.Failed()) {
   6158        aFormData->AddNameBlobPair(name, file);
   6159      }
   6160 
   6161      return rv.StealNSResult();
   6162    }
   6163 
   6164    for (uint32_t i = 0; i < files.Length(); ++i) {
   6165      if (files[i].IsFile()) {
   6166        aFormData->AddNameBlobPair(name, files[i].GetAsFile());
   6167      } else {
   6168        MOZ_ASSERT(files[i].IsDirectory());
   6169        aFormData->AddNameDirectoryPair(name, files[i].GetAsDirectory());
   6170      }
   6171    }
   6172 
   6173    return NS_OK;
   6174  }
   6175 
   6176  if (mType == FormControlType::InputHidden &&
   6177      name.LowerCaseEqualsLiteral("_charset_")) {
   6178    nsCString charset;
   6179    aFormData->GetCharset(charset);
   6180    return aFormData->AddNameValuePair(name, NS_ConvertASCIItoUTF16(charset));
   6181  }
   6182 
   6183  //
   6184  // Submit name=value
   6185  //
   6186 
   6187  // Get the value
   6188  nsAutoString value;
   6189  GetValue(value, CallerType::System);
   6190 
   6191  if (mType == FormControlType::InputSubmit && value.IsEmpty() &&
   6192      !HasAttr(nsGkAtoms::value)) {
   6193    // Get our default value, which is the same as our default label
   6194    nsAutoString defaultValue;
   6195    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
   6196                                            "Submit", OwnerDoc(), defaultValue);
   6197    value = defaultValue;
   6198  }
   6199 
   6200  const nsresult rv = aFormData->AddNameValuePair(name, value);
   6201  if (NS_FAILED(rv)) {
   6202    return rv;
   6203  }
   6204 
   6205  // Submit dirname=dir
   6206  if (IsAutoDirectionalityAssociated()) {
   6207    return SubmitDirnameDir(aFormData);
   6208  }
   6209 
   6210  return NS_OK;
   6211 }
   6212 
   6213 static nsTArray<FileContentData> SaveFileContentData(
   6214    const nsTArray<OwningFileOrDirectory>& aArray) {
   6215  nsTArray<FileContentData> res(aArray.Length());
   6216  for (const auto& it : aArray) {
   6217    if (it.IsFile()) {
   6218      RefPtr<BlobImpl> impl = it.GetAsFile()->Impl();
   6219      res.AppendElement(std::move(impl));
   6220    } else {
   6221      MOZ_ASSERT(it.IsDirectory());
   6222      nsString fullPath;
   6223      nsresult rv = it.GetAsDirectory()->GetFullRealPath(fullPath);
   6224      if (NS_WARN_IF(NS_FAILED(rv))) {
   6225        continue;
   6226      }
   6227      res.AppendElement(std::move(fullPath));
   6228    }
   6229  }
   6230  return res;
   6231 }
   6232 
   6233 void HTMLInputElement::SaveState() {
   6234  PresState* state = nullptr;
   6235  switch (GetValueMode()) {
   6236    case VALUE_MODE_DEFAULT_ON:
   6237      if (mCheckedChanged) {
   6238        state = GetPrimaryPresState();
   6239        if (!state) {
   6240          return;
   6241        }
   6242 
   6243        state->contentData() = CheckedContentData(mChecked);
   6244      }
   6245      break;
   6246    case VALUE_MODE_FILENAME:
   6247      if (!mFileData->mFilesOrDirectories.IsEmpty()) {
   6248        state = GetPrimaryPresState();
   6249        if (!state) {
   6250          return;
   6251        }
   6252 
   6253        state->contentData() =
   6254            SaveFileContentData(mFileData->mFilesOrDirectories);
   6255      }
   6256      break;
   6257    case VALUE_MODE_VALUE:
   6258    case VALUE_MODE_DEFAULT:
   6259      // VALUE_MODE_DEFAULT shouldn't have their value saved except 'hidden',
   6260      // mType should have never been FormControlType::InputPassword and value
   6261      // should have changed.
   6262      if ((GetValueMode() == VALUE_MODE_DEFAULT &&
   6263           mType != FormControlType::InputHidden) ||
   6264          mHasBeenTypePassword || !mValueChanged) {
   6265        break;
   6266      }
   6267 
   6268      state = GetPrimaryPresState();
   6269      if (!state) {
   6270        return;
   6271      }
   6272 
   6273      nsAutoString value;
   6274      GetValue(value, CallerType::System);
   6275 
   6276      if (!IsSingleLineTextControl(false) &&
   6277          NS_FAILED(nsLinebreakConverter::ConvertStringLineBreaks(
   6278              value, nsLinebreakConverter::eLinebreakPlatform,
   6279              nsLinebreakConverter::eLinebreakContent))) {
   6280        NS_ERROR("Converting linebreaks failed!");
   6281        return;
   6282      }
   6283 
   6284      state->contentData() =
   6285          TextContentData(value, mLastValueChangeWasInteractive);
   6286      break;
   6287  }
   6288 
   6289  if (mDisabledChanged) {
   6290    if (!state) {
   6291      state = GetPrimaryPresState();
   6292    }
   6293    if (state) {
   6294      // We do not want to save the real disabled state but the disabled
   6295      // attribute.
   6296      state->disabled() = HasAttr(nsGkAtoms::disabled);
   6297      state->disabledSet() = true;
   6298    }
   6299  }
   6300 }
   6301 
   6302 void HTMLInputElement::DoneCreatingElement() {
   6303  mDoneCreating = true;
   6304 
   6305  //
   6306  // Restore state as needed.  Note that disabled state applies to all control
   6307  // types.
   6308  //
   6309  bool restoredCheckedState = false;
   6310  if (!mInhibitRestoration) {
   6311    GenerateStateKey();
   6312    restoredCheckedState = RestoreFormControlState();
   6313  }
   6314 
   6315  //
   6316  // If restore does not occur, we initialize .checked using the CHECKED
   6317  // property.
   6318  //
   6319  if (!restoredCheckedState && mShouldInitChecked) {
   6320    DoSetChecked(DefaultChecked(), /* aNotify */ false,
   6321                 /* aSetValueChanged */ false, mForm || IsInComposedDoc());
   6322  }
   6323 
   6324  // Sanitize the value and potentially set mFocusedValue.
   6325  if (GetValueMode() == VALUE_MODE_VALUE) {
   6326    nsAutoString value;
   6327    GetValue(value, CallerType::System);
   6328    // TODO: What should we do if SetValueInternal fails?  (The allocation
   6329    // may potentially be big, but most likely we've failed to allocate
   6330    // before the type change.)
   6331    SetValueInternal(value, ValueSetterOption::ByInternalAPI);
   6332 
   6333    if (CreatesDateTimeWidget()) {
   6334      // mFocusedValue has to be set here, so that `FireChangeEventIfNeeded` can
   6335      // fire a change event if necessary.
   6336      mFocusedValue = value;
   6337    }
   6338  }
   6339 
   6340  mShouldInitChecked = false;
   6341 }
   6342 
   6343 void HTMLInputElement::DestroyContent() {
   6344  nsImageLoadingContent::Destroy();
   6345  TextControlElement::DestroyContent();
   6346 }
   6347 
   6348 void HTMLInputElement::UpdateValidityElementStates(bool aNotify) {
   6349  AutoStateChangeNotifier notifier(*this, aNotify);
   6350  RemoveStatesSilently(ElementState::VALIDITY_STATES);
   6351  if (!IsCandidateForConstraintValidation()) {
   6352    return;
   6353  }
   6354  ElementState state;
   6355  if (IsValid()) {
   6356    state |= ElementState::VALID;
   6357    if (mUserInteracted) {
   6358      state |= ElementState::USER_VALID;
   6359    }
   6360  } else {
   6361    state |= ElementState::INVALID;
   6362    if (mUserInteracted) {
   6363      state |= ElementState::USER_INVALID;
   6364    }
   6365  }
   6366  AddStatesSilently(state);
   6367 }
   6368 
   6369 static nsTArray<OwningFileOrDirectory> RestoreFileContentData(
   6370    nsPIDOMWindowInner* aWindow, const nsTArray<FileContentData>& aData) {
   6371  nsTArray<OwningFileOrDirectory> res(aData.Length());
   6372  for (const auto& it : aData) {
   6373    if (it.type() == FileContentData::TBlobImpl) {
   6374      if (!it.get_BlobImpl()) {
   6375        // Serialization failed, skip this file.
   6376        continue;
   6377      }
   6378 
   6379      RefPtr<File> file = File::Create(aWindow->AsGlobal(), it.get_BlobImpl());
   6380      if (NS_WARN_IF(!file)) {
   6381        continue;
   6382      }
   6383 
   6384      OwningFileOrDirectory* element = res.AppendElement();
   6385      element->SetAsFile() = file;
   6386    } else {
   6387      MOZ_ASSERT(it.type() == FileContentData::TnsString);
   6388      nsCOMPtr<nsIFile> file;
   6389      nsresult rv = NS_NewLocalFile(it.get_nsString(), getter_AddRefs(file));
   6390      if (NS_WARN_IF(NS_FAILED(rv))) {
   6391        continue;
   6392      }
   6393 
   6394      RefPtr<Directory> directory =
   6395          Directory::Create(aWindow->AsGlobal(), file);
   6396      MOZ_ASSERT(directory);
   6397 
   6398      OwningFileOrDirectory* element = res.AppendElement();
   6399      element->SetAsDirectory() = directory;
   6400    }
   6401  }
   6402  return res;
   6403 }
   6404 
   6405 bool HTMLInputElement::RestoreState(PresState* aState) {
   6406  bool restoredCheckedState = false;
   6407 
   6408  const PresContentData& inputState = aState->contentData();
   6409 
   6410  switch (GetValueMode()) {
   6411    case VALUE_MODE_DEFAULT_ON:
   6412      if (inputState.type() == PresContentData::TCheckedContentData) {
   6413        restoredCheckedState = true;
   6414        bool checked = inputState.get_CheckedContentData().checked();
   6415        DoSetChecked(checked, /* aNotify */ true, /* aSetValueChanged */ true);
   6416      }
   6417      break;
   6418    case VALUE_MODE_FILENAME:
   6419      if (inputState.type() == PresContentData::TArrayOfFileContentData) {
   6420        nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   6421        if (window) {
   6422          nsTArray<OwningFileOrDirectory> array =
   6423              RestoreFileContentData(window, inputState);
   6424          SetFilesOrDirectories(array, true);
   6425        }
   6426      }
   6427      break;
   6428    case VALUE_MODE_VALUE:
   6429    case VALUE_MODE_DEFAULT:
   6430      if (GetValueMode() == VALUE_MODE_DEFAULT &&
   6431          mType != FormControlType::InputHidden) {
   6432        break;
   6433      }
   6434 
   6435      if (inputState.type() == PresContentData::TTextContentData) {
   6436        // TODO: What should we do if SetValueInternal fails?  (The allocation
   6437        // may potentially be big, but most likely we've failed to allocate
   6438        // before the type change.)
   6439        SetValueInternal(inputState.get_TextContentData().value(),
   6440                         ValueSetterOption::SetValueChanged);
   6441        if (inputState.get_TextContentData().lastValueChangeWasInteractive()) {
   6442          SetLastValueChangeWasInteractive(true);
   6443        }
   6444      }
   6445      break;
   6446  }
   6447 
   6448  if (aState->disabledSet() && !aState->disabled()) {
   6449    SetDisabled(false, IgnoreErrors());
   6450  }
   6451 
   6452  return restoredCheckedState;
   6453 }
   6454 
   6455 /*
   6456 * Radio group stuff
   6457 */
   6458 
   6459 void HTMLInputElement::AddToRadioGroup() {
   6460  MOZ_ASSERT(!mRadioGroupContainer,
   6461             "Radio button must be removed from previous radio group container "
   6462             "before being added to another!");
   6463 
   6464  // If the element has no radio group container we can stop here.
   6465  auto* container = FindTreeRadioGroupContainer();
   6466  if (!container) {
   6467    return;
   6468  }
   6469 
   6470  nsAutoString name;
   6471  GetAttr(nsGkAtoms::name, name);
   6472  // If we are part of a radio group, the element must have a name.
   6473  MOZ_ASSERT(!name.IsEmpty());
   6474 
   6475  //
   6476  // Add the radio to the radio group container.
   6477  //
   6478  container->AddToRadioGroup(name, this, mForm);
   6479  mRadioGroupContainer = container;
   6480 
   6481  //
   6482  // If the input element is checked, and we add it to the group, it will
   6483  // deselect whatever is currently selected in that group
   6484  //
   6485  if (mChecked) {
   6486    //
   6487    // If it is checked, call "RadioSetChecked" to perform the selection/
   6488    // deselection ritual.  This has the side effect of repainting the
   6489    // radio button, but as adding a checked radio button into the group
   6490    // should not be that common an occurrence, I think we can live with
   6491    // that.
   6492    // Make sure not to notify if we're still being created.
   6493    //
   6494    RadioSetChecked(mDoneCreating, mForm || IsInComposedDoc());
   6495  } else {
   6496    bool indeterminate = !container->GetCurrentRadioButton(name);
   6497    SetStates(ElementState::INDETERMINATE, indeterminate, mDoneCreating);
   6498  }
   6499 
   6500  //
   6501  // For integrity purposes, we have to ensure that "checkedChanged" is
   6502  // the same for this new element as for all the others in the group
   6503  //
   6504  bool checkedChanged = mCheckedChanged;
   6505 
   6506  VisitGroup([&checkedChanged](HTMLInputElement* aRadio) {
   6507    checkedChanged = aRadio->GetCheckedChanged();
   6508    return false;
   6509  });
   6510 
   6511  SetCheckedChangedInternal(checkedChanged);
   6512 
   6513  // We initialize the validity of the element to the validity of the group
   6514  // because we assume UpdateValueMissingState() will be called after.
   6515  SetValidityState(VALIDITY_STATE_VALUE_MISSING,
   6516                   container->GetValueMissingState(name));
   6517 }
   6518 
   6519 void HTMLInputElement::RemoveFromRadioGroup() {
   6520  auto* container = GetCurrentRadioGroupContainer();
   6521  if (!container) {
   6522    return;
   6523  }
   6524 
   6525  nsAutoString name;
   6526  GetAttr(nsGkAtoms::name, name);
   6527 
   6528  // If this button was checked, we need to notify the group that there is no
   6529  // longer a selected radio button
   6530  if (mChecked) {
   6531    container->SetCurrentRadioButton(name, nullptr);
   6532    UpdateRadioGroupState();
   6533  } else {
   6534    AddStates(ElementState::INDETERMINATE);
   6535  }
   6536 
   6537  // Remove this radio from its group in the container.
   6538  // We need to call UpdateValueMissingValidityStateForRadio before to make sure
   6539  // the group validity is updated (with this element being ignored).
   6540  UpdateValueMissingValidityStateForRadio(true);
   6541  container->RemoveFromRadioGroup(name, this);
   6542  mRadioGroupContainer = nullptr;
   6543 }
   6544 
   6545 bool HTMLInputElement::IsHTMLFocusable(IsFocusableFlags aFlags,
   6546                                       bool* aIsFocusable, int32_t* aTabIndex) {
   6547  if (nsGenericHTMLFormControlElementWithState::IsHTMLFocusable(
   6548          aFlags, aIsFocusable, aTabIndex)) {
   6549    return true;
   6550  }
   6551 
   6552  if (IsDisabled()) {
   6553    *aIsFocusable = false;
   6554    return true;
   6555  }
   6556 
   6557  if (IsSingleLineTextControl(false) || mType == FormControlType::InputRange) {
   6558    *aIsFocusable = true;
   6559    return false;
   6560  }
   6561 
   6562  const bool defaultFocusable = IsFormControlDefaultFocusable(aFlags);
   6563  if (CreatesDateTimeWidget()) {
   6564    if (aTabIndex) {
   6565      // We only want our native anonymous child to be tabable to, not ourself.
   6566      *aTabIndex = -1;
   6567    }
   6568    *aIsFocusable = true;
   6569    return true;
   6570  }
   6571 
   6572  if (mType == FormControlType::InputHidden) {
   6573    if (aTabIndex) {
   6574      *aTabIndex = -1;
   6575    }
   6576    *aIsFocusable = false;
   6577    return false;
   6578  }
   6579 
   6580  if (!aTabIndex) {
   6581    // The other controls are all focusable
   6582    *aIsFocusable = defaultFocusable;
   6583    return false;
   6584  }
   6585 
   6586  if (mType != FormControlType::InputRadio) {
   6587    *aIsFocusable = defaultFocusable;
   6588    return false;
   6589  }
   6590 
   6591  if (mChecked) {
   6592    // Selected radio buttons are tabbable
   6593    *aIsFocusable = defaultFocusable;
   6594    return false;
   6595  }
   6596 
   6597  // Current radio button is not selected.
   6598  // Make it tabbable if nothing in group is selected and it is the first radio
   6599  // button.
   6600  auto* container = GetCurrentRadioGroupContainer();
   6601  if (!container) {
   6602    *aIsFocusable = defaultFocusable;
   6603    return false;
   6604  }
   6605 
   6606  nsAutoString name;
   6607  GetAttr(nsGkAtoms::name, name);
   6608 
   6609  // If there is a selected radio button but it is disabled or hidden, it
   6610  // shouldn't be considered as selected for this check. Otherwise, the entire
   6611  // group will be unreachable with the tab key.
   6612  HTMLInputElement* selectedRadio = container->GetCurrentRadioButton(name);
   6613  if ((selectedRadio && !selectedRadio->Disabled() &&
   6614       selectedRadio->GetPrimaryFrame()) ||
   6615      container->GetFirstRadioButton(name) != this) {
   6616    *aTabIndex = -1;
   6617  }
   6618  *aIsFocusable = defaultFocusable;
   6619  return false;
   6620 }
   6621 
   6622 template <typename VisitCallback>
   6623 void HTMLInputElement::VisitGroup(VisitCallback&& aCallback, bool aSkipThis) {
   6624  if (auto* container = GetCurrentRadioGroupContainer()) {
   6625    nsAutoString name;
   6626    GetAttr(nsGkAtoms::name, name);
   6627    container->WalkRadioGroup(name, aCallback, aSkipThis ? this : nullptr);
   6628    return;
   6629  }
   6630 
   6631  aCallback(this);
   6632 }
   6633 
   6634 HTMLInputElement::ValueModeType HTMLInputElement::GetValueMode() const {
   6635  switch (mType) {
   6636    case FormControlType::InputHidden:
   6637    case FormControlType::InputSubmit:
   6638    case FormControlType::InputButton:
   6639    case FormControlType::InputReset:
   6640    case FormControlType::InputImage:
   6641      return VALUE_MODE_DEFAULT;
   6642    case FormControlType::InputCheckbox:
   6643    case FormControlType::InputRadio:
   6644      return VALUE_MODE_DEFAULT_ON;
   6645    case FormControlType::InputFile:
   6646      return VALUE_MODE_FILENAME;
   6647 #ifdef DEBUG
   6648    case FormControlType::InputText:
   6649    case FormControlType::InputPassword:
   6650    case FormControlType::InputSearch:
   6651    case FormControlType::InputTel:
   6652    case FormControlType::InputEmail:
   6653    case FormControlType::InputUrl:
   6654    case FormControlType::InputNumber:
   6655    case FormControlType::InputRange:
   6656    case FormControlType::InputDate:
   6657    case FormControlType::InputTime:
   6658    case FormControlType::InputColor:
   6659    case FormControlType::InputMonth:
   6660    case FormControlType::InputWeek:
   6661    case FormControlType::InputDatetimeLocal:
   6662      return VALUE_MODE_VALUE;
   6663    default:
   6664      MOZ_ASSERT_UNREACHABLE("Unexpected input type in GetValueMode()");
   6665      return VALUE_MODE_VALUE;
   6666 #else   // DEBUG
   6667    default:
   6668      return VALUE_MODE_VALUE;
   6669 #endif  // DEBUG
   6670  }
   6671 }
   6672 
   6673 bool HTMLInputElement::IsMutable() const {
   6674  return !IsDisabled() &&
   6675         !(DoesReadWriteApply() && State().HasState(ElementState::READONLY));
   6676 }
   6677 
   6678 bool HTMLInputElement::DoesRequiredApply() const {
   6679  switch (mType) {
   6680    case FormControlType::InputHidden:
   6681    case FormControlType::InputButton:
   6682    case FormControlType::InputImage:
   6683    case FormControlType::InputReset:
   6684    case FormControlType::InputSubmit:
   6685    case FormControlType::InputRange:
   6686    case FormControlType::InputColor:
   6687      return false;
   6688 #ifdef DEBUG
   6689    case FormControlType::InputRadio:
   6690    case FormControlType::InputCheckbox:
   6691    case FormControlType::InputFile:
   6692    case FormControlType::InputText:
   6693    case FormControlType::InputPassword:
   6694    case FormControlType::InputSearch:
   6695    case FormControlType::InputTel:
   6696    case FormControlType::InputEmail:
   6697    case FormControlType::InputUrl:
   6698    case FormControlType::InputNumber:
   6699    case FormControlType::InputDate:
   6700    case FormControlType::InputTime:
   6701    case FormControlType::InputMonth:
   6702    case FormControlType::InputWeek:
   6703    case FormControlType::InputDatetimeLocal:
   6704      return true;
   6705    default:
   6706      MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesRequiredApply()");
   6707      return true;
   6708 #else   // DEBUG
   6709    default:
   6710      return true;
   6711 #endif  // DEBUG
   6712  }
   6713 }
   6714 
   6715 bool HTMLInputElement::PlaceholderApplies() const {
   6716  if (IsDateTimeInputType(mType)) {
   6717    return false;
   6718  }
   6719  return IsSingleLineTextControl(false);
   6720 }
   6721 
   6722 bool HTMLInputElement::DoesMinMaxApply() const {
   6723  switch (mType) {
   6724    case FormControlType::InputNumber:
   6725    case FormControlType::InputDate:
   6726    case FormControlType::InputTime:
   6727    case FormControlType::InputRange:
   6728    case FormControlType::InputMonth:
   6729    case FormControlType::InputWeek:
   6730    case FormControlType::InputDatetimeLocal:
   6731      return true;
   6732 #ifdef DEBUG
   6733    case FormControlType::InputReset:
   6734    case FormControlType::InputSubmit:
   6735    case FormControlType::InputImage:
   6736    case FormControlType::InputButton:
   6737    case FormControlType::InputHidden:
   6738    case FormControlType::InputRadio:
   6739    case FormControlType::InputCheckbox:
   6740    case FormControlType::InputFile:
   6741    case FormControlType::InputText:
   6742    case FormControlType::InputPassword:
   6743    case FormControlType::InputSearch:
   6744    case FormControlType::InputTel:
   6745    case FormControlType::InputEmail:
   6746    case FormControlType::InputUrl:
   6747    case FormControlType::InputColor:
   6748      return false;
   6749    default:
   6750      MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesRequiredApply()");
   6751      return false;
   6752 #else   // DEBUG
   6753    default:
   6754      return false;
   6755 #endif  // DEBUG
   6756  }
   6757 }
   6758 
   6759 bool HTMLInputElement::DoesAutocompleteApply() const {
   6760  switch (mType) {
   6761    case FormControlType::InputHidden:
   6762    case FormControlType::InputText:
   6763    case FormControlType::InputSearch:
   6764    case FormControlType::InputUrl:
   6765    case FormControlType::InputTel:
   6766    case FormControlType::InputEmail:
   6767    case FormControlType::InputPassword:
   6768    case FormControlType::InputDate:
   6769    case FormControlType::InputTime:
   6770    case FormControlType::InputNumber:
   6771    case FormControlType::InputRange:
   6772    case FormControlType::InputColor:
   6773    case FormControlType::InputMonth:
   6774    case FormControlType::InputWeek:
   6775    case FormControlType::InputDatetimeLocal:
   6776      return true;
   6777 #ifdef DEBUG
   6778    case FormControlType::InputReset:
   6779    case FormControlType::InputSubmit:
   6780    case FormControlType::InputImage:
   6781    case FormControlType::InputButton:
   6782    case FormControlType::InputRadio:
   6783    case FormControlType::InputCheckbox:
   6784    case FormControlType::InputFile:
   6785      return false;
   6786    default:
   6787      MOZ_ASSERT_UNREACHABLE(
   6788          "Unexpected input type in DoesAutocompleteApply()");
   6789      return false;
   6790 #else   // DEBUG
   6791    default:
   6792      return false;
   6793 #endif  // DEBUG
   6794  }
   6795 }
   6796 
   6797 Decimal HTMLInputElement::GetStep() const {
   6798  MOZ_ASSERT(DoesStepApply(), "GetStep() can only be called if @step applies");
   6799 
   6800  if (!HasAttr(nsGkAtoms::step)) {
   6801    return GetDefaultStep() * GetStepScaleFactor();
   6802  }
   6803 
   6804  nsAutoString stepStr;
   6805  GetAttr(nsGkAtoms::step, stepStr);
   6806 
   6807  if (stepStr.LowerCaseEqualsLiteral("any")) {
   6808    // The element can't suffer from step mismatch if there is no step.
   6809    return kStepAny;
   6810  }
   6811 
   6812  Decimal step = StringToDecimal(stepStr);
   6813  if (!step.isFinite() || step <= Decimal(0)) {
   6814    step = GetDefaultStep();
   6815  }
   6816 
   6817  // For input type=date, we round the step value to have a rounded day.
   6818  if (mType == FormControlType::InputDate ||
   6819      mType == FormControlType::InputMonth ||
   6820      mType == FormControlType::InputWeek) {
   6821    step = std::max(step.round(), Decimal(1));
   6822  }
   6823 
   6824  return step * GetStepScaleFactor();
   6825 }
   6826 
   6827 // ConstraintValidation
   6828 
   6829 void HTMLInputElement::SetCustomValidity(const nsAString& aError) {
   6830  ConstraintValidation::SetCustomValidity(aError);
   6831  UpdateValidityElementStates(true);
   6832 }
   6833 
   6834 bool HTMLInputElement::IsTooLong() {
   6835  if (!mValueChanged || !mLastValueChangeWasInteractive) {
   6836    return false;
   6837  }
   6838 
   6839  return mInputType->IsTooLong();
   6840 }
   6841 
   6842 bool HTMLInputElement::IsTooShort() {
   6843  if (!mValueChanged || !mLastValueChangeWasInteractive) {
   6844    return false;
   6845  }
   6846 
   6847  return mInputType->IsTooShort();
   6848 }
   6849 
   6850 bool HTMLInputElement::IsValueMissing() const {
   6851  // Should use UpdateValueMissingValidityStateForRadio() for type radio.
   6852  MOZ_ASSERT(mType != FormControlType::InputRadio);
   6853 
   6854  return mInputType->IsValueMissing();
   6855 }
   6856 
   6857 bool HTMLInputElement::HasTypeMismatch() const {
   6858  return mInputType->HasTypeMismatch();
   6859 }
   6860 
   6861 Maybe<bool> HTMLInputElement::HasPatternMismatch() const {
   6862  return mInputType->HasPatternMismatch();
   6863 }
   6864 
   6865 bool HTMLInputElement::IsRangeOverflow() const {
   6866  return mInputType->IsRangeOverflow();
   6867 }
   6868 
   6869 bool HTMLInputElement::IsRangeUnderflow() const {
   6870  return mInputType->IsRangeUnderflow();
   6871 }
   6872 
   6873 bool HTMLInputElement::ValueIsStepMismatch(const Decimal& aValue) const {
   6874  if (aValue.isNaN()) {
   6875    // The element can't suffer from step mismatch if its value isn't a
   6876    // number.
   6877    return false;
   6878  }
   6879 
   6880  Decimal step = GetStep();
   6881  if (step == kStepAny) {
   6882    return false;
   6883  }
   6884 
   6885  // Value has to be an integral multiple of step.
   6886  return NS_floorModulo(aValue - GetStepBase(), step) != Decimal(0);
   6887 }
   6888 
   6889 bool HTMLInputElement::HasStepMismatch() const {
   6890  return mInputType->HasStepMismatch();
   6891 }
   6892 
   6893 bool HTMLInputElement::HasBadInput() const { return mInputType->HasBadInput(); }
   6894 
   6895 void HTMLInputElement::UpdateTooLongValidityState() {
   6896  SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
   6897 }
   6898 
   6899 void HTMLInputElement::UpdateTooShortValidityState() {
   6900  SetValidityState(VALIDITY_STATE_TOO_SHORT, IsTooShort());
   6901 }
   6902 
   6903 void HTMLInputElement::UpdateValueMissingValidityStateForRadio(
   6904    bool aIgnoreSelf) {
   6905  MOZ_ASSERT(mType == FormControlType::InputRadio,
   6906             "This should be called only for radio input types");
   6907 
   6908  HTMLInputElement* selection = GetSelectedRadioButton();
   6909 
   6910  // If there is no selection, that might mean the radio is not in a group.
   6911  // In that case, we can look for the checked state of the radio.
   6912  bool selected = selection || (!aIgnoreSelf && mChecked);
   6913  bool required = !aIgnoreSelf && IsRequired();
   6914 
   6915  auto* container = GetCurrentRadioGroupContainer();
   6916  if (!container) {
   6917    SetValidityState(VALIDITY_STATE_VALUE_MISSING, false);
   6918    return;
   6919  }
   6920 
   6921  nsAutoString name;
   6922  GetAttr(nsGkAtoms::name, name);
   6923 
   6924  // If the current radio is required and not ignored, we can assume the entire
   6925  // group is required.
   6926  if (!required) {
   6927    required = (aIgnoreSelf && IsRequired())
   6928                   ? container->GetRequiredRadioCount(name) - 1
   6929                   : container->GetRequiredRadioCount(name);
   6930  }
   6931 
   6932  bool valueMissing = required && !selected;
   6933  if (container->GetValueMissingState(name) != valueMissing) {
   6934    container->SetValueMissingState(name, valueMissing);
   6935 
   6936    SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing);
   6937 
   6938    // nsRadioSetValueMissingState will call ElementStateChanged while visiting.
   6939    nsAutoScriptBlocker scriptBlocker;
   6940    VisitGroup([valueMissing](HTMLInputElement* aRadio) {
   6941      aRadio->SetValidityState(
   6942          nsIConstraintValidation::VALIDITY_STATE_VALUE_MISSING, valueMissing);
   6943      aRadio->UpdateValidityElementStates(true);
   6944      return true;
   6945    });
   6946  }
   6947 }
   6948 
   6949 void HTMLInputElement::UpdateValueMissingValidityState() {
   6950  if (mType == FormControlType::InputRadio) {
   6951    UpdateValueMissingValidityStateForRadio(false);
   6952    return;
   6953  }
   6954 
   6955  SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
   6956 }
   6957 
   6958 void HTMLInputElement::UpdateTypeMismatchValidityState() {
   6959  SetValidityState(VALIDITY_STATE_TYPE_MISMATCH, HasTypeMismatch());
   6960 }
   6961 
   6962 void HTMLInputElement::UpdatePatternMismatchValidityState() {
   6963  Maybe<bool> hasMismatch = HasPatternMismatch();
   6964  // Don't update if the JS engine failed to evaluate it.
   6965  if (hasMismatch.isSome()) {
   6966    SetValidityState(VALIDITY_STATE_PATTERN_MISMATCH, hasMismatch.value());
   6967  }
   6968 }
   6969 
   6970 void HTMLInputElement::UpdateRangeOverflowValidityState() {
   6971  SetValidityState(VALIDITY_STATE_RANGE_OVERFLOW, IsRangeOverflow());
   6972  UpdateInRange(true);
   6973 }
   6974 
   6975 void HTMLInputElement::UpdateRangeUnderflowValidityState() {
   6976  SetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW, IsRangeUnderflow());
   6977  UpdateInRange(true);
   6978 }
   6979 
   6980 void HTMLInputElement::UpdateStepMismatchValidityState() {
   6981  SetValidityState(VALIDITY_STATE_STEP_MISMATCH, HasStepMismatch());
   6982 }
   6983 
   6984 void HTMLInputElement::UpdateBadInputValidityState() {
   6985  SetValidityState(VALIDITY_STATE_BAD_INPUT, HasBadInput());
   6986 }
   6987 
   6988 void HTMLInputElement::UpdateAllValidityStates(bool aNotify) {
   6989  bool validBefore = IsValid();
   6990  UpdateAllValidityStatesButNotElementState();
   6991  if (validBefore != IsValid()) {
   6992    UpdateValidityElementStates(aNotify);
   6993  }
   6994 }
   6995 
   6996 void HTMLInputElement::UpdateAllValidityStatesButNotElementState() {
   6997  UpdateTooLongValidityState();
   6998  UpdateTooShortValidityState();
   6999  UpdateValueMissingValidityState();
   7000  UpdateTypeMismatchValidityState();
   7001  UpdatePatternMismatchValidityState();
   7002  UpdateRangeOverflowValidityState();
   7003  UpdateRangeUnderflowValidityState();
   7004  UpdateStepMismatchValidityState();
   7005  UpdateBadInputValidityState();
   7006 }
   7007 
   7008 void HTMLInputElement::UpdateBarredFromConstraintValidation() {
   7009  // NOTE: readonly attribute causes an element to be barred from constraint
   7010  // validation even if it doesn't apply to that input type. That's rather
   7011  // weird, but pre-existing behavior.
   7012  bool wasCandidate = IsCandidateForConstraintValidation();
   7013  SetBarredFromConstraintValidation(
   7014      mType == FormControlType::InputHidden ||
   7015      mType == FormControlType::InputButton ||
   7016      mType == FormControlType::InputReset || IsDisabled() ||
   7017      HasAttr(nsGkAtoms::readonly) ||
   7018      HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR));
   7019  if (IsCandidateForConstraintValidation() != wasCandidate) {
   7020    UpdateInRange(true);
   7021  }
   7022 }
   7023 
   7024 nsresult HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
   7025                                                ValidityStateType aType) {
   7026  return mInputType->GetValidationMessage(aValidationMessage, aType);
   7027 }
   7028 
   7029 bool HTMLInputElement::IsSingleLineTextControl() const {
   7030  return IsSingleLineTextControl(false);
   7031 }
   7032 
   7033 bool HTMLInputElement::IsTextArea() const { return false; }
   7034 
   7035 bool HTMLInputElement::IsPasswordTextControl() const {
   7036  return mType == FormControlType::InputPassword;
   7037 }
   7038 
   7039 Maybe<int32_t> HTMLInputElement::GetNumberInputCols() const {
   7040  // This logic is ported from WebKit, see
   7041  // https://github.com/whatwg/html/issues/10390
   7042  struct RenderSize {
   7043    uint32_t mBeforeDecimal = 0;
   7044    uint32_t mAfterDecimal = 0;
   7045 
   7046    RenderSize Max(const RenderSize& aOther) const {
   7047      return {std::max(mBeforeDecimal, aOther.mBeforeDecimal),
   7048              std::max(mAfterDecimal, aOther.mAfterDecimal)};
   7049    }
   7050 
   7051    static RenderSize From(const Decimal& aValue) {
   7052      MOZ_ASSERT(aValue.isFinite());
   7053      nsAutoCString tmp;
   7054      tmp.AppendInt(aValue.value().coefficient());
   7055      const uint32_t sizeOfDigits = tmp.Length();
   7056      const uint32_t sizeOfSign = aValue.isNegative() ? 1 : 0;
   7057      const int32_t exponent = aValue.exponent();
   7058      if (exponent >= 0) {
   7059        return {sizeOfSign + sizeOfDigits, 0};
   7060      }
   7061 
   7062      const int32_t sizeBeforeDecimalPoint = exponent + int32_t(sizeOfDigits);
   7063      if (sizeBeforeDecimalPoint > 0) {
   7064        // In case of "123.456"
   7065        return {sizeOfSign + sizeBeforeDecimalPoint,
   7066                sizeOfDigits - sizeBeforeDecimalPoint};
   7067      }
   7068 
   7069      // In case of "0.00012345"
   7070      const uint32_t sizeOfZero = 1;
   7071      const uint32_t numberOfZeroAfterDecimalPoint = -sizeBeforeDecimalPoint;
   7072      return {sizeOfSign + sizeOfZero,
   7073              numberOfZeroAfterDecimalPoint + sizeOfDigits};
   7074    }
   7075  };
   7076 
   7077  if (mType != FormControlType::InputNumber) {
   7078    return {};
   7079  }
   7080  Decimal min = GetMinimum();
   7081  if (!min.isFinite()) {
   7082    return {};
   7083  }
   7084  Decimal max = GetMaximum();
   7085  if (!max.isFinite()) {
   7086    return {};
   7087  }
   7088  Decimal step = GetStep();
   7089  if (step == kStepAny) {
   7090    return {};
   7091  }
   7092  MOZ_ASSERT(step.isFinite());
   7093  RenderSize size = RenderSize::From(min).Max(
   7094      RenderSize::From(max).Max(RenderSize::From(step)));
   7095  return Some(size.mBeforeDecimal + size.mAfterDecimal +
   7096              (size.mAfterDecimal ? 1 : 0));
   7097 }
   7098 
   7099 Maybe<int32_t> HTMLInputElement::GetCols() {
   7100  if (const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::size);
   7101      attr && attr->Type() == nsAttrValue::eInteger) {
   7102    int32_t cols = attr->GetIntegerValue();
   7103    if (cols > 0) {
   7104      return Some(cols);
   7105    }
   7106  }
   7107 
   7108  if (Maybe<int32_t> cols = GetNumberInputCols(); cols && *cols > 0) {
   7109    return cols;
   7110  }
   7111 
   7112  return {};
   7113 }
   7114 
   7115 int32_t HTMLInputElement::GetWrapCols() {
   7116  return 0;  // only textarea's can have wrap cols
   7117 }
   7118 
   7119 int32_t HTMLInputElement::GetRows() { return DEFAULT_ROWS; }
   7120 
   7121 void HTMLInputElement::GetDefaultValueFromContent(nsAString& aValue,
   7122                                                  bool aForDisplay) {
   7123  if (!GetEditorState()) {
   7124    return;
   7125  }
   7126  GetDefaultValue(aValue);
   7127  // This is called by the frame to show the value.
   7128  // We have to sanitize it when needed.
   7129  // FIXME: Do we want to sanitize even when aForDisplay is false?
   7130  if (mDoneCreating) {
   7131    SanitizeValue(aValue, aForDisplay ? SanitizationKind::ForDisplay
   7132                                      : SanitizationKind::ForValueGetter);
   7133  }
   7134 }
   7135 
   7136 bool HTMLInputElement::ValueChanged() const { return mValueChanged; }
   7137 
   7138 void HTMLInputElement::GetTextEditorValue(nsAString& aValue) const {
   7139  if (TextControlState* state = GetEditorState()) {
   7140    state->GetValue(aValue, /* aForDisplay = */ true);
   7141  }
   7142 }
   7143 
   7144 void HTMLInputElement::InitializeKeyboardEventListeners() {
   7145  TextControlState* state = GetEditorState();
   7146  if (state) {
   7147    state->InitializeKeyboardEventListeners();
   7148  }
   7149 }
   7150 
   7151 void HTMLInputElement::UpdatePlaceholderShownState() {
   7152  SetStates(ElementState::PLACEHOLDER_SHOWN,
   7153            IsValueEmpty() && PlaceholderApplies() &&
   7154                HasAttr(nsGkAtoms::placeholder));
   7155 }
   7156 
   7157 void HTMLInputElement::OnValueChanged(ValueChangeKind aKind,
   7158                                      bool aNewValueEmpty,
   7159                                      const nsAString* aKnownNewValue) {
   7160  MOZ_ASSERT_IF(aKnownNewValue, aKnownNewValue->IsEmpty() == aNewValueEmpty);
   7161  if (aKind != ValueChangeKind::Internal) {
   7162    mLastValueChangeWasInteractive = aKind == ValueChangeKind::UserInteraction;
   7163 
   7164    if (mLastValueChangeWasInteractive &&
   7165        State().HasState(ElementState::AUTOFILL)) {
   7166      RemoveStates(ElementState::AUTOFILL | ElementState::AUTOFILL_PREVIEW);
   7167    }
   7168  }
   7169 
   7170  if (aNewValueEmpty != IsValueEmpty()) {
   7171    SetStates(ElementState::VALUE_EMPTY, aNewValueEmpty);
   7172    UpdatePlaceholderShownState();
   7173  }
   7174 
   7175  UpdateAllValidityStates(true);
   7176 
   7177  ResetDirFormAssociatedElement(this, true, HasDirAuto(), aKnownNewValue);
   7178 }
   7179 
   7180 bool HTMLInputElement::HasCachedSelection() {
   7181  TextControlState* state = GetEditorState();
   7182  if (!state) {
   7183    return false;
   7184  }
   7185  return state->IsSelectionCached() && state->HasNeverInitializedBefore() &&
   7186         state->GetSelectionProperties().GetStart() !=
   7187             state->GetSelectionProperties().GetEnd();
   7188 }
   7189 
   7190 void HTMLInputElement::SetRevealPassword(bool aValue) {
   7191  if (NS_WARN_IF(mType != FormControlType::InputPassword)) {
   7192    return;
   7193  }
   7194  if (aValue == State().HasState(ElementState::REVEALED)) {
   7195    return;
   7196  }
   7197  RefPtr doc = OwnerDoc();
   7198  // We allow chrome code to prevent this. This is important for about:logins,
   7199  // which may need to run some OS-dependent authentication code before
   7200  // revealing the saved passwords.
   7201  bool defaultAction = true;
   7202  nsContentUtils::DispatchEventOnlyToChrome(
   7203      doc, this, u"MozWillToggleReveal"_ns, CanBubble::eYes, Cancelable::eYes,
   7204      &defaultAction);
   7205  if (NS_WARN_IF(!defaultAction)) {
   7206    return;
   7207  }
   7208  SetStates(ElementState::REVEALED, aValue);
   7209 }
   7210 
   7211 bool HTMLInputElement::RevealPassword() const {
   7212  if (NS_WARN_IF(mType != FormControlType::InputPassword)) {
   7213    return false;
   7214  }
   7215  return State().HasState(ElementState::REVEALED);
   7216 }
   7217 
   7218 void HTMLInputElement::FieldSetDisabledChanged(bool aNotify) {
   7219  // This *has* to be called *before* UpdateBarredFromConstraintValidation and
   7220  // UpdateValueMissingValidityState because these two functions depend on our
   7221  // disabled state.
   7222  nsGenericHTMLFormControlElementWithState::FieldSetDisabledChanged(aNotify);
   7223 
   7224  UpdateValueMissingValidityState();
   7225  UpdateBarredFromConstraintValidation();
   7226  UpdateValidityElementStates(aNotify);
   7227 }
   7228 
   7229 void HTMLInputElement::SetFilePickerFiltersFromAccept(
   7230    nsIFilePicker* filePicker) {
   7231  // We always add |filterAll|
   7232  filePicker->AppendFilters(nsIFilePicker::filterAll);
   7233 
   7234  NS_ASSERTION(HasAttr(nsGkAtoms::accept),
   7235               "You should not call SetFilePickerFiltersFromAccept if the"
   7236               " element has no accept attribute!");
   7237 
   7238  // Services to retrieve image/*, audio/*, video/* filters
   7239  nsCOMPtr<nsIStringBundleService> stringService =
   7240      components::StringBundle::Service();
   7241  if (!stringService) {
   7242    return;
   7243  }
   7244  nsCOMPtr<nsIStringBundle> filterBundle;
   7245  if (NS_FAILED(stringService->CreateBundle(
   7246          "chrome://global/content/filepicker.properties",
   7247          getter_AddRefs(filterBundle)))) {
   7248    return;
   7249  }
   7250 
   7251  // Service to retrieve mime type information for mime types filters
   7252  nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
   7253  if (!mimeService) {
   7254    return;
   7255  }
   7256 
   7257  nsAutoString accept;
   7258  GetAttr(nsGkAtoms::accept, accept);
   7259 
   7260  HTMLSplitOnSpacesTokenizer tokenizer(accept, ',');
   7261 
   7262  nsTArray<nsFilePickerFilter> filters;
   7263  nsString allExtensionsList;
   7264 
   7265  // Retrieve all filters
   7266  while (tokenizer.hasMoreTokens()) {
   7267    const nsDependentSubstring& token = tokenizer.nextToken();
   7268 
   7269    if (token.IsEmpty()) {
   7270      continue;
   7271    }
   7272 
   7273    int32_t filterMask = 0;
   7274    nsString filterName;
   7275    nsString extensionListStr;
   7276 
   7277    // First, check for image/audio/video filters...
   7278    if (token.EqualsLiteral("image/*")) {
   7279      filterMask = nsIFilePicker::filterImages;
   7280      filterBundle->GetStringFromName("imageFilter", extensionListStr);
   7281    } else if (token.EqualsLiteral("audio/*")) {
   7282      filterMask = nsIFilePicker::filterAudio;
   7283      filterBundle->GetStringFromName("audioFilter", extensionListStr);
   7284    } else if (token.EqualsLiteral("video/*")) {
   7285      filterMask = nsIFilePicker::filterVideo;
   7286      filterBundle->GetStringFromName("videoFilter", extensionListStr);
   7287    } else if (token.First() == '.') {
   7288      if (token.Contains(';') || token.Contains('*')) {
   7289        // Ignore this filter as it contains reserved characters
   7290        continue;
   7291      }
   7292      extensionListStr = u"*"_ns + token;
   7293      filterName = extensionListStr;
   7294    } else {
   7295      //... if no image/audio/video filter is found, check mime types filters
   7296      nsCOMPtr<nsIMIMEInfo> mimeInfo;
   7297      if (NS_FAILED(
   7298              mimeService->GetFromTypeAndExtension(NS_ConvertUTF16toUTF8(token),
   7299                                                   ""_ns,  // No extension
   7300                                                   getter_AddRefs(mimeInfo))) ||
   7301          !mimeInfo) {
   7302        continue;
   7303      }
   7304 
   7305      // Get a name for the filter: first try the description, then the mime
   7306      // type name if there is no description
   7307      mimeInfo->GetDescription(filterName);
   7308      if (filterName.IsEmpty()) {
   7309        nsCString mimeTypeName;
   7310        mimeInfo->GetType(mimeTypeName);
   7311        CopyUTF8toUTF16(mimeTypeName, filterName);
   7312      }
   7313 
   7314      // Get extension list
   7315      nsCOMPtr<nsIUTF8StringEnumerator> extensions;
   7316      mimeInfo->GetFileExtensions(getter_AddRefs(extensions));
   7317 
   7318      bool hasMore;
   7319      while (NS_SUCCEEDED(extensions->HasMore(&hasMore)) && hasMore) {
   7320        nsCString extension;
   7321        if (NS_FAILED(extensions->GetNext(extension))) {
   7322          continue;
   7323        }
   7324        if (!extensionListStr.IsEmpty()) {
   7325          extensionListStr.AppendLiteral("; ");
   7326        }
   7327        extensionListStr += u"*."_ns + NS_ConvertUTF8toUTF16(extension);
   7328      }
   7329    }
   7330 
   7331    if (!filterMask && (extensionListStr.IsEmpty() || filterName.IsEmpty())) {
   7332      // No valid filter found
   7333      continue;
   7334    }
   7335 
   7336    // At this point we're sure the token represents a valid filter, so pass
   7337    // it directly as a raw filter.
   7338    filePicker->AppendRawFilter(token);
   7339 
   7340    // If we arrived here, that means we have a valid filter: let's create it
   7341    // and add it to our list, if no similar filter is already present
   7342    nsFilePickerFilter filter;
   7343    if (filterMask) {
   7344      filter = nsFilePickerFilter(filterMask);
   7345    } else {
   7346      filter = nsFilePickerFilter(filterName, extensionListStr);
   7347    }
   7348 
   7349    if (!filters.Contains(filter)) {
   7350      if (!allExtensionsList.IsEmpty()) {
   7351        allExtensionsList.AppendLiteral("; ");
   7352      }
   7353      allExtensionsList += extensionListStr;
   7354      filters.AppendElement(filter);
   7355    }
   7356  }
   7357 
   7358  // Remove similar filters
   7359  // Iterate over a copy, as we might modify the original filters list
   7360  const nsTArray<nsFilePickerFilter> filtersCopy = filters.Clone();
   7361  for (uint32_t i = 0; i < filtersCopy.Length(); ++i) {
   7362    const nsFilePickerFilter& filterToCheck = filtersCopy[i];
   7363    if (filterToCheck.mFilterMask) {
   7364      continue;
   7365    }
   7366    for (uint32_t j = 0; j < filtersCopy.Length(); ++j) {
   7367      if (i == j) {
   7368        continue;
   7369      }
   7370      // Check if this filter's extension list is a substring of the other one.
   7371      // e.g. if filters are "*.jpeg" and "*.jpeg; *.jpg" the first one should
   7372      // be removed.
   7373      // Add an extra "; " to be sure the check will work and avoid cases like
   7374      // "*.xls" being a subtring of "*.xslx" while those are two differents
   7375      // filters and none should be removed.
   7376      if (FindInReadable(filterToCheck.mFilter + u";"_ns,
   7377                         filtersCopy[j].mFilter + u";"_ns)) {
   7378        // We already have a similar, less restrictive filter (i.e.
   7379        // filterToCheck extensionList is just a subset of another filter
   7380        // extension list): remove this one
   7381        filters.RemoveElement(filterToCheck);
   7382      }
   7383    }
   7384  }
   7385 
   7386  // Add "All Supported Types" filter
   7387  if (filters.Length() > 1) {
   7388    nsAutoString title;
   7389    nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
   7390                                       "AllSupportedTypes", title);
   7391    filePicker->AppendFilter(title, allExtensionsList);
   7392  }
   7393 
   7394  // Add each filter
   7395  for (uint32_t i = 0; i < filters.Length(); ++i) {
   7396    const nsFilePickerFilter& filter = filters[i];
   7397    if (filter.mFilterMask) {
   7398      filePicker->AppendFilters(filter.mFilterMask);
   7399    } else {
   7400      filePicker->AppendFilter(filter.mTitle, filter.mFilter);
   7401    }
   7402  }
   7403 
   7404  if (filters.Length() >= 1) {
   7405    // |filterAll| will always use index=0 so we need to set index=1 as the
   7406    // current filter. This will be "All Supported Types" for multiple filters.
   7407    filePicker->SetFilterIndex(1);
   7408  }
   7409 }
   7410 
   7411 Decimal HTMLInputElement::GetStepScaleFactor() const {
   7412  MOZ_ASSERT(DoesStepApply());
   7413 
   7414  switch (mType) {
   7415    case FormControlType::InputDate:
   7416      return kStepScaleFactorDate;
   7417    case FormControlType::InputNumber:
   7418    case FormControlType::InputRange:
   7419      return kStepScaleFactorNumberRange;
   7420    case FormControlType::InputTime:
   7421    case FormControlType::InputDatetimeLocal:
   7422      return kStepScaleFactorTime;
   7423    case FormControlType::InputMonth:
   7424      return kStepScaleFactorMonth;
   7425    case FormControlType::InputWeek:
   7426      return kStepScaleFactorWeek;
   7427    default:
   7428      MOZ_ASSERT(false, "Unrecognized input type");
   7429      return Decimal::nan();
   7430  }
   7431 }
   7432 
   7433 Decimal HTMLInputElement::GetDefaultStep() const {
   7434  MOZ_ASSERT(DoesStepApply());
   7435 
   7436  switch (mType) {
   7437    case FormControlType::InputDate:
   7438    case FormControlType::InputMonth:
   7439    case FormControlType::InputWeek:
   7440    case FormControlType::InputNumber:
   7441    case FormControlType::InputRange:
   7442      return kDefaultStep;
   7443    case FormControlType::InputTime:
   7444    case FormControlType::InputDatetimeLocal:
   7445      return kDefaultStepTime;
   7446    default:
   7447      MOZ_ASSERT(false, "Unrecognized input type");
   7448      return Decimal::nan();
   7449  }
   7450 }
   7451 
   7452 void HTMLInputElement::SetUserInteracted(bool aInteracted) {
   7453  if (mUserInteracted == aInteracted) {
   7454    return;
   7455  }
   7456  mUserInteracted = aInteracted;
   7457  UpdateValidityElementStates(true);
   7458 }
   7459 
   7460 void HTMLInputElement::UpdateInRange(bool aNotify) {
   7461  AutoStateChangeNotifier notifier(*this, aNotify);
   7462  RemoveStatesSilently(ElementState::INRANGE | ElementState::OUTOFRANGE);
   7463  if (!mHasRange || !IsCandidateForConstraintValidation()) {
   7464    return;
   7465  }
   7466  bool outOfRange = GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW) ||
   7467                    GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW);
   7468  AddStatesSilently(outOfRange ? ElementState::OUTOFRANGE
   7469                               : ElementState::INRANGE);
   7470 }
   7471 
   7472 void HTMLInputElement::UpdateHasRange(bool aNotify) {
   7473  // There is a range if min/max applies for the type and if the element
   7474  // currently have a valid min or max.
   7475  const bool newHasRange = [&] {
   7476    if (!DoesMinMaxApply()) {
   7477      return false;
   7478    }
   7479    return !GetMinimum().isNaN() || !GetMaximum().isNaN();
   7480  }();
   7481 
   7482  if (newHasRange == mHasRange) {
   7483    return;
   7484  }
   7485 
   7486  mHasRange = newHasRange;
   7487  UpdateInRange(aNotify);
   7488 }
   7489 
   7490 void HTMLInputElement::PickerClosed() {
   7491  mPickerRunning = false;
   7492  SetOpenState(false);
   7493 }
   7494 
   7495 JSObject* HTMLInputElement::WrapNode(JSContext* aCx,
   7496                                     JS::Handle<JSObject*> aGivenProto) {
   7497  return HTMLInputElement_Binding::Wrap(aCx, this, aGivenProto);
   7498 }
   7499 
   7500 GetFilesHelper* HTMLInputElement::GetOrCreateGetFilesHelper(bool aRecursiveFlag,
   7501                                                            ErrorResult& aRv) {
   7502  MOZ_ASSERT(mFileData);
   7503 
   7504  if (aRecursiveFlag) {
   7505    if (!mFileData->mGetFilesRecursiveHelper) {
   7506      mFileData->mGetFilesRecursiveHelper = GetFilesHelper::Create(
   7507          GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
   7508      if (NS_WARN_IF(aRv.Failed())) {
   7509        return nullptr;
   7510      }
   7511    }
   7512 
   7513    return mFileData->mGetFilesRecursiveHelper;
   7514  }
   7515 
   7516  if (!mFileData->mGetFilesNonRecursiveHelper) {
   7517    mFileData->mGetFilesNonRecursiveHelper = GetFilesHelper::Create(
   7518        GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
   7519    if (NS_WARN_IF(aRv.Failed())) {
   7520      return nullptr;
   7521    }
   7522  }
   7523 
   7524  return mFileData->mGetFilesNonRecursiveHelper;
   7525 }
   7526 
   7527 void HTMLInputElement::UpdateEntries(
   7528    const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories) {
   7529  MOZ_ASSERT(mFileData && mFileData->mEntries.IsEmpty());
   7530 
   7531  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   7532  MOZ_ASSERT(global);
   7533 
   7534  RefPtr<FileSystem> fs = FileSystem::Create(global);
   7535  if (NS_WARN_IF(!fs)) {
   7536    return;
   7537  }
   7538 
   7539  Sequence<RefPtr<FileSystemEntry>> entries;
   7540  for (uint32_t i = 0; i < aFilesOrDirectories.Length(); ++i) {
   7541    RefPtr<FileSystemEntry> entry =
   7542        FileSystemEntry::Create(global, aFilesOrDirectories[i], fs);
   7543    MOZ_ASSERT(entry);
   7544 
   7545    if (!entries.AppendElement(entry, fallible)) {
   7546      return;
   7547    }
   7548  }
   7549 
   7550  // The root fileSystem is a DirectoryEntry object that contains only the
   7551  // dropped fileEntry and directoryEntry objects.
   7552  fs->CreateRoot(entries);
   7553 
   7554  mFileData->mEntries = std::move(entries);
   7555 }
   7556 
   7557 void HTMLInputElement::GetWebkitEntries(
   7558    nsTArray<RefPtr<FileSystemEntry>>& aSequence) {
   7559  if (NS_WARN_IF(mType != FormControlType::InputFile)) {
   7560    return;
   7561  }
   7562 
   7563  glean::dom::blink_filesystem_used
   7564      .EnumGet(glean::dom::BlinkFilesystemUsedLabel::eTrue)
   7565      .Add();
   7566  aSequence.AppendElements(mFileData->mEntries);
   7567 }
   7568 
   7569 already_AddRefed<nsINodeList> HTMLInputElement::GetLabels() {
   7570  if (!IsLabelable()) {
   7571    return nullptr;
   7572  }
   7573 
   7574  return nsGenericHTMLElement::Labels();
   7575 }
   7576 
   7577 void HTMLInputElement::MaybeFireInputPasswordRemoved() {
   7578  // We want this event to be fired only when the password field is removed
   7579  // from the DOM tree, not when it is released (ex, tab is closed). So don't
   7580  // fire an event when the password input field doesn't have a docshell.
   7581  Document* doc = GetComposedDoc();
   7582  nsIDocShell* container = doc ? doc->GetDocShell() : nullptr;
   7583  if (!container) {
   7584    return;
   7585  }
   7586 
   7587  // Right now, only the password manager listens to the event and only listen
   7588  // to it under certain circumstances. So don't fire this event unless
   7589  // necessary.
   7590  if (!doc->ShouldNotifyFormOrPasswordRemoved()) {
   7591    return;
   7592  }
   7593 
   7594  AsyncEventDispatcher::RunDOMEventWhenSafe(
   7595      *this, u"DOMInputPasswordRemoved"_ns, CanBubble::eNo,
   7596      ChromeOnlyDispatch::eYes);
   7597 }
   7598 
   7599 void HTMLInputElement::UpdateRadioGroupState() {
   7600  VisitGroup([](HTMLInputElement* aRadio) {
   7601    aRadio->UpdateIndeterminateState(true);
   7602    aRadio->UpdateValidityElementStates(true);
   7603    return true;
   7604  });
   7605 }
   7606 
   7607 }  // namespace mozilla::dom
   7608 
   7609 #undef NS_ORIGINAL_CHECKED_VALUE