Bug 1823359 - Implement beforetoggle event for popover. r=emilio,smaug

Differential Revision: https://phabricator.services.mozilla.com/D172993
This commit is contained in:
Ziran Sun 2023-03-23 19:04:20 +00:00
Родитель e429856a99
Коммит 02d0389ca0
15 изменённых файлов: 84 добавлений и 60 удалений

Просмотреть файл

@ -223,6 +223,7 @@
#include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
#include "mozilla/dom/StyleSheetList.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/dom/ToggleEvent.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/dom/TreeOrderedArrayInlines.h"
@ -14895,18 +14896,30 @@ void Document::HideAllPopoversUntil(nsINode& aEndpoint,
}
}
// https://html.spec.whatwg.org/#dom-hidepopover
void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
bool aFireEvents, ErrorResult& aRv) {
auto* popoverHTMLEl = nsGenericHTMLElement::FromNode(aPopover);
RefPtr<nsGenericHTMLElement> popoverHTMLEl =
nsGenericHTMLElement::FromNode(aPopover);
NS_ASSERTION(popoverHTMLEl, "Not a HTML element");
if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Hidden,
if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
aRv)) {
return;
}
// TODO: Run auto popover steps.
// TODO: Fire beforetoggle event and re-check popover validity.
// Fire beforetoggle event and re-check popover validity.
if (aFireEvents) {
// Intentionally ignore the return value here as only on open event the
// cancelable attribute is initialized to true.
popoverHTMLEl->FireBeforeToggle(true);
if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
aRv)) {
return;
}
}
// TODO: Remove from Top Layer.
popoverHTMLEl->PopoverPseudoStateUpdate(false, true);
@ -14914,6 +14927,7 @@ void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
PopoverVisibilityState::Hidden);
// TODO: Queue popover toggle event task.
// TODO: Handle element focus.
}
nsTArray<Element*> Document::AutoPopoverList() const {

Просмотреть файл

@ -149,6 +149,7 @@
#endif /* BEFOREUNLOAD_EVENT */
EVENT(abort, eImageAbort, EventNameType_All, eBasicEventClass)
EVENT(beforetoggle, eBeforeToggle, EventNameType_HTMLXUL, eBasicEventClass)
EVENT(bounce, eMarqueeBounce, EventNameType_HTMLMarqueeOnly, eBasicEventClass)
EVENT(canplay, eCanPlay, EventNameType_HTML, eBasicEventClass)
EVENT(canplaythrough, eCanPlayThrough, EventNameType_HTML, eBasicEventClass)

Просмотреть файл

@ -92,6 +92,7 @@
#include "nsTextFragment.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/ToggleEvent.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/ErrorResult.h"
#include "nsHTMLDocument.h"
@ -673,14 +674,17 @@ nsresult nsGenericHTMLElement::AfterSetAttr(
// The missing value default is the no popover state.
newState = PopoverState::None;
}
if (newState != GetPopoverState()) {
if (PopoverOpen()) {
HidePopover(IgnoreErrors());
PopoverState oldState = GetPopoverState();
if (newState != oldState) {
if (oldState != PopoverState::None) {
HidePopoverInternal(/* aFireEvents = */ false, IgnoreErrors());
}
if (newState == PopoverState::None) {
ClearPopoverData();
} else {
if (newState != PopoverState::None) {
EnsurePopoverData().SetPopoverState(newState);
PopoverPseudoStateUpdate(false, true);
} else {
ClearPopoverData();
RemoveStates(ElementState::OPEN | ElementState::CLOSED);
}
}
} else if (aName == nsGkAtoms::dir) {
@ -3182,24 +3186,57 @@ void nsGenericHTMLElement::PopoverPseudoStateUpdate(bool aOpen, bool aNotify) {
ToggleStates(changedStates, aNotify);
}
bool nsGenericHTMLElement::FireBeforeToggle(bool aIsOpen) {
ToggleEventInit init;
init.mBubbles = false;
if (aIsOpen) {
init.mCancelable = false;
init.mOldState = u"open"_ns;
init.mNewState = u"closed"_ns;
} else {
init.mCancelable = true;
init.mOldState = u"closed"_ns;
init.mNewState = u"open"_ns;
}
RefPtr<ToggleEvent> event =
ToggleEvent::Constructor(this, u"beforetoggle"_ns, init);
event->SetTrusted(true);
EventDispatcher::DispatchDOMEvent(MOZ_KnownLive(ToSupports(this)), nullptr,
event, nullptr, nullptr);
return event->DefaultPrevented();
}
// https://html.spec.whatwg.org/#dom-showpopover
void nsGenericHTMLElement::ShowPopover(ErrorResult& aRv) {
if (!CheckPopoverValidity(PopoverVisibilityState::Hidden, aRv)) {
return;
}
// TODO: Fire beforetoggle event and re-check popover validity.
// Fire beforetoggle event and re-check popover validity.
if (FireBeforeToggle(false)) {
return;
}
if (!CheckPopoverValidity(PopoverVisibilityState::Hidden, aRv)) {
return;
}
// TODO: Run auto popover steps.
// TODO: Add to Top Layer.
GetPopoverData()->SetPopoverVisibilityState(PopoverVisibilityState::Showing);
PopoverPseudoStateUpdate(true, true);
GetPopoverData()->SetPopoverVisibilityState(PopoverVisibilityState::Showing);
// TODO: Handle popover focusing.
// TODO: Queue popover toggle event task.
}
// https://html.spec.whatwg.org/#dom-hidepopover
void nsGenericHTMLElement::HidePopover(ErrorResult& aRv) {
OwnerDoc()->HidePopover(*this, true, true, aRv);
HidePopoverInternal(true, aRv);
}
void nsGenericHTMLElement::HidePopoverInternal(bool aFireEvents,
ErrorResult& aRv) {
OwnerDoc()->HidePopover(*this, true, aFireEvents, aRv);
}
// https://html.spec.whatwg.org/multipage/popover.html#dom-togglepopover

Просмотреть файл

@ -155,7 +155,11 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase {
bool PopoverOpen() const;
bool CheckPopoverValidity(mozilla::dom::PopoverVisibilityState aExpectedState,
ErrorResult& aRv);
void ShowPopover(ErrorResult& aRv);
/** Returns true if the event has been cancelled. */
MOZ_CAN_RUN_SCRIPT bool FireBeforeToggle(bool aIsOpen);
MOZ_CAN_RUN_SCRIPT void ShowPopover(ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void HidePopoverInternal(bool aFireEvents,
ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void HidePopover(ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void TogglePopover(bool force, ErrorResult& aRv);

Просмотреть файл

@ -33,6 +33,8 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onauxclick;
[Pref="dom.input_events.beforeinput.enabled"]
attribute EventHandler onbeforeinput;
[Pref="dom.element.popover.enabled"]
attribute EventHandler onbeforetoggle;
attribute EventHandler oncanplay;
attribute EventHandler oncanplaythrough;
attribute EventHandler onchange;

Просмотреть файл

@ -63,9 +63,6 @@
[Removing a visible popover=auto element from the document should close the popover]
expected: FAIL
[A showing popover=auto does not match :modal]
expected: FAIL
[Removing a visible popover=hint element from the document should close the popover]
expected: FAIL
@ -75,9 +72,6 @@
[Removing a visible popover=manual element from the document should close the popover]
expected: FAIL
[A showing popover=manual does not match :modal]
expected: FAIL
[Changing the popover type in a "beforetoggle" event handler should throw an exception (during showPopover())]
expected: FAIL

Просмотреть файл

@ -1,3 +0,0 @@
[popover-beforetoggle-opening-event.html]
[Ensure the `beforetoggle` event can be used to populate content before the popover renders]
expected: FAIL

Просмотреть файл

@ -4,3 +4,6 @@
[Popover focus returns when popover is hidden by invoker]
expected: FAIL
[Circular reference tab navigation]
expected: FAIL

Просмотреть файл

@ -1,7 +1,4 @@
[popover-focus-child-dialog.html]
max-asserts: 2
expected:
if (os == "android") and fission: [ERROR, TIMEOUT]
ERROR
max-asserts: 2
[Popovers should not initially focus child popover elements.]
expected: NOTRUN

Просмотреть файл

@ -24,9 +24,6 @@
[Clicking inside a parent popover should close child popover]
expected: FAIL
[Clicking on invoking element, after using it for activation, shouldn't close its popover]
expected: FAIL
[Clicking on invoking element, after using it for activation, shouldn't close its popover (nested case)]
expected: FAIL
@ -57,12 +54,6 @@
[An invoking element that was not used to invoke the popover can still be part of the ancestor chain]
expected: FAIL
[Scrolling within a popover should not close the popover]
expected: FAIL
[Moving focus back to the anchor element should not dismiss the popover]
expected: FAIL
[Ensure circular/convoluted ancestral relationships are functional]
expected: FAIL
@ -77,3 +68,6 @@
[Light dismiss of mixed popover types including hints]
expected: FAIL
[Scrolling within a popover should not close the popover]
expected: FAIL

Просмотреть файл

@ -1,3 +0,0 @@
[popover-not-keyboard-focusable.html]
[Popover should not be keyboard focusable]
expected: FAIL

Просмотреть файл

@ -1,7 +1,4 @@
[popover-shadow-dom.html]
[Popovers located inside shadow DOM can still be shown]
expected: FAIL
[anchor references do not cross shadow boundaries]
expected: FAIL

Просмотреть файл

@ -1,28 +1,12 @@
[popover-top-layer-interactions.html]
expected: ERROR
[A Popover API should close a Popover API]
expected: FAIL
[A Modal Dialog should close a Popover API]
expected: NOTRUN
expected: FAIL
[A Fullscreen Element should close a Popover API]
expected: NOTRUN
[A Popover API should *not* close a Modal Dialog]
expected: NOTRUN
[A Popover API should *not* close a Fullscreen Element]
expected: NOTRUN
expected: FAIL
[A Fullscreen Element should *not* close a Fullscreen Element]
expected: NOTRUN
[A Modal Dialog should *not* close a Modal Dialog]
expected: NOTRUN
[A Fullscreen Element should *not* close a Modal Dialog]
expected: NOTRUN
[A Modal Dialog should *not* close a Fullscreen Element]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -90,6 +90,8 @@ NS_EVENT_MESSAGE(eContextMenu)
NS_EVENT_MESSAGE(eCueChange)
NS_EVENT_MESSAGE(eBeforeToggle)
NS_EVENT_MESSAGE(eLoad)
NS_EVENT_MESSAGE(eUnload)
NS_EVENT_MESSAGE(eHashChange)

Просмотреть файл

@ -1980,6 +1980,7 @@ STATIC_ATOMS = [
Atom("onmozshowdropdown_sourcetouch", "onmozshowdropdown-sourcetouch"),
Atom("onprintPreviewUpdate", "onprintPreviewUpdate"),
Atom("onscrollend", "onscrollend"),
Atom("onbeforetoggle", "onbeforetoggle"),
# WebExtensions
Atom("moz_extension", "moz-extension"),
Atom("all_urlsPermission", "<all_urls>"),