mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1857887 - Add invoketarget & invoketarget action attributes r=smaug
This adds support for the experimental `invoketarget` and `invokeaction` attributes, as specified in the open-ui "Invokers" explainer. (https://open-ui.org/components/invokers.explainer/) The `invoketarget` attribute maps to the IDL `invokeTargetElement`, similar to `popoverTargetElement`, and the `invokeaction` is a freeform string. The Button behaviour checks for `invokeTargetElement` in its activation behaviour, and dispatches an `InvokeEvent` if there is one. This also adds some basic scaffolding for `handleInvokeInternal` which will allow elements to provide their own invocation action algorithms. Differential Revision: https://phabricator.services.mozilla.com/D190449
This commit is contained in:
parent
b18ba60df7
commit
c9c6527520
@ -1097,6 +1097,9 @@ class Element : public FragmentOrElement {
|
||||
|
||||
static nsStaticAtom* const* HTMLSVGPropertiesToTraverseAndUnlink();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT virtual void HandleInvokeInternal(nsAtom* aAction,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
private:
|
||||
void DescribeAttribute(uint32_t index, nsAString& aOutDescription) const;
|
||||
|
||||
|
58
dom/events/InvokeEvent.cpp
Normal file
58
dom/events/InvokeEvent.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- 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 "mozilla/dom/InvokeEvent.h"
|
||||
#include "mozilla/dom/EventTarget.h"
|
||||
#include "mozilla/dom/InvokeEventBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InvokeEvent, Event, mInvoker)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(InvokeEvent, Event)
|
||||
NS_IMPL_RELEASE_INHERITED(InvokeEvent, Event)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InvokeEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(Event)
|
||||
|
||||
JSObject* InvokeEvent::WrapObjectInternal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return InvokeEvent_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<InvokeEvent> InvokeEvent::Constructor(
|
||||
mozilla::dom::EventTarget* aOwner, const nsAString& aType,
|
||||
const InvokeEventInit& aEventInitDict) {
|
||||
RefPtr<InvokeEvent> e = new InvokeEvent(aOwner);
|
||||
bool trusted = e->Init(aOwner);
|
||||
e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
|
||||
e->mInvoker = aEventInitDict.mInvoker;
|
||||
e->mAction = aEventInitDict.mAction;
|
||||
e->SetTrusted(trusted);
|
||||
e->SetComposed(aEventInitDict.mComposed);
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InvokeEvent> InvokeEvent::Constructor(
|
||||
const GlobalObject& aGlobal, const nsAString& aType,
|
||||
const InvokeEventInit& aEventInitDict) {
|
||||
nsCOMPtr<mozilla::dom::EventTarget> owner =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return Constructor(owner, aType, aEventInitDict);
|
||||
}
|
||||
|
||||
Element* InvokeEvent::GetInvoker() {
|
||||
EventTarget* currentTarget = GetCurrentTarget();
|
||||
if (currentTarget) {
|
||||
nsINode* retargeted = nsContentUtils::Retarget(
|
||||
static_cast<nsINode*>(mInvoker), currentTarget->GetAsNode());
|
||||
return retargeted->AsElement();
|
||||
}
|
||||
MOZ_ASSERT(!mEvent->mFlags.mIsBeingDispatched);
|
||||
return mInvoker;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
50
dom/events/InvokeEvent.h
Normal file
50
dom/events/InvokeEvent.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 DOM_INVOKEEVENT_H_
|
||||
#define DOM_INVOKEEVENT_H_
|
||||
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/InvokeEventBinding.h"
|
||||
|
||||
struct JSContext;
|
||||
namespace mozilla::dom {
|
||||
|
||||
class InvokeEvent : public Event {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InvokeEvent, Event)
|
||||
|
||||
virtual InvokeEvent* AsInvokeEvent() { return this; }
|
||||
|
||||
explicit InvokeEvent(EventTarget* aOwner) : Event(aOwner, nullptr, nullptr) {}
|
||||
|
||||
JSObject* WrapObjectInternal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<InvokeEvent> Constructor(
|
||||
EventTarget* aOwner, const nsAString& aType,
|
||||
const InvokeEventInit& aEventInitDict);
|
||||
|
||||
static already_AddRefed<InvokeEvent> Constructor(
|
||||
const GlobalObject& aGlobal, const nsAString& aType,
|
||||
const InvokeEventInit& aEventInitDict);
|
||||
|
||||
Element* GetInvoker();
|
||||
|
||||
void GetAction(nsAString& aAction) const { aAction = mAction; }
|
||||
|
||||
protected:
|
||||
~InvokeEvent() = default;
|
||||
|
||||
RefPtr<Element> mInvoker;
|
||||
nsString mAction;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // DOM_INVOKEEVENT_H_
|
@ -79,6 +79,7 @@ EXPORTS.mozilla.dom += [
|
||||
"FocusEvent.h",
|
||||
"ImageCaptureError.h",
|
||||
"InputEvent.h",
|
||||
"InvokeEvent.h",
|
||||
"KeyboardEvent.h",
|
||||
"MessageEvent.h",
|
||||
"MouseEvent.h",
|
||||
@ -133,6 +134,7 @@ UNIFIED_SOURCES += [
|
||||
"IMEContentObserver.cpp",
|
||||
"IMEStateManager.cpp",
|
||||
"InputEvent.cpp",
|
||||
"InvokeEvent.cpp",
|
||||
"JSEventHandler.cpp",
|
||||
"KeyboardEvent.cpp",
|
||||
"KeyEventHandler.cpp",
|
||||
|
@ -278,7 +278,11 @@ void HTMLButtonElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-button-state
|
||||
// NS_FORM_BUTTON_BUTTON do nothing.
|
||||
}
|
||||
HandlePopoverTargetAction();
|
||||
if (!GetInvokeTargetElement()) {
|
||||
HandlePopoverTargetAction();
|
||||
} else {
|
||||
HandleInvokeTargetAction();
|
||||
}
|
||||
}
|
||||
|
||||
EndSubmitClick(aVisitor);
|
||||
|
@ -4093,7 +4093,11 @@ void HTMLInputElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
|
||||
break;
|
||||
} // switch
|
||||
if (IsButtonControl()) {
|
||||
HandlePopoverTargetAction();
|
||||
if (!GetInvokeTargetElement()) {
|
||||
HandlePopoverTargetAction();
|
||||
} else {
|
||||
HandleInvokeTargetAction();
|
||||
}
|
||||
}
|
||||
|
||||
EndSubmitClick(aVisitor);
|
||||
|
@ -78,6 +78,8 @@
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/ToggleEvent.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/dom/InputEvent.h"
|
||||
#include "mozilla/dom/InvokeEvent.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsHTMLDocument.h"
|
||||
#include "nsGlobalWindowInner.h"
|
||||
@ -2817,15 +2819,27 @@ nsGenericHTMLFormControlElementWithState::
|
||||
bool nsGenericHTMLFormControlElementWithState::ParseAttribute(
|
||||
int32_t aNamespaceID, nsAtom* aAttribute, const nsAString& aValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal, nsAttrValue& aResult) {
|
||||
if (aNamespaceID == kNameSpaceID_None &&
|
||||
StaticPrefs::dom_element_popover_enabled()) {
|
||||
if (aAttribute == nsGkAtoms::popovertargetaction) {
|
||||
return aResult.ParseEnumValue(aValue, kPopoverTargetActionTable, false,
|
||||
kPopoverTargetActionDefault);
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (StaticPrefs::dom_element_popover_enabled()) {
|
||||
if (aAttribute == nsGkAtoms::popovertargetaction) {
|
||||
return aResult.ParseEnumValue(aValue, kPopoverTargetActionTable, false,
|
||||
kPopoverTargetActionDefault);
|
||||
}
|
||||
if (aAttribute == nsGkAtoms::popovertarget) {
|
||||
aResult.ParseAtom(aValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (aAttribute == nsGkAtoms::popovertarget) {
|
||||
aResult.ParseAtom(aValue);
|
||||
return true;
|
||||
|
||||
if (StaticPrefs::dom_element_invokers_enabled()) {
|
||||
if (aAttribute == nsGkAtoms::invokeaction) {
|
||||
aResult.ParseAtom(aValue);
|
||||
return true;
|
||||
}
|
||||
if (aAttribute == nsGkAtoms::invoketarget) {
|
||||
aResult.ParseAtom(aValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2868,6 +2882,69 @@ void nsGenericHTMLFormControlElementWithState::HandlePopoverTargetAction() {
|
||||
}
|
||||
}
|
||||
|
||||
void nsGenericHTMLFormControlElementWithState::GetInvokeAction(
|
||||
nsAString& aValue) const {
|
||||
GetInvokeAction()->ToString(aValue);
|
||||
}
|
||||
|
||||
nsAtom* nsGenericHTMLFormControlElementWithState::GetInvokeAction() const {
|
||||
const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::invokeaction);
|
||||
if (attr && attr->GetAtomValue() != nsGkAtoms::_empty) {
|
||||
return attr->GetAtomValue();
|
||||
}
|
||||
return nsGkAtoms::_auto;
|
||||
}
|
||||
|
||||
mozilla::dom::Element*
|
||||
nsGenericHTMLFormControlElementWithState::GetInvokeTargetElement() const {
|
||||
if (StaticPrefs::dom_element_invokers_enabled()) {
|
||||
return GetAttrAssociatedElement(nsGkAtoms::invoketarget);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void nsGenericHTMLFormControlElementWithState::SetInvokeTargetElement(
|
||||
mozilla::dom::Element* aElement) {
|
||||
ExplicitlySetAttrElement(nsGkAtoms::invoketarget, aElement);
|
||||
}
|
||||
|
||||
void nsGenericHTMLFormControlElementWithState::HandleInvokeTargetAction() {
|
||||
// 1. Let invokee be node's invoke target element.
|
||||
RefPtr<Element> invokee = GetInvokeTargetElement();
|
||||
|
||||
// 2. If invokee is null, then return.
|
||||
if (!invokee) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Let action be node's invokeaction attribute
|
||||
// 4. If action is null or empty, then let action be the string "auto".
|
||||
RefPtr<nsAtom> aAction = GetInvokeAction();
|
||||
MOZ_ASSERT(!aAction->IsEmpty(), "Action should not be empty");
|
||||
|
||||
// 5. Let notCancelled be the result of firing an event named invoke at
|
||||
// invokee with its action set to action, its invoker set to node,
|
||||
// and its cancelable attribute initialized to true.
|
||||
InvokeEventInit init;
|
||||
aAction->ToString(init.mAction);
|
||||
init.mInvoker = this;
|
||||
init.mCancelable = true;
|
||||
init.mComposed = true;
|
||||
RefPtr<Event> event = InvokeEvent::Constructor(this, u"invoke"_ns, init);
|
||||
event->SetTrusted(true);
|
||||
event->SetTarget(invokee);
|
||||
|
||||
EventDispatcher::DispatchDOMEvent(invokee, nullptr, event, nullptr, nullptr);
|
||||
|
||||
// 6. If notCancelled is true and invokee has an associated invocation action
|
||||
// algorithm then run the invokee's invocation action algorithm given action.
|
||||
if (event->DefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokee->HandleInvokeInternal(aAction, IgnoreErrors());
|
||||
}
|
||||
|
||||
void nsGenericHTMLFormControlElementWithState::GenerateStateKey() {
|
||||
// Keep the key if already computed
|
||||
if (!mStateKey.IsVoid()) {
|
||||
|
@ -1241,11 +1241,22 @@ class nsGenericHTMLFormControlElementWithState
|
||||
SetHTMLAttr(nsGkAtoms::popovertargetaction, aValue);
|
||||
}
|
||||
|
||||
// InvokerElement
|
||||
mozilla::dom::Element* GetInvokeTargetElement() const;
|
||||
void SetInvokeTargetElement(mozilla::dom::Element*);
|
||||
void GetInvokeAction(nsAString& aValue) const;
|
||||
nsAtom* GetInvokeAction() const;
|
||||
void SetInvokeAction(const nsAString& aValue) {
|
||||
SetHTMLAttr(nsGkAtoms::invokeaction, aValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://html.spec.whatwg.org/#popover-target-attribute-activation-behavior
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT void HandlePopoverTargetAction();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void HandleInvokeTargetAction();
|
||||
|
||||
/**
|
||||
* Get the presentation state for a piece of content, or create it if it does
|
||||
* not exist. Generally used by SaveState().
|
||||
|
@ -49,3 +49,5 @@ interface HTMLButtonElement : HTMLElement {
|
||||
};
|
||||
|
||||
HTMLButtonElement includes PopoverInvokerElement;
|
||||
|
||||
HTMLButtonElement includes InvokerElement;
|
||||
|
@ -238,6 +238,8 @@ HTMLInputElement includes MozImageLoadingContent;
|
||||
|
||||
HTMLInputElement includes PopoverInvokerElement;
|
||||
|
||||
HTMLInputElement includes InvokerElement;
|
||||
|
||||
// https://wicg.github.io/entries-api/#idl-index
|
||||
partial interface HTMLInputElement {
|
||||
[Pref="dom.webkitBlink.filesystem.enabled", Frozen, Cached, Pure]
|
||||
|
21
dom/webidl/InvokeEvent.webidl
Normal file
21
dom/webidl/InvokeEvent.webidl
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://github.com/whatwg/html/pull/9841
|
||||
*/
|
||||
|
||||
[Pref="dom.element.invokers.enabled",
|
||||
Exposed=Window]
|
||||
interface InvokeEvent : Event {
|
||||
constructor(DOMString type, optional InvokeEventInit eventInitDict = {});
|
||||
readonly attribute Element? invoker;
|
||||
readonly attribute DOMString action;
|
||||
};
|
||||
|
||||
dictionary InvokeEventInit : EventInit {
|
||||
Element? invoker = null;
|
||||
DOMString action = "auto";
|
||||
};
|
13
dom/webidl/InvokerElement.webidl
Normal file
13
dom/webidl/InvokerElement.webidl
Normal file
@ -0,0 +1,13 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://github.com/whatwg/html/pull/9841
|
||||
*/
|
||||
|
||||
interface mixin InvokerElement {
|
||||
[Pref="dom.element.invokers.enabled", CEReactions] attribute Element? invokeTargetElement;
|
||||
[Pref="dom.element.invokers.enabled", CEReactions] attribute DOMString invokeAction;
|
||||
};
|
@ -175,6 +175,9 @@ with Files("InputEvent.webidl"):
|
||||
with Files("InstallTrigger.webidl"):
|
||||
BUG_COMPONENT = ("Toolkit", "Add-ons Manager")
|
||||
|
||||
with Files("InvokerElement.webidl"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
|
||||
|
||||
with Files("KeyAlgorithm.webidl"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Security")
|
||||
|
||||
@ -684,6 +687,8 @@ WEBIDL_FILES = [
|
||||
"InputEvent.webidl",
|
||||
"IntersectionObserver.webidl",
|
||||
"IntlUtils.webidl",
|
||||
"InvokeEvent.webidl",
|
||||
"InvokerElement.webidl",
|
||||
"IterableIterator.webidl",
|
||||
"KeyAlgorithm.webidl",
|
||||
"KeyboardEvent.webidl",
|
||||
|
@ -2349,6 +2349,12 @@
|
||||
mirror: always
|
||||
rust: true
|
||||
|
||||
# Whether the invoketarget attribute implementation is enabled
|
||||
- name: dom.element.invokers.enabled
|
||||
type: bool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
- name: dom.mouse_capture.enabled
|
||||
type: bool
|
||||
value: true
|
||||
|
@ -0,0 +1 @@
|
||||
prefs: [dom.element.invokers.enabled:true]
|
@ -1,36 +0,0 @@
|
||||
[idlharness.tentative.html]
|
||||
[InvokeEvent interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: attribute invoker]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: attribute action]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent must be primary interface of new InvokeEvent("invoke")]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new InvokeEvent("invoke")]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: new InvokeEvent("invoke") must inherit property "invoker" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEvent interface: new InvokeEvent("invoke") must inherit property "action" with the proper type]
|
||||
expected: FAIL
|
@ -1,33 +0,0 @@
|
||||
[invokeelement-interface.tentative.html]
|
||||
[invokeTargetElement reflects invokee HTML element]
|
||||
expected: FAIL
|
||||
|
||||
[invokeTargetElement reflects set value]
|
||||
expected: FAIL
|
||||
|
||||
[invokeTargetElement reflects set value across shadow root into light dom]
|
||||
expected: FAIL
|
||||
|
||||
[invokeTargetElement does not reflect set value inside shadowroot]
|
||||
expected: FAIL
|
||||
|
||||
[invokeTargetElement throws error on assignment of non Element]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects 'auto' when attribute not present]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects 'auto' when attribute empty]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects 'auto' when attribute empty 2]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects tostring value]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects 'auto' when attribute set to [\]]
|
||||
expected: FAIL
|
||||
|
||||
[invokeAction reflects tostring value 2]
|
||||
expected: FAIL
|
@ -1,6 +0,0 @@
|
||||
[invokeevent-dispatch-shadow.tentative.html]
|
||||
[InvokeEvent propagates across shadow boundaries retargeting invoker]
|
||||
expected: FAIL
|
||||
|
||||
[cross shadow InvokeEvent retargets invoker to host element]
|
||||
expected: FAIL
|
@ -1,63 +0,0 @@
|
||||
[invokeevent-interface.tentative.html]
|
||||
[action is a readonly defaulting to 'auto']
|
||||
expected: FAIL
|
||||
|
||||
[invoker is readonly defaulting to null]
|
||||
expected: FAIL
|
||||
|
||||
[action reflects initialized attribute]
|
||||
expected: FAIL
|
||||
|
||||
[action set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[action set to null]
|
||||
expected: FAIL
|
||||
|
||||
[action set to false]
|
||||
expected: FAIL
|
||||
|
||||
[action set to true]
|
||||
expected: FAIL
|
||||
|
||||
[action set to a number]
|
||||
expected: FAIL
|
||||
|
||||
[action set to [\]]
|
||||
expected: FAIL
|
||||
|
||||
[action set to [1, 2, 3\]]
|
||||
expected: FAIL
|
||||
|
||||
[action set to an object]
|
||||
expected: FAIL
|
||||
|
||||
[action set to an object with a valueOf function]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEventInit properties set value]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEventInit properties set value 2]
|
||||
expected: FAIL
|
||||
|
||||
[InvokeEventInit properties set value 3]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to null]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to false]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to true]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to {}]
|
||||
expected: FAIL
|
||||
|
||||
[invoker set to non-Element EventTarget]
|
||||
expected: FAIL
|
@ -1,9 +0,0 @@
|
||||
[invoketarget-button-event-dispatch.tentative.html]
|
||||
[event dispatches on click]
|
||||
expected: FAIL
|
||||
|
||||
[event action is set to invokeAction]
|
||||
expected: FAIL
|
||||
|
||||
[event action is set to invokeaction attribute]
|
||||
expected: FAIL
|
@ -17,8 +17,8 @@
|
||||
const div = document.body.appendChild(document.createElement("div"));
|
||||
invoker.invokeTargetElement = div;
|
||||
assert_equals(invoker.invokeTargetElement, div);
|
||||
assert_equals(invoker.getAttribute('invoketarget'), '');
|
||||
assert_false(invoker.hasAttribute('invokeaction'));
|
||||
assert_equals(invoker.getAttribute("invoketarget"), "");
|
||||
assert_false(invoker.hasAttribute("invokeaction"));
|
||||
}, "invokeTargetElement reflects set value");
|
||||
|
||||
test(function () {
|
||||
@ -27,8 +27,8 @@
|
||||
const button = shadow.appendChild(document.createElement("button"));
|
||||
button.invokeTargetElement = invokee;
|
||||
assert_equals(button.invokeTargetElement, invokee);
|
||||
assert_equals(invoker.getAttribute('invoketarget'), '');
|
||||
assert_false(invoker.hasAttribute('invokeaction'));
|
||||
assert_equals(invoker.getAttribute("invoketarget"), "");
|
||||
assert_false(invoker.hasAttribute("invokeaction"));
|
||||
}, "invokeTargetElement reflects set value across shadow root into light dom");
|
||||
|
||||
test(function () {
|
||||
@ -37,8 +37,8 @@
|
||||
const div = shadow.appendChild(document.createElement("div"));
|
||||
invoker.invokeTargetElement = div;
|
||||
assert_equals(invoker.invokeTargetElement, null);
|
||||
assert_equals(invoker.getAttribute('invoketarget'), '');
|
||||
assert_false(invoker.hasAttribute('invokeaction'));
|
||||
assert_equals(invoker.getAttribute("invoketarget"), "");
|
||||
assert_false(invoker.hasAttribute("invokeaction"));
|
||||
}, "invokeTargetElement does not reflect set value inside shadowroot");
|
||||
|
||||
test(function () {
|
||||
@ -52,7 +52,7 @@
|
||||
}, "invokeTargetElement throws error on assignment of non Element");
|
||||
|
||||
test(function () {
|
||||
assert_false(invoker.hasAttribute('invokeaction'));
|
||||
assert_false(invoker.hasAttribute("invokeaction"));
|
||||
assert_equals(invoker.invokeAction, "auto");
|
||||
}, "invokeAction reflects 'auto' when attribute not present");
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
|
||||
test(function () {
|
||||
invoker.invokeAction = "fooBarBaz";
|
||||
assert_equals(invoker.getAttribute("invokeaction"), "fooBarBaz");
|
||||
assert_equals(invoker.invokeAction, "fooBarBaz");
|
||||
}, "invokeAction reflects same casing");
|
||||
|
||||
|
@ -45,6 +45,11 @@
|
||||
assert_equals(event.action, "false");
|
||||
}, "action set to false");
|
||||
|
||||
test(function () {
|
||||
const event = new InvokeEvent("test", { action: "" });
|
||||
assert_equals(event.action, "");
|
||||
}, "action explicitly set to empty string");
|
||||
|
||||
test(function () {
|
||||
const event = new InvokeEvent("test", { action: true });
|
||||
assert_equals(event.action, "true");
|
||||
@ -57,7 +62,7 @@
|
||||
|
||||
test(function () {
|
||||
const event = new InvokeEvent("test", { action: [] });
|
||||
assert_equals(event.action, "auto");
|
||||
assert_equals(event.action, "");
|
||||
}, "action set to []");
|
||||
|
||||
test(function () {
|
||||
@ -73,13 +78,13 @@
|
||||
test(function () {
|
||||
const event = new InvokeEvent("test", {
|
||||
action: {
|
||||
valueOf: function () {
|
||||
toString() {
|
||||
return "sample";
|
||||
},
|
||||
},
|
||||
});
|
||||
assert_equals(event.action, "[object Object]");
|
||||
}, "action set to an object with a valueOf function");
|
||||
assert_equals(event.action, "sample");
|
||||
}, "action set to an object with a toString function");
|
||||
|
||||
test(function () {
|
||||
const eventInit = { action: "sample", invoker: document.body };
|
||||
|
@ -20,7 +20,7 @@
|
||||
assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
|
||||
assert_equals(event.type, "invoke", "type");
|
||||
assert_equals(event.bubbles, false, "bubbles");
|
||||
assert_equals(event.composed, false, "composed");
|
||||
assert_equals(event.composed, true, "composed");
|
||||
assert_equals(event.isTrusted, true, "isTrusted");
|
||||
assert_equals(event.action, "auto", "action");
|
||||
assert_equals(event.target, invokee, "target");
|
||||
@ -35,7 +35,7 @@
|
||||
assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
|
||||
assert_equals(event.type, "invoke", "type");
|
||||
assert_equals(event.bubbles, false, "bubbles");
|
||||
assert_equals(event.composed, false, "composed");
|
||||
assert_equals(event.composed, true, "composed");
|
||||
assert_equals(event.isTrusted, true, "isTrusted");
|
||||
assert_equals(event.action, "fooBar", "action");
|
||||
assert_equals(event.target, invokee, "target");
|
||||
@ -50,7 +50,7 @@
|
||||
assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
|
||||
assert_equals(event.type, "invoke", "type");
|
||||
assert_equals(event.bubbles, false, "bubbles");
|
||||
assert_equals(event.composed, false, "composed");
|
||||
assert_equals(event.composed, true, "composed");
|
||||
assert_equals(event.isTrusted, true, "isTrusted");
|
||||
assert_equals(event.action, "BaRbAz", "action");
|
||||
assert_equals(event.target, invokee, "target");
|
||||
@ -78,6 +78,7 @@
|
||||
}, "event does not dispatch if click:preventDefault is called");
|
||||
|
||||
promise_test(async function (t) {
|
||||
t.add_cleanup(() => invokerbutton.removeAttribute('disabled'));
|
||||
let called = false;
|
||||
invokee.addEventListener(
|
||||
"invoke",
|
||||
@ -90,4 +91,26 @@
|
||||
await clickOn(invokerbutton);
|
||||
assert_false(called, "event was not called");
|
||||
}, "event does not dispatch if invoker is disabled");
|
||||
|
||||
promise_test(async function (t) {
|
||||
svgInvokee = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
t.add_cleanup(() => {
|
||||
invokerbutton.invokeTargetElement = invokee;
|
||||
svgInvokee.remove();
|
||||
});
|
||||
document.body.append(svgInvokee);
|
||||
let called = false;
|
||||
assert_false(svgInvokee instanceof HTMLElement);
|
||||
assert_true(svgInvokee instanceof Element);
|
||||
svgInvokee.addEventListener(
|
||||
"invoke",
|
||||
(event) => {
|
||||
called = true;
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
invokerbutton.invokeTargetElement = svgInvokee;
|
||||
await clickOn(invokerbutton);
|
||||
assert_true(called, "event was called");
|
||||
}, "event dispatches if invoker is non-HTML Element");
|
||||
</script>
|
||||
|
@ -11,5 +11,5 @@ interface InvokeEvent : Event {
|
||||
|
||||
dictionary InvokeEventInit : EventInit {
|
||||
Element? invoker = null;
|
||||
DOMString action = "";
|
||||
DOMString action = "auto";
|
||||
};
|
||||
|
@ -567,6 +567,8 @@ STATIC_ATOMS = [
|
||||
Atom("internals", "internals"),
|
||||
Atom("intersection", "intersection"),
|
||||
Atom("intersectionobserverlist", "intersectionobserverlist"),
|
||||
Atom("invoketarget", "invoketarget"),
|
||||
Atom("invokeaction", "invokeaction"),
|
||||
Atom("is", "is"),
|
||||
Atom("ismap", "ismap"),
|
||||
Atom("itemid", "itemid"),
|
||||
|
Loading…
Reference in New Issue
Block a user