2011-01-19 Dai Mikurube <dmikurube@google.com>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Jan 2011 12:56:25 +0000 (12:56 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Jan 2011 12:56:25 +0000 (12:56 +0000)
        Reviewed by Kent Tamura.

        Implement onformchange and onforminput event handlers
        https://bugs.webkit.org/show_bug.cgi?id=26141

        * fast/dom/Window/window-properties-expected.txt: Added form events.
        * fast/dom/Window/window-property-descriptors-expected.txt: Added form events.
        * fast/forms/formchange-event-expected.txt: Added.
        * fast/forms/formchange-event.html: Added to test formchange events.
        * fast/forms/forminput-event-expected.txt: Added.
        * fast/forms/forminput-event.html: Added to test forminput events.
        * fast/forms/script-tests/formchange-event.js: Added.
        (sendKey):
        (sendText):
        * fast/forms/script-tests/forminput-event.js: Added.
        (sendKey):
        (sendText):
2011-01-19  Dai Mikurube  <dmikurube@google.com>

        Reviewed by Kent Tamura.

        Implement onformchange and onforminput event handlers
        https://bugs.webkit.org/show_bug.cgi?id=26141

        Tests: fast/forms/formchange-event.html
               fast/forms/forminput-event.html

        * bindings/scripts/CodeGeneratorGObject.pm: Added event names.
        * dom/Document.h: Added event definitions.
        * dom/Document.idl: Added event definitions.
        * dom/Element.h: Added event definitions.
        * dom/Element.idl: Added event definitions.
        * dom/Event.cpp:
        (WebCore::Event::fromUserGesture): Added a condition for a formchange event in handling user gestures.
        * dom/EventNames.h: Added event definitions.
        * dom/InputElement.cpp:
        (WebCore::InputElement::setValueFromRenderer): Replaced a direct dispatchEvent() call into dispatchInputEvents(), a virtual function which broadcasts forminput events for HTML elements.
        * dom/Node.cpp:
        (WebCore::Node::dispatchInputEvents): Defined basic dispatchInputEvents() described above.
        (WebCore::Node::dispatchChangeEvents): Defined basic dispatchChangeEvents() described above.
        (WebCore::Node::defaultEventHandler): Replaced a direct dispatchEvent() call into dispatchInputEvents().
        * dom/Node.h:
        * html/FormAssociatedElement.h: Added isResettable() to check the element is resettable or not.
        * html/HTMLAttributeNames.in: Added event names.
        * html/HTMLElement.cpp:
        (WebCore::HTMLElement::parseMappedAttribute): Added event handling.
        (WebCore::HTMLElement::shadowAncestorOwnerForm): Added to get an ancestor <form> element from a shadow element.
        (WebCore::HTMLElement::dispatchChangeEvents): Defined dispatchChangeEvents() for HTML elements described above.
        (WebCore::HTMLElement::dispatchInputEvents): Defined dispatchInputEvents() for HTML elements described above.
        * html/HTMLElement.h:
        * html/HTMLFormControlElement.cpp:
        (WebCore::HTMLFormControlElement::dispatchFormControlChangeEvent): Added calling dispatchFormChange() to broadcast formchange events.
        (WebCore::HTMLFormControlElement::dispatchFormControlInputEvent): Defined newly to dispatch an input event with broadcasting forminput events.
        * html/HTMLFormControlElement.h: Added isResettable().
        * html/HTMLFormElement.cpp:
        (WebCore::HTMLFormElement::broadcastFormEvent): Added to broadcast forminput or formchange events.
        (WebCore::HTMLFormElement::dispatchFormInput): Defined newly to broadcast forminput events.
        (WebCore::HTMLFormElement::dispatchFormChange): Defined newly to broadcast formchange events.
        * html/HTMLFormElement.h:
        * html/HTMLFormElement.idl: Added dispatchFormInput() and dispatchFormChange() DOM API definitions.
        * html/HTMLInputElement.cpp:
        (WebCore::HTMLInputElement::stepUpFromRenderer): Replaced a direct dispatchEvent() call into dispatchInputEvents().
        * html/HTMLInputElement.h: Added isResettable().
        * html/HTMLKeygenElement.h: Added isResettable().
        * html/HTMLObjectElement.h: Added isResettable().
        * html/HTMLOutputElement.h: Added isResettable().
        * html/HTMLSelectElement.h: Added isResettable().
        * html/HTMLTextAreaElement.h: Added isResettable().
        * page/DOMWindow.h: Added event definitions.
        * page/DOMWindow.idl: Added event definitions.
        * rendering/TextControlInnerElements.cpp:
        (WebCore::SearchFieldCancelButtonElement::defaultEventHandler): Replaced a direct dispatchEvent() call into dispatchInputEvents().

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@76115 268f45cc-cd09-0410-ab3c-d52691b4dbfc

39 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/Window/window-properties-expected.txt
LayoutTests/fast/dom/Window/window-property-descriptors-expected.txt
LayoutTests/fast/forms/formchange-event-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/formchange-event.html [new file with mode: 0644]
LayoutTests/fast/forms/forminput-event-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/forminput-event.html [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/formchange-event.js [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/forminput-event.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/scripts/CodeGeneratorGObject.pm
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
Source/WebCore/dom/Element.h
Source/WebCore/dom/Element.idl
Source/WebCore/dom/Event.cpp
Source/WebCore/dom/EventNames.h
Source/WebCore/dom/InputElement.cpp
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/html/FormAssociatedElement.h
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLElement.cpp
Source/WebCore/html/HTMLElement.h
Source/WebCore/html/HTMLFormControlElement.cpp
Source/WebCore/html/HTMLFormControlElement.h
Source/WebCore/html/HTMLFormElement.cpp
Source/WebCore/html/HTMLFormElement.h
Source/WebCore/html/HTMLFormElement.idl
Source/WebCore/html/HTMLInputElement.cpp
Source/WebCore/html/HTMLInputElement.h
Source/WebCore/html/HTMLKeygenElement.h
Source/WebCore/html/HTMLObjectElement.h
Source/WebCore/html/HTMLOutputElement.h
Source/WebCore/html/HTMLSelectElement.h
Source/WebCore/html/HTMLTextAreaElement.h
Source/WebCore/page/DOMWindow.h
Source/WebCore/page/DOMWindow.idl
Source/WebCore/rendering/TextControlInnerElements.cpp

index 80b8ad3..aaf952f 100644 (file)
@@ -1,3 +1,23 @@
+2011-01-19  Dai Mikurube  <dmikurube@google.com>
+
+        Reviewed by Kent Tamura.
+
+        Implement onformchange and onforminput event handlers
+        https://bugs.webkit.org/show_bug.cgi?id=26141
+
+        * fast/dom/Window/window-properties-expected.txt: Added form events.
+        * fast/dom/Window/window-property-descriptors-expected.txt: Added form events.
+        * fast/forms/formchange-event-expected.txt: Added.
+        * fast/forms/formchange-event.html: Added to test formchange events.
+        * fast/forms/forminput-event-expected.txt: Added.
+        * fast/forms/forminput-event.html: Added to test forminput events.
+        * fast/forms/script-tests/formchange-event.js: Added.
+        (sendKey):
+        (sendText):
+        * fast/forms/script-tests/forminput-event.js: Added.
+        (sendKey):
+        (sendText):
+
 2011-01-18  Mikhail Naganov  <mnaganov@chromium.org>
 
         Reviewed by Yury Semikhatsky.
index 5ebad83..9448f30 100644 (file)
@@ -2311,6 +2311,8 @@ window.onemptied [null]
 window.onended [null]
 window.onerror [null]
 window.onfocus [null]
+window.onformchange [null]
+window.onforminput [null]
 window.onhashchange [null]
 window.oninput [null]
 window.oninvalid [null]
index 68315ad..e507839 100644 (file)
@@ -393,6 +393,8 @@ PASS typeof Object.getOwnPropertyDescriptor(window, 'onemptied') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'onended') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'onerror') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'onfocus') is 'object'
+PASS typeof Object.getOwnPropertyDescriptor(window, 'onformchange') is 'object'
+PASS typeof Object.getOwnPropertyDescriptor(window, 'onforminput') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'onhashchange') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'oninput') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'oninvalid') is 'object'
diff --git a/LayoutTests/fast/forms/formchange-event-expected.txt b/LayoutTests/fast/forms/formchange-event-expected.txt
new file mode 100644 (file)
index 0000000..64b11ba
--- /dev/null
@@ -0,0 +1,38 @@
+Test for formchange events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Change for input (type=text)
+PASS result is "Fired"
+Change for input (type=number)
+PASS result is "Fired"
+Change for input (type=radio)
+PASS result is "Fired"
+Change for input (type=checkbox)
+PASS result is "Fired"
+Change for textarea
+PASS result is "Fired"
+Change for select
+PASS result is "Fired"
+form.dispatchFormChange()
+PASS result is "Fired"
+
+Is formchange delivered for?
+PASS resultInputText is "Delivered"
+PASS resultInputNumber is "Delivered"
+PASS resultInputRadio is "Delivered"
+PASS resultInputCheckbox is "Delivered"
+PASS resultTextarea is "Delivered"
+PASS resultKeygen is "Delivered"
+PASS resultObject is "Not delivered"
+PASS resultOutput is "Delivered"
+PASS resultSelect is "Delivered"
+PASS resultP is "Not delivered"
+PASS resultRemovingInput is "Delivered"
+PASS resultRemovingForm is "Delivered"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/forms/formchange-event.html b/LayoutTests/fast/forms/formchange-event.html
new file mode 100644 (file)
index 0000000..826bbac
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/formchange-event.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/forminput-event-expected.txt b/LayoutTests/fast/forms/forminput-event-expected.txt
new file mode 100644 (file)
index 0000000..36e58a6
--- /dev/null
@@ -0,0 +1,38 @@
+Test for forminput events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Change for input (type=text)
+PASS result is "Fired"
+Change for input (type=number)
+PASS result is "Fired"
+Change for input (type=radio)
+PASS result is "Not fired"
+Change for input (type=checkbox)
+PASS result is "Not fired"
+Change for textarea
+PASS result is "Fired"
+Change for select
+PASS result is "Not fired"
+form.dispatchFormInput()
+PASS result is "Fired"
+
+Is forminput delivered for?
+PASS resultInputText is "Delivered"
+PASS resultInputNumber is "Delivered"
+PASS resultInputRadio is "Delivered"
+PASS resultInputCheckbox is "Delivered"
+PASS resultTextarea is "Delivered"
+PASS resultKeygen is "Delivered"
+PASS resultObject is "Not delivered"
+PASS resultOutput is "Delivered"
+PASS resultSelect is "Delivered"
+PASS resultP is "Not delivered"
+PASS resultRemovingInput is "Delivered"
+PASS resultRemovingForm is "Delivered"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/forms/forminput-event.html b/LayoutTests/fast/forms/forminput-event.html
new file mode 100644 (file)
index 0000000..e27a920
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/forminput-event.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/script-tests/formchange-event.js b/LayoutTests/fast/forms/script-tests/formchange-event.js
new file mode 100644 (file)
index 0000000..18e9c24
--- /dev/null
@@ -0,0 +1,177 @@
+description('Test for formchange events.');
+
+function sendKey(element, keyName) {
+    var event = document.createEvent('KeyboardEvent');
+    event.initKeyboardEvent('keydown', true, true, document.defaultView, keyName);
+    element.dispatchEvent(event);
+}
+
+function sendText(element, text) {
+    var event = document.createEvent('TextEvent');
+    event.initTextEvent('textInput', true, true, document.defaultView, text);
+    element.dispatchEvent(event);
+}
+
+function sendMouseClick(element) {
+    var x = element.clientLeft + 10;
+    var y = element.clientTop + 10;
+    var event = document.createEvent("MouseEvent");
+    event.initMouseEvent("mousedown", true, true, document.defaultView, 1, x, y, x, y, false, false, false, false, 0, document);
+    element.dispatchEvent(event);
+    var event = document.createEvent("MouseEvent");
+    event.initMouseEvent("mouseup", true, true, document.defaultView, 1, x, y, x, y, false, false, false, false, 0, document);
+    element.dispatchEvent(event);
+}
+
+function $(id) {
+    return document.getElementById(id);
+}
+
+
+var form = document.createElement('form');
+form.setAttribute('id', 'form');
+form.innerHTML =
+    '<input type="text" id="default-input" value="Input here." />' +
+    '<input type="text" id="input-text" value="Input here." />' +
+    '<input type="number" id="input-number" value="24" />' +
+    '<input type="radio" name="input-radio" id="input-radio1" value="1" checked/>' +
+    '<input type="radio" name="input-radio" id="input-radio2" value="2" />' +
+    '<input type="checkbox" name="input-check" id="input-check1" value="1" checked/>' +
+    '<input type="checkbox" name="input-check" id="input-check2" value="2" />' +
+    '<textarea id="input-textarea">Input here.</textarea>' +
+    '<select id="input-select" multiple>' +
+    '<option id="input-option1" value="option1">option1</option>' +
+    '<option id="input-option2" value="option2">option2</option>' +
+    '</select>' +
+
+    '<input type="text" id="default-handler" onformchange="result = \'Fired\'" />' +
+    '<input type="text" id="handler-text" onformchange="resultInputText = \'Delivered\'" />' +
+    '<input type="number" id="handler-number" onformchange="resultInputNumber = \'Delivered\'" />' +
+    '<input type="radio" name="handler-radio" id="handler-radio" onformchange="resultInputRadio = \'Delivered\'" />' +
+    '<input type="checkbox" name="handler-check" id="handler-check" onformchange="resultInputCheckbox = \'Delivered\'" />' +
+    '<textarea id="handler-textarea" onformchange="resultTextarea = \'Delivered\'"></textarea>' +
+    '<keygen id="handler-keygen" onformchange="resultKeygen = \'Delivered\'" />' +
+    '<object id="handler-object" onformchange="resultObject = \'Delivered\'" />' +
+    '<output id="handler-output" onformchange="resultOutput = \'Delivered\'" />' +
+    '<select id="handler-select" onformchange="resultSelect = \'Delivered\'" />' +
+
+    '<p id="handler-p" onformchange="resultP = \'Delivered\'">Hi!</p>';
+document.body.appendChild(form);
+
+
+function onformchangeRemovingInput() {
+    $('form-removing-input').removeChild($('handler-removing-input'));
+    resultRemovingInput = 'Delivered';
+}
+
+var form_removing_input = document.createElement('form');
+form_removing_input.setAttribute('id', 'form-removing-input');
+form_removing_input.innerHTML =
+    '<input type="text" id="input-removing-input" value="Input here." />' +
+    '<input type="text" id="handler-removing-input" onformchange="onformchangeRemovingInput()" />';
+document.body.appendChild(form_removing_input);
+
+
+function onformchangeRemovingForm() {
+    document.body.removeChild($('form-removing-form'));
+    resultRemovingForm = 'Delivered';
+}
+
+var form_removing_form = document.createElement('form');
+form_removing_form.setAttribute('id', 'form-removing-form');
+form_removing_form.innerHTML =
+    '<input type="text" id="input-removing-form" value="Input here." />' +
+    '<input type="text" id="handler-removing-form" onformchange="onformchangeRemovingForm()" />';
+document.body.appendChild(form_removing_form);
+
+
+var result;
+
+debug('Change for input (type=text)');
+result = "Not fired";
+$('input-text').focus();
+sendText($('input-text'), 'New text.');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for input (type=number)');
+result = "Not fired";
+$('input-number').focus();
+sendKey($('input-number'), 'Up');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for input (type=radio)');
+result = "Not fired";
+$('input-radio2').click();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for input (type=checkbox)');
+result = "Not fired";
+$('input-check2').click();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for textarea');
+result = "Not fired";
+$('input-textarea').focus();
+sendText($('input-textarea'), 'New text.');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for select');
+result = "Not fired";
+sendMouseClick($('input-select'));
+shouldBeEqualToString("result", "Fired");
+
+debug('form.dispatchFormChange()');
+result = "Not fired";
+form.dispatchFormChange();
+shouldBeEqualToString("result", "Fired");
+
+
+debug('');
+
+
+debug('Is formchange delivered for?');
+var resultInputText = "Not delivered";
+var resultInputNumber = "Not delivered";
+var resultInputRadio = "Not delivered";
+var resultInputCheckbox = "Not delivered";
+var resultTextarea = "Not delivered";
+var resultKeygen = "Not delivered";
+var resultObject = "Not delivered";
+var resultOutput = "Not delivered";
+var resultSelect = "Not delivered";
+var resultP = "Not delivered";
+$('default-input').focus();
+sendText($('default-input'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultInputText", "Delivered");
+shouldBeEqualToString("resultInputNumber", "Delivered");
+shouldBeEqualToString("resultInputRadio", "Delivered");
+shouldBeEqualToString("resultInputCheckbox", "Delivered");
+shouldBeEqualToString("resultTextarea", "Delivered");
+shouldBeEqualToString("resultKeygen", "Delivered");
+shouldBeEqualToString("resultObject", "Not delivered");
+shouldBeEqualToString("resultOutput", "Delivered");
+shouldBeEqualToString("resultSelect", "Delivered");
+shouldBeEqualToString("resultP", "Not delivered");
+
+var resultRemovingInput = "Not delivered";
+$('input-removing-input').focus();
+sendText($('input-removing-input'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultRemovingInput", "Delivered");
+
+var resultRemovingForm = "Not delivered";
+$('input-removing-form').focus();
+sendText($('input-removing-form'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultRemovingForm", "Delivered");
+
+
+document.body.removeChild(form);
+debug('');
+
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/forms/script-tests/forminput-event.js b/LayoutTests/fast/forms/script-tests/forminput-event.js
new file mode 100644 (file)
index 0000000..c1b3ec9
--- /dev/null
@@ -0,0 +1,177 @@
+description('Test for forminput events.');
+
+function sendKey(element, keyName) {
+    var event = document.createEvent('KeyboardEvent');
+    event.initKeyboardEvent('keydown', true, true, document.defaultView, keyName);
+    element.dispatchEvent(event);
+}
+
+function sendText(element, text) {
+    var event = document.createEvent('TextEvent');
+    event.initTextEvent('textInput', true, true, document.defaultView, text);
+    element.dispatchEvent(event);
+}
+
+function sendMouseClick(element) {
+    var x = element.clientLeft + 10;
+    var y = element.clientTop + 10;
+    var event = document.createEvent("MouseEvent");
+    event.initMouseEvent("mousedown", true, true, document.defaultView, 1, x, y, x, y, false, false, false, false, 0, document);
+    element.dispatchEvent(event);
+    var event = document.createEvent("MouseEvent");
+    event.initMouseEvent("mouseup", true, true, document.defaultView, 1, x, y, x, y, false, false, false, false, 0, document);
+    element.dispatchEvent(event);
+}
+
+function $(id) {
+    return document.getElementById(id);
+}
+
+
+var form = document.createElement('form');
+form.setAttribute('id', 'form');
+form.innerHTML =
+    '<input type="text" id="default-input" value="Input here." />' +
+    '<input type="text" id="input-text" value="Input here." />' +
+    '<input type="number" id="input-number" value="24" />' +
+    '<input type="radio" name="input-radio" id="input-radio1" value="1" checked/>' +
+    '<input type="radio" name="input-radio" id="input-radio2" value="2" />' +
+    '<input type="checkbox" name="input-check" id="input-check1" value="1" checked/>' +
+    '<input type="checkbox" name="input-check" id="input-check2" value="2" />' +
+    '<textarea id="input-textarea">Input here.</textarea>' +
+    '<select id="input-select" multiple>' +
+    '<option id="input-option1" value="option1">option1</option>' +
+    '<option id="input-option2" value="option2">option2</option>' +
+    '</select>' +
+
+    '<input type="text" id="default-handler" onforminput="result = \'Fired\'" />' +
+    '<input type="text" id="handler-text" onforminput="resultInputText = \'Delivered\'" />' +
+    '<input type="number" id="handler-number" onforminput="resultInputNumber = \'Delivered\'" />' +
+    '<input type="radio" name="handler-radio" id="handler-radio" onforminput="resultInputRadio = \'Delivered\'" />' +
+    '<input type="checkbox" name="handler-check" id="handler-check" onforminput="resultInputCheckbox = \'Delivered\'" />' +
+    '<textarea id="handler-textarea" onforminput="resultTextarea = \'Delivered\'"></textarea>' +
+    '<keygen id="handler-keygen" onforminput="resultKeygen = \'Delivered\'" />' +
+    '<object id="handler-object" onforminput="resultObject = \'Delivered\'" />' +
+    '<output id="handler-output" onforminput="resultOutput = \'Delivered\'" />' +
+    '<select id="handler-select" onforminput="resultSelect = \'Delivered\'" />' +
+
+    '<p id="handler-p" onforminput="resultP = \'Delivered\'">Hi!</p>';
+document.body.appendChild(form);
+
+
+function onforminputRemovingInput() {
+    $('form-removing-input').removeChild($('handler-removing-input'));
+    resultRemovingInput = 'Delivered';
+}
+
+var formRemovingInput = document.createElement('form');
+formRemovingInput.setAttribute('id', 'form-removing-input');
+formRemovingInput.innerHTML =
+    '<input type="text" id="input-removing-input" value="Input here." />' +
+    '<input type="text" id="handler-removing-input" onforminput="onforminputRemovingInput()" />';
+document.body.appendChild(formRemovingInput);
+
+
+function onforminputRemovingForm() {
+    document.body.removeChild($('form-removing-form'));
+    resultRemovingForm = 'Delivered';
+}
+
+var formRemovingForm = document.createElement('form');
+formRemovingForm.setAttribute('id', 'form-removing-form');
+formRemovingForm.innerHTML =
+    '<input type="text" id="input-removing-form" value="Input here." />' +
+    '<input type="text" id="handler-removing-form" onforminput="onforminputRemovingForm()" />';
+document.body.appendChild(formRemovingForm);
+
+
+var result;
+
+debug('Change for input (type=text)');
+result = "Not fired";
+$('input-text').focus();
+sendText($('input-text'), 'New text.');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for input (type=number)');
+result = "Not fired";
+$('input-number').focus();
+sendKey($('input-number'), 'Up');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for input (type=radio)');
+result = "Not fired";
+$('input-radio2').click();
+shouldBeEqualToString("result", "Not fired");
+
+debug('Change for input (type=checkbox)');
+result = "Not fired";
+$('input-check2').click();
+shouldBeEqualToString("result", "Not fired");
+
+debug('Change for textarea');
+result = "Not fired";
+$('input-textarea').focus();
+sendText($('input-textarea'), 'New text.');
+$('default-input').focus();
+shouldBeEqualToString("result", "Fired");
+
+debug('Change for select');
+result = "Not fired";
+sendMouseClick($('input-select'));
+shouldBeEqualToString("result", "Not fired");
+
+debug('form.dispatchFormInput()');
+result = "Not fired";
+form.dispatchFormInput();
+shouldBeEqualToString("result", "Fired");
+
+
+debug('');
+
+
+debug('Is forminput delivered for?');
+var resultInputText = "Not delivered";
+var resultInputNumber = "Not delivered";
+var resultInputRadio = "Not delivered";
+var resultInputCheckbox = "Not delivered";
+var resultTextarea = "Not delivered";
+var resultKeygen = "Not delivered";
+var resultObject = "Not delivered";
+var resultOutput = "Not delivered";
+var resultSelect = "Not delivered";
+var resultP = "Not delivered";
+$('default-input').focus();
+sendText($('default-input'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultInputText", "Delivered");
+shouldBeEqualToString("resultInputNumber", "Delivered");
+shouldBeEqualToString("resultInputRadio", "Delivered");
+shouldBeEqualToString("resultInputCheckbox", "Delivered");
+shouldBeEqualToString("resultTextarea", "Delivered");
+shouldBeEqualToString("resultKeygen", "Delivered");
+shouldBeEqualToString("resultObject", "Not delivered");
+shouldBeEqualToString("resultOutput", "Delivered");
+shouldBeEqualToString("resultSelect", "Delivered");
+shouldBeEqualToString("resultP", "Not delivered");
+
+var resultRemovingInput = "Not delivered";
+$('input-removing-input').focus();
+sendText($('input-removing-input'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultRemovingInput", "Delivered");
+
+var resultRemovingForm = "Not delivered";
+$('input-removing-form').focus();
+sendText($('input-removing-form'), 'New text.');
+$('default-handler').focus();
+shouldBeEqualToString("resultRemovingForm", "Delivered");
+
+
+document.body.removeChild(form);
+debug('');
+
+
+var successfullyParsed = true;
index 7dfe09b..ae2f29e 100644 (file)
@@ -1,3 +1,59 @@
+2011-01-19  Dai Mikurube  <dmikurube@google.com>
+
+        Reviewed by Kent Tamura.
+
+        Implement onformchange and onforminput event handlers
+        https://bugs.webkit.org/show_bug.cgi?id=26141
+
+        Tests: fast/forms/formchange-event.html
+               fast/forms/forminput-event.html
+
+        * bindings/scripts/CodeGeneratorGObject.pm: Added event names.
+        * dom/Document.h: Added event definitions.
+        * dom/Document.idl: Added event definitions.
+        * dom/Element.h: Added event definitions.
+        * dom/Element.idl: Added event definitions.
+        * dom/Event.cpp:
+        (WebCore::Event::fromUserGesture): Added a condition for a formchange event in handling user gestures.
+        * dom/EventNames.h: Added event definitions.
+        * dom/InputElement.cpp:
+        (WebCore::InputElement::setValueFromRenderer): Replaced a direct dispatchEvent() call into dispatchInputEvents(), a virtual function which broadcasts forminput events for HTML elements.
+        * dom/Node.cpp:
+        (WebCore::Node::dispatchInputEvents): Defined basic dispatchInputEvents() described above.
+        (WebCore::Node::dispatchChangeEvents): Defined basic dispatchChangeEvents() described above.
+        (WebCore::Node::defaultEventHandler): Replaced a direct dispatchEvent() call into dispatchInputEvents().
+        * dom/Node.h:
+        * html/FormAssociatedElement.h: Added isResettable() to check the element is resettable or not.
+        * html/HTMLAttributeNames.in: Added event names.
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::parseMappedAttribute): Added event handling.
+        (WebCore::HTMLElement::shadowAncestorOwnerForm): Added to get an ancestor <form> element from a shadow element.
+        (WebCore::HTMLElement::dispatchChangeEvents): Defined dispatchChangeEvents() for HTML elements described above.
+        (WebCore::HTMLElement::dispatchInputEvents): Defined dispatchInputEvents() for HTML elements described above.
+        * html/HTMLElement.h:
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::dispatchFormControlChangeEvent): Added calling dispatchFormChange() to broadcast formchange events.
+        (WebCore::HTMLFormControlElement::dispatchFormControlInputEvent): Defined newly to dispatch an input event with broadcasting forminput events.
+        * html/HTMLFormControlElement.h: Added isResettable().
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::broadcastFormEvent): Added to broadcast forminput or formchange events.
+        (WebCore::HTMLFormElement::dispatchFormInput): Defined newly to broadcast forminput events.
+        (WebCore::HTMLFormElement::dispatchFormChange): Defined newly to broadcast formchange events.
+        * html/HTMLFormElement.h:
+        * html/HTMLFormElement.idl: Added dispatchFormInput() and dispatchFormChange() DOM API definitions.
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::stepUpFromRenderer): Replaced a direct dispatchEvent() call into dispatchInputEvents().
+        * html/HTMLInputElement.h: Added isResettable().
+        * html/HTMLKeygenElement.h: Added isResettable().
+        * html/HTMLObjectElement.h: Added isResettable().
+        * html/HTMLOutputElement.h: Added isResettable().
+        * html/HTMLSelectElement.h: Added isResettable().
+        * html/HTMLTextAreaElement.h: Added isResettable().
+        * page/DOMWindow.h: Added event definitions.
+        * page/DOMWindow.idl: Added event definitions.
+        * rendering/TextControlInnerElements.cpp:
+        (WebCore::SearchFieldCancelButtonElement::defaultEventHandler): Replaced a direct dispatchEvent() call into dispatchInputEvents().
+
 2011-01-19  Andrey Kosyakov  <caseq@chromium.org>
 
         Reviewed by Pavel Feldman.
index 1863d8f..ed76d08 100644 (file)
@@ -518,7 +518,7 @@ my @eventSignalNames = (
     # User Interface Event types
     "focus", "blur",
     # Basic Event types
-    "load", "unload", "abort", "error", "select", "change", "submit", "reset",
+    "load", "unload", "abort", "error", "select", "change", "formchange", "submit", "reset",
     "resize", "scroll",
     # Mouse Event types
     "click", "dblclick", "mousedown", "mouseup",
@@ -540,7 +540,7 @@ my @eventSignalNames = (
     # Animations
     "webkitanimationend", "webkitanimationstart", "webkitanimationiteration",
     # Other
-    "contextmenu", "input", "invalid", "search", "selectstart");
+    "contextmenu", "input", "forminput", "invalid", "search", "selectstart");
 
 sub GenerateProperties {
     my ($object, $interfaceName, $dataNode) = @_;
index b38d3dd..2cd26f9 100644 (file)
@@ -249,6 +249,8 @@ public:
     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(drag);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(formchange);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(forminput);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(input);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown);
index 8d7a71b..c215df0 100644 (file)
@@ -266,6 +266,8 @@ module core {
         attribute [DontEnum] EventListener ondrop;
         attribute [DontEnum] EventListener onerror;
         attribute [DontEnum] EventListener onfocus;
+        attribute [DontEnum] EventListener onformchange;
+        attribute [DontEnum] EventListener onforminput;
         attribute [DontEnum] EventListener oninput;
         attribute [DontEnum] EventListener oninvalid;
         attribute [DontEnum] EventListener onkeydown;
@@ -288,8 +290,6 @@ module core {
         // attribute [DontEnum] EventListener ondurationchange;
         // attribute [DontEnum] EventListener onemptied;
         // attribute [DontEnum] EventListener onended;
-        // attribute [DontEnum] EventListener onformchange;
-        // attribute [DontEnum] EventListener onforminput;
         // attribute [DontEnum] EventListener onloadeddata;
         // attribute [DontEnum] EventListener onloadedmetadata;
         // attribute [DontEnum] EventListener onloadstart;
index 5e6c047..4510478 100644 (file)
@@ -63,6 +63,8 @@ public:
     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(drag);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(formchange);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(forminput);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(input);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown);
index 7cb63c2..652a75b 100644 (file)
@@ -153,6 +153,8 @@ module core {
         attribute [DontEnum] EventListener ondrop;
         attribute [DontEnum] EventListener onerror;
         attribute [DontEnum] EventListener onfocus;
+        attribute [DontEnum] EventListener onformchange;
+        attribute [DontEnum] EventListener onforminput;
         attribute [DontEnum] EventListener oninput;
         attribute [DontEnum] EventListener oninvalid;
         attribute [DontEnum] EventListener onkeydown;
@@ -174,8 +176,6 @@ module core {
         // attribute [DontEnum] EventListener ondurationchange;
         // attribute [DontEnum] EventListener onemptied;
         // attribute [DontEnum] EventListener onended;
-        // attribute [DontEnum] EventListener onformchange;
-        // attribute [DontEnum] EventListener onforminput;
         // attribute [DontEnum] EventListener onloadeddata;
         // attribute [DontEnum] EventListener onloadedmetadata;
         // attribute [DontEnum] EventListener onloadstart;
index 0a1538b..24efc4e 100644 (file)
@@ -265,7 +265,7 @@ bool Event::fromUserGesture()
         // other accepted events
         || type == eventNames().selectEvent || type == eventNames().changeEvent
         || type == eventNames().focusEvent || type == eventNames().blurEvent
-        || type == eventNames().submitEvent;
+        || type == eventNames().submitEvent || type == eventNames().formchangeEvent;
 }
 
 bool Event::storesResultAsString() const
index 496804e..72768d2 100644 (file)
@@ -66,6 +66,8 @@ namespace WebCore {
     macro(focus) \
     macro(focusin) \
     macro(focusout) \
+    macro(formchange) \
+    macro(forminput) \
     macro(hashchange) \
     macro(input) \
     macro(invalid) \
index 37211d8..db89c16 100644 (file)
@@ -140,7 +140,7 @@ void InputElement::setValueFromRenderer(InputElementData& data, InputElement* in
 
     // Input event is fired by the Node::defaultEventHandler for editable controls.
     if (!inputElement->isTextField())
-        element->dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+        element->dispatchInputEvents();
     notifyFormStateChanged(element);
 }
 
index 08a9a47..2e42e84 100644 (file)
@@ -2963,6 +2963,16 @@ void Node::dispatchBlurEvent()
     dispatchEvent(Event::create(eventNames().blurEvent, false, false));
 }
 
+void Node::dispatchChangeEvents()
+{
+    dispatchEvent(Event::create(eventNames().changeEvent, true, false));
+}
+
+void Node::dispatchInputEvents()
+{
+    dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+}
+
 bool Node::disabled() const
 {
     return false;
@@ -3020,7 +3030,7 @@ void Node::defaultEventHandler(Event* event)
             if (Frame* frame = document()->frame())
                 frame->eventHandler()->defaultWheelEventHandler(startNode, wheelEvent);
     } else if (event->type() == eventNames().webkitEditableContentChangedEvent) {
-        dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+        dispatchInputEvents();
     }
 }
 
index 7c88f0f..74e1cc4 100644 (file)
@@ -541,6 +541,8 @@ public:
 
     virtual void dispatchFocusEvent();
     virtual void dispatchBlurEvent();
+    virtual void dispatchChangeEvents();
+    virtual void dispatchInputEvents();
 
     // Perform the default action for an event.
     virtual void defaultEventHandler(Event*);
index 873bdf4..ebefdc6 100644 (file)
@@ -46,6 +46,7 @@ public:
 
     virtual bool isFormControlElement() const = 0;
     virtual bool isEnumeratable() const = 0;
+    virtual bool isResettable() const = 0;
 
     const AtomicString& name() const { return formControlName(); }
 
index 513e44f..eac9a73 100644 (file)
@@ -177,6 +177,8 @@ onerror
 onfocus
 onfocusin
 onfocusout
+onformchange
+onforminput
 onhashchange
 oninput
 oninvalid
index af3115c..1a4fc85 100644 (file)
@@ -190,6 +190,10 @@ void HTMLElement::parseMappedAttribute(Attribute* attr)
         setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr));
     } else if (attr->name() == onfocusoutAttr) {
         setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr));
+    } else if (attr->name() == onformchangeAttr) {
+        setAttributeEventListener(eventNames().formchangeEvent, createAttributeEventListener(this, attr));
+    } else if (attr->name() == onforminputAttr) {
+        setAttributeEventListener(eventNames().forminputEvent, createAttributeEventListener(this, attr));
     } else if (attr->name() == onblurAttr) {
         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
     } else if (attr->name() == onkeydownAttr) {
@@ -837,6 +841,40 @@ HTMLFormElement* HTMLElement::virtualForm() const
     return findFormAncestor();
 }
 
+HTMLFormElement* HTMLElement::shadowAncestorOwnerForm()
+{
+    Node* ancestorNode = shadowAncestorNode();
+    if (!ancestorNode)
+        return form();
+
+    if (!ancestorNode->isHTMLElement())
+        return 0;
+    HTMLElement* ancestorHTML = static_cast<HTMLElement*>(ancestorNode);
+    if (!ancestorHTML)
+        return 0;
+    return ancestorHTML->form();
+}
+
+void HTMLElement::dispatchChangeEvents()
+{
+    RefPtr<HTMLElement> protector(this);
+    RefPtr<HTMLFormElement> ownerForm(shadowAncestorOwnerForm());
+
+    Node::dispatchChangeEvents();
+    if (ownerForm)
+        ownerForm->dispatchFormChange();
+}
+
+void HTMLElement::dispatchInputEvents()
+{
+    RefPtr<HTMLElement> protector(this);
+    RefPtr<HTMLFormElement> ownerForm(shadowAncestorOwnerForm());
+
+    Node::dispatchInputEvents();
+    if (ownerForm)
+        ownerForm->dispatchFormInput();
+}
+
 } // namespace WebCore
 
 #ifndef NDEBUG
index a64db2c..3356bab 100644 (file)
@@ -84,6 +84,9 @@ public:
 
     HTMLFormElement* findFormAncestor() const;
 
+    virtual void dispatchChangeEvents();
+    virtual void dispatchInputEvents();
+
 protected:
     HTMLElement(const QualifiedName& tagName, Document*);
 
@@ -101,6 +104,8 @@ private:
 
     Node* insertAdjacent(const String& where, Node* newChild, ExceptionCode&);
     PassRefPtr<DocumentFragment> textToFragment(const String&, ExceptionCode&);
+
+    HTMLFormElement* shadowAncestorOwnerForm();
 };
 
 inline HTMLElement::HTMLElement(const QualifiedName& tagName, Document* document)
