Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/html/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 247 kB image not shown  

Quelle  HTMLInputElement.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/HTMLInputElement.h"

#include "mozilla/ArrayUtils.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Components.h"
#include "mozilla/dom/AutocompleteInfoBinding.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/CustomEvent.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/FormData.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/NumericInputTypes.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/InputType.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/MouseEvent.h"
#include "mozilla/dom/MutationEventBinding.h"
#include "mozilla/dom/WheelEventBinding.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/ImageInputTelemetry.h"
#include "mozilla/Maybe.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_signon.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Try.h"
#include "mozilla/Unused.h"
#include "nsAttrValueInlines.h"
#include "nsCRTGlue.h"
#include "nsIFilePicker.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"

#include "HTMLDataListElement.h"
#include "HTMLFormSubmissionConstants.h"
#include "mozilla/Telemetry.h"
#include "nsBaseCommandController.h"
#include "nsIStringBundle.h"
#include "nsFocusManager.h"
#include "nsColorControlFrame.h"
#include "nsFileControlFrame.h"
#include "nsNumberControlFrame.h"
#include "nsSearchControlFrame.h"
#include "nsPIDOMWindow.h"
#include "nsRepeatService.h"
#include "mozilla/dom/ProgressEvent.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsPresContext.h"
#include "nsIFormControl.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/HTMLDataListElement.h"
#include "mozilla/dom/HTMLOptionElement.h"
#include "nsIFrame.h"
#include "nsRangeFrame.h"
#include "nsError.h"
#include "nsIEditor.h"
#include "nsIPromptCollection.h"

#include "mozilla/PresState.h"
#include "nsLinebreakConverter.h"  //to strip out carriage returns
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "nsLayoutUtils.h"
#include "nsVariant.h"

#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/MappedDeclarationsBuilder.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/TextControlState.h"
#include "mozilla/TextEditor.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"

#include <algorithm>

// input type=radio
#include "mozilla/dom/RadioGroupContainer.h"
#include "nsIRadioVisitor.h"
#include "nsRadioVisitor.h"

// input type=file
#include "mozilla/dom/FileSystemEntry.h"
#include "mozilla/dom/FileSystem.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileList.h"
#include "nsIFile.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIContentPrefService2.h"
#include "nsIMIMEService.h"
#include "nsIObserverService.h"

// input type=image
#include "nsImageLoadingContent.h"
#include "imgRequestProxy.h"

#include "mozAutoDocUpdate.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"

#include "mozilla/LookAndFeel.h"
#include "mozilla/Preferences.h"
#include "mozilla/MathAlgorithms.h"

#include <limits>

#include "nsIColorPicker.h"
#include "nsIStringEnumerator.h"
#include "HTMLSplitOnSpacesTokenizer.h"
#include "nsIMIMEInfo.h"
#include "nsFrameSelection.h"
#include "nsXULControllers.h"

// input type=date
#include "js/Date.h"

NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)

// XXX align=left, hspace, vspace, border? other nav4 attrs

namespace mozilla::dom {

// First bits are needed for the control type.
#define NS_OUTER_ACTIVATE_EVENT (1 << 9)
#define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
// (1 << 11 is unused)
#define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
#define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
#define NS_IN_SUBMIT_CLICK (1 << 15)
#define NS_CONTROL_TYPE(bits)                                              \
  ((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE |        \
              NS_ORIGINAL_INDETERMINATE_VALUE | NS_PRE_HANDLE_BLUR_EVENT | \
              NS_IN_SUBMIT_CLICK))

// whether textfields should be selected once focused:
//  -1: no, 1: yes, 0: uninitialized
static int32_t gSelectTextFieldOnFocus;
UploadLastDir* HTMLInputElement::gUploadLastDir;

static const nsAttrValue::EnumTable kInputTypeTable[] = {
    {"button", FormControlType::InputButton},
    {"checkbox", FormControlType::InputCheckbox},
    {"color", FormControlType::InputColor},
    {"date", FormControlType::InputDate},
    {"datetime-local", FormControlType::InputDatetimeLocal},
    {"email", FormControlType::InputEmail},
    {"file", FormControlType::InputFile},
    {"hidden", FormControlType::InputHidden},
    {"reset", FormControlType::InputReset},
    {"image", FormControlType::InputImage},
    {"month", FormControlType::InputMonth},
    {"number", FormControlType::InputNumber},
    {"password", FormControlType::InputPassword},
    {"radio", FormControlType::InputRadio},
    {"range", FormControlType::InputRange},
    {"search", FormControlType::InputSearch},
    {"submit", FormControlType::InputSubmit},
    {"tel", FormControlType::InputTel},
    {"time", FormControlType::InputTime},
    {"url", FormControlType::InputUrl},
    {"week", FormControlType::InputWeek},
    // "text" must be last for ParseAttribute to work right.  If you add things
    // before it, please update kInputDefaultType.
    {"text", FormControlType::InputText},
    {nullptr, 0}};

// Default type is 'text'.
static const nsAttrValue::EnumTable* kInputDefaultType =
    &kInputTypeTable[std::size(kInputTypeTable) - 2];

static const nsAttrValue::EnumTable kCaptureTable[] = {
    {"user", nsIFilePicker::captureUser},
    {"environment", nsIFilePicker::captureEnv},
    {"", nsIFilePicker::captureDefault},
    {nullptr, nsIFilePicker::captureNone}};

static const nsAttrValue::EnumTable* kCaptureDefault = &kCaptureTable[2];

using namespace blink;

constexpr Decimal HTMLInputElement::kStepScaleFactorDate(86400000_d);
constexpr Decimal HTMLInputElement::kStepScaleFactorNumberRange(1_d);
constexpr Decimal HTMLInputElement::kStepScaleFactorTime(1000_d);
constexpr Decimal HTMLInputElement::kStepScaleFactorMonth(1_d);
constexpr Decimal HTMLInputElement::kStepScaleFactorWeek(7 * 86400000_d);
constexpr Decimal HTMLInputElement::kDefaultStepBase(0_d);
constexpr Decimal HTMLInputElement::kDefaultStepBaseWeek(-259200000_d);
constexpr Decimal HTMLInputElement::kDefaultStep(1_d);
constexpr Decimal HTMLInputElement::kDefaultStepTime(60_d);
constexpr Decimal HTMLInputElement::kStepAny(0_d);

const double HTMLInputElement::kMinimumYear = 1;
const double HTMLInputElement::kMaximumYear = 275760;
const double HTMLInputElement::kMaximumWeekInMaximumYear = 37;
const double HTMLInputElement::kMaximumDayInMaximumYear = 13;
const double HTMLInputElement::kMaximumMonthInMaximumYear = 9;
const double HTMLInputElement::kMaximumWeekInYear = 53;
const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000;

// An helper class for the dispatching of the 'change' event.
// This class is used when the FilePicker finished its task (or when files and
// directories are set by some chrome/test only method).
// The task of this class is to postpone the dispatching of 'change' and 'input'
// events at the end of the exploration of the directories.
class DispatchChangeEventCallback final : public GetFilesCallback {
 public:
  explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
      : mInputElement(aInputElement) {
    MOZ_ASSERT(aInputElement);
  }

  virtual void Callback(
      nsresult aStatus,
      const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
    if (!mInputElement->GetOwnerGlobal()) {
      return;
    }

    nsTArray<OwningFileOrDirectory> array;
    for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) {
      OwningFileOrDirectory* element = array.AppendElement();
      RefPtr<File> file =
          File::Create(mInputElement->GetOwnerGlobal(), aBlobImpls[i]);
      if (NS_WARN_IF(!file)) {
        return;
      }

      element->SetAsFile() = file;
    }

    mInputElement->SetFilesOrDirectories(array, true);
    Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
  }

  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  nsresult DispatchEvents() {
    RefPtr<HTMLInputElement> inputElement(mInputElement);
    nsresult rv = nsContentUtils::DispatchInputEvent(inputElement);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
    mInputElement->SetUserInteracted(true);
    rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
                                              mInputElement, u"change"_ns,
                                              CanBubble::eYes, Cancelable::eNo);

