Bug 428135, form submission event shouldn't bubble to parent forms, r=jst,sr=sicking,a=beltzner
--- a/content/events/public/nsPIDOMEventTarget.h
+++ b/content/events/public/nsPIDOMEventTarget.h
@@ -44,20 +44,20 @@
class nsIDOMEvent;
class nsPresContext;
class nsEventChainPreVisitor;
class nsEventChainPostVisitor;
class nsIEventListenerManager;
class nsIDOMEventListener;
class nsIDOMEventGroup;
-// 360fa72e-c709-42cc-9285-1f755ec90376
+// f35ffc3b-c8c0-43fd-b0b0-f339e95f574a
#define NS_PIDOMEVENTTARGET_IID \
- { 0x44a6597b, 0x9fc3, 0x4a8d, \
- { 0xb7, 0xa4, 0xd9, 0x00, 0x9a, 0xbf, 0x9d, 0x15 } }
+ { 0xf35ffc3b, 0xc8c0, 0x43fd, \
+ { 0xb0, 0xb0, 0xf3, 0x39, 0xe9, 0x5f, 0x57, 0x4a } }
class nsPIDOMEventTarget : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMEVENTTARGET_IID)
/**
* Returns the nsPIDOMEventTarget object which should be used as the target
@@ -88,16 +88,24 @@ public:
* @param aVisitor the visitor object which is used to create the
* event target chain for event dispatching.
*
* @note Only nsEventDispatcher should call this method.
*/
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor) = 0;
/**
+ * Called just before possible event handlers on this object will be called.
+ */
+ virtual nsresult WillHandleEvent(nsEventChainPostVisitor& aVisitor)
+ {
+ return NS_OK;
+ }
+
+ /**
* Called after the bubble phase of the system event group.
* The default handling of the event should happen here.
* @param aVisitor the visitor object which is used during post handling.
*
* @see nsEventDispatcher.h for documentation about aVisitor.
* @note Only nsEventDispatcher should call this method.
*/
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor) = 0;
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -189,16 +189,20 @@ nsEventTargetChainItem::PreHandleEvent(n
mItemData = aVisitor.mItemData;
return rv;
}
nsresult
nsEventTargetChainItem::HandleEvent(nsEventChainPostVisitor& aVisitor,
PRUint32 aFlags)
{
+ mTarget->WillHandleEvent(aVisitor);
+ if (aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
+ return NS_OK;
+ }
if (!mManager) {
mTarget->GetListenerManager(PR_FALSE, getter_AddRefs(mManager));
}
if (mManager) {
aVisitor.mEvent->currentTarget = CurrentTarget()->GetTargetForDOMEvent();
if (aVisitor.mEvent->currentTarget) {
mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
&aVisitor.mDOMEvent,
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -206,16 +206,17 @@ public:
nsIFormControl* aRadio);
// nsIContent
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult);
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+ virtual nsresult WillHandleEvent(nsEventChainPostVisitor& aVisitor);
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
@@ -828,16 +829,31 @@ nsHTMLFormElement::PreHandleEvent(nsEven
}
mGeneratingReset = PR_TRUE;
}
}
return nsGenericHTMLElement::PreHandleEvent(aVisitor);
}
nsresult
+nsHTMLFormElement::WillHandleEvent(nsEventChainPostVisitor& aVisitor)
+{
+ // If this is the bubble stage and there is a nested form below us which
+ // received a submit event we do *not* want to handle the submit event
+ // for this form too.
+ if ((aVisitor.mEvent->message == NS_FORM_SUBMIT ||
+ aVisitor.mEvent->message == NS_FORM_RESET) &&
+ aVisitor.mEvent->flags & NS_EVENT_FLAG_BUBBLE &&
+ aVisitor.mEvent->originalTarget != static_cast<nsIContent*>(this)) {
+ aVisitor.mEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
+ }
+ return NS_OK;
+}
+
+nsresult
nsHTMLFormElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
if (aVisitor.mEvent->originalTarget == static_cast<nsIContent*>(this)) {
PRUint32 msg = aVisitor.mEvent->message;
if (msg == NS_FORM_SUBMIT) {
// let the form know not to defer subsequent submissions
mDeferSubmission = PR_FALSE;
}
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -104,14 +104,15 @@ include $(topsrcdir)/config/rules.mk
bug392567.jar \
bug392567.jar^headers^ \
test_bug394700.html \
test_bug395107.html \
test_bug401160.xhtml \
test_bug408231.html \
test_bug417760.html \
file_bug417760.png \
+ test_bug428135.xhtml \
test_bug406596.html \
test_bug421640.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug428135.xhtml
@@ -0,0 +1,157 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428135
+-->
+<head>
+ <title>Test for Bug 428135</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428135">Mozilla Bug 428135</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 428135 **/
+
+var expectedCurrentTargets = new Array();
+
+function d(el, ename) {
+ var e = document.createEvent("Events");
+ e.initEvent(ename, true, true);
+ el.dispatchEvent(e);
+}
+
+function testListener(e) {
+ e.preventDefault();
+ var expected = expectedCurrentTargets.shift();
+ ok(expected == e.currentTarget,
+ "Unexpected current target [" + e.currentTarget + "], event=" + e.type +
+ ", phase=" + e.eventPhase + ", target should have been " + expected);
+}
+
+function getAndAddListeners(elname) {
+ var el = document;
+ if (elname) {
+ el = document.getElementById(elname);
+ }
+ el.addEventListener("submit", testListener, true);
+ el.addEventListener("submit", testListener, false);
+ el.addEventListener("reset", testListener, true);
+ el.addEventListener("reset", testListener, false);
+ el.addEventListener("fooEvent", testListener, true);
+ el.addEventListener("fooEvent", testListener, false);
+ return el;
+}
+
+function testSubmitResetEvents() {
+ getAndAddListeners(null);
+ var outerForm = getAndAddListeners("outerForm");
+ var outerSubmit = getAndAddListeners("outerSubmit");
+ var outerReset = getAndAddListeners("outerReset");
+ var outerSubmitDispatcher = getAndAddListeners("outerSubmitDispatcher");
+ var outerResetDispatcher = getAndAddListeners("outerResetDispatcher");
+ var outerChild = getAndAddListeners("outerChild");
+ var innerForm = getAndAddListeners("innerForm");
+ var innerSubmit = getAndAddListeners("innerSubmit");
+ var innerReset = getAndAddListeners("innerReset");
+ var innerSubmitDispatcher = getAndAddListeners("innerSubmitDispatcher");
+ var innerResetDispatcher = getAndAddListeners("innerResetDispatcher");
+
+ expectedCurrentTargets = new Array(document, outerForm, outerForm, document);
+ outerSubmit.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(1) expectedCurrentTargets isn't empty!");
+
+ expectedCurrentTargets = new Array(document, outerForm, outerForm, document);
+ outerReset.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(2) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerSubmitDispatcher, outerSubmitDispatcher);
+ outerSubmitDispatcher.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(3) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerResetDispatcher, outerResetDispatcher);
+ outerResetDispatcher.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(4) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerForm, outerChild);
+ innerSubmit.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(5) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerForm, outerChild);
+ innerReset.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(6) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to inner/outerForm or document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerSubmitDispatcher,
+ innerSubmitDispatcher);
+ innerSubmitDispatcher.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(7) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to inner/outerForm or document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerResetDispatcher,
+ innerResetDispatcher);
+ innerResetDispatcher.click();
+ ok(expectedCurrentTargets.length == 0,
+ "(8) expectedCurrentTargets isn't empty!");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testSubmitResetEvents);
+addLoadEvent(SimpleTest.finish);
+
+
+]]>
+</script>
+</pre>
+<form id="outerForm">
+ <input type="submit" value="outer" id="outerSubmit"/>
+ <input type="reset" value="reset outer" id="outerReset"/>
+ <input type="button" value="dispatch submit" onclick="d(this, 'submit')"
+ id="outerSubmitDispatcher"/>
+ <input type="button" value="dispatch reset" onclick="d(this, 'reset')"
+ id="outerResetDispatcher"/>
+ <div id="outerChild">
+ <form id="innerForm">
+ <input type="submit" value="inner" id="innerSubmit"/>
+ <input type="reset" value="reset inner" id="innerReset"/>
+ <input type="button" value="dispatch submit" onclick="d(this, 'submit')"
+ id="innerSubmitDispatcher"/>
+ <input type="button" value="dispatch reset" onclick="d(this, 'reset')"
+ id="innerResetDispatcher"/>
+ </form>
+ </div>
+</form>
+</body>
+</html>
+