Bug 1225412 Part 2 - Add support to dispatch toggle events to details element. r=smaug

Add ontoggle event handler, and dispatch toggle events to the details
element if the open attribute is added or changed. According to the
spec, if a new toggle event has been queued, previous toggle events
should be aborted.

MozReview-Commit-ID: EN6Jf5hVHHD

--HG--
extra : rebase_source : 35605e49950bb59a0eb6dca594c3ede465ff587d
This commit is contained in:
Ting-Yu Lin 2016-03-12 19:53:51 +08:00
parent e112dca692
commit b8972c7eb9
9 changed files with 119 additions and 47 deletions

View File

@ -923,6 +923,7 @@ GK_ATOM(onsubmit, "onsubmit")
GK_ATOM(onsuccess, "onsuccess") GK_ATOM(onsuccess, "onsuccess")
GK_ATOM(ontypechange, "ontypechange") GK_ATOM(ontypechange, "ontypechange")
GK_ATOM(ontext, "ontext") GK_ATOM(ontext, "ontext")
GK_ATOM(ontoggle, "ontoggle")
GK_ATOM(ontouchstart, "ontouchstart") GK_ATOM(ontouchstart, "ontouchstart")
GK_ATOM(ontouchend, "ontouchend") GK_ATOM(ontouchend, "ontouchend")
GK_ATOM(ontouchmove, "ontouchmove") GK_ATOM(ontouchmove, "ontouchmove")

View File

@ -421,6 +421,10 @@ EVENT(timeupdate,
eTimeUpdate, eTimeUpdate,
EventNameType_HTML, EventNameType_HTML,
eBasicEventClass) eBasicEventClass)
EVENT(toggle,
eToggle,
EventNameType_HTML,
eBasicEventClass)
EVENT(volumechange, EVENT(volumechange,
eVolumeChange, eVolumeChange,
EventNameType_HTML, EventNameType_HTML,

View File

@ -71,6 +71,25 @@ HTMLDetailsElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
return hint; return hint;
} }
nsresult
HTMLDetailsElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsAttrValueOrString* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::open) {
bool setOpen = aValue != nullptr;
if (Open() != setOpen) {
if (mToggleEventDispatcher) {
mToggleEventDispatcher->Cancel();
}
mToggleEventDispatcher = new ToggleEventDispatcher(this);
mToggleEventDispatcher->PostDOMEvent();
}
}
return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
aNotify);
}
JSObject* JSObject*
HTMLDetailsElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) HTMLDetailsElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{ {

View File

@ -6,6 +6,7 @@
#ifndef mozilla_dom_HTMLDetailsElement_h #ifndef mozilla_dom_HTMLDetailsElement_h
#define mozilla_dom_HTMLDetailsElement_h #define mozilla_dom_HTMLDetailsElement_h
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "nsGenericHTMLElement.h" #include "nsGenericHTMLElement.h"
@ -38,12 +39,14 @@ public:
nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
int32_t aModType) const override; int32_t aModType) const override;
nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsAttrValueOrString* aValue, bool aNotify) override;
// HTMLDetailsElement WebIDL // HTMLDetailsElement WebIDL
bool Open() const { return GetBoolAttr(nsGkAtoms::open); } bool Open() const { return GetBoolAttr(nsGkAtoms::open); }
void SetOpen(bool aOpen, ErrorResult& aError) void SetOpen(bool aOpen, ErrorResult& aError)
{ {
// TODO: Bug 1225412: Need to follow the spec to fire "toggle" event.
SetHTMLBoolAttr(nsGkAtoms::open, aOpen, aError); SetHTMLBoolAttr(nsGkAtoms::open, aOpen, aError);
} }
@ -59,6 +62,26 @@ protected:
JSObject* WrapNode(JSContext* aCx, JSObject* WrapNode(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override; JS::Handle<JSObject*> aGivenProto) override;
class ToggleEventDispatcher final : public AsyncEventDispatcher
{
public:
// According to the html spec, a 'toggle' event is a simple event which does
// not bubble.
explicit ToggleEventDispatcher(nsINode* aTarget)
: AsyncEventDispatcher(aTarget, NS_LITERAL_STRING("toggle"), false)
{
}
NS_IMETHOD Run() override
{
auto* details = static_cast<HTMLDetailsElement*>(mTarget.get());
details->mToggleEventDispatcher = nullptr;
return AsyncEventDispatcher::Run();
}
};
RefPtr<ToggleEventDispatcher> mToggleEventDispatcher;
}; };
} // namespace dom } // namespace dom