index b3ad7c8..bb42dfd 100644 (file)
@@ -178,7 +178,12 @@ void HTMLFormControlElement::setName(const AtomicString& value)
 
 void HTMLFormControlElement::dispatchFormControlChangeEvent()
 {
-    dispatchEvent(Event::create(eventNames().changeEvent, true, false));
+    HTMLElement::dispatchChangeEvents();
+}
+
+void HTMLFormControlElement::dispatchFormControlInputEvent()
+{
+    HTMLElement::dispatchInputEvents();
 }
 
 void HTMLFormControlElement::setDisabled(bool b)
index 8403b84..c88905c 100644 (file)
@@ -53,12 +53,14 @@ public:
     virtual void setFormControlValueMatchesRenderer(bool b) { m_valueMatchesRenderer = b; }
 
     virtual void dispatchFormControlChangeEvent();
+    virtual void dispatchFormControlInputEvent();
 
     virtual bool disabled() const { return m_disabled; }
     void setDisabled(bool);
 
     virtual bool isFocusable() const;
     virtual bool isEnumeratable() const { return false; }
+    virtual bool isResettable() const { return false; }
 
     // Determines whether or not a control will be automatically focused.
     virtual bool autofocus() const;
index c896bbf..d778601 100644 (file)
@@ -590,6 +590,39 @@ bool HTMLFormElement::checkValidity()
     return controls.isEmpty();
 }
 
