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:
keithamus 2023-11-08 13:41:51 +00:00
parent b18ba60df7
commit c9c6527520
25 changed files with 315 additions and 172 deletions

View File

@ -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;

View 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
View 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_

View File

@ -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",

View File

@ -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);

View File

@ -4093,7 +4093,11 @@ void HTMLInputElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
break;
} // switch
if (IsButtonControl()) {
HandlePopoverTargetAction();
if (!GetInvokeTargetElement()) {
HandlePopoverTargetAction();
} else {
HandleInvokeTargetAction();
}
}
EndSubmitClick(aVisitor);

View File

@ -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()) {

View File

@ -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().

View File

@ -49,3 +49,5 @@ interface HTMLButtonElement : HTMLElement {
};
HTMLButtonElement includes PopoverInvokerElement;
HTMLButtonElement includes InvokerElement;

View File

@ -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]

View 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";
};

View 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;
};

View File

@ -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",

View File

@ -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

View File

@ -0,0 +1 @@
prefs: [dom.element.invokers.enabled:true]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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 };

View File

@ -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>

View File

@ -11,5 +11,5 @@ interface InvokeEvent : Event {
dictionary InvokeEventInit : EventInit {
Element? invoker = null;
DOMString action = "";
DOMString action = "auto";
};

View File

@ -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"),