Bug 1514040 - Dispatch events to hidden datetimebox UA Widget r=smaug

The XBL binding implementation relied on nsDateTimeControlFrame to call into
its nsIDateTimeInputArea implementation. This is correct because the XBL binding
is only constructed when the element has a frame. If the value is set while the
element is hidden, the XBL binding will pick up the correct value during construction.

That is not the case for UA Widget. As it is constructed when the DOM is attached,
relying on nsDateTimeControlFrame to send an event when attributes change means
the event won't be sent to the already constructed UA Widget.

This patch fixes that by moving the event dispatching calls originating from
HTMLInputElement out of nsDateTimeControlFrame, so they will behave correctly in
the absence of the frame.

I've also moved the gut of nsDateTimeControlFrame::HasBadInput() to
DateTimeInputTypeBase::HasBadInput(). Content script should be allowed to validate
the input without the frame.

Sadly this means the XBL implementation and the UA Widget implementation
have further diverged. The complexity should go away when we could finally
remove the XBL implementation.

nsDateTimeControlFrame still dispatches a few events to UA Widget, in
AttributeChanged() and SyncDisabledState(), as they are originated from the layout.

The name of the events in AttributeChanged() are incorrect though -- I am correcting
that in this patch too.

Differential Revision: https://phabricator.services.mozilla.com/D15601
This commit is contained in:
Timothy Guan-tin Chien
2019-01-04 21:53:51 +00:00
parent 9ad1578504
commit 9b412c1daf
9 changed files with 210 additions and 159 deletions

View File

@@ -2057,12 +2057,26 @@ void HTMLInputElement::GetDateTimeInputBoxValue(DateTimeValue& aValue) {
aValue = *mDateTimeInputBoxValue;
}
Element* HTMLInputElement::GetDateTimeBoxElementInUAWidget() {
if (GetShadowRoot()) {
// The datetimebox <div> is the only child of the UA Widget Shadow Root
// if it is present.
MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
MOZ_ASSERT(1 >= GetShadowRoot()->GetChildCount());
if (nsIContent* inputAreaContent = GetShadowRoot()->GetFirstChild()) {
return inputAreaContent->AsElement();
}
}
return nullptr;
}
Element* HTMLInputElement::GetDateTimeBoxElement() {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame && frame->GetInputAreaContent()) {
return frame->GetInputAreaContent()->AsElement();
}
return nullptr;
return GetDateTimeBoxElementInUAWidget();
}
void HTMLInputElement::OpenDateTimePicker(const DateTimeValue& aInitialValue) {
@@ -2620,9 +2634,17 @@ nsresult HTMLInputElement::SetValueInternal(const nsAString& aValue,
mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType) &&
!(aFlags & nsTextEditorState::eSetValue_BySetUserInput)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->OnValueChanged();
if (Element* dateTimeBoxElement = GetDateTimeBoxElementInUAWidget()) {
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
dateTimeBoxElement,
NS_LITERAL_STRING("MozDateTimeValueChanged"), CanBubble::eNo,
ChromeOnlyDispatch::eNo);
dispatcher->RunDOMEventWhenSafe();
} else {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->OnValueChanged();
}
}
}
if (mDoneCreating) {
@@ -2909,10 +2931,18 @@ void HTMLInputElement::Blur(ErrorResult& aError) {
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleBlurEvent();
if (Element* dateTimeBoxElement = GetDateTimeBoxElementInUAWidget()) {
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
dateTimeBoxElement, NS_LITERAL_STRING("MozBlurInnerTextBox"),
CanBubble::eNo, ChromeOnlyDispatch::eNo);
dispatcher->RunDOMEventWhenSafe();
return;
} else {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleBlurEvent();
return;
}
}
}
@@ -2935,10 +2965,18 @@ void HTMLInputElement::Focus(ErrorResult& aError) {
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleFocusEvent();
if (Element* dateTimeBoxElement = GetDateTimeBoxElementInUAWidget()) {
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
dateTimeBoxElement, NS_LITERAL_STRING("MozFocusInnerTextBox"),
CanBubble::eNo, ChromeOnlyDispatch::eNo);
dispatcher->RunDOMEventWhenSafe();
return;
} else {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleFocusEvent();
return;
}
}
}
@@ -3245,11 +3283,18 @@ void HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType) && aVisitor.mEvent->mMessage == eFocus &&
aVisitor.mEvent->mOriginalTarget == this) {
// If original target is this and not the anonymous text control, we should
// pass the focus to the anonymous text control.
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleFocusEvent();
// If original target is this and not the inner text control, we should
// pass the focus to the inner text control.
if (Element* dateTimeBoxElement = GetDateTimeBoxElementInUAWidget()) {
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
dateTimeBoxElement, NS_LITERAL_STRING("MozFocusInnerTextBox"),
CanBubble::eNo, ChromeOnlyDispatch::eNo);
dispatcher->RunDOMEventWhenSafe();
} else {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleFocusEvent();
}
}
}