+void HTMLFormElement::broadcastFormEvent(const AtomicString& eventName)
+{
+    RefPtr<HTMLFormElement> protector(this);
+    // Copy m_associatedElements because event handlers called from
+    // formElement->dispatchEvent() might change m_associatedElements.
+    Vector<RefPtr<FormAssociatedElement> > elements;
+    elements.reserveCapacity(m_associatedElements.size());
+    for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
+        if (!m_associatedElements[i]->isResettable())
+            continue;
+        elements.append(m_associatedElements[i]);
+    }
+
+    for (unsigned i = 0; i < elements.size(); ++i) {
+        // We can assume a resettable control is always a HTMLFormControlElement.
+        // FIXME: We should handle resettable non-HTMLFormControlElements maybe in the future.
+        ASSERT(elements[i]->isFormControlElement());
+        HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(elements[i].get());
+        if (!formElement->dispatchEvent(Event::create(eventName, false, false)))
+            continue;
+    }
+}
+
+void HTMLFormElement::dispatchFormInput()
+{
+    broadcastFormEvent(eventNames().forminputEvent);
+}
+
+void HTMLFormElement::dispatchFormChange()
+{
+    broadcastFormEvent(eventNames().formchangeEvent);
+}
+
 void HTMLFormElement::collectUnhandledInvalidControls(Vector<RefPtr<FormAssociatedElement> >& unhandledInvalidControls)
 {
     RefPtr<HTMLFormElement> protector(this);
index bd087b8..7d7f4f8 100644 (file)
@@ -114,6 +114,9 @@ public:
 
     const Vector<FormAssociatedElement*>& associatedElements() const { return m_associatedElements; }
 
+    void dispatchFormInput();
+    void dispatchFormChange();
+
 private:
     HTMLFormElement(const QualifiedName&, Document*);
 
@@ -144,6 +147,8 @@ private:
     // event was not canceled to the specified vector.
     void collectUnhandledInvalidControls(Vector<RefPtr<FormAssociatedElement> >&);
 
+    void broadcastFormEvent(const AtomicString&);
+
     friend class HTMLFormCollection;
 
     typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<HTMLFormControlElement> > AliasMap;
index e9759e2..3e9e46d 100644 (file)
@@ -43,6 +43,9 @@ module html {
 #endif
         void reset();
         boolean checkValidity();
+
+        void dispatchFormInput();
+        void dispatchFormChange();
     };
 
 }