    return rv;
  }

 private:
  RefPtr<HTMLInputElement> mInputElement;
};

struct HTMLInputElement::FileData {
  /**
   * The value of the input if it is a file input. This is the list of files or
   * directories DOM objects used when uploading a file. It is vital that this
   * is kept separate from mValue so that it won't be possible to 'leak' the
   * value from a text-input to a file-input. Additionally, the logic for this
   * value is kept as simple as possible to avoid accidental errors where the
   * wrong filename is used.  Therefor the list of filenames is always owned by
   * this member, never by the frame. Whenever the frame wants to change the
   * filename it has to call SetFilesOrDirectories to update this member.
   */

  nsTArray<OwningFileOrDirectory> mFilesOrDirectories;

  RefPtr<GetFilesHelper> mGetFilesRecursiveHelper;
  RefPtr<GetFilesHelper> mGetFilesNonRecursiveHelper;

  /**
   * Hack for bug 1086684: Stash the .value when we're a file picker.
   */

  nsString mFirstFilePath;

  RefPtr<FileList> mFileList;
  Sequence<RefPtr<FileSystemEntry>> mEntries;

  nsString mStaticDocFileList;

  void ClearGetFilesHelpers() {
    if (mGetFilesRecursiveHelper) {
      mGetFilesRecursiveHelper->Unlink();
      mGetFilesRecursiveHelper = nullptr;
    }

    if (mGetFilesNonRecursiveHelper) {
      mGetFilesNonRecursiveHelper->Unlink();
      mGetFilesNonRecursiveHelper = nullptr;
    }
  }

  // Cycle Collection support.
  void Traverse(nsCycleCollectionTraversalCallback& cb) {
    FileData* tmp = this;
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries)
    if (mGetFilesRecursiveHelper) {
      mGetFilesRecursiveHelper->Traverse(cb);
    }

    if (mGetFilesNonRecursiveHelper) {
      mGetFilesNonRecursiveHelper->Traverse(cb);
    }
  }

  void Unlink() {
    FileData* tmp = this;
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries)
    ClearGetFilesHelpers();
  }
};

HTMLInputElement::nsFilePickerShownCallback::nsFilePickerShownCallback(
    HTMLInputElement* aInput, nsIFilePicker* aFilePicker)
    : mFilePicker(aFilePicker), mInput(aInput) {}

NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)

NS_IMETHODIMP
UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason) {
  nsCOMPtr<nsIFile> localFile;
  nsAutoString prefStr;

  if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
    Preferences::GetString("dom.input.fallbackUploadDir", prefStr);
  }

  if (prefStr.IsEmpty() && mResult) {
    nsCOMPtr<nsIVariant> pref;
    mResult->GetValue(getter_AddRefs(pref));
    pref->GetAsAString(prefStr);
  }

  if (!prefStr.IsEmpty()) {
    nsresult rv = NS_NewLocalFile(prefStr, getter_AddRefs(localFile));
    Unused << NS_WARN_IF(NS_FAILED(rv));
  }

  if (localFile) {
    mFilePicker->SetDisplayDirectory(localFile);
  } else {
    // If no custom directory was set through the pref, default to
    // "desktop" directory for each platform.
    mFilePicker->SetDisplaySpecialDirectory(
        NS_LITERAL_STRING_FROM_CSTRING(NS_OS_DESKTOP_DIR));
  }

  mFilePicker->Open(mFpCallback);
  return NS_OK;
}

NS_IMETHODIMP
UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref) {
  mResult = pref;
  return NS_OK;
}

NS_IMETHODIMP
UploadLastDir::ContentPrefCallback::HandleError(nsresult error) {
  // HandleCompletion is always called (even with HandleError was called),
  // so we don't need to do anything special here.
  return NS_OK;
}

namespace {

/**
 * This may return nullptr if the DOM File's implementation of
 * File::mozFullPathInternal does not successfully return a non-empty
 * string that is a valid path. This can happen on Firefox OS, for example,
 * where the file picker can create Blobs.
 */

static already_AddRefed<nsIFile> LastUsedDirectory(
    const OwningFileOrDirectory& aData) {
  if (aData.IsFile()) {
    nsAutoString path;
    ErrorResult error;
    aData.GetAsFile()->GetMozFullPathInternal(path, error);
    if (error.Failed() || path.IsEmpty()) {
      error.SuppressException();
      return nullptr;
    }

    nsCOMPtr<nsIFile> localFile;
    nsresult rv = NS_NewLocalFile(path, getter_AddRefs(localFile));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }

    nsCOMPtr<nsIFile> parentFile;
    rv = localFile->GetParent(getter_AddRefs(parentFile));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }

    return parentFile.forget();
  }

  MOZ_ASSERT(aData.IsDirectory());

  nsCOMPtr<nsIFile> localFile = aData.GetAsDirectory()->GetInternalNsIFile();
  MOZ_ASSERT(localFile);

  return localFile.forget();
}

void GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
                               nsAString& aName) {
  if (aData.IsFile()) {
    aData.GetAsFile()->GetName(aName);
  } else {
    MOZ_ASSERT(aData.IsDirectory());
    ErrorResult rv;
    aData.GetAsDirectory()->GetName(aName, rv);
    if (NS_WARN_IF(rv.Failed())) {
      rv.SuppressException();
    }
  }
}

void GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
                               nsAString& aPath, ErrorResult& aRv) {
  if (aData.IsFile()) {
    aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
  } else {
    MOZ_ASSERT(aData.IsDirectory());
    aData.GetAsDirectory()->GetFullRealPath(aPath);
  }
}

}  // namespace

NS_IMETHODIMP
HTMLInputElement::nsFilePickerShownCallback::Done(
    nsIFilePicker::ResultCode aResult) {
  mInput->PickerClosed();

  if (aResult == nsIFilePicker::returnCancel) {
    RefPtr<HTMLInputElement> inputElement(mInput);
    return nsContentUtils::DispatchTrustedEvent(
        inputElement->OwnerDoc(), inputElement, u"cancel"_ns, CanBubble::eYes,
        Cancelable::eNo);
  }

  mInput->OwnerDoc()->NotifyUserGestureActivation();

  nsIFilePicker::Mode mode;
  mFilePicker->GetMode(&mode);

  // Collect new selected filenames
  nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
  if (mode == nsIFilePicker::modeOpenMultiple) {
    nsCOMPtr<nsISimpleEnumerator> iter;
    nsresult rv =
        mFilePicker->GetDomFileOrDirectoryEnumerator(getter_AddRefs(iter));
    NS_ENSURE_SUCCESS(rv, rv);

    if (!iter) {
      return NS_OK;
    }

    nsCOMPtr<nsISupports> tmp;
    bool hasMore = true;

    while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
      iter->GetNext(getter_AddRefs(tmp));
      RefPtr<Blob> domBlob = do_QueryObject(tmp);
      MOZ_ASSERT(domBlob,
                 "Null file object from FilePicker's file enumerator?");
      if (!domBlob) {
        continue;
      }

      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
      element->SetAsFile() = domBlob->ToFile();

      ImageInputTelemetry::MaybeRecordFilePickerImageInputTelemetry(domBlob);
    }
  } else {
    MOZ_ASSERT(mode == nsIFilePicker::modeOpen ||
               mode == nsIFilePicker::modeGetFolder);
    nsCOMPtr<nsISupports> tmp;
    nsresult rv = mFilePicker->GetDomFileOrDirectory(getter_AddRefs(tmp));
    NS_ENSURE_SUCCESS(rv, rv);

    if (!tmp) {
      return NS_OK;
    }

    // Show a prompt to get user confirmation before allowing folder access.
    // This is to prevent sites from tricking the user into uploading files.
    // See Bug 1338637.
    if (mode == nsIFilePicker::modeGetFolder) {
      nsCOMPtr<nsIPromptCollection> prompter =
          do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
      if (!prompter) {
        return NS_ERROR_NOT_AVAILABLE;
      }

      bool confirmed = false;
      BrowsingContext* bc = mInput->OwnerDoc()->GetBrowsingContext();

      // Get directory name
      RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
      nsAutoString directoryName;
      ErrorResult error;
      directory->GetName(directoryName, error);
      if (NS_WARN_IF(error.Failed())) {
        return error.StealNSResult();
      }

      rv = prompter->ConfirmFolderUpload(bc, directoryName, &confirmed);
      NS_ENSURE_SUCCESS(rv, rv);
      if (!confirmed) {
        // User aborted upload
        return NS_OK;
      }
    }

    RefPtr<Blob> blob = do_QueryObject(tmp);
    if (blob) {
      RefPtr<File> file = blob->ToFile();
      MOZ_ASSERT(file);

      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
      element->SetAsFile() = file;

      ImageInputTelemetry::MaybeRecordFilePickerImageInputTelemetry(blob);
    } else if (tmp) {
      RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
      element->SetAsDirectory() = directory;
    }
  }

  if (newFilesOrDirectories.IsEmpty()) {
    return NS_OK;
  }

  // Store the last used directory using the content pref service:
  nsCOMPtr<nsIFile> lastUsedDir = LastUsedDirectory(newFilesOrDirectories[0]);

  if (lastUsedDir) {
    HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(mInput->OwnerDoc(),
                                                             lastUsedDir);
  }

  // The text control frame (if there is one) isn't going to send a change
  // event because it will think this is done by a script.
  // So, we can safely send one by ourself.
  mInput->SetFilesOrDirectories(newFilesOrDirectories, true);

  // mInput(HTMLInputElement) has no scriptGlobalObject, don't create
  // DispatchChangeEventCallback
  if (!mInput->GetOwnerGlobal()) {
    return NS_OK;
  }
  RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
      new DispatchChangeEventCallback(mInput);

  if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
      mInput->HasAttr(nsGkAtoms::webkitdirectory)) {
    ErrorResult error;
    GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
    if (NS_WARN_IF(error.Failed())) {
      return error.StealNSResult();
    }

    helper->AddCallback(dispatchChangeEventCallback);
    return NS_OK;
  }

  return dispatchChangeEventCallback->DispatchEvents();
}

NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
                  nsIFilePickerShownCallback)

class nsColorPickerShownCallback final : public nsIColorPickerShownCallback {
  ~nsColorPickerShownCallback() = default;

 public:
  nsColorPickerShownCallback(HTMLInputElement* aInput,
                             nsIColorPicker* aColorPicker)
      : mInput(aInput), mColorPicker(aColorPicker), mValueChanged(false) {}

  NS_DECL_ISUPPORTS

  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  NS_IMETHOD Update(const nsAString& aColor) override;
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  NS_IMETHOD Done(const nsAString& aColor) override;

 private:
  /**
   * Updates the internals of the object using aColor as the new value.
   * If aTrustedUpdate is true, it will consider that aColor is a new value.
   * Otherwise, it will check that aColor is different from the current value.
   */

  MOZ_CAN_RUN_SCRIPT
  nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate);

  RefPtr<HTMLInputElement> mInput;
  nsCOMPtr<nsIColorPicker> mColorPicker;
  bool mValueChanged;
};

nsresult nsColorPickerShownCallback::UpdateInternal(const nsAString& aColor,
                                                    bool aTrustedUpdate) {
  bool valueChanged = false;
  nsAutoString oldValue;
  if (aTrustedUpdate) {
    mInput->OwnerDoc()->NotifyUserGestureActivation();
    valueChanged = true;
  } else {
    mInput->GetValue(oldValue, CallerType::System);
  }

  mInput->SetValue(aColor, CallerType::System, IgnoreErrors());

  if (!aTrustedUpdate) {
    nsAutoString newValue;
    mInput->GetValue(newValue, CallerType::System);
    if (!oldValue.Equals(newValue)) {
      valueChanged = true;
    }
  }

  if (!valueChanged) {
    return NS_OK;
  }

  mValueChanged = true;
  RefPtr<HTMLInputElement> input(mInput);
  DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(input);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                       "Failed to dispatch input event");
  return NS_OK;
}

NS_IMETHODIMP
nsColorPickerShownCallback::Update(const nsAString& aColor) {
  return UpdateInternal(aColor, true);
}

NS_IMETHODIMP
nsColorPickerShownCallback::Done(const nsAString& aColor) {
  /**
   * When Done() is called, we might be at the end of a serie of Update() calls
   * in which case mValueChanged is set to true and a change event will have to
   * be fired but we might also be in a one shot Done() call situation in which
   * case we should fire a change event iif the value actually changed.
   * UpdateInternal(bool) is taking care of that logic for us.
   */

  nsresult rv = NS_OK;

  mInput->PickerClosed();

  if (!aColor.IsEmpty()) {
    UpdateInternal(aColor, false);
  }

  if (mValueChanged) {
    mInput->SetUserInteracted(true);
    rv = nsContentUtils::DispatchTrustedEvent(
        mInput->OwnerDoc(), static_cast<Element*>(mInput.get()), u"change"_ns,
        CanBubble::eYes, Cancelable::eNo);
  }

  return rv;
}

NS_IMPL_ISUPPORTS(nsColorPickerShownCallback, nsIColorPickerShownCallback)

static bool IsPickerBlocked(Document* aDoc) {
  if (aDoc->ConsumeTransientUserGestureActivation()) {
    return false;
  }

  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
                                  nsContentUtils::eDOM_PROPERTIES,
                                  "InputPickerBlockedNoUserActivation");
  return true;
}

nsTArray<nsString> HTMLInputElement::GetColorsFromList() {
  RefPtr<HTMLDataListElement> dataList = GetList();
  if (!dataList) {
    return {};
  }

  nsTArray<nsString> colors;

  RefPtr<nsContentList> options = dataList->Options();
  uint32_t length = options->Length(true);
  for (uint32_t i = 0; i < length; ++i) {
    auto* option = HTMLOptionElement::FromNodeOrNull(options->Item(i, false));
    if (!option) {
      continue;
    }

    nsString value;
    option->GetValue(value);
    if (IsValidSimpleColor(value)) {
      ToLowerCase(value);
      colors.AppendElement(value);
    }
  }

  return colors;
}

nsresult HTMLInputElement::InitColorPicker() {
  MOZ_ASSERT(IsMutable());

  if (mPickerRunning) {
    NS_WARNING("Just one nsIColorPicker is allowed");
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<Document> doc = OwnerDoc();

  RefPtr<BrowsingContext> bc = doc->GetBrowsingContext();
  if (!bc) {
    return NS_ERROR_FAILURE;
  }

  if (IsPickerBlocked(doc)) {
    return NS_OK;
  }

  // Get Loc title
  nsAutoString title;
  nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                     "ColorPicker", title);

  nsCOMPtr<nsIColorPicker> colorPicker =
      do_CreateInstance("@mozilla.org/colorpicker;1");
  if (!colorPicker) {
    return NS_ERROR_FAILURE;
  }

  nsAutoString initialValue;
  GetNonFileValueInternal(initialValue);
  nsTArray<nsString> colors = GetColorsFromList();
  nsresult rv = colorPicker->Init(bc, title, initialValue, colors);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIColorPickerShownCallback> callback =
      new nsColorPickerShownCallback(this, colorPicker);

  rv = colorPicker->Open(callback);
  if (NS_SUCCEEDED(rv)) {
    mPickerRunning = true;
    SetStates(ElementState::OPEN, true);
  }

  return rv;
}

nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
  MOZ_ASSERT(IsMutable());

  if (mPickerRunning) {
    NS_WARNING("Just one nsIFilePicker is allowed");
    return NS_ERROR_FAILURE;
  }

  // Get parent nsPIDOMWindow object.
  nsCOMPtr<Document> doc = OwnerDoc();

  RefPtr<BrowsingContext> bc = doc->GetBrowsingContext();
  if (!bc) {
    return NS_ERROR_FAILURE;
  }

  if (IsPickerBlocked(doc)) {
    return NS_OK;
  }

  // Get Loc title
  nsAutoString title;
  nsAutoString okButtonLabel;
  if (aType == FILE_PICKER_DIRECTORY) {
    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                            "DirectoryUpload", doc, title);

    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                            "DirectoryPickerOkButtonLabel", doc,
                                            okButtonLabel);
  } else {
    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                            "FileUpload", doc, title);
  }

  nsCOMPtr<nsIFilePicker> filePicker =
      do_CreateInstance("@mozilla.org/filepicker;1");
  if (!filePicker) return NS_ERROR_FAILURE;

  nsIFilePicker::Mode mode;

  if (aType == FILE_PICKER_DIRECTORY) {
    mode = nsIFilePicker::modeGetFolder;
  } else if (HasAttr(nsGkAtoms::multiple)) {
    mode = nsIFilePicker::modeOpenMultiple;
  } else {
    mode = nsIFilePicker::modeOpen;
  }

  nsresult rv = filePicker->Init(bc, title, mode);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!okButtonLabel.IsEmpty()) {
    filePicker->SetOkButtonLabel(okButtonLabel);
  }

  // Native directory pickers ignore file type filters, so we don't spend
  // cycles adding them for FILE_PICKER_DIRECTORY.
  if (HasAttr(nsGkAtoms::accept) && aType != FILE_PICKER_DIRECTORY) {
    SetFilePickerFiltersFromAccept(filePicker);

    if (StaticPrefs::dom_capture_enabled()) {
      if (const nsAttrValue* captureVal = GetParsedAttr(nsGkAtoms::capture)) {
        filePicker->SetCapture(static_cast<nsIFilePicker::CaptureTarget>(
            captureVal->GetEnumValue()));
      }
    }
  } else {
    filePicker->AppendFilters(nsIFilePicker::filterAll);
  }

  // Set default directory and filename
  nsAutoString defaultName;

  const nsTArray<OwningFileOrDirectory>& oldFiles =
      GetFilesOrDirectoriesInternal();

  nsCOMPtr<nsIFilePickerShownCallback> callback =
      new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);

  if (!oldFiles.IsEmpty() && aType != FILE_PICKER_DIRECTORY) {
    nsAutoString path;

    nsCOMPtr<nsIFile> parentFile = LastUsedDirectory(oldFiles[0]);
    if (parentFile) {
      filePicker->SetDisplayDirectory(parentFile);
    }

    // Unfortunately nsIFilePicker doesn't allow multiple files to be
    // default-selected, so only select something by default if exactly
    // one file was selected before.
    if (oldFiles.Length() == 1) {
      nsAutoString leafName;
      GetDOMFileOrDirectoryName(oldFiles[0], leafName);

      if (!leafName.IsEmpty()) {
        filePicker->SetDefaultString(leafName);
      }
    }

    rv = filePicker->Open(callback);
    if (NS_SUCCEEDED(rv)) {
      mPickerRunning = true;
      SetStates(ElementState::OPEN, true);
    }

    return rv;
  }

  HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(
      doc, filePicker, callback);
  mPickerRunning = true;
  SetStates(ElementState::OPEN, true);
  return NS_OK;
}

#define CPS_PREF_NAME u"browser.upload.lastDir"_ns

NS_IMPL_ISUPPORTS(UploadLastDir, nsIObserver, nsISupportsWeakReference)

void HTMLInputElement::InitUploadLastDir() {
  gUploadLastDir = new UploadLastDir();
  NS_ADDREF(gUploadLastDir);

  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  if (observerService && gUploadLastDir) {
    observerService->AddObserver(gUploadLastDir,
                                 "browser:purge-session-history"true);
  }
}

void HTMLInputElement::DestroyUploadLastDir() { NS_IF_RELEASE(gUploadLastDir); }

nsresult UploadLastDir::FetchDirectoryAndDisplayPicker(
    Document* aDoc, nsIFilePicker* aFilePicker,
    nsIFilePickerShownCallback* aFpCallback) {
  MOZ_ASSERT(aDoc, "aDoc is null");
  MOZ_ASSERT(aFilePicker, "aFilePicker is null");
  MOZ_ASSERT(aFpCallback, "aFpCallback is null");

  nsIURI* docURI = aDoc->GetDocumentURI();
  MOZ_ASSERT(docURI, "docURI is null");

  nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
  nsCOMPtr<nsIContentPrefCallback2> prefCallback =
      new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);

  // Attempt to get the CPS, if it's not present we'll fallback to use the
  // Desktop folder
  nsCOMPtr<nsIContentPrefService2> contentPrefService =
      do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
  if (!contentPrefService) {
    prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
    return NS_OK;
  }

  nsAutoCString cstrSpec;
  docURI->GetSpec(cstrSpec);
  NS_ConvertUTF8toUTF16 spec(cstrSpec);

  contentPrefService->GetByDomainAndName(spec, CPS_PREF_NAME, loadContext,
                                         prefCallback);
  return NS_OK;
}

nsresult UploadLastDir::StoreLastUsedDirectory(Document* aDoc, nsIFile* aDir) {
  MOZ_ASSERT(aDoc, "aDoc is null");
  if (!aDir) {
    return NS_OK;
  }

  nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
  MOZ_ASSERT(docURI, "docURI is null");

  // Attempt to get the CPS, if it's not present we'll just return
  nsCOMPtr<nsIContentPrefService2> contentPrefService =
      do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
  if (!contentPrefService) return NS_ERROR_NOT_AVAILABLE;

  nsAutoCString cstrSpec;
  docURI->GetSpec(cstrSpec);
  NS_ConvertUTF8toUTF16 spec(cstrSpec);

  // Find the parent of aFile, and store it
  nsString unicodePath;
  aDir->GetPath(unicodePath);
  if (unicodePath.IsEmpty())  // nothing to do
    return NS_OK;
  RefPtr<nsVariantCC> prefValue = new nsVariantCC();
  prefValue->SetAsAString(unicodePath);

  // Use the document's current load context to ensure that the content pref
  // service doesn't persistently store this directory for this domain if the
  // user is using private browsing:
  nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
  return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext,
                                 nullptr);
}

NS_IMETHODIMP
UploadLastDir::Observe(nsISupports* aSubject, char const* aTopic,
                       char16_t const* aData) {
  if (strcmp(aTopic, "browser:purge-session-history") == 0) {
    nsCOMPtr<nsIContentPrefService2> contentPrefService =
        do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
    if (contentPrefService)
      contentPrefService->RemoveByName(CPS_PREF_NAME, nullptr, nullptr);
  }
  return NS_OK;
}

#ifdef ACCESSIBILITY
// Helper method
static nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
                                          EventMessage aEventMessage);
#endif

//
// construction, destruction
//

HTMLInputElement::HTMLInputElement(already_AddRefed<dom::NodeInfo>&& aNodeInfo,
                                   FromParser aFromParser, FromClone aFromClone)
    : TextControlElement(std::move(aNodeInfo), aFromParser,
                         FormControlType(kInputDefaultType->value)),
      mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
      mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
      mDisabledChanged(false),
      mValueChanged(false),
      mUserInteracted(false),
      mLastValueChangeWasInteractive(false),
      mCheckedChanged(false),
      mChecked(false),
      mShouldInitChecked(false),
      mDoneCreating(aFromParser == NOT_FROM_PARSER &&
                    aFromClone == FromClone::No),
      mInInternalActivate(false),
      mCheckedIsToggled(false),
      mIndeterminate(false),
      mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT),
      mHasRange(false),
      mIsDraggingRange(false),
      mNumberControlSpinnerIsSpinning(false),
      mNumberControlSpinnerSpinsUp(false),
      mPickerRunning(false),
      mIsPreviewEnabled(false),
      mHasBeenTypePassword(false),
      mHasPatternAttribute(false),
      mRadioGroupContainer(nullptr) {
  // If size is above 512, mozjemalloc allocates 1kB, see
  // memory/build/mozjemalloc.cpp
  static_assert(sizeof(HTMLInputElement) <= 512,
                "Keep the size of HTMLInputElement under 512 to avoid "
                "performance regression!");

  // We are in a type=text but we create TextControlState lazily.
  mInputData.mState = nullptr;

  void* memory = mInputTypeMem;
  mInputType = InputType::Create(this, mType, memory);

  if (!gUploadLastDir) HTMLInputElement::InitUploadLastDir();

  // Set up our default state.  By default we're enabled (since we're a control
  // type that can be disabled but not actually disabled right now), optional,
  // read-write, and valid. Also by default we don't have to show validity UI
  // and so forth.
  AddStatesSilently(ElementState::ENABLED | ElementState::OPTIONAL_ |
                    ElementState::VALID | ElementState::VALUE_EMPTY |
                    ElementState::READWRITE);
  RemoveStatesSilently(ElementState::READONLY);
  UpdateApzAwareFlag();
}

