/* -*- 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/. */
// 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;
staticconst 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'. staticconst nsAttrValue::EnumTable* kInputDefaultType =
&kInputTypeTable[std::size(kInputTypeTable) - 2];
// 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);
}
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;
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));
}
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;
}
// 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;
}
}
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();
}
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);
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 (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);
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;
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);
}
}
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;
}
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;
// 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);
}
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;
} elseif (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;
}
}
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;
}
// 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();
} elseif (aName == nsGkAtoms::required) { auto* container = GetCurrentRadioGroupContainer();
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();
} elseif (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);
}
// This *has* to be called *after* validity has changed. if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
UpdateBarredFromConstraintValidation();
}
needValidityUpdate = true;
} elseif (aName == nsGkAtoms::maxlength) {
UpdateTooLongValidityState();
needValidityUpdate = true;
} elseif (aName == nsGkAtoms::minlength) {
UpdateTooShortValidityState();
needValidityUpdate = true;
} elseif (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;
} elseif (aName == nsGkAtoms::multiple) {
UpdateTypeMismatchValidityState();
needValidityUpdate = true;
} elseif (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");
} elseif (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");
} elseif (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");
} elseif (aName == nsGkAtoms::dir && aValue &&
aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
ResetDirFormAssociatedElement(this, aNotify, true);
} elseif (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;
}
} elseif (aName == nsGkAtoms::autocomplete) { // Clear the cached @autocomplete attribute and autocompleteInfo state.
mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
} elseif (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);
}
}
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);
}
}
// 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. constauto* 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;
}
// 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::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();
}
} elseif (!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;
}
}
/* 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. staticconst 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);
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;
}
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;
}
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;
}
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;
}
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
--> --------------------
¤ Dauer der Verarbeitung: 0.40 Sekunden
(vorverarbeitet)
¤
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.