index a1e7d8b..3c11bfa 100644 (file)
@@ -1379,7 +1379,7 @@ void HTMLInputElement::stepUpFromRenderer(int n)
         if (m_inputType->isRangeControl())
             dispatchFormControlChangeEvent();
         else
-            dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+            dispatchFormControlInputEvent();
     }
 }
 
index eb30de0..403248c 100644 (file)
@@ -211,6 +211,7 @@ private:
     virtual bool isKeyboardFocusable(KeyboardEvent*) const;
     virtual bool isMouseFocusable() const;
     virtual bool isEnumeratable() const;
+    virtual bool isResettable() const { return true; }
     virtual void updateFocusAppearance(bool restorePreviousSelection);
     virtual void aboutToUnload();
     virtual bool shouldUseInputMethod() const;
index 80f94b5..b8b144f 100644 (file)
@@ -35,6 +35,8 @@ public:
 private:
     HTMLKeygenElement(const QualifiedName&, Document*, HTMLFormElement*);
 
+    virtual bool isResettable() const { return true; }
+
     virtual const AtomicString& formControlType() const;
     virtual void parseMappedAttribute(Attribute*);
     virtual bool appendFormData(FormDataList&, bool);
index ff773f1..cc8a03c 100644 (file)
@@ -50,6 +50,7 @@ public:
     virtual bool isFormControlElement() const { return false; }
 
     virtual bool isEnumeratable() const { return true; }