HTMLInputElement::~HTMLInputElement() {
  if (mNumberControlSpinnerIsSpinning) {
    StopNumberControlSpinnerSpin(eDisallowDispatchingEvents);
  }
  nsImageLoadingContent::Destroy();
  FreeData();
}

void HTMLInputElement::FreeData() {
  if (!IsSingleLineTextControl(false)) {
    free(mInputData.mValue);
    mInputData.mValue = nullptr;
  } else if (mInputData.mState) {
    // XXX Passing nullptr to UnbindFromFrame doesn't do anything!
    UnbindFromFrame(nullptr);
    mInputData.mState->Destroy();
    mInputData.mState = nullptr;
  }

  if (mInputType) {
    mInputType->DropReference();
    mInputType = nullptr;
  }
}

void HTMLInputElement::EnsureEditorState() {
  MOZ_ASSERT(IsSingleLineTextControl(false));
  if (!mInputData.mState) {
    mInputData.mState = TextControlState::Construct(this);
  }
}

TextControlState* HTMLInputElement::GetEditorState() const {
  if (!IsSingleLineTextControl(false)) {
    return nullptr;
  }

  // We've postponed allocating TextControlState, doing that in a const
  // method is fine.
  const_cast<HTMLInputElement*>(this)->EnsureEditorState();

  MOZ_ASSERT(mInputData.mState,
             "Single line text controls need to have a state"
             " associated with them");

  return mInputData.mState;
}

// nsISupports

NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLInputElement)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
                                                  TextControlElement)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
  if (tmp->IsSingleLineTextControl(false) && tmp->mInputData.mState) {
    tmp->mInputData.mState->Traverse(cb);
  }

  if (tmp->mFileData) {
    tmp->mFileData->Traverse(cb);
  }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
                                                TextControlElement)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
  if (tmp->IsSingleLineTextControl(false) && tmp->mInputData.mState) {
    tmp->mInputData.mState->Unlink();
  }

  if (tmp->mFileData) {
    tmp->mFileData->Unlink();
  }
  // XXX should unlink more?
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLInputElement,
                                             TextControlElement,
                                             imgINotificationObserver,
                                             nsIImageLoadingContent,
                                             nsIConstraintValidation)

// nsINode

nsresult HTMLInputElement::Clone(dom::NodeInfo* aNodeInfo,
                                 nsINode** aResult) const {
  *aResult = nullptr;

  RefPtr<HTMLInputElement> it = new (aNodeInfo->NodeInfoManager())
      HTMLInputElement(do_AddRef(aNodeInfo), NOT_FROM_PARSER, FromClone::Yes);

  nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it);
  NS_ENSURE_SUCCESS(rv, rv);

  switch (GetValueMode()) {
    case VALUE_MODE_VALUE:
      if (mValueChanged) {
        // We don't have our default value anymore.  Set our value on
        // the clone.
        nsAutoString value;
        GetNonFileValueInternal(value);
        // SetValueInternal handles setting the VALUE_CHANGED bit for us
        if (NS_WARN_IF(
                NS_FAILED(rv = it->SetValueInternal(
                              value, {ValueSetterOption::SetValueChanged})))) {
          return rv;
        }
      }
      break;
    case VALUE_MODE_FILENAME:
      if (it->OwnerDoc()->IsStaticDocument()) {
        // We're going to be used in print preview.  Since the doc is static
        // we can just grab the pretty string and use it as wallpaper
        GetDisplayFileName(it->mFileData->mStaticDocFileList);
      } else {
        it->mFileData->ClearGetFilesHelpers();
        it->mFileData->mFilesOrDirectories.Clear();
        it->mFileData->mFilesOrDirectories.AppendElements(
            mFileData->mFilesOrDirectories);
      }
      break;
    case VALUE_MODE_DEFAULT_ON:
    case VALUE_MODE_DEFAULT:
      break;
  }

  if (mCheckedChanged) {
    // We no longer have our original checked state.  Set our
    // checked state on the clone.
    it->DoSetChecked(mChecked, /* aNotify */ false,
                     /* aSetValueChanged */ true);
    // Then tell DoneCreatingElement() not to overwrite:
    it->mShouldInitChecked = false;
  }

  it->mIndeterminate = mIndeterminate;

  it->DoneCreatingElement();

  it->SetLastValueChangeWasInteractive(mLastValueChangeWasInteractive);
  it.forget(aResult);
  return NS_OK;
}

void HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
                                     const nsAttrValue* aValue, bool aNotify) {
  if (aNameSpaceID == kNameSpaceID_None) {
    if (aNotify && aName == nsGkAtoms::disabled) {
      mDisabledChanged = true;
    }

    // When name or type changes, radio should be removed from radio group.
    // If we are not done creating the radio, we also should not do it.
    if (mType == FormControlType::InputRadio) {
      if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
          (mForm || mDoneCreating)) {
        RemoveFromRadioGroup();
      } else if (aName == nsGkAtoms::required) {
        auto* container = GetCurrentRadioGroupContainer();

        if (container && ((aValue && !HasAttr(aNameSpaceID, aName)) ||
                          (!aValue && HasAttr(aNameSpaceID, aName)))) {
          nsAutoString name;
          GetAttr(nsGkAtoms::name, name);
          container->RadioRequiredWillChange(name, !!aValue);
        }
      }
    }

    if (aName == nsGkAtoms::webkitdirectory) {
      Telemetry::Accumulate(Telemetry::WEBKIT_DIRECTORY_USED, true);
    }
  }

  return nsGenericHTMLFormControlElementWithState::BeforeSetAttr(
      aNameSpaceID, aName, aValue, aNotify);
}

void HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
                                    const nsAttrValue* aValue,
                                    const nsAttrValue* aOldValue,
                                    nsIPrincipal* aSubjectPrincipal,
                                    bool aNotify) {
  if (aNameSpaceID == kNameSpaceID_None) {
    bool needValidityUpdate = false;
    if (aName == nsGkAtoms::src) {
      mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
          this, aValue ? aValue->GetStringValue() : EmptyString(),
          aSubjectPrincipal);
      if (aNotify && mType == FormControlType::InputImage) {
        if (aValue) {
          // Mark channel as urgent-start before load image if the image load is
          // initiated by a user interaction.
          mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();

          LoadImage(aValue->GetStringValue(), true, aNotify,
                    eImageLoadType_Normal, mSrcTriggeringPrincipal);
        } else {
          // Null value means the attr got unset; drop the image
          CancelImageRequests(aNotify);
        }
      }
    }

    if (aName == nsGkAtoms::value) {
      // If the element has a value in value mode, the value content attribute
      // is the default value. So if the elements value didn't change from the
      // default, we have to re-set it.
      if (!mValueChanged && GetValueMode() == VALUE_MODE_VALUE) {
        SetDefaultValueAsValue();
      } else if (GetValueMode() == VALUE_MODE_DEFAULT) {
        ResetDirFormAssociatedElement(this, aNotify, HasDirAuto());
      }
      // GetStepBase() depends on the `value` attribute if `min` is not present,
      // even if the value doesn't change.
      UpdateStepMismatchValidityState();
      needValidityUpdate = true;
    }

    // Checked must be set no matter what type of control it is, since
    // mChecked must reflect the new value
    if (aName == nsGkAtoms::checked) {
      if (IsRadioOrCheckbox()) {
        SetStates(ElementState::DEFAULT, !!aValue, aNotify);
      }
      if (!mCheckedChanged) {
        // Delay setting checked if we are creating this element (wait
        // until everything is set)
        if (!mDoneCreating) {
          mShouldInitChecked = true;
        } else {
          DoSetChecked(!!aValue, aNotify, /* aSetValueChanged */ false);
        }
      }
      needValidityUpdate = true;
    }

    if (aName == nsGkAtoms::type) {
      FormControlType newType;
      if (!aValue) {
        // We're now a text input.
        newType = FormControlType(kInputDefaultType->value);
      } else {
        newType = FormControlType(aValue->GetEnumValue());
      }
      if (newType != mType) {
        HandleTypeChange(newType, aNotify);
        needValidityUpdate = true;
      }
    }

    // When name or type changes, radio should be added to radio group.
    // If we are not done creating the radio, we also should not do it.
    if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
        mType == FormControlType::InputRadio && (mForm || mDoneCreating)) {
      AddToRadioGroup();
      UpdateValueMissingValidityStateForRadio(false);
      needValidityUpdate = true;
    }

    if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
        aName == nsGkAtoms::readonly) {
      if (aName == nsGkAtoms::disabled) {
        // This *has* to be called *before* validity state check because
        // UpdateBarredFromConstraintValidation and
        // UpdateValueMissingValidityState depend on our disabled state.
        UpdateDisabledState(aNotify);
      }

      if (aName == nsGkAtoms::required && DoesRequiredApply()) {
        // This *has* to be called *before* UpdateValueMissingValidityState
        // because UpdateValueMissingValidityState depends on our required
        // state.
        UpdateRequiredState(!!aValue, aNotify);
      }

      if (aName == nsGkAtoms::readonly && !!aValue != !!aOldValue) {
        UpdateReadOnlyState(aNotify);
      }

      UpdateValueMissingValidityState();

      // This *has* to be called *after* validity has changed.
      if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
        UpdateBarredFromConstraintValidation();
      }
      needValidityUpdate = true;
    } else if (aName == nsGkAtoms::maxlength) {
      UpdateTooLongValidityState();
      needValidityUpdate = true;
    } else if (aName == nsGkAtoms::minlength) {
      UpdateTooShortValidityState();
      needValidityUpdate = true;
    } else if (aName == nsGkAtoms::pattern) {
      // Although pattern attribute only applies to single line text controls,
      // we set this flag for all input types to save having to check the type
      // here.
      mHasPatternAttribute = !!aValue;

      if (mDoneCreating) {
        UpdatePatternMismatchValidityState();
      }
      needValidityUpdate = true;
    } else if (aName == nsGkAtoms::multiple) {
      UpdateTypeMismatchValidityState();
      needValidityUpdate = true;
    } else if (aName == nsGkAtoms::max) {
      UpdateHasRange(aNotify);
      mInputType->MinMaxStepAttrChanged();
      // Validity state must be updated *after* the UpdateValueDueToAttrChange
      // call above or else the following assert will not be valid.
      // We don't assert the state of underflow during creation since
      // DoneCreatingElement sanitizes.
      UpdateRangeOverflowValidityState();
      needValidityUpdate = true;
      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                 "HTML5 spec does not allow underflow for type=range");
    } else if (aName == nsGkAtoms::min) {
      UpdateHasRange(aNotify);
      mInputType->MinMaxStepAttrChanged();
      // See corresponding @max comment
      UpdateRangeUnderflowValidityState();
      UpdateStepMismatchValidityState();
      needValidityUpdate = true;
      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                 "HTML5 spec does not allow underflow for type=range");
    } else if (aName == nsGkAtoms::step) {
      mInputType->MinMaxStepAttrChanged();
      // See corresponding @max comment
      UpdateStepMismatchValidityState();
      needValidityUpdate = true;
      MOZ_ASSERT(!mDoneCreating || mType != FormControlType::InputRange ||
                     !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                 "HTML5 spec does not allow underflow for type=range");
    } else if (aName == nsGkAtoms::dir && aValue &&
               aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
      ResetDirFormAssociatedElement(this, aNotify, true);
    } else if (aName == nsGkAtoms::lang) {
      // FIXME(emilio, bug 1651070): This doesn't account for lang changes on
      // ancestors.
      if (mType == FormControlType::InputNumber) {
        // The validity of our value may have changed based on the locale.
        UpdateValidityState();
        needValidityUpdate = true;
      }
    } else if (aName == nsGkAtoms::autocomplete) {
      // Clear the cached @autocomplete attribute and autocompleteInfo state.
      mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
      mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
    } else if (aName == nsGkAtoms::placeholder) {
      // Full addition / removals of the attribute reconstruct right now.
      if (nsTextControlFrame* f = do_QueryFrame(GetPrimaryFrame())) {
        f->PlaceholderChanged(aOldValue, aValue);
      }
      UpdatePlaceholderShownState();
      needValidityUpdate = true;
    }

    if (CreatesDateTimeWidget()) {
      if (aName == nsGkAtoms::value || aName == nsGkAtoms::readonly ||
          aName == nsGkAtoms::tabindex || aName == nsGkAtoms::required ||
          aName == nsGkAtoms::disabled) {
        // If original target is this and not the inner text control, we should
        // pass the focus to the inner text control.
        if (Element* dateTimeBoxElement = GetDateTimeBoxElement()) {
          AsyncEventDispatcher::RunDOMEventWhenSafe(
              *dateTimeBoxElement,
              aName == nsGkAtoms::value ? u"MozDateTimeValueChanged"_ns
                                        : u"MozDateTimeAttributeChanged"_ns,
              CanBubble::eNo, ChromeOnlyDispatch::eNo);
        }
      }
    }
    if (needValidityUpdate) {
      UpdateValidityElementStates(aNotify);
    }
  }

  return nsGenericHTMLFormControlElementWithState::AfterSetAttr(
      aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
}

void HTMLInputElement::BeforeSetForm(HTMLFormElement* aForm, bool aBindToTree) {
  // No need to remove from radio group if we are just binding to tree.
  if (mType == FormControlType::InputRadio && !aBindToTree) {
    RemoveFromRadioGroup();
  }

  // Dispatch event when <input> @form is set
  if (!aBindToTree) {
    MaybeDispatchLoginManagerEvents(aForm);
  }
}

void HTMLInputElement::AfterClearForm(bool aUnbindOrDelete) {
  MOZ_ASSERT(!mForm);

  // Do not add back to radio group if we are releasing or unbinding from tree.
  if (mType == FormControlType::InputRadio && !aUnbindOrDelete &&
      !GetCurrentRadioGroupContainer()) {
    AddToRadioGroup();
    UpdateValueMissingValidityStateForRadio(false);
  }
}

void HTMLInputElement::ResultForDialogSubmit(nsAString& aResult) {
  if (mType == FormControlType::InputImage) {
    // Get a property set by the frame to find out where it was clicked.
    const auto* lastClickedPoint =
        static_cast<CSSIntPoint*>(GetProperty(nsGkAtoms::imageClickedPoint));
    int32_t x, y;
    if (lastClickedPoint) {
      x = lastClickedPoint->x;
      y = lastClickedPoint->y;
    } else {
      x = y = 0;
    }
    aResult.AppendInt(x);
    aResult.AppendLiteral(",");
    aResult.AppendInt(y);
  } else {
    GetAttr(nsGkAtoms::value, aResult);
  }
}

void HTMLInputElement::GetAutocomplete(nsAString& aValue) {
  if (!DoesAutocompleteApply()) {
    return;
  }

  aValue.Truncate();
  const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);

  mAutocompleteAttrState = nsContentUtils::SerializeAutocompleteAttribute(
      attributeVal, aValue, mAutocompleteAttrState);
}