View File

@ -92,6 +92,9 @@ interface GlobalEventHandlers {
[Pref="dom.select_events.enabled"] [Pref="dom.select_events.enabled"]
attribute EventHandler onselectstart; attribute EventHandler onselectstart;
[Pref="dom.details_element.enabled"]
attribute EventHandler ontoggle;
// Pointer events handlers // Pointer events handlers
[Pref="dom.w3c_pointer_events.enabled"] [Pref="dom.w3c_pointer_events.enabled"]
attribute EventHandler onpointercancel; attribute EventHandler onpointercancel;

View File

@ -136,9 +136,6 @@
[Document interface: attribute onsort] [Document interface: attribute onsort]
expected: FAIL expected: FAIL
[Document interface: attribute ontoggle]
expected: FAIL
[Stringification of iframe.contentDocument] [Stringification of iframe.contentDocument]
expected: FAIL expected: FAIL
@ -205,9 +202,6 @@
[Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (148)] [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (148)]
expected: FAIL expected: FAIL
[Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (153)]
expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "origin" with the proper type (3)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "origin" with the proper type (3)]
expected: FAIL expected: FAIL
@ -409,9 +403,6 @@
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (148)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (148)]
expected: FAIL expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (153)]
expected: FAIL
[Touch interface: attribute region] [Touch interface: attribute region]
expected: FAIL expected: FAIL
@ -592,9 +583,6 @@
[HTMLElement interface: attribute onsort] [HTMLElement interface: attribute onsort]
expected: FAIL expected: FAIL
[HTMLElement interface: attribute ontoggle]
expected: FAIL
[HTMLElement interface: document.createElement("noscript") must inherit property "translate" with the proper type (2)] [HTMLElement interface: document.createElement("noscript") must inherit property "translate" with the proper type (2)]
expected: FAIL expected: FAIL
@ -646,9 +634,6 @@
[HTMLElement interface: document.createElement("noscript") must inherit property "onsort" with the proper type (87)] [HTMLElement interface: document.createElement("noscript") must inherit property "onsort" with the proper type (87)]
expected: FAIL expected: FAIL
[HTMLElement interface: document.createElement("noscript") must inherit property "ontoggle" with the proper type (92)]
expected: FAIL
[Element interface: document.createElement("noscript") must inherit property "prepend" with the proper type (31)] [Element interface: document.createElement("noscript") must inherit property "prepend" with the proper type (31)]
expected: FAIL expected: FAIL
@ -1965,9 +1950,6 @@
[Window interface: attribute onsort] [Window interface: attribute onsort]
expected: FAIL expected: FAIL
[Window interface: attribute ontoggle]
expected: FAIL
[Window interface: operation createImageBitmap(ImageBitmapSource,long,long,long,long)] [Window interface: operation createImageBitmap(ImageBitmapSource,long,long,long,long)]
expected: FAIL expected: FAIL
@ -2000,9 +1982,6 @@
[Window interface: window must inherit property "onsort" with the proper type (93)] [Window interface: window must inherit property "onsort" with the proper type (93)]
expected: FAIL expected: FAIL
[Window interface: window must inherit property "ontoggle" with the proper type (98)]
expected: FAIL
[Window interface: calling showModalDialog(DOMString,any) on window with too few arguments must throw TypeError] [Window interface: calling showModalDialog(DOMString,any) on window with too few arguments must throw TypeError]
expected: expected:
if not e10s: PASS if not e10s: PASS
@ -2515,9 +2494,6 @@
[Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (149)] [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (149)]
expected: FAIL expected: FAIL
[Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (154)]
expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (32)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (32)]
expected: FAIL expected: FAIL
@ -2665,9 +2641,6 @@
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (149)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (149)]
expected: FAIL expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (154)]
expected: FAIL
[Location interface: window.location must have own property "ancestorOrigins"] [Location interface: window.location must have own property "ancestorOrigins"]
expected: FAIL expected: FAIL

View File

@ -1,21 +1,3 @@
[toggleEvent.html] [toggleEvent.html]
type: testharness type: testharness
expected: TIMEOUT prefs: [dom.details_element.enabled:true]
[Adding open to 'details' should fire a toggle event at the 'details' element]
expected: NOTRUN
[Removing open from 'details' should fire a toggle event at the 'details' element]
expected: NOTRUN
[Adding open to 'details' (display:none) should fire a toggle event at the 'details' element]
expected: NOTRUN
[Adding open from 'details' (no children) should fire a toggle event at the 'details' element]
expected: NOTRUN
[Calling open twice on 'details' fires only one toggle event]
expected: FAIL
[Adding open to 'details' (not in the document) should fire a toggle event at the 'details' element]
expected: TIMEOUT

