diff options
Diffstat (limited to 'dom')
35 files changed, 1482 insertions, 609 deletions
diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 15df2b337..a38c38293 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -105,6 +105,7 @@ #include "mozilla/dom/CanvasPath.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLVideoElement.h" +#include "mozilla/dom/SVGImageElement.h" #include "mozilla/dom/SVGMatrix.h" #include "mozilla/dom/TextMetrics.h" #include "mozilla/dom/SVGMatrix.h" @@ -2477,10 +2478,10 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource, return nullptr; } - Element* htmlElement; + Element* element; if (aSource.IsHTMLCanvasElement()) { HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement(); - htmlElement = canvas; + element = canvas; nsIntSize size = canvas->GetSize(); if (size.width == 0 || size.height == 0) { @@ -2505,7 +2506,7 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource, } RefPtr<CanvasPattern> pat = - new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false); + new CanvasPattern(this, srcSurf, repeatMode, element->NodePrincipal(), canvas->IsWriteOnly(), false); return pat.forget(); } @@ -2516,11 +2517,19 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource, return nullptr; } - htmlElement = img; + element = img; + } else if (aSource.IsSVGImageElement()) { + SVGImageElement* img = &aSource.GetAsSVGImageElement(); + if (img->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN)) { + aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + element = img; } else if (aSource.IsHTMLVideoElement()) { auto& video = aSource.GetAsHTMLVideoElement(); video.MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_PATTERN); - htmlElement = &video; + element = &video; } else { // Special case for ImageBitmap ImageBitmap& imgBitmap = aSource.GetAsImageBitmap(); @@ -2559,7 +2568,7 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource, // The canvas spec says that createPattern should use the first frame // of animated images nsLayoutUtils::SurfaceFromElementResult res = - nsLayoutUtils::SurfaceFromElement(htmlElement, + nsLayoutUtils::SurfaceFromElement(element, nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget); if (!res.GetSourceSurface()) { @@ -4949,6 +4958,9 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage, if (aImage.IsHTMLImageElement()) { HTMLImageElement* img = &aImage.GetAsHTMLImageElement(); element = img; + } else if (aImage.IsSVGImageElement()) { + SVGImageElement* img = &aImage.GetAsSVGImageElement(); + element = img; } else { HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement(); video->MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::DRAW_IMAGE); diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index c3ee3bdcb..d5dff8f3b 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -38,8 +38,8 @@ class SourceSurface; } // namespace gl namespace dom { -class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap; -typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource; +class HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap; +typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource; class ImageData; class StringOrCanvasGradientOrCanvasPattern; class OwningStringOrCanvasGradientOrCanvasPattern; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index d46eccdbc..52062c4b9 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -114,15 +114,12 @@ #include <limits> #include "nsIColorPicker.h" -#include "nsIDatePicker.h" #include "nsIStringEnumerator.h" #include "HTMLSplitOnSpacesTokenizer.h" #include "nsIController.h" #include "nsIMIMEInfo.h" #include "nsFrameSelection.h" -#include "nsIConsoleService.h" - // input type=date #include "js/Date.h" @@ -543,9 +540,8 @@ GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData, bool HTMLInputElement::ValueAsDateEnabled(JSContext* cx, JSObject* obj) { - return Preferences::GetBool("dom.experimental_forms", false) || - Preferences::GetBool("dom.forms.datepicker", false) || - Preferences::GetBool("dom.forms.datetime", false); + return IsExperimentalFormsEnabled() || IsInputDateTimeEnabled() || + IsInputDateTimeOthersEnabled(); } NS_IMETHODIMP @@ -628,7 +624,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback = new DispatchChangeEventCallback(mInput); - if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) && + if (IsWebkitDirPickerEnabled() && mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) { ErrorResult error; GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error); @@ -747,59 +743,6 @@ nsColorPickerShownCallback::Done(const nsAString& aColor) NS_IMPL_ISUPPORTS(nsColorPickerShownCallback, nsIColorPickerShownCallback) -class DatePickerShownCallback final : public nsIDatePickerShownCallback -{ - ~DatePickerShownCallback() {} -public: - DatePickerShownCallback(HTMLInputElement* aInput, - nsIDatePicker* aDatePicker) - : mInput(aInput) - , mDatePicker(aDatePicker) - {} - - NS_DECL_ISUPPORTS - - NS_IMETHOD Done(const nsAString& aDate) override; - NS_IMETHOD Cancel() override; - -private: - RefPtr<HTMLInputElement> mInput; - nsCOMPtr<nsIDatePicker> mDatePicker; -}; - -NS_IMETHODIMP -DatePickerShownCallback::Cancel() -{ - mInput->PickerClosed(); - return NS_OK; -} - -NS_IMETHODIMP -DatePickerShownCallback::Done(const nsAString& aDate) -{ - nsAutoString oldValue; - - mInput->PickerClosed(); - mInput->GetValue(oldValue); - - if(!oldValue.Equals(aDate)){ - mInput->SetValue(aDate); - nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(), - static_cast<nsIDOMHTMLInputElement*>(mInput.get()), - NS_LITERAL_STRING("input"), true, - false); - return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(), - static_cast<nsIDOMHTMLInputElement*>(mInput.get()), - NS_LITERAL_STRING("change"), true, - false); - } - - return NS_OK; -} - -NS_IMPL_ISUPPORTS(DatePickerShownCallback, nsIDatePickerShownCallback) - - bool HTMLInputElement::IsPopupBlocked() const { @@ -825,56 +768,6 @@ HTMLInputElement::IsPopupBlocked() const } nsresult -HTMLInputElement::InitDatePicker() -{ - if (!Preferences::GetBool("dom.forms.datepicker", false)) { - return NS_OK; - } - - if (mPickerRunning) { - NS_WARNING("Just one nsIDatePicker is allowed"); - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIDocument> doc = OwnerDoc(); - - nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow(); - if (!win) { - return NS_ERROR_FAILURE; - } - - if (IsPopupBlocked()) { - win->FirePopupBlockedEvent(doc, nullptr, EmptyString(), EmptyString()); - return NS_OK; - } - - // Get Loc title - nsXPIDLString title; - nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, - "DatePicker", title); - - nsresult rv; - nsCOMPtr<nsIDatePicker> datePicker = do_CreateInstance("@mozilla.org/datepicker;1", &rv); - if (!datePicker) { - return rv; - } - - nsAutoString initialValue; - GetValueInternal(initialValue); - rv = datePicker->Init(win, title, initialValue); - - nsCOMPtr<nsIDatePickerShownCallback> callback = - new DatePickerShownCallback(this, datePicker); - - rv = datePicker->Open(callback); - if (NS_SUCCEEDED(rv)) { - mPickerRunning = true; - } - - return rv; -} - -nsresult HTMLInputElement::InitColorPicker() { if (mPickerRunning) { @@ -1919,6 +1812,22 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue, aResultValue = Decimal::fromDouble(days * kMsPerDay); return true; } + case NS_FORM_INPUT_DATETIME_LOCAL: + { + uint32_t year, month, day, timeInMs; + if (!ParseDateTimeLocal(aValue, &year, &month, &day, &timeInMs)) { + return false; + } + + JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day, + timeInMs)); + if (!time.isValid()) { + return false; + } + + aResultValue = Decimal::fromDouble(time.toDouble()); + return true; + } default: MOZ_ASSERT(false, "Unrecognized input type"); return false; @@ -2108,21 +2017,17 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue, } case NS_FORM_INPUT_TIME: { + aValue = aValue.floor(); // Per spec, we need to truncate |aValue| and we should only represent // times inside a day [00:00, 24:00[, which means that we should do a // modulo on |aValue| using the number of milliseconds in a day (86400000). - uint32_t value = NS_floorModulo(aValue.floor(), Decimal(86400000)).toDouble(); - - uint16_t milliseconds = value % 1000; - value /= 1000; + uint32_t value = + NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble(); - uint8_t seconds = value % 60; - value /= 60; - - uint8_t minutes = value % 60; - value /= 60; - - uint8_t hours = value; + uint16_t milliseconds, seconds, minutes, hours; + if (!GetTimeFromMs(value, &hours, &minutes, &seconds, &milliseconds)) { + return false; + } if (milliseconds != 0) { aResultString.AppendPrintf("%02d:%02d:%02d.%03d", @@ -2192,6 +2097,42 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue, aResultString.AppendPrintf("%04.0f-W%02d", year, week); return true; } + case NS_FORM_INPUT_DATETIME_LOCAL: + { + aValue = aValue.floor(); + + uint32_t timeValue = + NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble(); + + uint16_t milliseconds, seconds, minutes, hours; + if (!GetTimeFromMs(timeValue, + &hours, &minutes, &seconds, &milliseconds)) { + return false; + } + + double year = JS::YearFromTime(aValue.toDouble()); + double month = JS::MonthFromTime(aValue.toDouble()); + double day = JS::DayFromTime(aValue.toDouble()); + + if (IsNaN(year) || IsNaN(month) || IsNaN(day)) { + return false; + } + + if (milliseconds != 0) { + aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d.%03d", + year, month + 1, day, hours, minutes, + seconds, milliseconds); + } else if (seconds != 0) { + aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d", + year, month + 1, day, hours, minutes, + seconds); + } else { + aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d", + year, month + 1, day, hours, minutes); + } + + return true; + } default: MOZ_ASSERT(false, "Unrecognized input type"); return false; @@ -2202,8 +2143,7 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue, Nullable<Date> HTMLInputElement::GetValueAsDate(ErrorResult& aRv) { - // TODO: this is temporary until bug 888331 is fixed. - if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) { + if (!IsDateTimeInputType(mType)) { return Nullable<Date>(); } @@ -2261,6 +2201,19 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv) return Nullable<Date>(Date(time)); } + case NS_FORM_INPUT_DATETIME_LOCAL: + { + uint32_t year, month, day, timeInMs; + nsAutoString value; + GetValueInternal(value); + if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) { + return Nullable<Date>(); + } + + JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day, + timeInMs)); + return Nullable<Date>(Date(time)); + } } MOZ_ASSERT(false, "Unrecognized input type"); @@ -2271,8 +2224,7 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv) void HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv) { - // TODO: this is temporary until bug 888331 is fixed. - if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) { + if (!IsDateTimeInputType(mType)) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } @@ -2380,11 +2332,8 @@ HTMLInputElement::GetMaximum() const Decimal HTMLInputElement::GetStepBase() const { - MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER || - mType == NS_FORM_INPUT_DATE || - mType == NS_FORM_INPUT_TIME || - mType == NS_FORM_INPUT_MONTH || - mType == NS_FORM_INPUT_WEEK || + MOZ_ASSERT(IsDateTimeInputType(mType) || + mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_RANGE, "Check that kDefaultStepBase is correct for this new type"); @@ -2515,11 +2464,8 @@ HTMLInputElement::ApplyStep(int32_t aStep) bool HTMLInputElement::IsExperimentalMobileType(uint8_t aType) { - return (aType == NS_FORM_INPUT_DATE && - !Preferences::GetBool("dom.forms.datetime", false) && - !Preferences::GetBool("dom.forms.datepicker", false)) || - (aType == NS_FORM_INPUT_TIME && - !Preferences::GetBool("dom.forms.datetime", false)); + return (aType == NS_FORM_INPUT_DATE || aType == NS_FORM_INPUT_TIME) && + !IsInputDateTimeEnabled(); } bool @@ -2832,7 +2778,8 @@ HTMLInputElement::GetOwnerDateTimeControl() HTMLInputElement::FromContentOrNull( GetParent()->GetParent()->GetParent()->GetParent()); if (ownerDateTimeControl && - ownerDateTimeControl->mType == NS_FORM_INPUT_TIME) { + (ownerDateTimeControl->mType == NS_FORM_INPUT_TIME || + ownerDateTimeControl->mType == NS_FORM_INPUT_DATE)) { return ownerDateTimeControl; } } @@ -3024,8 +2971,8 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const nsXPIDLString value; if (mFilesOrDirectories.IsEmpty()) { - if ((Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs()) || - (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) && + if ((IsDirPickerEnabled() && Allowdirs()) || + (IsWebkitDirPickerEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) { nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, "NoDirSelected", value); @@ -3054,7 +3001,7 @@ HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& a { ClearGetFilesHelpers(); - if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) { + if (IsWebkitFileSystemEnabled()) { HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this); mEntries.Clear(); } @@ -3073,7 +3020,7 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, mFilesOrDirectories.Clear(); ClearGetFilesHelpers(); - if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) { + if (IsWebkitFileSystemEnabled()) { HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this); mEntries.Clear(); } @@ -3096,14 +3043,14 @@ HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirect { SetFilesOrDirectories(aFilesOrDirectories, true); - if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) { + if (IsWebkitFileSystemEnabled()) { UpdateEntries(aFilesOrDirectories); } RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback = new DispatchChangeEventCallback(this); - if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) && + if (IsWebkitDirPickerEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) { ErrorResult rv; GetFilesHelper* helper = GetOrCreateGetFilesHelper(true /* recursionFlag */, @@ -3181,8 +3128,8 @@ HTMLInputElement::GetFiles() return nullptr; } - if (Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs() && - (!Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) || + if (IsDirPickerEnabled() && Allowdirs() && + (!IsWebkitDirPickerEnabled() || !HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) { return nullptr; } @@ -3282,7 +3229,8 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags) if (frame) { frame->UpdateForValueChange(); } - } else if (mType == NS_FORM_INPUT_TIME && + } else if ((mType == NS_FORM_INPUT_TIME || + mType == NS_FORM_INPUT_DATE) && !IsExperimentalMobileType(mType)) { nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame()); if (frame) { @@ -3591,7 +3539,8 @@ HTMLInputElement::Blur(ErrorResult& aError) } } - if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) { + if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) && + !IsExperimentalMobileType(mType)) { nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame()); if (frame) { frame->HandleBlurEvent(); @@ -3618,7 +3567,8 @@ HTMLInputElement::Focus(ErrorResult& aError) } } - if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) { + if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) && + !IsExperimentalMobileType(mType)) { nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame()); if (frame) { frame->HandleFocusEvent(); @@ -3956,7 +3906,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - if (mType == NS_FORM_INPUT_TIME && + if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) && !IsExperimentalMobileType(mType) && aVisitor.mEvent->mMessage == eFocus && aVisitor.mEvent->mOriginalTarget == this) { @@ -4083,7 +4033,8 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // Stop the event if the related target's first non-native ancestor is the // same as the original target's first non-native ancestor (we are moving // inside of the same element). - if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType) && + if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) && + !IsExperimentalMobileType(mType) && (aVisitor.mEvent->mMessage == eFocus || aVisitor.mEvent->mMessage == eFocusIn || aVisitor.mEvent->mMessage == eFocusOut || @@ -4361,8 +4312,8 @@ HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor) do_QueryInterface(aVisitor.mEvent->mOriginalTarget); if (target && target->FindFirstNonChromeOnlyAccessContent() == this && - ((Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs()) || - (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) && + ((IsDirPickerEnabled() && Allowdirs()) || + (IsWebkitDirPickerEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)))) { type = FILE_PICKER_DIRECTORY; } @@ -4371,9 +4322,6 @@ HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor) if (mType == NS_FORM_INPUT_COLOR) { return InitColorPicker(); } - if (mType == NS_FORM_INPUT_DATE) { - return InitDatePicker(); - } return NS_OK; } @@ -5383,6 +5331,29 @@ HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const } bool +HTMLInputElement::GetTimeFromMs(double aValue, uint16_t* aHours, + uint16_t* aMinutes, uint16_t* aSeconds, + uint16_t* aMilliseconds) const { + MOZ_ASSERT(aValue >= 0 && aValue < kMsPerDay, + "aValue must be milliseconds within a day!"); + + uint32_t value = floor(aValue); + + *aMilliseconds = value % 1000; + value /= 1000; + + *aSeconds = value % 60; + value /= 60; + + *aMinutes = value % 60; + value /= 60; + + *aHours = value; + + return true; +} + +bool HTMLInputElement::IsValidWeek(const nsAString& aValue) const { uint32_t year, week; @@ -5730,20 +5701,131 @@ HTMLInputElement::ParseTime(const nsAString& aValue, uint32_t* aResult) return true; } -static bool -IsDateTimeEnabled(int32_t aNewType) +/* static */ bool +HTMLInputElement::IsDateTimeTypeSupported(uint8_t aDateTimeInputType) +{ + return ((aDateTimeInputType == NS_FORM_INPUT_DATE || + aDateTimeInputType == NS_FORM_INPUT_TIME) && + (IsInputDateTimeEnabled() || IsExperimentalFormsEnabled())) || + ((aDateTimeInputType == NS_FORM_INPUT_MONTH || + aDateTimeInputType == NS_FORM_INPUT_WEEK || + aDateTimeInputType == NS_FORM_INPUT_DATETIME_LOCAL) && + IsInputDateTimeOthersEnabled()); +} + +/* static */ bool +HTMLInputElement::IsWebkitDirPickerEnabled() { - return (aNewType == NS_FORM_INPUT_DATE && - (Preferences::GetBool("dom.forms.datetime", false) || - Preferences::GetBool("dom.experimental_forms", false) || - Preferences::GetBool("dom.forms.datepicker", false))) || - (aNewType == NS_FORM_INPUT_TIME && - (Preferences::GetBool("dom.forms.datetime", false) || - Preferences::GetBool("dom.experimental_forms", false))) || - ((aNewType == NS_FORM_INPUT_MONTH || - aNewType == NS_FORM_INPUT_WEEK || - aNewType == NS_FORM_INPUT_DATETIME_LOCAL) && - Preferences::GetBool("dom.forms.datetime", false)); + static bool sWebkitDirPickerEnabled = false; + static bool sWebkitDirPickerPrefCached = false; + if (!sWebkitDirPickerPrefCached) { + sWebkitDirPickerPrefCached = true; + Preferences::AddBoolVarCache(&sWebkitDirPickerEnabled, + "dom.webkitBlink.dirPicker.enabled", + false); + } + + return sWebkitDirPickerEnabled; +} + +/* static */ bool +HTMLInputElement::IsWebkitFileSystemEnabled() +{ + static bool sWebkitFileSystemEnabled = false; + static bool sWebkitFileSystemPrefCached = false; + if (!sWebkitFileSystemPrefCached) { + sWebkitFileSystemPrefCached = true; + Preferences::AddBoolVarCache(&sWebkitFileSystemEnabled, + "dom.webkitBlink.filesystem.enabled", + false); + } + + return sWebkitFileSystemEnabled; +} + +/* static */ bool +HTMLInputElement::IsDirPickerEnabled() +{ + static bool sDirPickerEnabled = false; + static bool sDirPickerPrefCached = false; + if (!sDirPickerPrefCached) { + sDirPickerPrefCached = true; + Preferences::AddBoolVarCache(&sDirPickerEnabled, "dom.input.dirpicker", + false); + } + + return sDirPickerEnabled; +} + +/* static */ bool +HTMLInputElement::IsExperimentalFormsEnabled() +{ + static bool sExperimentalFormsEnabled = false; + static bool sExperimentalFormsPrefCached = false; + if (!sExperimentalFormsPrefCached) { + sExperimentalFormsPrefCached = true; + Preferences::AddBoolVarCache(&sExperimentalFormsEnabled, + "dom.experimental_forms", + false); + } + + return sExperimentalFormsEnabled; +} + +/* static */ bool +HTMLInputElement::IsInputDateTimeEnabled() +{ + static bool sDateTimeEnabled = false; + static bool sDateTimePrefCached = false; + if (!sDateTimePrefCached) { + sDateTimePrefCached = true; + Preferences::AddBoolVarCache(&sDateTimeEnabled, "dom.forms.datetime", + false); + } + + return sDateTimeEnabled; +} + +/* static */ bool +HTMLInputElement::IsInputDateTimeOthersEnabled() +{ + static bool sDateTimeOthersEnabled = false; + static bool sDateTimeOthersPrefCached = false; + if (!sDateTimeOthersPrefCached) { + sDateTimeOthersPrefCached = true; + Preferences::AddBoolVarCache(&sDateTimeOthersEnabled, + "dom.forms.datetime.others", false); + } + + return sDateTimeOthersEnabled; +} + +/* static */ bool +HTMLInputElement::IsInputNumberEnabled() +{ + static bool sInputNumberEnabled = false; + static bool sInputNumberPrefCached = false; + if (!sInputNumberPrefCached) { + sInputNumberPrefCached = true; + Preferences::AddBoolVarCache(&sInputNumberEnabled, "dom.forms.number", + false); + } + + return sInputNumberEnabled; +} + +/* static */ bool +HTMLInputElement::IsInputColorEnabled() +{ + static bool sInputColorEnabled = false; + static bool sInputColorPrefCached = false; + if (!sInputColorPrefCached) { + sInputColorPrefCached = true; + Preferences::AddBoolVarCache(&sInputColorEnabled, "dom.forms.color", + false); + } + + return sInputColorEnabled; } bool @@ -5760,13 +5842,9 @@ HTMLInputElement::ParseAttribute(int32_t aNamespaceID, bool success = aResult.ParseEnumValue(aValue, kInputTypeTable, false); if (success) { newType = aResult.GetEnumValue(); - if ((IsExperimentalMobileType(newType) && - !Preferences::GetBool("dom.experimental_forms", false)) || - (newType == NS_FORM_INPUT_NUMBER && - !Preferences::GetBool("dom.forms.number", false)) || - (newType == NS_FORM_INPUT_COLOR && - !Preferences::GetBool("dom.forms.color", false)) || - (IsDateTimeInputType(newType) && !IsDateTimeEnabled(newType))) { + if ((newType == NS_FORM_INPUT_NUMBER && !IsInputNumberEnabled()) || + (newType == NS_FORM_INPUT_COLOR && !IsInputColorEnabled()) || + (IsDateTimeInputType(newType) && !IsDateTimeTypeSupported(newType))) { newType = kInputDefaultType->value; aResult.SetTo(newType, &aValue); } @@ -7161,13 +7239,15 @@ HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t* if (mType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_NUMBER || - mType == NS_FORM_INPUT_TIME) { + mType == NS_FORM_INPUT_TIME || + mType == NS_FORM_INPUT_DATE) { if (aTabIndex) { // We only want our native anonymous child to be tabable to, not ourself. *aTabIndex = -1; } if (mType == NS_FORM_INPUT_NUMBER || - mType == NS_FORM_INPUT_TIME) { + mType == NS_FORM_INPUT_TIME || + mType == NS_FORM_INPUT_DATE) { *aIsFocusable = true; } else { *aIsFocusable = defaultFocusable; @@ -7650,8 +7730,7 @@ HTMLInputElement::HasPatternMismatch() const bool HTMLInputElement::IsRangeOverflow() const { - // TODO: this is temporary until bug 888331 is fixed. - if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) { + if (!DoesMinMaxApply()) { return false; } @@ -7671,8 +7750,7 @@ HTMLInputElement::IsRangeOverflow() const bool HTMLInputElement::IsRangeUnderflow() const { - // TODO: this is temporary until bug 888331 is fixed. - if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) { + if (!DoesMinMaxApply()) { return false; } @@ -8622,6 +8700,7 @@ HTMLInputElement::GetStepScaleFactor() const case NS_FORM_INPUT_RANGE: return kStepScaleFactorNumberRange; case NS_FORM_INPUT_TIME: + case NS_FORM_INPUT_DATETIME_LOCAL: return kStepScaleFactorTime; case NS_FORM_INPUT_MONTH: return kStepScaleFactorMonth; @@ -8646,6 +8725,7 @@ HTMLInputElement::GetDefaultStep() const case NS_FORM_INPUT_RANGE: return kDefaultStep; case NS_FORM_INPUT_TIME: + case NS_FORM_INPUT_DATETIME_LOCAL: return kDefaultStepTime; default: MOZ_ASSERT(false, "Unrecognized input type"); @@ -8680,8 +8760,7 @@ HTMLInputElement::UpdateHasRange() mHasRange = false; - // TODO: this is temporary until bug 888331 is fixed. - if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) { + if (!DoesMinMaxApply()) { return; } diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 9ca876aee..98a590443 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -796,6 +796,15 @@ public: void UpdateDateTimePicker(const DateTimeValue& aValue); void CloseDateTimePicker(); + /* + * The following are called from datetime input box binding to get the + * corresponding computed values. + */ + double GetStepAsDouble() { return GetStep().toDouble(); } + double GetStepBaseAsDouble() { return GetStepBase().toDouble(); } + double GetMinimumAsDouble() { return GetMinimum().toDouble(); } + double GetMaximumAsDouble() { return GetMaximum().toDouble(); } + HTMLInputElement* GetOwnerNumberControl(); HTMLInputElement* GetOwnerDateTimeControl(); @@ -1061,11 +1070,7 @@ protected: /** * Returns if the step attribute apply for the current type. */ - bool DoesStepApply() const - { - // TODO: this is temporary until bug 888331 is fixed. - return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL; - } + bool DoesStepApply() const { return DoesMinMaxApply(); } /** * Returns if stepDown and stepUp methods apply for the current type. @@ -1075,11 +1080,7 @@ protected: /** * Returns if valueAsNumber attribute applies for the current type. */ - bool DoesValueAsNumberApply() const - { - // TODO: this is temporary until bug 888331 is fixed. - return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL; - } + bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); } /** * Returns if autocomplete attribute applies for the current type. @@ -1287,6 +1288,7 @@ protected: * https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string */ void NormalizeDateTimeLocal(nsAString& aValue) const; + /** * This methods returns the number of days since epoch for a given year and * week. @@ -1318,6 +1320,13 @@ protected: uint32_t MaximumWeekInYear(uint32_t aYear) const; /** + * This method converts aValue (milliseconds within a day) to hours, minutes, + * seconds and milliseconds. + */ + bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes, + uint16_t* aSeconds, uint16_t* aMilliseconds) const; + + /** * This methods returns true if it's a leap year. */ bool IsLeapYear(uint32_t aYear) const; @@ -1442,7 +1451,6 @@ protected: }; nsresult InitFilePicker(FilePickerType aType); nsresult InitColorPicker(); - nsresult InitDatePicker(); /** * Use this function before trying to open a picker. @@ -1632,9 +1640,73 @@ private: return IsSingleLineTextControl(false, aType) || aType == NS_FORM_INPUT_RANGE || aType == NS_FORM_INPUT_NUMBER || - aType == NS_FORM_INPUT_TIME; + aType == NS_FORM_INPUT_TIME || + aType == NS_FORM_INPUT_DATE; } + /** + * Checks if aDateTimeInputType should be supported based on "dom.forms.datetime", + * and "dom.experimental_forms". + */ + static bool + IsDateTimeTypeSupported(uint8_t aDateTimeInputType); + + /** + * Checks preference "dom.webkitBlink.dirPicker.enabled" to determine if + * webkitdirectory should be supported. + */ + static bool + IsWebkitDirPickerEnabled(); + + /** + * Checks preference "dom.webkitBlink.filesystem.enabled" to determine if + * webkitEntries should be supported. + */ + static bool + IsWebkitFileSystemEnabled(); + + /** + * Checks preference "dom.input.dirpicker" to determine if file and directory + * entries API should be supported. + */ + static bool + IsDirPickerEnabled(); + + /** + * Checks preference "dom.experimental_forms" to determine if experimental + * implementation of input element should be enabled. + */ + static bool + IsExperimentalFormsEnabled(); + + /** + * Checks preference "dom.forms.datetime" to determine if input date and time + * should be supported. + */ + static bool + IsInputDateTimeEnabled(); + + /** + * Checks preference "dom.forms.datetime.others" to determine if input week, + * month and datetime-local should be supported. + */ + static bool + IsInputDateTimeOthersEnabled(); + + /** + * Checks preference "dom.forms.number" to determine if input type=number + * should be supported. + */ + static bool + IsInputNumberEnabled(); + + /** + * Checks preference "dom.forms.color" to determine if date/time related + * types should be supported. + */ + static bool + IsInputColorEnabled(); + struct nsFilePickerFilter { nsFilePickerFilter() : mFilterMask(0) {} diff --git a/dom/html/nsIFormControl.h b/dom/html/nsIFormControl.h index aaa92146c..e07f7c829 100644 --- a/dom/html/nsIFormControl.h +++ b/dom/html/nsIFormControl.h @@ -270,8 +270,8 @@ nsIFormControl::IsSingleLineTextControl(bool aExcludePassword, uint32_t aType) #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) // On Android/B2G, date/time input appears as a normal text box. aType == NS_FORM_INPUT_TIME || -#endif aType == NS_FORM_INPUT_DATE || +#endif aType == NS_FORM_INPUT_MONTH || aType == NS_FORM_INPUT_WEEK || aType == NS_FORM_INPUT_DATETIME_LOCAL || diff --git a/dom/html/test/forms/mochitest.ini b/dom/html/test/forms/mochitest.ini index 35955b189..199e4baf8 100644 --- a/dom/html/test/forms/mochitest.ini +++ b/dom/html/test/forms/mochitest.ini @@ -30,8 +30,14 @@ skip-if = os == "android" # up/down arrow keys not supported on android skip-if = android_version == '18' # Android, bug 1147974 [test_input_color_picker_update.html] skip-if = android_version == '18' # Android, bug 1147974 +[test_input_date_key_events.html] +skip-if = os == "android" +[test_input_datetime_input_change_events.html] +skip-if = os == "android" [test_input_datetime_focus_blur.html] skip-if = os == "android" +[test_input_datetime_focus_blur_events.html] +skip-if = os == "android" [test_input_datetime_tabindex.html] skip-if = os == "android" [test_input_defaultValue.html] @@ -61,8 +67,6 @@ skip-if = os == "android" [test_input_textarea_set_value_no_scroll.html] [test_input_time_key_events.html] skip-if = os == "android" -[test_input_time_focus_blur_events.html] -skip-if = os == "android" [test_input_types_pref.html] [test_input_typing_sanitization.html] [test_input_untrusted_key_events.html] diff --git a/dom/html/test/forms/test_input_date_key_events.html b/dom/html/test/forms/test_input_date_key_events.html new file mode 100644 index 000000000..f502d6a4d --- /dev/null +++ b/dom/html/test/forms/test_input_date_key_events.html @@ -0,0 +1,228 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1286182 +--> +<head> + <title>Test key events for time control</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <meta charset="UTF-8"> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1286182">Mozilla Bug 1286182</a> +<p id="display"></p> +<div id="content"> + <input id="input" type="date"> +</div> +<pre id="test"> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); +// Turn off Spatial Navigation because it hijacks arrow keydown events: +SimpleTest.waitForFocus(function() { + SpecialPowers.pushPrefEnv({"set":[["snav.enabled", false]]}, function() { + test(); + SimpleTest.finish(); + }); +}); + +var testData = [ + /** + * keys: keys to send to the input element. + * initialVal: initial value set to the input element. + * expectedVal: expected value of the input element after sending the keys. + */ + { + // Type 11222016, default order is month, day, year. + keys: ["11222016"], + initialVal: "", + expectedVal: "2016-11-22" + }, + { + // Type 3 in the month field will automatically advance to the day field, + // then type 5 in the day field will automatically advance to the year + // field. + keys: ["352016"], + initialVal: "", + expectedVal: "2016-03-05" + }, + { + // Type 13 in the month field will set it to the maximum month, which is + // 12. + keys: ["13012016"], + initialVal: "", + expectedVal: "2016-12-01" + }, + { + // Type 00 in the month field will set it to the minimum month, which is 1. + keys: ["00012016"], + initialVal: "", + expectedVal: "2016-01-01" + }, + { + // Type 33 in the day field will set it to the maximum day, which is 31. + keys: ["12332016"], + initialVal: "", + expectedVal: "2016-12-31" + }, + { + // Type 00 in the day field will set it to the minimum day, which is 1. + keys: ["12002016"], + initialVal: "", + expectedVal: "2016-12-01" + }, + { + // Type 275769 in the year field will set it to the maximum year, which is + // 275760. + keys: ["0101275769"], + initialVal: "", + expectedVal: "275760-01-01" + }, + { + // Type 000000 in the year field will set it to the minimum year, which is + // 0001. + keys: ["0101000000"], + initialVal: "", + expectedVal: "0001-01-01" + }, + { + // Advance to year field and decrement. + keys: ["VK_TAB", "VK_TAB", "VK_DOWN"], + initialVal: "2016-11-25", + expectedVal: "2015-11-25" + }, + { + // Right key should do the same thing as TAB key. + keys: ["VK_RIGHT", "VK_RIGHT", "VK_DOWN"], + initialVal: "2016-11-25", + expectedVal: "2015-11-25" + }, + { + // Advance to day field then back to month field and decrement. + keys: ["VK_RIGHT", "VK_LEFT", "VK_DOWN"], + initialVal: "2000-05-01", + expectedVal: "2000-04-01" + }, + { + // Focus starts on the first field, month in this case, and increment. + keys: ["VK_UP"], + initialVal: "2000-03-01", + expectedVal: "2000-04-01" + }, + { + // Advance to day field and decrement. + keys: ["VK_TAB", "VK_DOWN"], + initialVal: "1234-01-01", + expectedVal: "1234-01-31" + }, + { + // Advance to day field and increment. + keys: ["VK_TAB", "VK_UP"], + initialVal: "1234-01-01", + expectedVal: "1234-01-02" + }, + { + // PageUp on month field increments month by 3. + keys: ["VK_PAGE_UP"], + initialVal: "1999-01-01", + expectedVal: "1999-04-01" + }, + { + // PageDown on month field decrements month by 3. + keys: ["VK_PAGE_DOWN"], + initialVal: "1999-01-01", + expectedVal: "1999-10-01" + }, + { + // PageUp on day field increments day by 7. + keys: ["VK_TAB", "VK_PAGE_UP"], + initialVal: "1999-01-01", + expectedVal: "1999-01-08" + }, + { + // PageDown on day field decrements day by 7. + keys: ["VK_TAB", "VK_PAGE_DOWN"], + initialVal: "1999-01-01", + expectedVal: "1999-01-25" + }, + { + // PageUp on year field increments year by 10. + keys: ["VK_TAB", "VK_TAB", "VK_PAGE_UP"], + initialVal: "1999-01-01", + expectedVal: "2009-01-01" + }, + { + // PageDown on year field decrements year by 10. + keys: ["VK_TAB", "VK_TAB", "VK_PAGE_DOWN"], + initialVal: "1999-01-01", + expectedVal: "1989-01-01" + }, + { + // Home key on month field sets it to the minimum month, which is 01. + keys: ["VK_HOME"], + initialVal: "2016-06-01", + expectedVal: "2016-01-01" + }, + { + // End key on month field sets it to the maximum month, which is 12. + keys: ["VK_END"], + initialVal: "2016-06-01", + expectedVal: "2016-12-01" + }, + { + // Home key on day field sets it to the minimum day, which is 01. + keys: ["VK_TAB", "VK_HOME"], + initialVal: "2016-01-10", + expectedVal: "2016-01-01" + }, + { + // End key on day field sets it to the maximum day, which is 31. + keys: ["VK_TAB", "VK_END"], + initialVal: "2016-01-10", + expectedVal: "2016-01-31" + }, + { + // Home key should have no effect on year field. + keys: ["VK_TAB", "VK_TAB", "VK_HOME"], + initialVal: "2016-01-01", + expectedVal: "2016-01-01" + }, + { + // End key should have no effect on year field. + keys: ["VK_TAB", "VK_TAB", "VK_END"], + initialVal: "2016-01-01", + expectedVal: "2016-01-01" + }, +]; + +function sendKeys(aKeys) { + for (let i = 0; i < aKeys.length; i++) { + let key = aKeys[i]; + if (key.startsWith("VK")) { + synthesizeKey(key, {}); + } else { + sendString(key); + } + } +} + +function test() { + var elem = document.getElementById("input"); + + for (let { keys, initialVal, expectedVal } of testData) { + elem.focus(); + elem.value = initialVal; + sendKeys(keys); + elem.blur(); + is(elem.value, expectedVal, + "Test with " + keys + ", result should be " + expectedVal); + elem.value = ""; + } +} + +</script> +</pre> +</body> +</html> diff --git a/dom/html/test/forms/test_input_datetime_focus_blur.html b/dom/html/test/forms/test_input_datetime_focus_blur.html index 5b8d95b25..85f7b4bb4 100644 --- a/dom/html/test/forms/test_input_datetime_focus_blur.html +++ b/dom/html/test/forms/test_input_datetime_focus_blur.html @@ -4,7 +4,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591 --> <head> - <title>Test focus/blur behaviour for <input type='time'></title> + <title>Test focus/blur behaviour for date/time input types</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> @@ -12,7 +12,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a> <p id="display"></p> <div id="content"> - <input id="input" type="time"> + <input id="input_time" type="time"> + <input id="input_date" type="date"> </div> <pre id="test"> <script type="application/javascript"> @@ -30,15 +31,15 @@ SimpleTest.waitForFocus(function() { SimpleTest.finish(); }); -function test() { - let time = document.getElementById("input"); - time.focus(); +function testFocusBlur(type) { + let input = document.getElementById("input_" + type); + input.focus(); - // The active element returns the input type=time. + // The active element returns the date/time input element. let activeElement = document.activeElement; - is(activeElement, time, "activeElement should be the time element"); + is(activeElement, input, "activeElement should be the date/time input element"); is(activeElement.localName, "input", "activeElement should be an input element"); - is(activeElement.type, "time", "activeElement should be of type time"); + is(activeElement.type, type, "activeElement should be of type " + type); // Use FocusManager to check that the actual focus is on the anonymous // text control. @@ -48,10 +49,17 @@ function test() { is(focusedElement.localName, "input", "focusedElement should be an input element"); is(focusedElement.type, "text", "focusedElement should be of type text"); - time.blur(); - isnot(document.activeElement, time, "activeElement should no longer be the time element"); + input.blur(); + isnot(document.activeElement, input, "activeElement should no longer be the datetime input element"); } +function test() { + let inputTypes = ["time", "date"]; + + for (let i = 0; i < inputTypes.length; i++) { + testFocusBlur(inputTypes[i]); + } +} </script> </pre> </body> diff --git a/dom/html/test/forms/test_input_datetime_focus_blur_events.html b/dom/html/test/forms/test_input_datetime_focus_blur_events.html new file mode 100644 index 000000000..873dda627 --- /dev/null +++ b/dom/html/test/forms/test_input_datetime_focus_blur_events.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1301306 +--> +<head> +<title>Test for Bug 1301306</title> +<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301306">Mozilla Bug 722599</a> +<p id="display"></p> +<div id="content"> +<input type="time" id="input_time" onfocus="++focusEvents[0]" + onblur="++blurEvents[0]" onfocusin="++focusInEvents[0]" + onfocusout="++focusOutEvents[0]"> +<input type="date" id="input_date" onfocus="++focusEvents[1]" + onblur="++blurEvents[1]" onfocusin="++focusInEvents[1]" + onfocusout="++focusOutEvents[1]"> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** + * Test for Bug 1301306. + * This test checks that when moving inside the time input element, e.g. jumping + * through the inner text boxes, does not fire extra focus/blur events. + **/ + +var inputTypes = ["time", "date"]; +var focusEvents = [0, 0]; +var focusInEvents = [0, 0]; +var focusOutEvents = [0, 0]; +var blurEvents = [0, 0]; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(function() { + test(); + SimpleTest.finish(); +}); + +function test() { + for (var i = 0; i < inputTypes.length; i++) { + var input = document.getElementById("input_" + inputTypes[i]); + + input.focus(); + is(focusEvents[i], 1, inputTypes[i] + " input element should have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should have dispatched focusin event."); + is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event."); + is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event."); + + // Move around inside the input element's input box. + synthesizeKey("VK_TAB", {}); + is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event."); + is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event."); + is(blurEvents[i], 0, inputTypes[i] + " time input element should not have dispatched blur event."); + + synthesizeKey("VK_RIGHT", {}); + is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event."); + is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event."); + is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event."); + + synthesizeKey("VK_LEFT", {}); + is(focusEvents[i], 1,inputTypes[i] + " input element should not have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event."); + is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event."); + is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event."); + + synthesizeKey("VK_RIGHT", {}); + is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event."); + is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event."); + is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event."); + + input.blur(); + is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event."); + is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event."); + is(focusOutEvents[i], 1, inputTypes[i] + " input element should have dispatched focusout event."); + is(blurEvents[i], 1, inputTypes[i] + " input element should have dispatched blur event."); + } +} + +</script> +</pre> +</body> +</html> diff --git a/dom/html/test/forms/test_input_datetime_input_change_events.html b/dom/html/test/forms/test_input_datetime_input_change_events.html new file mode 100644 index 000000000..e636995d3 --- /dev/null +++ b/dom/html/test/forms/test_input_datetime_input_change_events.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1370858 +--> +<head> +<title>Test for Bug 1370858</title> +<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1370858">Mozilla Bug 722599</a> +<p id="display"></p> +<div id="content"> +<input type="time" id="input_time" onchange="++changeEvents[0]" + oninput="++inputEvents[0]"> +<input type="date" id="input_date" onchange="++changeEvents[1]" + oninput="++inputEvents[1]"> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** + * Test for Bug 1370858. + * Test that change and input events are (not) fired for date/time inputs. + **/ + +var inputTypes = ["time", "date"]; +var changeEvents = [0, 0]; +var inputEvents = [0, 0]; +var values = ["10:30", "2017-06-08"]; +var expectedValues = [["09:30", "01:30"], ["2017-05-08", "2017-01-08"]]; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(function() { + test(); + SimpleTest.finish(); +}); + +function test() { + for (var i = 0; i < inputTypes.length; i++) { + var input = document.getElementById("input_" + inputTypes[i]); + var inputRect = input.getBoundingClientRect(); + + // Points over the input's reset button + var resetButton_X = inputRect.width - 15; + var resetButton_Y = inputRect.height / 2; + + is(changeEvents[i], 0, "Number of change events should be 0 at start."); + is(inputEvents[i], 0, "Number of input events should be 0 at start."); + + // Test that change and input events are not dispatched setting .value by + // script. + input.value = values[i]; + is(input.value, values[i], "Check that value was set correctly (0)."); + is(changeEvents[i], 0, "Change event should not have dispatched (0)."); + is(inputEvents[i], 0, "Input event should not have dispatched (0)."); + + // Test that change and input events are fired when changing the value using + // up/down keys. + input.focus(); + synthesizeKey("VK_DOWN", {}); + is(input.value, expectedValues[i][0], "Check that value was set correctly (1)."); + is(changeEvents[i], 1, "Change event should be dispatched (1)."); + is(inputEvents[i], 1, "Input event should ne dispatched (1)."); + + // Test that change and input events are fired when changing the value with + // the keyboard. + synthesizeKey("0", {}); + synthesizeKey("1", {}); + is(input.value, expectedValues[i][1], "Check that value was set correctly (2)."); + is(changeEvents[i], 2, "Change event should be dispatched (2)."); + is(inputEvents[i], 2, "Input event should be dispatched (2)."); + + // Test that change and input events are fired when clearing the value using + // the reset button. + synthesizeMouse(input, resetButton_X, resetButton_Y, {}); + is(input.value, "", "Check that value was set correctly (3)."); + is(changeEvents[i], 3, "Change event should be dispatched (3)."); + is(inputEvents[i], 3, "Input event should be dispatched (3)."); + } +} + +</script> +</pre> +</body> +</html> diff --git a/dom/html/test/forms/test_input_datetime_tabindex.html b/dom/html/test/forms/test_input_datetime_tabindex.html index fb7c9b2f1..8023ccf9b 100644 --- a/dom/html/test/forms/test_input_datetime_tabindex.html +++ b/dom/html/test/forms/test_input_datetime_tabindex.html @@ -4,7 +4,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591 --> <head> - <title>Test tabindex attribute for <input type='time'></title> + <title>Test tabindex attribute for date/time input types</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> @@ -16,13 +16,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591 <input id="time1" type="time" tabindex="0"> <input id="time2" type="time" tabindex="-1"> <input id="time3" type="time" tabindex="0"> + <input id="date1" type="date" tabindex="0"> + <input id="date2" type="date" tabindex="-1"> + <input id="date3" type="date" tabindex="0"> </div> <pre id="test"> <script type="application/javascript"> /** * Test for Bug 1288591. - * This test checks whether date/time input types' tabindex attribute works + * This test checks whether date/time input types tabindex attribute works * correctly. **/ SimpleTest.waitForExplicitFinish(); @@ -31,41 +34,49 @@ SimpleTest.waitForFocus(function() { SimpleTest.finish(); }); -function test() { - let time1 = document.getElementById("time1"); - let time2 = document.getElementById("time2"); - let time3 = document.getElementById("time3"); +function testTabindex(type) { + let input1 = document.getElementById(type + "1"); + let input2 = document.getElementById(type + "2"); + let input3 = document.getElementById(type + "3"); - time1.focus(); - is(document.activeElement, time1, + input1.focus(); + is(document.activeElement, input1, "input element with tabindex=0 is focusable"); - // Advance to time1 minute field + // Advance to next inner field synthesizeKey("VK_TAB", {}); - is(document.activeElement, time1, + is(document.activeElement, input1, "input element with tabindex=0 is tabbable"); - // Advance to time1 AM/PM field + // Advance to next inner field synthesizeKey("VK_TAB", {}); - is(document.activeElement, time1, + is(document.activeElement, input1, "input element with tabindex=0 is tabbable"); // Advance to next element synthesizeKey("VK_TAB", {}); - is(document.activeElement, time3, + is(document.activeElement, input3, "input element with tabindex=-1 is not tabbable"); - time2.focus(); - is(document.activeElement, time2, + input2.focus(); + is(document.activeElement, input2, "input element with tabindex=-1 is still focusable"); // Changing the tabindex attribute dynamically. - time3.setAttribute("tabindex", "-1"); - synthesizeKey("VK_TAB", {}); // need only one TAB since time2 is not tabbable - isnot(document.activeElement, time3, + input3.setAttribute("tabindex", "-1"); + synthesizeKey("VK_TAB", {}); // need only one TAB since input2 is not tabbable + isnot(document.activeElement, input3, "element with tabindex changed to -1 should not be tabbable"); } +function test() { + let inputTypes = ["time", "date"]; + + for (let i = 0; i < inputTypes.length; i++) { + testTabindex(inputTypes[i]); + } +} + </script> </pre> </body> diff --git a/dom/html/test/forms/test_input_time_focus_blur_events.html b/dom/html/test/forms/test_input_time_focus_blur_events.html deleted file mode 100644 index 483741477..000000000 --- a/dom/html/test/forms/test_input_time_focus_blur_events.html +++ /dev/null @@ -1,82 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1301306 ---> -<head> -<title>Test for Bug 1301306</title> -<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> -<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301306">Mozilla Bug 722599</a> -<p id="display"></p> -<div id="content"> -<input type="time" id="input_time" onfocus="++focusEvent" onblur="++blurEvent" - onfocusin="++focusInEvent" onfocusout="++focusOutEvent"> -</div> -<pre id="test"> -<script class="testbody" type="text/javascript"> - -/** - * Test for Bug 1301306. - * This test checks that when moving inside the time input element, e.g. jumping - * through the inner text boxes, does not fire extra focus/blur events. - **/ - -var focusEvent = 0; -var focusInEvent = 0; -var focusOutEvent = 0; -var blurEvent = 0; - -SimpleTest.waitForExplicitFinish(); -SimpleTest.waitForFocus(function() { - test(); - SimpleTest.finish(); -}); - -function test() { - var time = document.getElementById("input_time"); - time.focus(); - is(focusEvent, 1, "time input element should have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 0, "time input element should not have dispatched focusout event."); - is(blurEvent, 0, "time input element should not have dispatched blur event."); - - // Move around inside the input element's input box. - synthesizeKey("VK_TAB", {}); - is(focusEvent, 1, "time input element should not have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 0, "time input element should not have dispatched focusout event."); - is(blurEvent, 0, "time input element should not have dispatched blur event."); - - synthesizeKey("VK_RIGHT", {}); - is(focusEvent, 1, "time input element should not have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 0, "time input element should not have dispatched focusout event."); - is(blurEvent, 0, "time input element should not have dispatched blur event."); - - synthesizeKey("VK_LEFT", {}); - is(focusEvent, 1, "time input element should not have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 0, "time input element should not have dispatched focusout event."); - is(blurEvent, 0, "time input element should not have dispatched blur event."); - - synthesizeKey("VK_RIGHT", {}); - is(focusEvent, 1, "time input element should not have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 0, "time input element should not have dispatched focusout event."); - is(blurEvent, 0, "time input element should not have dispatched blur event."); - - time.blur(); - is(focusEvent, 1, "time input element should not have dispatched focus event."); - is(focusInEvent, 1, "time input element should have dispatched focusin event."); - is(focusOutEvent, 1, "time input element should not have dispatched focusout event."); - is(blurEvent, 1, "time input element should have dispatched blur event."); -} - -</script> -</pre> -</body> -</html> diff --git a/dom/html/test/forms/test_input_types_pref.html b/dom/html/test/forms/test_input_types_pref.html index 243836f34..5279d6a2a 100644 --- a/dom/html/test/forms/test_input_types_pref.html +++ b/dom/html/test/forms/test_input_types_pref.html @@ -37,47 +37,63 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=764481 inputType: "color", expectedType: "color" }, { - prefs: [["dom.experimental_forms", false], ["dom.forms.datepicker", false], - ["dom.forms.datetime", false]], + prefs: [["dom.experimental_forms", false], ["dom.forms.datetime", false]], inputType: "date", expectedType: "text" }, { - prefs: [["dom.experimental_forms", true], ["dom.forms.datepicker", false], - ["dom.forms.datetime", false]], + prefs: [["dom.experimental_forms", true], ["dom.forms.datetime", false]], inputType: "date", expectedType: "date" }, { - prefs: [["dom.experimental_forms", false], ["dom.forms.datepicker", true], - ["dom.forms.datetime", false]], + prefs: [["dom.experimental_forms", false], ["dom.forms.datetime", true]], inputType: "date", expectedType: "date" }, { - prefs: [["dom.experimental_forms", false], ["dom.forms.datepicker", false], - ["dom.forms.datetime", true]], - inputType: "date", - expectedType: "date" + prefs: [["dom.experimental_forms", false], ["dom.forms.datetime", false]], + inputType: "time", + expectedType: "text" + }, { + prefs: [["dom.experimental_forms", true], ["dom.forms.datetime", false]], + inputType: "time", + expectedType: "time" + }, { + prefs: [["dom.experimental_forms", false], ["dom.forms.datetime", true]], + inputType: "time", + expectedType: "time" + }, { + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", false]], + inputType: "month", + expectedType: "text" }, { - prefs: [["dom.forms.datetime", false]], + prefs: [["dom.forms.datetime", true], ["dom.forms.datetime.others", false]], inputType: "month", expectedType: "text" }, { - prefs: [["dom.forms.datetime", true]], + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", true]], inputType: "month", expectedType: "month" }, { - prefs: [["dom.forms.datetime", false]], + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", false]], + inputType: "week", + expectedType: "text" + }, { + prefs: [["dom.forms.datetime", true], ["dom.forms.datetime.others", false]], inputType: "week", expectedType: "text" }, { - prefs: [["dom.forms.datetime", true]], + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", true]], inputType: "week", expectedType: "week" }, { - prefs: [["dom.forms.datetime", false]], + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", false]], + inputType: "datetime-local", + expectedType: "text" + }, { + prefs: [["dom.forms.datetime", true], ["dom.forms.datetime.others", false]], inputType: "datetime-local", expectedType: "text" }, { - prefs: [["dom.forms.datetime", true]], + prefs: [["dom.forms.datetime", false], ["dom.forms.datetime.others", true]], inputType: "datetime-local", expectedType: "datetime-local" } diff --git a/dom/html/test/forms/test_input_typing_sanitization.html b/dom/html/test/forms/test_input_typing_sanitization.html index 0896f19df..eee300b33 100644 --- a/dom/html/test/forms/test_input_typing_sanitization.html +++ b/dom/html/test/forms/test_input_typing_sanitization.html @@ -26,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=765772 * This test checks that when a user types in some input types, it will not be * in a state where the value will be un-sanitized and usable (by a script). */ +const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent); var input = document.getElementById('i'); var form = document.getElementById('f'); @@ -143,6 +144,7 @@ function runTest() ] }, { + mobileOnly: true, type: 'date', validData: [ '0001-01-01', @@ -161,6 +163,28 @@ function runTest() ] }, { + mobileOnly: true, + type: 'time', + validData: [ + '00:00', + '09:09:00', + '08:23:23.1', + '21:43:56.12', + '23:12:45.100', + ], + invalidData: [ + '00:', + '00:00:', + '25:00', + '-00:00', + '00:00:00.', + '00:60', + '10:58:99', + ':19:10', + '23:08:09.1012', + ] + }, + { type: 'month', validData: [ '0001-01', @@ -218,6 +242,10 @@ function runTest() for (test of data) { gCurrentTest = test; + if (gCurrentTest.mobileOnly && isDesktop) { + continue; + } + input.type = test.type; gValidData = test.validData; gInvalidData = test.invalidData; diff --git a/dom/html/test/forms/test_max_attribute.html b/dom/html/test/forms/test_max_attribute.html index 4007cfad6..091ad321b 100644 --- a/dom/html/test/forms/test_max_attribute.html +++ b/dom/html/test/forms/test_max_attribute.html @@ -31,8 +31,7 @@ var data = [ { type: 'month', apply: true }, { type: 'week', apply: true }, { type: 'time', apply: true }, - // TODO: temporary set to false until bug 888331 is fixed. - { type: 'datetime-local', apply: false }, + { type: 'datetime-local', apply: true }, { type: 'number', apply: true }, { type: 'range', apply: true }, { type: 'color', apply: false }, @@ -71,7 +70,8 @@ function checkValidity(aElement, aValidity, aApply, aRangeApply) "element overflow status should be " + !aValidity); var overflowMsg = (aElement.type == "date" || aElement.type == "time" || - aElement.type == "month" || aElement.type == "week") ? + aElement.type == "month" || aElement.type == "week" || + aElement.type == "datetime-local") ? ("Please select a value that is no later than " + aElement.max + ".") : ("Please select a value that is no more than " + aElement.max + "."); is(aElement.validationMessage, @@ -148,7 +148,7 @@ for (var test of data) { input.max = '2016-W39'; break; case 'datetime-local': - // TODO: this is temporary until bug 888331 is fixed. + input.max = '2016-12-31T23:59:59'; break; default: ok(false, 'please, add a case for this new type (' + input.type + ')'); @@ -421,7 +421,44 @@ for (var test of data) { break; case 'datetime-local': - // TODO: this is temporary until bug 888331 is fixed. + input.value = '2016-01-01T12:00'; + checkValidity(input, true, apply, apply); + + input.value = '2016-12-31T23:59:59'; + checkValidity(input, true, apply, apply); + + input.value = 'foo'; + checkValidity(input, true, apply, apply); + + input.value = '2016-12-31T23:59:59.123'; + checkValidity(input, false, apply, apply); + + input.value = '2017-01-01T10:00'; + checkValidity(input, false, apply, apply); + + input.max = '2017-01-01T10:00'; + checkValidity(input, true, apply, apply); + + input.value = '2017-01-01T10:00:30'; + checkValidity(input, false, apply, apply); + + input.value = '1000-01-01T12:00'; + checkValidity(input, true, apply, apply); + + input.value = '2100-01-01T12:00'; + checkValidity(input, false, apply, apply); + + input.max = '0050-12-31T23:59:59.999'; + checkValidity(input, false, apply, apply); + + input.value = '0050-12-31T23:59:59'; + checkValidity(input, true, apply, apply); + + input.max = ''; + checkValidity(input, true, apply, false); + + input.max = 'foo'; + checkValidity(input, true, apply, false); break; } diff --git a/dom/html/test/forms/test_min_attribute.html b/dom/html/test/forms/test_min_attribute.html index 1258babec..22f21de39 100644 --- a/dom/html/test/forms/test_min_attribute.html +++ b/dom/html/test/forms/test_min_attribute.html @@ -31,8 +31,7 @@ var data = [ { type: 'month', apply: true }, { type: 'week', apply: true }, { type: 'time', apply: true }, - // TODO: temporary set to false until bug 888331 is fixed. - { type: 'datetime-local', apply: false }, + { type: 'datetime-local', apply: true }, { type: 'number', apply: true }, { type: 'range', apply: true }, { type: 'color', apply: false }, @@ -71,7 +70,8 @@ function checkValidity(aElement, aValidity, aApply, aRangeApply) "element underflow status should be " + !aValidity); var underflowMsg = (aElement.type == "date" || aElement.type == "time" || - aElement.type == "month" || aElement.type == "week") ? + aElement.type == "month" || aElement.type == "week" || + aElement.type == "datetime-local") ? ("Please select a value that is no earlier than " + aElement.min + ".") : ("Please select a value that is no less than " + aElement.min + "."); is(aElement.validationMessage, @@ -146,10 +146,10 @@ for (var test of data) { input.min = '2016-06'; break; case 'week': - input.min = "2016-W39"; + input.min = '2016-W39'; break; case 'datetime-local': - // TODO: this is temporary until bug 888331 is fixed. + input.min = '2017-01-01T00:00'; break; default: ok(false, 'please, add a case for this new type (' + input.type + ')'); @@ -420,7 +420,44 @@ for (var test of data) { checkValidity(input, true, apply, false); break; case 'datetime-local': - // TODO: this is temporary until bug 888331 is fixed. + input.value = '2017-12-31T23:59'; + checkValidity(input, true, apply, apply); + + input.value = '2017-01-01T00:00'; + checkValidity(input, true, apply, apply); + + input.value = '2017-01-01T00:00:00.123'; + checkValidity(input, true, apply, apply); + + input.value = 'foo'; + checkValidity(input, true, apply, apply); + + input.value = '2016-12-31T23:59'; + checkValidity(input, false, apply, apply); + + input.min = '2016-01-01T00:00'; + checkValidity(input, true, apply, apply); + + input.value = '2015-12-31T23:59'; + checkValidity(input, false, apply, apply); + + input.value = '1000-01-01T00:00'; + checkValidity(input, false, apply, apply); + + input.value = '10000-01-01T00:00'; + checkValidity(input, true, apply, apply); + + input.min = '0010-01-01T12:00'; + checkValidity(input, true, apply, apply); + + input.value = '0010-01-01T10:00'; + checkValidity(input, false, apply, apply); + + input.min = ''; + checkValidity(input, true, apply, false); + + input.min = 'foo'; + checkValidity(input, true, apply, false); break; default: ok(false, 'write tests for ' + input.type); diff --git a/dom/html/test/forms/test_step_attribute.html b/dom/html/test/forms/test_step_attribute.html index 31277860c..a14afa461 100644 --- a/dom/html/test/forms/test_step_attribute.html +++ b/dom/html/test/forms/test_step_attribute.html @@ -31,8 +31,7 @@ var data = [ { type: 'month', apply: true }, { type: 'week', apply: true }, { type: 'time', apply: true }, - // TODO: temporary set to false until bug 888331 is fixed. - { type: 'datetime-local', apply: false }, + { type: 'datetime-local', apply: true }, { type: 'number', apply: true }, { type: 'range', apply: true }, { type: 'color', apply: false }, @@ -950,7 +949,104 @@ for (var test of data) { break; case 'datetime-local': - // TODO: this is temporary until bug 888331 is fixed. + // When step is invalid, every datetime is valid + input.step = 0; + input.value = '2017-02-06T12:00'; + checkValidity(input, true, apply); + + input.step = 'foo'; + input.value = '1970-01-01T00:00'; + checkValidity(input, true, apply); + + input.step = '-1'; + input.value = '1969-12-12 00:10'; + checkValidity(input, true, apply); + + input.removeAttribute('step'); + input.value = '1500-01-01T12:00'; + checkValidity(input, true, apply); + + input.step = 'any'; + input.value = '1966-12-12T12:00'; + checkValidity(input, true, apply); + + input.step = 'ANY'; + input.value = '2017-01-01 12:00'; + checkValidity(input, true, apply); + + // When min is set to a valid datetime, there is a step base. + input.min = '2017-01-01T00:00:00'; + input.step = '2'; + input.value = '2017-01-01T00:00:02'; + checkValidity(input, true, apply); + + input.value = '2017-01-01T00:00:03'; + checkValidity(input, false, apply, + { low: "2017-01-01T00:00:02", high: "2017-01-01T00:00:04" }); + + input.min = '2017-01-01T00:00:05'; + input.value = '2017-01-01T00:00:08'; + checkValidity(input, false, apply, + { low: "2017-01-01T00:00:07", high: "2017-01-01T00:00:09" }); + + input.min = '2000-01-01T00:00'; + input.step = '120'; + input.value = '2000-01-01T00:02'; + checkValidity(input, true, apply); + + // Without any step attribute the datetime is valid + input.removeAttribute('step'); + checkValidity(input, true, apply); + + input.min = '1950-01-01T00:00'; + input.step = '129600'; // 1.5 day + input.value = '1950-01-02T00:00'; + checkValidity(input, false, apply, + { low: "1950-01-01T00:00", high: "1950-01-02T12:00" }); + + input.step = '259200'; // 3 days + input.value = '1950-01-04T12:00'; + checkValidity(input, false, apply, + { low: "1950-01-04T00:00", high: "1950-01-07T00:00" }); + + input.value = '1950-01-10T00:00'; + checkValidity(input, true, apply); + + input.step = '0.5'; // half a second + input.value = '1950-01-01T00:00:00.123'; + checkValidity(input, false, apply, + { low: "1950-01-01T00:00", high: "1950-01-01T00:00:00.500" }); + + input.value = '2000-01-01T12:30:30.600'; + checkValidity(input, false, apply, + { low: "2000-01-01T12:30:30.500", high: "2000-01-01T12:30:31" }); + + input.value = '1950-01-05T00:00:00.500'; + checkValidity(input, true, apply); + + input.step = '2.1'; + input.min = '1991-01-01T12:00'; + input.value = '1991-01-01T12:00'; + checkValidity(input, true, apply); + + input.value = '1991-01-01T12:00:03'; + checkValidity(input, false, apply, + { low: "1991-01-01T12:00:02.100", high: "1991-01-01T12:00:04.200" }); + + input.value = '1991-01-01T12:00:06.3'; + checkValidity(input, true, apply); + + input.step = '2.1'; + input.min = '1969-12-20T10:00:05'; + input.value = '1969-12-20T10:00:05'; + checkValidity(input, true, apply); + + input.value = '1969-12-20T10:00:08'; + checkValidity(input, false, apply, + { low: "1969-12-20T10:00:07.100", high: "1969-12-20T10:00:09.200" }); + + input.value = '1969-12-20T10:00:09.200'; + checkValidity(input, true, apply); break; default: diff --git a/dom/html/test/forms/test_stepup_stepdown.html b/dom/html/test/forms/test_stepup_stepdown.html index d96895180..21cde58aa 100644 --- a/dom/html/test/forms/test_stepup_stepdown.html +++ b/dom/html/test/forms/test_stepup_stepdown.html @@ -52,13 +52,8 @@ function checkAvailability() ["time", true], ["month", true], ["week", true], - ["color", false], - ]; - - var todoList = - [ - ["datetime", true], ["datetime-local", true], + ["color", false], ]; var element = document.createElement("input"); @@ -82,27 +77,6 @@ function checkAvailability() } is(exceptionCaught, !data[1], "stepUp() availability is not correct"); } - - for (data of todoList) { - var exceptionCaught = false; - element.type = data[0]; - try { - element.stepDown(); - } catch (e) { - exceptionCaught = true; - } - todo_is(exceptionCaught, !data[1], - "stepDown() availability is not correct"); - - exceptionCaught = false; - try { - element.stepUp(); - } catch (e) { - exceptionCaught = true; - } - todo_is(exceptionCaught, !data[1], - "stepUp() availability is not correct"); - } } function checkStepDown() @@ -509,6 +483,80 @@ function checkStepDown() [ '2016-W01', 'AnY', null, null, 1, null, true ], [ '2016-W01', 'aNy', null, null, 1, null, true ], ]}, + { type: 'datetime-local', data: [ + // Regular case. + [ '2017-02-07T09:30', null, null, null, null, '2017-02-07T09:29', false ], + // Argument testing. + [ '2017-02-07T09:30', null, null, null, 1, '2017-02-07T09:29', false ], + [ '2017-02-07T09:30', null, null, null, 5, '2017-02-07T09:25', false ], + [ '2017-02-07T09:30', null, null, null, -1, '2017-02-07T09:31', false ], + [ '2017-02-07T09:30', null, null, null, 0, '2017-02-07T09:30', false ], + // hour/minutes/seconds wrapping. + [ '2000-01-01T05:00', null, null, null, null, '2000-01-01T04:59', false ], + [ '2000-01-01T05:00:00', 1, null, null, null, '2000-01-01T04:59:59', false ], + [ '2000-01-01T05:00:00', 0.1, null, null, null, '2000-01-01T04:59:59.900', false ], + [ '2000-01-01T05:00:00', 0.01, null, null, null, '2000-01-01T04:59:59.990', false ], + [ '2000-01-01T05:00:00', 0.001, null, null, null, '2000-01-01T04:59:59.999', false ], + // month/year wrapping. + [ '2012-08-01T12:00', null, null, null, 1440, '2012-07-31T12:00', false ], + [ '1969-01-02T12:00', null, null, null, 5760, '1968-12-29T12:00', false ], + [ '1969-12-31T00:00', null, null, null, -1440, '1970-01-01T00:00', false ], + [ '2012-02-29T00:00', null, null, null, -1440, '2012-03-01T00:00', false ], + // stepDown() on '00:00' gives '23:59'. + [ '2017-02-07T00:00', null, null, null, 1, '2017-02-06T23:59', false ], + [ '2017-02-07T00:00', null, null, null, 3, '2017-02-06T23:57', false ], + // Some random step values.. + [ '2017-02-07T16:07', '0.5', null, null, null, '2017-02-07T16:06:59.500', false ], + [ '2017-02-07T16:07', '2', null, null, null, '2017-02-07T16:06:58', false ], + [ '2017-02-07T16:07', '0.25', null, null, 4, '2017-02-07T16:06:59', false ], + [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 1, '2017-02-07T16:06:59.100', false ], + [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 2, '2017-02-07T16:06:58', false ], + [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 10, '2017-02-07T16:06:49.200', false ], + [ '2017-02-07T16:07', '129600', '2017-02-01T00:00', null, 2, '2017-02-05T12:00', false ], + // step = 0 isn't allowed (-> step = 1). + [ '2017-02-07T10:15', '0', null, null, null, '2017-02-07T10:14', false ], + // step < 0 isn't allowed (-> step = 1). + [ '2017-02-07T10:15', '-1', null, null, null, '2017-02-07T10:14', false ], + // step = NaN isn't allowed (-> step = 1). + [ '2017-02-07T10:15', 'foo', null, null, null, '2017-02-07T10:14', false ], + // Min values testing. + [ '2012-02-02T17:02', '60', 'foo', null, 2, '2012-02-02T17:00', false ], + [ '2012-02-02T17:10', '60', '2012-02-02T17:09', null, null, '2012-02-02T17:09', false ], + [ '2012-02-02T17:10', '60', '2012-02-02T17:10', null, null, '2012-02-02T17:10', false ], + [ '2012-02-02T17:10', '60', '2012-02-02T17:30', null, 1, '2012-02-02T17:10', false ], + [ '2012-02-02T17:10', '180', '2012-02-02T17:05', null, null, '2012-02-02T17:08', false ], + [ '2012-02-03T20:05', '86400', '2012-02-02T17:05', null, null, '2012-02-03T17:05', false ], + [ '2012-02-03T18:00', '129600', '2012-02-01T00:00', null, null, '2012-02-02T12:00', false ], + // Max values testing. + [ '2012-02-02T17:15', '60', null, 'foo', null, '2012-02-02T17:14', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:20', null, '2012-02-02T17:14', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:15', null, '2012-02-02T17:14', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:13', 4, '2012-02-02T17:11', false ], + [ '2012-02-02T17:15', '120', null, '2012-02-02T17:13', 3, '2012-02-02T17:09', false ], + [ '2012-02-03T20:05', '86400', null, '2012-02-03T20:05', null, '2012-02-02T20:05', false ], + [ '2012-02-03T18:00', '129600', null, '2012-02-03T20:00', null, '2012-02-02T06:00', false ], + // Step mismatch. + [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, null, '2017-02-07T17:18', false ], + [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, 2, '2017-02-07T17:16', false ], + [ '2017-02-07T17:19', '120', '2017-02-07T17:18', '2017-02-07T17:25', null, '2017-02-07T17:18', false ], + [ '2017-02-07T17:19', '120', null, null, null, '2017-02-07T17:17', false ], + [ '2017-02-07T17:19', '180', null, null, null, '2017-02-07T17:16', false ], + [ '2017-02-07T17:19', '172800', '2017-02-02T17:19', '2017-02-10T17:19', null, '2017-02-06T17:19', false ], + // Clamping. + [ '2017-02-07T17:22', null, null, '2017-02-07T17:11', null, '2017-02-07T17:11', false ], + [ '2017-02-07T17:22', '120', '2017-02-07T17:20', '2017-02-07T17:22', null, '2017-02-07T17:20', false ], + [ '2017-02-07T17:22', '300', '2017-02-07T17:12', '2017-02-07T17:20', 10, '2017-02-07T17:12', false ], + [ '2017-02-07T17:22', '300', '2017-02-07T17:18', '2017-02-07T17:20', 2, '2017-02-07T17:18', false ], + [ '2017-02-07T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:00', 15, '2017-02-07T15:00', false ], + [ '2017-02-07T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:00', 2, '2017-02-07T17:00', false ], + // value = "" (NaN). + [ '', null, null, null, null, '1969-12-31T23:59', false ], + // With step = 'any'. + [ '2017-02-07T15:20', 'any', null, null, 1, null, true ], + [ '2017-02-07T15:20', 'ANY', null, null, 1, null, true ], + [ '2017-02-07T15:20', 'AnY', null, null, 1, null, true ], + [ '2017-02-07T15:20', 'aNy', null, null, 1, null, true ], + ]}, ]; for (var test of testData) { @@ -958,6 +1006,78 @@ function checkStepUp() [ '2016-W01', 'AnY', null, null, 1, null, true ], [ '2016-W01', 'aNy', null, null, 1, null, true ], ]}, + { type: 'datetime-local', data: [ + // Regular case. + [ '2017-02-07T17:09', null, null, null, null, '2017-02-07T17:10', false ], + // Argument testing. + [ '2017-02-07T17:10', null, null, null, 1, '2017-02-07T17:11', false ], + [ '2017-02-07T17:10', null, null, null, 5, '2017-02-07T17:15', false ], + [ '2017-02-07T17:10', null, null, null, -1, '2017-02-07T17:09', false ], + [ '2017-02-07T17:10', null, null, null, 0, '2017-02-07T17:10', false ], + // hour/minutes/seconds wrapping. + [ '2000-01-01T04:59', null, null, null, null, '2000-01-01T05:00', false ], + [ '2000-01-01T04:59:59', 1, null, null, null, '2000-01-01T05:00', false ], + [ '2000-01-01T04:59:59.900', 0.1, null, null, null, '2000-01-01T05:00', false ], + [ '2000-01-01T04:59:59.990', 0.01, null, null, null, '2000-01-01T05:00', false ], + [ '2000-01-01T04:59:59.999', 0.001, null, null, null, '2000-01-01T05:00', false ], + // month/year wrapping. + [ '2012-07-31T12:00', null, null, null, 1440, '2012-08-01T12:00', false ], + [ '1968-12-29T12:00', null, null, null, 5760, '1969-01-02T12:00', false ], + [ '1970-01-01T00:00', null, null, null, -1440, '1969-12-31T00:00', false ], + [ '2012-03-01T00:00', null, null, null, -1440, '2012-02-29T00:00', false ], + // stepUp() on '23:59' gives '00:00'. + [ '2017-02-07T23:59', null, null, null, 1, '2017-02-08T00:00', false ], + [ '2017-02-07T23:59', null, null, null, 3, '2017-02-08T00:02', false ], + // Some random step values.. + [ '2017-02-07T17:40', '0.5', null, null, null, '2017-02-07T17:40:00.500', false ], + [ '2017-02-07T17:40', '2', null, null, null, '2017-02-07T17:40:02', false ], + [ '2017-02-07T17:40', '0.25', null, null, 4, '2017-02-07T17:40:01', false ], + [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 1, '2017-02-07T17:40:00.200', false ], + [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 2, '2017-02-07T17:40:01.300', false ], + [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 10, '2017-02-07T17:40:10.100', false ], + [ '2017-02-07T17:40', '129600', '2017-02-01T00:00', null, 2, '2017-02-10T00:00', false ], + // step = 0 isn't allowed (-> step = 1). + [ '2017-02-07T17:39', '0', null, null, null, '2017-02-07T17:40', false ], + // step < 0 isn't allowed (-> step = 1). + [ '2017-02-07T17:39', '-1', null, null, null, '2017-02-07T17:40', false ], + // step = NaN isn't allowed (-> step = 1). + [ '2017-02-07T17:39', 'foo', null, null, null, '2017-02-07T17:40', false ], + // Min values testing. + [ '2012-02-02T17:00', '60', 'foo', null, 2, '2012-02-02T17:02', false ], + [ '2012-02-02T17:10', '60', '2012-02-02T17:10', null, null, '2012-02-02T17:11', false ], + [ '2012-02-02T17:10', '60', '2012-02-02T17:30', null, 1, '2012-02-02T17:30', false ], + [ '2012-02-02T17:10', '180', '2012-02-02T17:05', null, null, '2012-02-02T17:11', false ], + [ '2012-02-02T17:10', '86400', '2012-02-02T17:05', null, null, '2012-02-03T17:05', false ], + [ '2012-02-02T17:10', '129600', '2012-02-01T00:00', null, null, '2012-02-04T00:00', false ], + // Max values testing. + [ '2012-02-02T17:15', '60', null, 'foo', null, '2012-02-02T17:16', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:20', null, '2012-02-02T17:16', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:15', null, '2012-02-02T17:15', false ], + [ '2012-02-02T17:15', null, null, '2012-02-02T17:13', 4, '2012-02-02T17:15', false ], + [ '2012-02-02T20:05', '86400', null, '2012-02-03T20:05', null, '2012-02-03T20:05', false ], + [ '2012-02-02T18:00', '129600', null, '2012-02-04T20:00', null, '2012-02-04T06:00', false ], + // Step mismatch. + [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, null, '2017-02-07T17:20', false ], + [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, 2, '2017-02-07T17:22', false ], + [ '2017-02-07T17:19', '120', '2017-02-07T17:18', '2017-02-07T17:25', null, '2017-02-07T17:20', false ], + [ '2017-02-07T17:19', '120', null, null, null, '2017-02-07T17:21', false ], + [ '2017-02-07T17:19', '180', null, null, null, '2017-02-07T17:22', false ], + [ '2017-02-03T17:19', '172800', '2017-02-02T17:19', '2017-02-10T17:19', null, '2017-02-04T17:19', false ], + // Clamping. + [ '2017-02-07T17:22', null, null, '2017-02-07T17:11', null, '2017-02-07T17:22', false ], + [ '2017-02-07T17:22', '120', '2017-02-07T17:20', '2017-02-07T17:22', null, '2017-02-07T17:22', false ], + [ '2017-02-07T17:22', '300', '2017-02-07T17:12', '2017-02-07T17:20', 10, '2017-02-07T17:22', false ], + [ '2017-02-07T17:22', '300', '2017-02-07T17:18', '2017-02-07T17:20', 2, '2017-02-07T17:22', false ], + [ '2017-02-06T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:20', 15, '2017-02-06T19:50', false ], + [ '2017-02-06T17:22', '600', '2017-02-02T17:10', '2017-02-07T17:20', 2, '2017-02-06T17:40', false ], + // value = "" (NaN). + [ '', null, null, null, null, '1970-01-01T00:01', false ], + // With step = 'any'. + [ '2017-02-07T17:30', 'any', null, null, 1, null, true ], + [ '2017-02-07T17:30', 'ANY', null, null, 1, null, true ], + [ '2017-02-07T17:30', 'AnY', null, null, 1, null, true ], + [ '2017-02-07T17:30', 'aNy', null, null, 1, null, true ], + ]}, ]; for (var test of testData) { diff --git a/dom/html/test/forms/test_valueAsDate_pref.html b/dom/html/test/forms/test_valueAsDate_pref.html index 8518c291b..0369980e4 100644 --- a/dom/html/test/forms/test_valueAsDate_pref.html +++ b/dom/html/test/forms/test_valueAsDate_pref.html @@ -12,8 +12,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=874640 /** Test for Bug 874640 **/ var states = [ - // dom.experimental_forms, dom.forms.datepicker, dom.forms.datetime, expectedValueAsDate - [ 'true', 'true', 'true', 'true' ], + // dom.experimental_forms, dom.forms.datetime, dom.forms.datetime.others, expectedValueAsDate + [ 'true', 'true', ,'true', 'true' ], [ 'true', 'false', 'false', 'true' ], [ 'false', 'true', 'false', 'true' ], [ 'false', 'false', 'true', 'true' ], @@ -33,8 +33,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=874640 SpecialPowers.pushPrefEnv({"set":[ ["dom.experimental_forms", state[0] === 'true'], - ["dom.forms.datepicker", state[1] === 'true'], - ["dom.forms.datetime", state[2] === 'true']]}, + ["dom.forms.datetime", state[1] === 'true'], + ["dom.forms.datetime.others", state[2] === 'true']]}, function() { iframe.src = 'data:text/html,<script>' + 'parent.is("valueAsDate" in document.createElement("input"), ' + diff --git a/dom/html/test/forms/test_valueasdate_attribute.html b/dom/html/test/forms/test_valueasdate_attribute.html index 8c19fefd9..65cab3b8e 100644 --- a/dom/html/test/forms/test_valueasdate_attribute.html +++ b/dom/html/test/forms/test_valueasdate_attribute.html @@ -47,8 +47,7 @@ var validTypes = ["color", false], ["month", true], ["week", true], - // TODO: temporary set to false until bug 888331 is fixed. - ["datetime-local", false], + ["datetime-local", true], ]; function checkAvailability() @@ -622,6 +621,107 @@ function checkWeekSet() } } +function checkDatetimeLocalGet() +{ + var validData = + [ + // Simple cases. + [ "2016-12-27T10:30", Date.UTC(2016, 11, 27, 10, 30, 0) ], + [ "2016-12-27T10:30:40", Date.UTC(2016, 11, 27, 10, 30, 40) ], + [ "2016-12-27T10:30:40.567", Date.UTC(2016, 11, 27, 10, 30, 40, 567) ], + [ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ], + [ "1970-01-01T00:00", 0 ], + // Leap years. + [ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ], + [ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ], + [ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ], + [ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ], + [ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ], + // Maximum valid datetime-local (limited by the ecma date object range). + [ "275760-09-13T00:00", 8640000000000000 ], + // Minimum valid datetime-local (limited by the input element minimum valid value). + [ "0001-01-01T00:00", -62135596800000 ], + ]; + + var invalidData = + [ + [ "invaliddateime-local" ], + [ "0000-01-01T00:00" ], + [ "2016-12-25T00:00Z" ], + [ "2015-02-29T12:34" ], + [ "1-1-1T12:00" ], + [ "" ], + // This datetime-local is valid for the input element, but is out of the + // date object range. In this case, on getting valueAsDate, a Date object + // will be created, but it will have a NaN internal value, and will return + // the string "Invalid Date". + [ "275760-09-13T12:00", true ], + ]; + + element.type = "datetime-local"; + for (let data of validData) { + element.value = data[0]; + is(element.valueAsDate.valueOf(), data[1], + "valueAsDate should return the " + + "valid date object representing this datetime-local"); + } + + for (let data of invalidData) { + element.value = data[0]; + if (data[1]) { + is(String(element.valueAsDate), "Invalid Date", + "valueAsDate should return an invalid Date object " + + "when the element value is not a valid datetime-local"); + } else { + is(element.valueAsDate, null, + "valueAsDate should return null " + + "when the element value is not a valid datetime-local"); + } + } +} + +function checkDatetimeLocalSet() +{ + var testData = + [ + // Simple cases. + [ Date.UTC(2016, 11, 27, 10, 30, 0), "2016-12-27T10:30" ], + [ Date.UTC(2016, 11, 27, 10, 30, 30), "2016-12-27T10:30:30" ], + [ Date.UTC(1999, 11, 31, 23, 59, 59), "1999-12-31T23:59:59" ], + [ Date.UTC(1999, 11, 31, 23, 59, 59, 999), "1999-12-31T23:59:59.999" ], + [ Date.UTC(123456, 7, 8, 9, 10), "123456-08-08T09:10" ], + [ 0, "1970-01-01T00:00" ], + // Maximum valid datetime-local (limited by the ecma date object range). + [ 8640000000000000, "275760-09-13T00:00" ], + // Minimum valid datetime-local (limited by the input element minimum valid value). + [ -62135596800000, "0001-01-01T00:00" ], + // Leap years. + [ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ], + [ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ], + [ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ], + [ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ], + [ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ], + // "Values must be truncated to valid datetime-local" + [ 123.123456789123, "1970-01-01T00:00:00.123" ], + [ 1e-1, "1970-01-01T00:00" ], + [ -1.1, "1969-12-31T23:59:59.999" ], + [ -345600000, "1969-12-28T00:00" ], + // Negative years, this is out of range for the input element, + // the corresponding datetime-local string is the empty string + [ -62135596800001, "" ], + ]; + + element.type = "datetime-local"; + for (let data of testData) { + element.valueAsDate = new Date(data[0]); + is(element.value, data[1], "valueAsDate should set the value to " + + data[1]); + element.valueAsDate = new testFrame.Date(data[0]); + is(element.value, data[1], "valueAsDate with other-global date should " + + "set the value to " + data[1]); + } +} + checkAvailability(); checkGarbageValues(); checkWithBustedPrototype(); @@ -642,6 +742,10 @@ checkMonthSet(); checkWeekGet(); checkWeekSet(); +// Test <input type='datetime-local'>. +checkDatetimeLocalGet(); +checkDatetimeLocalSet(); + </script> </pre> </body> diff --git a/dom/html/test/forms/test_valueasnumber_attribute.html b/dom/html/test/forms/test_valueasnumber_attribute.html index d7471502b..2660fc7ed 100644 --- a/dom/html/test/forms/test_valueasnumber_attribute.html +++ b/dom/html/test/forms/test_valueasnumber_attribute.html @@ -46,8 +46,7 @@ function checkAvailability() ["color", false], ["month", true], ["week", true], - // TODO: temporary set to false until bug 888331 is fixed. - ["datetime-local", false], + ["datetime-local", true], ]; var element = document.createElement('input'); @@ -612,7 +611,6 @@ function checkWeekGet() var element = document.createElement('input'); element.type = "week"; for (let data of validData) { - dump("Test: " + data[0]); element.value = data[0]; is(element.valueAsNumber, data[1], "valueAsNumber should return the " + "integer value representing this week"); @@ -698,7 +696,120 @@ function checkWeekSet() try { element.valueAsNumber = data[0]; - is(element.value, data[1], "valueAsNumber should set the value to " + data[1]); + is(element.value, data[1], "valueAsNumber should set the value to " + + data[1]); + } catch(e) { + caught = true; + } + + if (data[2]) { + ok(caught, "valueAsNumber should have thrown"); + is(element.value, data[1], "the value should not have changed"); + } else { + ok(!caught, "valueAsNumber should not have thrown"); + } + } +} + +function checkDatetimeLocalGet() { + var validData = + [ + // Simple cases. + [ "2016-12-20T09:58", Date.UTC(2016, 11, 20, 9, 58) ], + [ "2016-12-20T09:58:30", Date.UTC(2016, 11, 20, 9, 58, 30) ], + [ "2016-12-20T09:58:30.123", Date.UTC(2016, 11, 20, 9, 58, 30, 123) ], + [ "2017-01-01T10:00", Date.UTC(2017, 0, 1, 10, 0, 0) ], + [ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ], + [ "1970-01-01T00:00", 0 ], + // Leap years. + [ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ], + [ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ], + [ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ], + [ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ], + [ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ], + // Maximum valid datetime-local (limited by the ecma date object range). + [ "275760-09-13T00:00", 8640000000000000 ], + // Minimum valid datetime-local (limited by the input element minimum valid value). + [ "0001-01-01T00:00", -62135596800000 ], + ]; + + var invalidData = + [ + "invaliddatetime-local", + "0000-01-01T00:00", + "2016-12-25T00:00Z", + "2015-02-29T12:34", + "1-1-1T12:00", + // Out of range. + "275760-09-13T12:00", + ]; + + var element = document.createElement('input'); + element.type = "datetime-local"; + for (let data of validData) { + element.value = data[0]; + is(element.valueAsNumber, data[1], "valueAsNumber should return the " + + "integer value representing this datetime-local"); + } + + for (let data of invalidData) { + element.value = data; + ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " + + "when the element value is not a valid datetime-local"); + } +} + +function checkDatetimeLocalSet() +{ + var testData = + [ + // Simple cases. + [ Date.UTC(2016, 11, 20, 9, 58, 0), "2016-12-20T09:58", ], + [ Date.UTC(2016, 11, 20, 9, 58, 30), "2016-12-20T09:58:30" ], + [ Date.UTC(2016, 11, 20, 9, 58, 30, 123), "2016-12-20T09:58:30.123" ], + [ Date.UTC(2017, 0, 1, 10, 0, 0), "2017-01-01T10:00" ], + [ Date.UTC(1969, 11, 31, 12, 0, 0), "1969-12-31T12:00" ], + [ 0, "1970-01-01T00:00" ], + // Maximum valid week (limited by the ecma date object range). + [ 8640000000000000, "275760-09-13T00:00" ], + // Minimum valid datetime-local (limited by the input element minimum valid value). + [ -62135596800000, "0001-01-01T00:00" ], + // Leap years. + [ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ], + [ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ], + [ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ], + [ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ], + [ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ], + // "Values must be truncated to valid datetime-local" + [ 0.3, "1970-01-01T00:00" ], + [ 1e-1, "1970-01-01T00:00" ], + [ -1 , "1969-12-31T23:59:59.999" ], + [ -345600000, "1969-12-28T00:00" ], + // Invalid numbers. + // Those are implicitly converted to numbers + [ "", "1970-01-01T00:00" ], + [ true, "1970-01-01T00:00:00.001" ], + [ false, "1970-01-01T00:00" ], + [ null, "1970-01-01T00:00" ], + // Those are converted to NaN, the corresponding week string is the empty string + [ "invaliddatetime-local", "" ], + [ NaN, "" ], + [ undefined, "" ], + // Infinity will keep the current value and throw (so we need to set a current value). + [ Date.UTC(2016, 11, 27, 15, 10, 0), "2016-12-27T15:10" ], + [ Infinity, "2016-12-27T15:10", true ], + [ -Infinity, "2016-12-27T15:10", true ], + ]; + + var element = document.createElement('input'); + element.type = "datetime-local"; + for (let data of testData) { + var caught = false; + + try { + element.valueAsNumber = data[0]; + is(element.value, data[1], "valueAsNumber should set the value to " + + data[1]); } catch(e) { caught = true; } @@ -738,6 +849,10 @@ checkMonthSet(); checkWeekGet(); checkWeekSet(); +// <input type='datetime-local'> test +checkDatetimeLocalGet(); +checkDatetimeLocalSet(); + </script> </pre> </body> diff --git a/dom/html/test/test_bug558788-1.html b/dom/html/test/test_bug558788-1.html index 94b7a5f00..4db61ed73 100644 --- a/dom/html/test/test_bug558788-1.html +++ b/dom/html/test/test_bug558788-1.html @@ -154,13 +154,14 @@ function checkInputURL() sendString("ttp://mozilla.org"); checkValidApplies(element); - for (var i=0; i<13; ++i) { + for (var i=0; i<10; ++i) { synthesizeKey("VK_BACK_SPACE", {}); checkValidApplies(element); } synthesizeKey("VK_BACK_SPACE", {}); - for (var i=0; i<4; ++i) { + // "http://" is now invalid + for (var i=0; i<7; ++i) { checkInvalidApplies(element); synthesizeKey("VK_BACK_SPACE", {}); } diff --git a/dom/ipc/DatePickerParent.cpp b/dom/ipc/DatePickerParent.cpp deleted file mode 100644 index 509944ddd..000000000 --- a/dom/ipc/DatePickerParent.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- 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 "DatePickerParent.h" -#include "nsComponentManagerUtils.h" -#include "nsIDocument.h" -#include "nsIDOMWindow.h" -#include "mozilla/Unused.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/TabParent.h" - -using mozilla::Unused; -using namespace mozilla::dom; - -NS_IMPL_ISUPPORTS(DatePickerParent::DatePickerShownCallback, - nsIDatePickerShownCallback); - -NS_IMETHODIMP -DatePickerParent::DatePickerShownCallback::Cancel() -{ - if (mDatePickerParent) { - Unused << mDatePickerParent->SendCancel(); - } - return NS_OK; -} - -NS_IMETHODIMP -DatePickerParent::DatePickerShownCallback::Done(const nsAString& aDate) -{ - if (mDatePickerParent) { - Unused << mDatePickerParent->Send__delete__(mDatePickerParent, - nsString(aDate)); - } - return NS_OK; -} - -void -DatePickerParent::DatePickerShownCallback::Destroy() -{ - mDatePickerParent = nullptr; -} - -bool -DatePickerParent::CreateDatePicker() -{ - mPicker = do_CreateInstance("@mozilla.org/datepicker;1"); - if (!mPicker) { - return false; - } - - Element* ownerElement = TabParent::GetFrom(Manager())->GetOwnerElement(); - if (!ownerElement) { - return false; - } - - nsCOMPtr<mozIDOMWindowProxy> window = do_QueryInterface(ownerElement->OwnerDoc()->GetWindow()); - if (!window) { - return false; - } - - return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialDate)); -} - -bool -DatePickerParent::RecvOpen() -{ - if (!CreateDatePicker()) { - Unused << Send__delete__(this, mInitialDate); - return true; - } - - mCallback = new DatePickerShownCallback(this); - - mPicker->Open(mCallback); - return true; -}; - -void -DatePickerParent::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mCallback) { - mCallback->Destroy(); - } -} diff --git a/dom/ipc/DatePickerParent.h b/dom/ipc/DatePickerParent.h deleted file mode 100644 index 73b66f96c..000000000 --- a/dom/ipc/DatePickerParent.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- 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/. */ - -#ifndef mozilla_dom_DatePickerParent_h -#define mozilla_dom_DatePickerParent_h - -#include "mozilla/dom/PDatePickerParent.h" -#include "nsIDatePicker.h" - -namespace mozilla { -namespace dom { - -class DatePickerParent : public PDatePickerParent -{ - public: - DatePickerParent(const nsString& aTitle, - const nsString& aInitialDate) - : mTitle(aTitle) - , mInitialDate(aInitialDate) - {} - - virtual bool RecvOpen() override; - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - - class DatePickerShownCallback final - : public nsIDatePickerShownCallback - { - public: - explicit DatePickerShownCallback(DatePickerParent* aDatePickerParnet) - : mDatePickerParent(aDatePickerParnet) - {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIDATEPICKERSHOWNCALLBACK - - void Destroy(); - - private: - ~DatePickerShownCallback() {} - DatePickerParent* mDatePickerParent; - }; - - private: - virtual ~DatePickerParent() {} - - bool CreateDatePicker(); - - RefPtr<DatePickerShownCallback> mCallback; - nsCOMPtr<nsIDatePicker> mPicker; - - nsString mTitle; - nsString mInitialDate; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_DatePickerParent_h diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 9dfccbc5c..f09e484ee 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -9,7 +9,6 @@ include protocol PBlob; include protocol PColorPicker; include protocol PContent; include protocol PContentBridge; -include protocol PDatePicker; include protocol PDocAccessible; include protocol PFilePicker; include protocol PIndexedDBPermissionRequest; @@ -116,7 +115,6 @@ nested(upto inside_cpow) sync protocol PBrowser manager PContent or PContentBridge; manages PColorPicker; - manages PDatePicker; manages PDocAccessible; manages PFilePicker; manages PIndexedDBPermissionRequest; @@ -441,12 +439,6 @@ parent: */ async PColorPicker(nsString title, nsString initialColor); - /** - * Create an asynchronous date picker on the parent side, - * but don't open it yet. - */ - async PDatePicker(nsString title, nsString initialDate); - async PFilePicker(nsString aTitle, int16_t aMode); /** diff --git a/dom/ipc/PDatePicker.ipdl b/dom/ipc/PDatePicker.ipdl deleted file mode 100644 index 90a2654bb..000000000 --- a/dom/ipc/PDatePicker.ipdl +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ - -/* 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 protocol PBrowser; - -namespace mozilla { -namespace dom { - -protocol PDatePicker -{ - manager PBrowser; - -parent: - async Open(); - -child: - async Cancel(); - - async __delete__(nsString color); -}; - -} // namespace dom -} // namespace mozilla diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index c8a0c6e3f..705799c54 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -96,7 +96,6 @@ #include "LayersLogging.h" #include "nsDOMClassInfoID.h" #include "nsColorPickerProxy.h" -#include "nsDatePickerProxy.h" #include "nsContentPermissionHelper.h" #include "nsPresShell.h" #include "nsIAppsService.h" @@ -2013,21 +2012,6 @@ TabChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker) return true; } -PDatePickerChild* -TabChild::AllocPDatePickerChild(const nsString&, const nsString&) -{ - NS_RUNTIMEABORT("unused"); - return nullptr; -} - -bool -TabChild::DeallocPDatePickerChild(PDatePickerChild* aDatePicker) -{ - nsDatePickerProxy* picker = static_cast<nsDatePickerProxy*>(aDatePicker); - NS_RELEASE(picker); - return true; -} - PFilePickerChild* TabChild::AllocPFilePickerChild(const nsString&, const int16_t&) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 2232ffeaf..d9988a596 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -451,10 +451,6 @@ public: virtual bool DeallocPColorPickerChild(PColorPickerChild* aActor) override; - virtual PDatePickerChild* - AllocPDatePickerChild(const nsString& title, const nsString& initialDate) override; - virtual bool DeallocPDatePickerChild(PDatePickerChild* actor) override; - virtual PFilePickerChild* AllocPFilePickerChild(const nsString& aTitle, const int16_t& aMode) override; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 8e98de3ce..0f190708f 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -80,7 +80,6 @@ #include "PermissionMessageUtils.h" #include "StructuredCloneData.h" #include "ColorPickerParent.h" -#include "DatePickerParent.h" #include "FilePickerParent.h" #include "TabChild.h" #include "LoadContext.h" @@ -2424,20 +2423,6 @@ TabParent::DeallocPColorPickerParent(PColorPickerParent* actor) return true; } -PDatePickerParent* -TabParent::AllocPDatePickerParent(const nsString& aTitle, - const nsString& aInitialDate) -{ - return new DatePickerParent(aTitle, aInitialDate); -} - -bool -TabParent::DeallocPDatePickerParent(PDatePickerParent* actor) -{ - delete actor; - return true; -} - PRenderFrameParent* TabParent::AllocPRenderFrameParent() { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 09bb999f3..3624ce320 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -347,10 +347,6 @@ public: virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override; - virtual PDatePickerParent* - AllocPDatePickerParent(const nsString& aTitle, const nsString& aInitialDate) override; - virtual bool DeallocPDatePickerParent(PDatePickerParent* aDatePicker) override; - virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&, const uint32_t&, const IAccessibleHolder&) override; diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index ff3880bc2..71d193d44 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -55,7 +55,6 @@ UNIFIED_SOURCES += [ 'ContentProcess.cpp', 'ContentProcessManager.cpp', 'CrashReporterParent.cpp', - 'DatePickerParent.cpp', 'FilePickerParent.cpp', 'nsIContentChild.cpp', 'nsIContentParent.cpp', @@ -95,7 +94,6 @@ IPDL_SOURCES += [ 'PContentPermissionRequest.ipdl', 'PCrashReporter.ipdl', 'PCycleCollectWithLogs.ipdl', - 'PDatePicker.ipdl', 'PFilePicker.ipdl', 'PMemoryReportRequest.ipdl', 'PPluginWidget.ipdl', diff --git a/dom/locales/en-US/chrome/layout/HtmlForm.properties b/dom/locales/en-US/chrome/layout/HtmlForm.properties index 621a0b2b3..82274b2bc 100644 --- a/dom/locales/en-US/chrome/layout/HtmlForm.properties +++ b/dom/locales/en-US/chrome/layout/HtmlForm.properties @@ -34,7 +34,6 @@ NoDirSelected=No directory selected. # %S will be a number greater or equal to 2. XFilesSelected=%S files selected. ColorPicker=Choose a color -DatePicker=Choose a date # LOCALIZATION NOTE (AndNMoreFiles): Semi-colon list of plural forms. # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals # This string is shown at the end of the tooltip text for <input type='file' diff --git a/dom/url/tests/test_url.html b/dom/url/tests/test_url.html index d07a752bb..73e75667d 100644 --- a/dom/url/tests/test_url.html +++ b/dom/url/tests/test_url.html @@ -399,6 +399,18 @@ </script> <script> + /** Test for Bug 1275746 **/ + SimpleTest.doesThrow(() => { var url = new URL("http:"); }, "http: is not a valid URL"); + SimpleTest.doesThrow(() => { var url = new URL("http:///"); }, "http: is not a valid URL"); + + var url = new URL("file:"); + is(url.href, "file:///", "Parsing file: should work."); + + url = new URL("file:///"); + is(url.href, "file:///", "Parsing file:/// should work."); + </script> + + <script> var url = new URL("scheme:path/to/file?query#hash"); is(url.href, "scheme:path/to/file?query#hash"); is(url.pathname, "path/to/file"); diff --git a/dom/webidl/CanvasRenderingContext2D.webidl b/dom/webidl/CanvasRenderingContext2D.webidl index 38a30f9e3..1c5564215 100644 --- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -27,6 +27,9 @@ dictionary HitRegionOptions { }; typedef (HTMLImageElement or + SVGImageElement) HTMLOrSVGImageElement; + +typedef (HTMLOrSVGImageElement or HTMLCanvasElement or HTMLVideoElement or ImageBitmap) CanvasImageSource; diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index 050d19510..cf3e9a4c7 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -238,6 +238,9 @@ partial interface HTMLInputElement { dictionary DateTimeValue { long hour; long minute; + long year; + long month; + long day; }; partial interface HTMLInputElement { @@ -250,6 +253,14 @@ partial interface HTMLInputElement { [Pref="dom.forms.datetime", ChromeOnly] void setDateTimePickerState(boolean open); + [Pref="dom.forms.datetime", ChromeOnly, + BinaryName="getMinimumAsDouble"] + double getMinimum(); + + [Pref="dom.forms.datetime", ChromeOnly, + BinaryName="getMaximumAsDouble"] + double getMaximum(); + [Pref="dom.forms.datetime", Func="IsChromeOrXBL"] void openDateTimePicker(optional DateTimeValue initialValue); @@ -258,4 +269,12 @@ partial interface HTMLInputElement { [Pref="dom.forms.datetime", Func="IsChromeOrXBL"] void closeDateTimePicker(); + + [Pref="dom.forms.datetime", Func="IsChromeOrXBL", + BinaryName="getStepAsDouble"] + double getStep(); + + [Pref="dom.forms.datetime", Func="IsChromeOrXBL", + BinaryName="getStepBaseAsDouble"] + double getStepBase(); }; |