void HTMLInputElement::GetAutocompleteInfo(Nullable<AutocompleteInfo>& aInfo) {
  if (!DoesAutocompleteApply()) {
    aInfo.SetNull();
    return;
  }

  const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
  mAutocompleteInfoState = nsContentUtils::SerializeAutocompleteAttribute(
      attributeVal, aInfo.SetValue(), mAutocompleteInfoState, true);
}

void HTMLInputElement::GetCapture(nsAString& aValue) {
  GetEnumAttr(nsGkAtoms::capture, kCaptureDefault->tag, aValue);
}

void HTMLInputElement::GetFormEnctype(nsAString& aValue) {
  GetEnumAttr(nsGkAtoms::formenctype, "", kFormDefaultEnctype->tag, aValue);
}

void HTMLInputElement::GetFormMethod(nsAString& aValue) {
  GetEnumAttr(nsGkAtoms::formmethod, "", kFormDefaultMethod->tag, aValue);
}

void HTMLInputElement::GetType(nsAString& aValue) const {
  GetEnumAttr(nsGkAtoms::type, kInputDefaultType->tag, aValue);
}

int32_t HTMLInputElement::TabIndexDefault() { return 0; }

uint32_t HTMLInputElement::Height() {
  if (mType != FormControlType::InputImage) {
    return 0;
  }
  return GetWidthHeightForImage().height;
}

void HTMLInputElement::SetIndeterminateInternal(bool aValue,
                                                bool aShouldInvalidate) {
  mIndeterminate = aValue;
  if (mType != FormControlType::InputCheckbox) {
    return;
  }

  SetStates(ElementState::INDETERMINATE, aValue);

  if (aShouldInvalidate) {
    // Repaint the frame
    if (nsIFrame* frame = GetPrimaryFrame()) {
      frame->InvalidateFrameSubtree();
    }
  }
}

void HTMLInputElement::SetIndeterminate(bool aValue) {
  SetIndeterminateInternal(aValue, true);
}

uint32_t HTMLInputElement::Width() {
  if (mType != FormControlType::InputImage) {
    return 0;
  }
  return GetWidthHeightForImage().width;
}

bool HTMLInputElement::SanitizesOnValueGetter() const {
  // Don't return non-sanitized value for datetime types, email, or number.
  return mType == FormControlType::InputEmail ||
         mType == FormControlType::InputNumber || IsDateTimeInputType(mType);
}

void HTMLInputElement::GetValue(nsAString& aValue, CallerType aCallerType) {
  GetValueInternal(aValue, aCallerType);

  // In the case where we need to sanitize an input value without affecting
  // the displayed user's input, we instead sanitize only on .value accesses.
  // For the more general case of input elements displaying text that isn't
  // their current value, see bug 805049.
  if (SanitizesOnValueGetter()) {
    SanitizeValue(aValue, SanitizationKind::ForValueGetter);
  }
}

void HTMLInputElement::GetValueInternal(nsAString& aValue,
                                        CallerType aCallerType) const {
  if (mType != FormControlType::InputFile) {
    GetNonFileValueInternal(aValue);
    return;
  }

  if (aCallerType == CallerType::System) {
    aValue.Assign(mFileData->mFirstFilePath);
    return;
  }

  if (mFileData->mFilesOrDirectories.IsEmpty()) {
    aValue.Truncate();
    return;
  }

  nsAutoString file;
  GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], file);
  if (file.IsEmpty()) {
    aValue.Truncate();
    return;
  }

  aValue.AssignLiteral("C:\\fakepath\\");
  aValue.Append(file);
}

void HTMLInputElement::GetNonFileValueInternal(nsAString& aValue) const {
  switch (GetValueMode()) {
    case VALUE_MODE_VALUE:
      if (IsSingleLineTextControl(false)) {
        if (mInputData.mState) {
          mInputData.mState->GetValue(aValue, true/* aForDisplay = */ false);
        } else {
          // Value hasn't been set yet.
          aValue.Truncate();
        }
      } else if (!aValue.Assign(mInputData.mValue, fallible)) {
        aValue.Truncate();
      }
      return;

    case VALUE_MODE_FILENAME:
      MOZ_ASSERT_UNREACHABLE("Someone screwed up here");
      // We'll just return empty string if someone does screw up.
      aValue.Truncate();
      return;

    case VALUE_MODE_DEFAULT:
      // Treat defaultValue as value.
      GetAttr(nsGkAtoms::value, aValue);
      return;

    case VALUE_MODE_DEFAULT_ON:
      // Treat default value as value and returns "on" if no value.
      if (!GetAttr(nsGkAtoms::value, aValue)) {
        aValue.AssignLiteral("on");
      }
      return;
  }
}

void HTMLInputElement::ClearFiles(bool aSetValueChanged) {
  nsTArray<OwningFileOrDirectory> data;
  SetFilesOrDirectories(data, aSetValueChanged);
}

int32_t HTMLInputElement::MonthsSinceJan1970(uint32_t aYear,
                                             uint32_t aMonth) const {
  return (aYear - 1970) * 12 + aMonth - 1;
}

/* static */
Decimal HTMLInputElement::StringToDecimal(const nsAString& aValue) {
  if (!IsAscii(aValue)) {
    return Decimal::nan();
  }
  NS_LossyConvertUTF16toASCII asciiString(aValue);
  std::string stdString(asciiString.get(), asciiString.Length());
  auto decimal = Decimal::fromString(stdString);
  if (!decimal.isFinite()) {
    return Decimal::nan();
  }
  // Numbers are considered finite IEEE 754 Double-precision floating point
  // values, but decimal supports a bigger range.
  static const Decimal maxDouble =
      Decimal::fromDouble(std::numeric_limits<double>::max());
  if (decimal < -maxDouble || decimal > maxDouble) {
    return Decimal::nan();
  }
  return decimal;
}

Decimal HTMLInputElement::GetValueAsDecimal() const {
  nsAutoString stringValue;
  GetNonFileValueInternal(stringValue);
  Decimal result = mInputType->ConvertStringToNumber(stringValue).mResult;
  return result.isFinite() ? result : Decimal::nan();
}

void HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
                                ErrorResult& aRv) {
  // check security.  Note that setting the value to the empty string is always
  // OK and gives pages a way to clear a file input if necessary.
  if (mType == FormControlType::InputFile) {
    if (!aValue.IsEmpty()) {
      if (aCallerType != CallerType::System) {
        // setting the value of a "FILE" input widget requires
        // chrome privilege
        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
        return;
      }
      Sequence<nsString> list;
      if (!list.AppendElement(aValue, fallible)) {
        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
        return;
      }

      MozSetFileNameArray(list, aRv);
      return;
    }
    ClearFiles(true);
  } else {
    if (MayFireChangeOnBlur()) {
      // If the value has been set by a script, we basically want to keep the
      // current change event state. If the element is ready to fire a change
      // event, we should keep it that way. Otherwise, we should make sure the
      // element will not fire any event because of the script interaction.
      //
      // NOTE: this is currently quite expensive work (too much string
      // manipulation). We should probably optimize that.
      nsAutoString currentValue;
      GetNonFileValueInternal(currentValue);

      nsresult rv = SetValueInternal(
          aValue, ¤tValue,
          {ValueSetterOption::ByContentAPI, ValueSetterOption::SetValueChanged,
           ValueSetterOption::MoveCursorToEndIfValueChanged});
      if (NS_FAILED(rv)) {
        aRv.Throw(rv);
        return;
      }

      if (mFocusedValue.Equals(currentValue)) {
        GetValue(mFocusedValue, aCallerType);
      }
    } else {
      nsresult rv = SetValueInternal(
          aValue,
          {ValueSetterOption::ByContentAPI, ValueSetterOption::SetValueChanged,
           ValueSetterOption::MoveCursorToEndIfValueChanged});
      if (NS_FAILED(rv)) {
        aRv.Throw(rv);
        return;
      }
    }
  }
}