View File

@ -24,17 +24,42 @@
<summary>Lorem ipsum</summary> <summary>Lorem ipsum</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</details> </details>
<details id=details7>
<summary>Lorem ipsum</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</details>
<details id=details8 open>
<summary>Lorem ipsum</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</details>
<details id=details9 open>
<summary>Lorem ipsum</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</details>
<details id=details10>
<summary>Lorem ipsum</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</details>
<script> <script>
var t1 = async_test("Adding open to 'details' should fire a toggle event at the 'details' element"), var t1 = async_test("Adding open to 'details' should fire a toggle event at the 'details' element"),
t2 = async_test("Removing open from 'details' should fire a toggle event at the 'details' element"), t2 = async_test("Removing open from 'details' should fire a toggle event at the 'details' element"),
t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element"), t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element"),
t4 = async_test("Adding open from 'details' (no children) should fire a toggle event at the 'details' element"), t4 = async_test("Adding open from 'details' (no children) should fire a toggle event at the 'details' element"),
t6 = async_test("Calling open twice on 'details' fires only one toggle event"), t6 = async_test("Calling open twice on 'details' fires only one toggle event"),
t7 = async_test("Calling setAttribute('open', '') to 'details' should fire a toggle event at the 'details' element"),
t8 = async_test("Calling removeAttribute('open') to 'details' should fire a toggle event at the 'details' element"),
t9 = async_test("Setting open=true to opened 'details' element should not fire a toggle event at the 'details' element"),
t10 = async_test("Setting open=false to closed 'details' element should not fire a toggle event at the 'details' element"),
details1 = document.getElementById('details1'), details1 = document.getElementById('details1'),
details2 = document.getElementById('details2'), details2 = document.getElementById('details2'),
details3 = document.getElementById('details3'), details3 = document.getElementById('details3'),
details4 = document.getElementById('details4'), details4 = document.getElementById('details4'),
details6 = document.getElementById('details6'), details6 = document.getElementById('details6'),
details7 = document.getElementById('details7'),
details8 = document.getElementById('details8'),
details9 = document.getElementById('details9'),
details10 = document.getElementById('details10'),
loop=false; loop=false;
function testEvent(evt) { function testEvent(evt) {
@ -90,4 +115,43 @@
assert_true(loop); assert_true(loop);
t6.done(); t6.done();
}), 0); }), 0);
details7.ontoggle = t7.step_func_done(function(evt) {
assert_true(details7.open);
testEvent(evt)
});
details7.setAttribute('open', ''); // opens details7
details8.ontoggle = t8.step_func_done(function(evt) {
assert_false(details8.open);
testEvent(evt)
});
details8.removeAttribute('open'); // closes details8
var toggleFiredOnDetails9 = false;
details9.ontoggle = t9.step_func_done(function(evt) {
if (toggleFiredOnDetails9) {
assert_unreached("toggle event fired twice on opened details element");
} else {
toggleFiredOnDetails9 = true;
}
});
// The toggle event should be fired once when declaring details9 with open
// attribute.
details9.open = true; // opens details9
setTimeout(t9.step_func(function() {
assert_true(details9.open);
assert_true(toggleFiredOnDetails9);
t9.done();
}), 0);
details10.ontoggle = t10.step_func_done(function(evt) {
assert_unreached("toggle event fired on closed details element");
});
details10.open = false;
setTimeout(t10.step_func(function() {
assert_false(details10.open);
t10.done();
}), 0);
</script> </script>

View File

@ -415,6 +415,9 @@ NS_EVENT_MESSAGE(eEditorInput)
NS_EVENT_MESSAGE(eSelectStart) NS_EVENT_MESSAGE(eSelectStart)
NS_EVENT_MESSAGE(eSelectionChange) NS_EVENT_MESSAGE(eSelectionChange)
// Details element events.
NS_EVENT_MESSAGE(eToggle)
#ifdef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST #ifdef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST
#undef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST #undef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST
#undef NS_EVENT_MESSAGE_FIRST_LAST #undef NS_EVENT_MESSAGE_FIRST_LAST