+    virtual bool isResettable() const { return false; }
     virtual bool appendFormData(FormDataList&, bool);
 
     // Implementations of constraint validation API.
index 83ecee2..4c5c684 100644 (file)
@@ -56,6 +56,7 @@ private:
     virtual void parseMappedAttribute(Attribute*);
     virtual const AtomicString& formControlType() const;
     virtual bool isEnumeratable() const { return true; }
+    virtual bool isResettable() const { return true; }
     virtual void childrenChanged(bool createdByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0);
     virtual void reset();
 
index a22df02..42e8963 100644 (file)
@@ -107,6 +107,7 @@ private:
     virtual bool canStartSelection() const { return false; }
 
     virtual bool isEnumeratable() const { return true; }
+    virtual bool isResettable() const { return true; }
 
     virtual bool saveFormControlState(String& value) const;
     virtual void restoreFormControlState(const String&);
index 669fcd8..21b5880 100644 (file)
@@ -80,6 +80,7 @@ private:
     virtual void defaultEventHandler(Event*);
 
     virtual bool isEnumeratable() const { return true; }
+    virtual bool isResettable() const { return true; }
 
     virtual const AtomicString& formControlType() const;
 
index ca2488b..81931b5 100644 (file)
@@ -269,6 +269,8 @@ namespace WebCore {
         DEFINE_ATTRIBUTE_EVENT_LISTENER(ended);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(focus);
+        DEFINE_ATTRIBUTE_EVENT_LISTENER(formchange);
+        DEFINE_ATTRIBUTE_EVENT_LISTENER(forminput);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(hashchange);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(input);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid);
index 0cb9a2c..9b71cce 100644 (file)
@@ -263,6 +263,8 @@ module window {
         attribute EventListener onended;
         attribute EventListener onerror;
         attribute EventListener onfocus;
+        attribute EventListener onformchange;
+        attribute EventListener onforminput;
         attribute EventListener onhashchange;
         attribute EventListener oninput;
         attribute EventListener oninvalid;
@@ -307,8 +309,6 @@ module window {
         // Not implemented yet.
         // attribute EventListener onafterprint;
         // attribute EventListener onbeforeprint;
-        // attribute EventListener onformchange;
-        // attribute EventListener onforminput;
         // attribute EventListener onreadystatechange;
         // attribute EventListener onredo;
         // attribute EventListener onshow;
index 5b8712d..b6e3dd4 100644 (file)
@@ -243,7 +243,7 @@ void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
                 input->setValue("");
                 if (!oldValue.isEmpty()) {
                     toRenderTextControl(input->renderer())->setChangedSinceLastChangeEvent(true);
-                    input->dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+                    input->dispatchFormControlInputEvent();
                 }
                 input->onSearch();
                 event->setDefaultHandled();