HTMLDataListElement* HTMLInputElement::GetList() const {
  nsAutoString dataListId;
  GetAttr(nsGkAtoms::list_, dataListId);
  if (dataListId.IsEmpty()) {
    return nullptr;
  }

  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
  if (!docOrShadow) {
    return nullptr;
  }

  return HTMLDataListElement::FromNodeOrNull(
      docOrShadow->GetElementById(dataListId));
}

void HTMLInputElement::SetValue(Decimal aValue, CallerType aCallerType) {
  MOZ_ASSERT(!aValue.isInfinity(), "aValue must not be Infinity!");

  if (aValue.isNaN()) {
    SetValue(u""_ns, aCallerType, IgnoreErrors());
    return;
  }

  nsAutoString value;
  mInputType->ConvertNumberToString(aValue, value);
  SetValue(value, aCallerType, IgnoreErrors());
}

void HTMLInputElement::GetValueAsDate(JSContext* aCx,
                                      JS::MutableHandle<JSObject*> aObject,
                                      ErrorResult& aRv) {
  aObject.set(nullptr);
  if (!IsDateTimeInputType(mType)) {
    return;
  }

  Maybe<JS::ClippedTime> time;

  switch (mType) {
    case FormControlType::InputDate: {
      uint32_t year, month, day;
      nsAutoString value;
      GetNonFileValueInternal(value);
      if (!ParseDate(value, &year, &month, &day)) {
        return;
      }

      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day)));
      break;
    }
    case FormControlType::InputTime: {
      uint32_t millisecond;
      nsAutoString value;
      GetNonFileValueInternal(value);
      if (!ParseTime(value, &millisecond)) {
        return;
      }

      time.emplace(JS::TimeClip(millisecond));
      MOZ_ASSERT(time->toDouble() == millisecond,
                 "HTML times are restricted to the day after the epoch and "
                 "never clip");
      break;
    }
    case FormControlType::InputMonth: {
      uint32_t year, month;
      nsAutoString value;
      GetNonFileValueInternal(value);
      if (!ParseMonth(value, &year, &month)) {
        return;
      }

      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, 1)));
      break;
    }
    case FormControlType::InputWeek: {
      uint32_t year, week;
      nsAutoString value;
      GetNonFileValueInternal(value);
      if (!ParseWeek(value, &year, &week)) {
        return;
      }

      double days = DaysSinceEpochFromWeek(year, week);
      time.emplace(JS::TimeClip(days * kMsPerDay));

      break;
    }
    case FormControlType::InputDatetimeLocal: {
      uint32_t year, month, day, timeInMs;
      nsAutoString value;
      GetNonFileValueInternal(value);
      if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) {
        return;
      }

      time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day, timeInMs)));
      break;
    }
    default:
      break;
  }

  if (time) {
    aObject.set(JS::NewDateObject(aCx, *time));
    if (!aObject) {
      aRv.NoteJSContextException(aCx);
    }
    return;
  }

  MOZ_ASSERT(false"Unrecognized input type");
  aRv.Throw(NS_ERROR_UNEXPECTED);
}

void HTMLInputElement::SetValueAsDate(JSContext* aCx,
                                      JS::Handle<JSObject*> aObj,
                                      ErrorResult& aRv) {
  if (!IsDateTimeInputType(mType)) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  if (aObj) {
    bool isDate;
    if (!JS::ObjectIsDate(aCx, aObj, &isDate)) {
      aRv.NoteJSContextException(aCx);
      return;
    }
    if (!isDate) {
      aRv.ThrowTypeError("Value being assigned is not a date.");
      return;
    }
  }

  double milliseconds;
  if (aObj) {
    if (!js::DateGetMsecSinceEpoch(aCx, aObj, &milliseconds)) {
      aRv.NoteJSContextException(aCx);
      return;
    }
  } else {
    milliseconds = UnspecifiedNaN<double>();
  }

  // At this point we know we're not a file input, so we can just pass "not
  // system" as the caller type, since the caller type only matters in the file
  // input case.
  if (std::isnan(milliseconds)) {
    SetValue(u""_ns, CallerType::NonSystem, aRv);
    return;
  }

  if (mType != FormControlType::InputMonth) {
    SetValue(Decimal::fromDouble(milliseconds), CallerType::NonSystem);
    return;
  }

  // type=month expects the value to be number of months.
  double year = JS::YearFromTime(milliseconds);
  double month = JS::MonthFromTime(milliseconds);

  if (std::isnan(year) || std::isnan(month)) {
    SetValue(u""_ns, CallerType::NonSystem, aRv);
    return;
  }

  int32_t months = MonthsSinceJan1970(year, month + 1);
  SetValue(Decimal(int32_t(months)), CallerType::NonSystem);
}

void HTMLInputElement::SetValueAsNumber(double aValueAsNumber,
                                        ErrorResult& aRv) {
  // TODO: return TypeError when HTMLInputElement is converted to WebIDL, see
  // bug 825197.
  if (std::isinf(aValueAsNumber)) {
    aRv.Throw(NS_ERROR_INVALID_ARG);
    return;
  }

  if (!DoesValueAsNumberApply()) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  // At this point we know we're not a file input, so we can just pass "not
  // system" as the caller type, since the caller type only matters in the file
  // input case.
  SetValue(Decimal::fromDouble(aValueAsNumber), CallerType::NonSystem);
}

Decimal HTMLInputElement::GetMinimum() const {
  MOZ_ASSERT(
      DoesValueAsNumberApply(),
      "GetMinimum() should only be used for types that allow .valueAsNumber");

  // Only type=range has a default minimum
  Decimal defaultMinimum =
      mType == FormControlType::InputRange ? Decimal(0) : Decimal::nan();

  if (!HasAttr(nsGkAtoms::min)) {
    return defaultMinimum;
  }

  nsAutoString minStr;
  GetAttr(nsGkAtoms::min, minStr);

  Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
  return min.isFinite() ? min : defaultMinimum;
}

Decimal HTMLInputElement::GetMaximum() const {
  MOZ_ASSERT(
      DoesValueAsNumberApply(),
      "GetMaximum() should only be used for types that allow .valueAsNumber");

  // Only type=range has a default maximum
  Decimal defaultMaximum =
      mType == FormControlType::InputRange ? Decimal(100) : Decimal::nan();

  if (!HasAttr(nsGkAtoms::max)) {
    return defaultMaximum;
  }

  nsAutoString maxStr;
  GetAttr(nsGkAtoms::max, maxStr);

  Decimal max = mInputType->ConvertStringToNumber(maxStr).mResult;
  return max.isFinite() ? max : defaultMaximum;
}

Decimal HTMLInputElement::GetStepBase() const {
  MOZ_ASSERT(IsDateTimeInputType(mType) ||
                 mType == FormControlType::InputNumber ||
                 mType == FormControlType::InputRange,
             "Check that kDefaultStepBase is correct for this new type");
  // Do NOT use GetMinimum here - the spec says to use "the min content
  // attribute", not "the minimum".
  nsAutoString minStr;
  if (GetAttr(nsGkAtoms::min, minStr)) {
    Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
    if (min.isFinite()) {
      return min;
    }
  }

  // If @min is not a double, we should use @value.
  nsAutoString valueStr;
  if (GetAttr(nsGkAtoms::value, valueStr)) {
    Decimal value = mInputType->ConvertStringToNumber(valueStr).mResult;
    if (value.isFinite()) {
      return value;
    }
  }

  if (mType == FormControlType::InputWeek) {
    return kDefaultStepBaseWeek;
  }

  return kDefaultStepBase;
}

Decimal HTMLInputElement::GetValueIfStepped(int32_t aStep,
                                            StepCallerType aCallerType,
                                            ErrorResult& aRv) {
  constexpr auto kNaN = Decimal::nan();
  if (!DoStepDownStepUpApply()) {
    aRv.ThrowInvalidStateError("Step doesn't apply to this input type");
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.