forked from mirrors/gecko-dev
Bug 1860457 - implement popover invoke defaults r=dom-core,sefeng
Differential Revision: https://phabricator.services.mozilla.com/D206184
This commit is contained in:
parent
31308a6d79
commit
236262d7e6
9 changed files with 132 additions and 92 deletions
|
|
@ -1131,8 +1131,20 @@ class Element : public FragmentOrElement {
|
|||
virtual bool IsValidInvokeAction(InvokeAction aAction) const {
|
||||
return aAction == InvokeAction::Auto;
|
||||
}
|
||||
MOZ_CAN_RUN_SCRIPT virtual void HandleInvokeInternal(InvokeAction aAction,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
/**
|
||||
* Elements can provide their own default behaviours for "Invoke" (see
|
||||
* invoketarget/invokeaction attributes).
|
||||
* If the action is not recognised, they can choose to ignore it and `return
|
||||
* false`. If an action is recognised then they should `return true` to
|
||||
* indicate to sub-classes that this has been handled and no further steps
|
||||
* should be run.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT virtual bool HandleInvokeInternal(Element* invoker,
|
||||
InvokeAction aAction,
|
||||
ErrorResult& aRv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void DescribeAttribute(uint32_t index, nsAString& aOutDescription) const;
|
||||
|
|
|
|||
|
|
@ -3343,6 +3343,11 @@ Element* nsINode::GetNearestInclusiveOpenPopover() const {
|
|||
|
||||
Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
|
||||
for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
|
||||
if (auto* popover = el->GetEffectiveInvokeTargetElement()) {
|
||||
if (popover->IsAutoPopover() && popover->IsPopoverOpen()) {
|
||||
return popover;
|
||||
}
|
||||
}
|
||||
if (auto* popover = el->GetEffectivePopoverTargetElement()) {
|
||||
if (popover->IsAutoPopover() && popover->IsPopoverOpen()) {
|
||||
return popover;
|
||||
|
|
@ -3352,6 +3357,26 @@ Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsGenericHTMLElement* nsINode::GetEffectiveInvokeTargetElement() const {
|
||||
if (!StaticPrefs::dom_element_invokers_enabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto* formControl =
|
||||
nsGenericHTMLFormControlElementWithState::FromNode(this);
|
||||
if (!formControl || formControl->IsDisabled() ||
|
||||
!formControl->IsButtonControl()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (auto* popover = nsGenericHTMLElement::FromNodeOrNull(
|
||||
formControl->GetInvokeTargetElement())) {
|
||||
if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) {
|
||||
return popover;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsGenericHTMLElement* nsINode::GetEffectivePopoverTargetElement() const {
|
||||
if (!StaticPrefs::dom_element_popover_enabled()) {
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -542,6 +542,8 @@ class nsINode : public mozilla::dom::EventTarget {
|
|||
*/
|
||||
mozilla::dom::Element* GetNearestInclusiveTargetPopoverForInvoker() const;
|
||||
|
||||
nsGenericHTMLElement* GetEffectiveInvokeTargetElement() const;
|
||||
|
||||
/**
|
||||
* https://html.spec.whatwg.org/multipage/popover.html#popover-target-element
|
||||
*/
|
||||
|
|
@ -2473,34 +2475,34 @@ inline nsISupports* ToSupports(nsINode* aPointer) { return aPointer; }
|
|||
|
||||
// Some checks are faster to do on nsIContent or Element than on
|
||||
// nsINode, so spit out FromNode versions taking those types too.
|
||||
#define NS_IMPL_FROMNODE_GENERIC(_class, _check, _const) \
|
||||
template <typename T> \
|
||||
static auto FromNode(_const T& aNode) \
|
||||
-> decltype(static_cast<_const _class*>(&aNode)) { \
|
||||
return aNode._check ? static_cast<_const _class*>(&aNode) : nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromNode(_const T* aNode) { \
|
||||
return FromNode(*aNode); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromNodeOrNull(_const T* aNode) { \
|
||||
return aNode ? FromNode(*aNode) : nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static auto FromEventTarget(_const T& aEventTarget) \
|
||||
-> decltype(static_cast<_const _class*>(&aEventTarget)) { \
|
||||
return aEventTarget.IsNode() && aEventTarget.AsNode()->_check \
|
||||
? static_cast<_const _class*>(&aEventTarget) \
|
||||
: nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromEventTarget(_const T* aEventTarget) { \
|
||||
return FromEventTarget(*aEventTarget); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromEventTargetOrNull(_const T* aEventTarget) { \
|
||||
return aEventTarget ? FromEventTarget(*aEventTarget) : nullptr; \
|
||||
#define NS_IMPL_FROMNODE_GENERIC(_class, _check, _const) \
|
||||
template <typename T> \
|
||||
static auto FromNode( \
|
||||
_const T& aNode) -> decltype(static_cast<_const _class*>(&aNode)) { \
|
||||
return aNode._check ? static_cast<_const _class*>(&aNode) : nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromNode(_const T* aNode) { \
|
||||
return FromNode(*aNode); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromNodeOrNull(_const T* aNode) { \
|
||||
return aNode ? FromNode(*aNode) : nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static auto FromEventTarget(_const T& aEventTarget) \
|
||||
-> decltype(static_cast<_const _class*>(&aEventTarget)) { \
|
||||
return aEventTarget.IsNode() && aEventTarget.AsNode()->_check \
|
||||
? static_cast<_const _class*>(&aEventTarget) \
|
||||
: nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromEventTarget(_const T* aEventTarget) { \
|
||||
return FromEventTarget(*aEventTarget); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static _const _class* FromEventTargetOrNull(_const T* aEventTarget) { \
|
||||
return aEventTarget ? FromEventTarget(*aEventTarget) : nullptr; \
|
||||
}
|
||||
|
||||
#define NS_IMPL_FROMNODE_HELPER(_class, _check) \
|
||||
|
|
|
|||
|
|
@ -145,24 +145,34 @@ JSObject* HTMLDetailsElement::WrapNode(JSContext* aCx,
|
|||
}
|
||||
|
||||
bool HTMLDetailsElement::IsValidInvokeAction(InvokeAction aAction) const {
|
||||
return Element::IsValidInvokeAction(aAction) ||
|
||||
return nsGenericHTMLElement::IsValidInvokeAction(aAction) ||
|
||||
aAction == InvokeAction::Toggle || aAction == InvokeAction::Close ||
|
||||
aAction == InvokeAction::Open;
|
||||
}
|
||||
|
||||
void HTMLDetailsElement::HandleInvokeInternal(InvokeAction aAction,
|
||||
bool HTMLDetailsElement::HandleInvokeInternal(Element* aInvoker,
|
||||
InvokeAction aAction,
|
||||
ErrorResult& aRv) {
|
||||
if (nsGenericHTMLElement::HandleInvokeInternal(aInvoker, aAction, aRv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aAction == InvokeAction::Auto || aAction == InvokeAction::Toggle) {
|
||||
ToggleOpen();
|
||||
return true;
|
||||
} else if (aAction == InvokeAction::Close) {
|
||||
if (Open()) {
|
||||
SetOpen(false, IgnoreErrors());
|
||||
}
|
||||
return true;
|
||||
} else if (aAction == InvokeAction::Open) {
|
||||
if (!Open()) {
|
||||
SetOpen(true, IgnoreErrors());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ class HTMLDetailsElement final : public nsGenericHTMLElement {
|
|||
void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
|
||||
|
||||
bool IsValidInvokeAction(InvokeAction aAction) const override;
|
||||
void HandleInvokeInternal(InvokeAction aAction, ErrorResult& aRv) override;
|
||||
MOZ_CAN_RUN_SCRIPT bool HandleInvokeInternal(Element* invoker,
|
||||
InvokeAction aAction,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
protected:
|
||||
virtual ~HTMLDetailsElement();
|
||||
|
|
|
|||
|
|
@ -2988,7 +2988,48 @@ void nsGenericHTMLFormControlElementWithState::HandleInvokeTargetAction() {
|
|||
return;
|
||||
}
|
||||
|
||||
invokee->HandleInvokeInternal(action, IgnoreErrors());
|
||||
invokee->HandleInvokeInternal(this, action, IgnoreErrors());
|
||||
}
|
||||
|
||||
bool nsGenericHTMLElement::IsValidInvokeAction(InvokeAction aAction) const {
|
||||
return Element::IsValidInvokeAction(aAction) ||
|
||||
aAction == InvokeAction::ShowPopover ||
|
||||
aAction == InvokeAction::TogglePopover ||
|
||||
aAction == InvokeAction::HidePopover;
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT bool nsGenericHTMLElement::HandleInvokeInternal(
|
||||
Element* aInvoker, InvokeAction aAction, ErrorResult& aRv) {
|
||||
if (Element::HandleInvokeInternal(aInvoker, aAction, aRv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the element is a `popover` then we may want to handle the
|
||||
// invokeaction...
|
||||
auto popoverState = GetPopoverAttributeState();
|
||||
if (popoverState == PopoverAttributeState::None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool canShow = aAction == InvokeAction::Auto ||
|
||||
aAction == InvokeAction::TogglePopover ||
|
||||
aAction == InvokeAction::ShowPopover;
|
||||
const bool canHide = aAction == InvokeAction::Auto ||
|
||||
aAction == InvokeAction::TogglePopover ||
|
||||
aAction == InvokeAction::HidePopover;
|
||||
|
||||
if (canShow && !IsPopoverOpen()) {
|
||||
ShowPopoverInternal(aInvoker, aRv);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (canHide && IsPopoverOpen()) {
|
||||
HidePopoverInternal(/* aFocusPreviousElement = */ true,
|
||||
/* aFireEvents = */ true, IgnoreErrors());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void nsGenericHTMLFormControlElementWithState::GenerateStateKey() {
|
||||
|
|
|
|||
|
|
@ -185,6 +185,12 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase {
|
|||
void ForgetPreviouslyFocusedElementAfterHidingPopover();
|
||||
MOZ_CAN_RUN_SCRIPT void FocusPreviousElementAfterHidingPopover();
|
||||
|
||||
bool IsValidInvokeAction(mozilla::dom::InvokeAction aAction) const override;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT bool HandleInvokeInternal(
|
||||
Element* aInvoker, mozilla::dom::InvokeAction aAction,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void FocusCandidate(Element*, bool aClearUpFocus);
|
||||
|
||||
void SetNonce(const nsAString& aNonce) {
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
[invoketarget-on-popover-behavior.tentative.html]
|
||||
prefs: [dom.element.popover.enabled: true]
|
||||
[invoking (as auto) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as togglepopover) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as showpopover) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as showpopover) open popover is noop]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as auto) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as togglepopover) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
||||
[changing invokeaction attribute inside invokeevent doesn't impact the invocation]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as explicit empty) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as tOgGlEpOpOvEr) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as sHoWpOpOvEr) closed popover opens]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as explicit empty) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as hidepopover) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as tOgGlEpOpOvEr) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as hIdEpOpOvEr) from within open popover closes]
|
||||
expected: FAIL
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
[invoketarget-on-popover-invalid-behavior.tentative.html]
|
||||
[invoking (as foo-bar) on open popover does nothing]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as showmodal) on open popover does nothing]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as showpicker) on open popover does nothing]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as open) on open popover does nothing]
|
||||
expected: FAIL
|
||||
|
||||
[invoking (as close) on open popover does nothing]
|
||||
expected: FAIL
|
||||
Loading…
Reference in a new issue