Commit 59951184 authored by rniwa@webkit.org's avatar rniwa@webkit.org

2010-12-09 Ryosuke Niwa <rniwa@webkit.org>

        Reviewed by Ojan Vafai.

        Implemented DOM mutations events as scoped events. A scoped event is an event whose
        dispatch is done via ScopedEventQueue. The behavior of the queue is controlled by
        EventQueueScope objects (RAII idiom), which increments and decrements the scoping level
        on its constructor and destructor respectively.

        When the scoping level is 0 (initial level), scoped events are dispatched as soon as
        they are enqueued and act like synchronous events. When the scoping level is greater than 0,
        however, events are queued in ScopedEventQueue and their dispatches are delayed until
        the scoping level goes back to 0 (by the destruction of EventQueueScope).

        DOMSubtreeModified, DOMNodeInserted, DOMNodeRemoved, DOMNodeRemovedFromDocument,
        DOMNodeInsertedIntoDocument, DOMFocusIn, DOMFocusOut, focusin, and focusout are treated as
        scoped events, and a scope object is instantiated in EditCommand::apply to delay dispatches
        of the events until the completion of each call of EditCommand::doApply.

        Test: fast/events/mutation/execCommands.html

        * Android.mk: Added ScopedEventQueue.cpp.
        * CMakeLists.txt: Ditto.
        * WebCore.pro: Ditto.
        * GNUmakefile.am: Added ScopedEventQueue.cpp and ScopedEventQueue.h.
        * WebCore.gypi:  Ditto.
        * WebCore.vcproj/project.vcproj: Ditto.
        * WebCore.xcodeproj/project.pbxproj: Ditto.
        * dom/ContainerNode.cpp:
        (WebCore::dispatchChildInsertionEvents): Calls dispatchScopedEvent.
        (WebCore::dispatchChildRemovalEvents): Ditto.
        * dom/DOMAllInOne.cpp: Added ScopedEventQueue.cpp.
        * dom/Element.cpp:
        (WebCore::Element::dispatchAttrRemovalEvent): Ditto. 
        (WebCore::Element::dispatchAttrAdditionEvent): Ditto.
        * dom/Node.cpp:
        (WebCore::Node::dispatchScopedEvent): Added.
        (WebCore::Node::dispatchSubtreeModifiedEvent): Calls dispatchScopedEvent.
        * dom/Node.h:
        * dom/ScopedEventQueue.cpp: Added.
        (WebCore::ScopedEventQueue::initialize): Added.
        (WebCore::ScopedEventQueue::enqueueEvent): Added.
        (WebCore::ScopedEventQueue::dispatchAllEvents): Added.
        (WebCore::ScopedEventQueue::dispatchEvent): Added.
        (WebCore::ScopedEventQueue::instance): Added.
        (WebCore::ScopedEventQueue::incrementScopingLevel): Added.
        (WebCore::ScopedEventQueue::decrementScopingLevel): Added.
        * dom/ScopedEventQueue.h: Added.
        (WebCore::ScopedEventQueue::~ScopedEventQueue): Added.
        (WebCore::ScopedEventQueue::ScopedEventQueue): Added.
        (WebCore::EventQueueScope::EventQueueScope): Added.
        (WebCore::EventQueueScope::~EventQueueScope): Added.
        * editing/EditCommand.cpp:
        (WebCore::EditCommand::apply): Instantiates EventQueueScope.
2010-12-09  Ryosuke Niwa  <rniwa@webkit.org>

        Reviewed by Ojan Vafai.

        Added a test to ensure no DOM mutation events are fired while execCommand is in progress.

        * fast/events/crash-on-mutate-during-drop.html: DOMNodeInserted is fired after BR is detached
        from the document, and can't be observed. Modify the DOM when text node is inserted.
        * fast/events/mutation: Added.
        * fast/events/mutation/execCommands.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@73684 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 20cffe9e
2010-12-09 Ryosuke Niwa <rniwa@webkit.org>
Reviewed by Ojan Vafai.
Added a test to ensure no DOM mutation events are fired while execCommand is in progress.
* fast/events/crash-on-mutate-during-drop.html: DOMNodeInserted is fired after BR is detached
from the document, and can't be observed. Modify the DOM when text node is inserted.
* fast/events/mutation: Added.
* fast/events/mutation/execCommands.html: Added.
2010-12-09 Abhishek Arya <inferno@chromium.org>
Reviewed by James Robinson.
......
......@@ -2,7 +2,7 @@
<head>
<script>
function foo() {
if (event.type == "DOMNodeInserted" && event.target.nodeName == "BR")
if (event.type == "DOMNodeInserted" && event.target.nodeType == 3)
document.body.innerHTML = "PASSED";
}
......@@ -12,7 +12,7 @@ function runTest() {
window.layoutTestController.dumpAsText();
document.body.addEventListener("DOMNodeInserted", function() { foo() }, true);
document.addEventListener("DOMNodeInserted", function() { foo() }, true);
// Select the element 'dragSource'.
var selection = window.getSelection();
......
This test ensures WebKit does not fire DOM mutation events while execCommand is in progress.
PASS: execCommand('BackColor', false, 'blue')
PASS: execCommand('CreateLink', false, 'about:blank')
PASS: execCommand('Delete', false, null)
PASS: execCommand('FontName', false, 'Arial')
PASS: execCommand('FontSize', false, '5')
PASS: execCommand('FontSizeDelta', false, '5')
PASS: execCommand('ForeColor', false, 'blue')
PASS: execCommand('FormatBlock', false, 'pre')
PASS: execCommand('ForwardDelete', false, null)
PASS: execCommand('HiliteColor', false, 'red')
PASS: execCommand('Indent', false, null)
PASS: execCommand('InsertHTML', false, '<i>hello')
PASS: execCommand('InsertHorizontalRule', false, null)
PASS: execCommand('InsertImage', false, '../resources/abe.png')
PASS: execCommand('InsertLineBreak', false, null)
PASS: execCommand('InsertNewlineInQuotedContent', false, null)
PASS: execCommand('InsertOrderedList', false, null)
PASS: execCommand('InsertParagraph', false, null)
PASS: execCommand('InsertText', false, 'webkit')
PASS: execCommand('InsertUnorderedList', false, null)
PASS: execCommand('Italic', false, null)
PASS: execCommand('JustifyCenter', false, null)
PASS: execCommand('JustifyFull', false, null)
PASS: execCommand('JustifyLeft', false, null)
PASS: execCommand('JustifyNone', false, null)
PASS: execCommand('JustifyRight', false, null)
PASS: execCommand('Outdent', false, null)
PASS: execCommand('RemoveFormat', false, null)
PASS: execCommand('Strikethrough', false, null)
PASS: execCommand('Subscript', false, null)
PASS: execCommand('Superscript', false, null)
PASS: execCommand('Transpose', false, null)
PASS: execCommand('Underline', false, null)
PASS: execCommand('Unlink', false, null)
DONE
<!DOCTYPE html>
<html>
<body>
<p>This test ensures WebKit does not fire DOM mutation events while execCommand is in progress.</p>
<div id="test" contenteditable></div>
<pre>
<script>
if (window.layoutTestController)
layoutTestController.dumpAsText();
var commands = [
{name: 'BackColor', value: 'blue'},
{name: 'CreateLink', value: 'about:blank'},
{name: 'Delete', value: null},
{name: 'FontName', value: 'Arial'},
{name: 'FontSize', value: '5'},
{name: 'FontSizeDelta', value: '5'},
{name: 'ForeColor', value: 'blue'},
{name: 'FormatBlock', value: 'pre'},
{name: 'ForwardDelete', value: null},
{name: 'HiliteColor', value: 'red'},
{name: 'Indent', value: null},
{name: 'InsertHTML', value: "<i>hello</i>"},
{name: 'InsertHorizontalRule', value: null},
{name: 'InsertImage', value: '../resources/abe.png'},
{name: 'InsertLineBreak', value: null},
{name: 'InsertNewlineInQuotedContent', value: null},
{name: 'InsertOrderedList', value: null},
{name: 'InsertParagraph', value: null},
{name: 'InsertText', value: 'webkit'},
{name: 'InsertUnorderedList', value: null},
{name: 'Italic', value: null},
{name: 'JustifyCenter', value: null},
{name: 'JustifyFull', value: null},
{name: 'JustifyLeft', value: null},
{name: 'JustifyNone', value: null},
{name: 'JustifyRight', value: null},
{name: 'Outdent', value: null},
{name: 'RemoveFormat', value: null},
{name: 'Strikethrough', value: null},
{name: 'Subscript', value: null},
{name: 'Superscript', value: null},
{name: 'Transpose', value: null, selector: function (test) { window.getSelection().setPosition(test.firstChild, 1); }},
{name: 'Underline', value: null},
{name: 'Unlink', value: null},
];
var events = {
'DOMSubtreeModified': false,
'DOMNodeInserted': false,
'DOMNodeRemoved': false,
'DOMNodeRemovedFromDocument': false,
'DOMNodeInsertedIntoDocument': true, // this event can never be observed.
'DOMFocusIn': false,
'DOMFocusOut': false,
'focusin': false,
'focusout': false,
};
var log = [];
var test = document.getElementById('test');
function addEventListeners(node) {
for (var e in events) {
node.addEventListener(e, function (event) {
log.push(test.innerHTML);
events[event.type] = true;
}, false);
}
}
function isLogConsistent() {
for (var i= 1; i < log.length; i++) {
if (log[0] != log[i]) {
console.log(log);
return false;
}
}
return true;
}
addEventListeners(test);
var initial = 'hello, <input type="text"><blockquote align="right"><u><a href="about:blank">world</a></u></blockquote>';
for (var i = 0; i < commands.length; i++) {
test.innerHTML = initial;
if (i)
document.write("\n");
if (test.innerHTML != initial) {
document.write("FAIL: initial innerHTML didn't match");
continue;
}
if (commands[i].selector)
commands[i].selector(test);
else {
document.getElementsByTagName('input')[0].focus();
window.getSelection().selectAllChildren(test);
}
addEventListeners(test.childNodes[2]);
log = []; // clear log
document.execCommand(commands[i].name, false, commands[i].value);
var quotedValue = commands[i].value ? "'" + commands[i].value.replace('<', '&lt;') + "'" : null;
var action = "execCommand('" + commands[i].name + "', false, " + quotedValue + ")";
if (test.innerHTML == initial || log.length <= 0)
document.write('FAIL: ' + action + ' made no change to the DOM.');
else if (!isLogConsistent())
document.write('FAIL: ' + action + ' dispatched events before finalizing the DOM tree.');
else
document.write('PASS: ' + action);
}
test.style.display = 'none';
document.write('\n');
for (var e in events) {
if (!events[e])
document.write('\nWARNING: ' + e + ' was never observed.');
}
document.write('\n\nDONE');
</script>
</pre>
</body>
</html>
......@@ -165,6 +165,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
dom/QualifiedName.cpp \
dom/Range.cpp \
dom/RegisteredEventListener.cpp \
dom/ScopedEventQueue.cpp \
dom/ScriptableDocumentParser.cpp \
dom/ScriptElement.cpp \
dom/ScriptExecutionContext.cpp \
......
......@@ -881,6 +881,7 @@ SET(WebCore_SOURCES
dom/QualifiedName.cpp
dom/Range.cpp
dom/RegisteredEventListener.cpp
dom/ScopedEventQueue.cpp
dom/ScriptableDocumentParser.cpp
dom/ScriptElement.cpp
dom/ScriptExecutionContext.cpp
......
2010-12-09 Ryosuke Niwa <rniwa@webkit.org>
Reviewed by Ojan Vafai.
Implemented DOM mutations events as scoped events. A scoped event is an event whose
dispatch is done via ScopedEventQueue. The behavior of the queue is controlled by
EventQueueScope objects (RAII idiom), which increments and decrements the scoping level
on its constructor and destructor respectively.
When the scoping level is 0 (initial level), scoped events are dispatched as soon as
they are enqueued and act like synchronous events. When the scoping level is greater than 0,
however, events are queued in ScopedEventQueue and their dispatches are delayed until
the scoping level goes back to 0 (by the destruction of EventQueueScope).
DOMSubtreeModified, DOMNodeInserted, DOMNodeRemoved, DOMNodeRemovedFromDocument,
DOMNodeInsertedIntoDocument, DOMFocusIn, DOMFocusOut, focusin, and focusout are treated as
scoped events, and a scope object is instantiated in EditCommand::apply to delay dispatches
of the events until the completion of each call of EditCommand::doApply.
Test: fast/events/mutation/execCommands.html
* Android.mk: Added ScopedEventQueue.cpp.
* CMakeLists.txt: Ditto.
* WebCore.pro: Ditto.
* GNUmakefile.am: Added ScopedEventQueue.cpp and ScopedEventQueue.h.
* WebCore.gypi: Ditto.
* WebCore.vcproj/project.vcproj: Ditto.
* WebCore.xcodeproj/project.pbxproj: Ditto.
* dom/ContainerNode.cpp:
(WebCore::dispatchChildInsertionEvents): Calls dispatchScopedEvent.
(WebCore::dispatchChildRemovalEvents): Ditto.
* dom/DOMAllInOne.cpp: Added ScopedEventQueue.cpp.
* dom/Element.cpp:
(WebCore::Element::dispatchAttrRemovalEvent): Ditto.
(WebCore::Element::dispatchAttrAdditionEvent): Ditto.
* dom/Node.cpp:
(WebCore::Node::dispatchScopedEvent): Added.
(WebCore::Node::dispatchSubtreeModifiedEvent): Calls dispatchScopedEvent.
* dom/Node.h:
* dom/ScopedEventQueue.cpp: Added.
(WebCore::ScopedEventQueue::initialize): Added.
(WebCore::ScopedEventQueue::enqueueEvent): Added.
(WebCore::ScopedEventQueue::dispatchAllEvents): Added.
(WebCore::ScopedEventQueue::dispatchEvent): Added.
(WebCore::ScopedEventQueue::instance): Added.
(WebCore::ScopedEventQueue::incrementScopingLevel): Added.
(WebCore::ScopedEventQueue::decrementScopingLevel): Added.
* dom/ScopedEventQueue.h: Added.
(WebCore::ScopedEventQueue::~ScopedEventQueue): Added.
(WebCore::ScopedEventQueue::ScopedEventQueue): Added.
(WebCore::EventQueueScope::EventQueueScope): Added.
(WebCore::EventQueueScope::~EventQueueScope): Added.
* editing/EditCommand.cpp:
(WebCore::EditCommand::apply): Instantiates EventQueueScope.
2010-12-09 Sam Weinig <sam@webkit.org>
Reviewed by Gavin Barraclough.
......
......@@ -1268,6 +1268,8 @@ webcore_sources += \
WebCore/dom/RawDataDocumentParser.h \
WebCore/dom/RegisteredEventListener.cpp \
WebCore/dom/RegisteredEventListener.h \
WebCore/dom/ScopedEventQueue.cpp \
WebCore/dom/ScopedEventQueue.h \
WebCore/dom/ScriptableDocumentParser.cpp \
WebCore/dom/ScriptableDocumentParser.h \
WebCore/dom/ScriptElement.cpp \
......
......@@ -1318,6 +1318,8 @@
'dom/RawDataDocumentParser.h',
'dom/RegisteredEventListener.cpp',
'dom/RegisteredEventListener.h',
'dom/ScopedEventQueue.cpp',
'dom/ScopedEventQueue.h',
'dom/ScriptableDocumentParser.cpp',
'dom/ScriptableDocumentParser.h',
'dom/ScriptElement.cpp',
......
......@@ -762,6 +762,7 @@ SOURCES += \
dom/Range.cpp \
dom/RawDataDocumentParser.h \
dom/RegisteredEventListener.cpp \
dom/ScopedEventQueue.cpp \
dom/ScriptableDocumentParser.cpp \
dom/ScriptElement.cpp \
dom/ScriptExecutionContext.cpp \
......
......@@ -45313,6 +45313,62 @@
RelativePath="..\dom\RegisteredEventListener.h"
>
</File>
<File
RelativePath="..\dom\ScopedEventQueue.cpp"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_Cairo_CFLite|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_Cairo_CFLite|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_All|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_LTCG|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\dom\ScopedEventQueue.h"
>
</File>
<File
RelativePath="..\dom\ScriptableDocumentParser.cpp"
>
......@@ -3029,6 +3029,8 @@
9B417065125662B3006B28FC /* ApplyBlockElementCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B417063125662B3006B28FC /* ApplyBlockElementCommand.cpp */; };
9BAB6C6C12550631001626D4 /* EditingStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BAB6C6A12550631001626D4 /* EditingStyle.h */; settings = {ATTRIBUTES = (Private, ); }; };
9BAB6C6D12550631001626D4 /* EditingStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB6C6B12550631001626D4 /* EditingStyle.cpp */; };
9BD0BF9312A42BF50072FD43 /* ScopedEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */; };
9BD0BF9412A42BF50072FD43 /* ScopedEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */; };
9F0D6B2E121BFEBA006C0288 /* InspectorProfilerAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */; };
9F0D6B2F121BFEBA006C0288 /* InspectorProfilerAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F0D6B2D121BFEBA006C0288 /* InspectorProfilerAgent.h */; };
9F3B947E12241758005304E7 /* ScriptHeapSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F3B947D12241758005304E7 /* ScriptHeapSnapshot.h */; };
......@@ -9341,6 +9343,8 @@
9B417063125662B3006B28FC /* ApplyBlockElementCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplyBlockElementCommand.cpp; sourceTree = "<group>"; };
9BAB6C6A12550631001626D4 /* EditingStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditingStyle.h; sourceTree = "<group>"; };
9BAB6C6B12550631001626D4 /* EditingStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditingStyle.cpp; sourceTree = "<group>"; };
9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScopedEventQueue.h; sourceTree = "<group>"; };
9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScopedEventQueue.cpp; sourceTree = "<group>"; };
9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorProfilerAgent.cpp; sourceTree = "<group>"; };
9F0D6B2D121BFEBA006C0288 /* InspectorProfilerAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorProfilerAgent.h; sourceTree = "<group>"; };
9F3B947D12241758005304E7 /* ScriptHeapSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptHeapSnapshot.h; sourceTree = "<group>"; };
......@@ -18860,6 +18864,8 @@
A84D827B11D333ED00972990 /* RawDataDocumentParser.h */,
85031B350A44EFC700F992E0 /* RegisteredEventListener.cpp */,
85031B360A44EFC700F992E0 /* RegisteredEventListener.h */,
9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */,
9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */,
A84D82C011D3474800972990 /* ScriptableDocumentParser.cpp */,
A84D82BF11D3474800972990 /* ScriptableDocumentParser.h */,
08A484750E5272C500C3FE76 /* ScriptElement.cpp */,
......@@ -21469,6 +21475,7 @@
5DFE8F570D16477C0076E937 /* ScheduledAction.h in Headers */,
1CEFC9B90D78DC8C007D2579 /* SchedulePair.h in Headers */,
5162C7F511F77EFB00612EFE /* SchemeRegistry.h in Headers */,
9BD0BF9312A42BF50072FD43 /* ScopedEventQueue.h in Headers */,
BCEC01BE0C274DAC009F4EC9 /* Screen.h in Headers */,
A84D82C111D3474800972990 /* ScriptableDocumentParser.h in Headers */,
F39BE95C12673BF400E0A674 /* ScriptArguments.h in Headers */,
......@@ -24239,6 +24246,7 @@
1CEFC9BA0D78DC8C007D2579 /* SchedulePair.cpp in Sources */,
1CE24F970D7CAF0E007E04C2 /* SchedulePairMac.mm in Sources */,
5162C7F411F77EFB00612EFE /* SchemeRegistry.cpp in Sources */,
9BD0BF9412A42BF50072FD43 /* ScopedEventQueue.cpp in Sources */,
BCEC01BD0C274DAC009F4EC9 /* Screen.cpp in Sources */,
A84D82C211D3474800972990 /* ScriptableDocumentParser.cpp in Sources */,
F39BE95B12673BF400E0A674 /* ScriptArguments.cpp in Sources */,
......@@ -1027,12 +1027,12 @@ static void dispatchChildInsertionEvents(Node* child)
RefPtr<Document> document = child->document();
if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode()));
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode()));
// dispatch the DOMNodeInsertedIntoDocument event to all descendants
if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
for (; c; c = c->traverseNextNode(child))
c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false));
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false));
}
}
......@@ -1049,12 +1049,12 @@ static void dispatchChildRemovalEvents(Node* child)
// dispatch pre-removal mutation events
if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER))
c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode()));
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode()));
// dispatch the DOMNodeRemovedFromDocument event to all descendants
if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
for (; c; c = c->traverseNextNode(child))
c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false));
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false));
}
}
......
......@@ -100,6 +100,7 @@
#include "ProgressEvent.cpp"
#include "Range.cpp"
#include "RegisteredEventListener.cpp"
#include "ScopedEventQueue.cpp"
#include "ScriptElement.cpp"
#include "ScriptExecutionContext.cpp"
#include "ScriptableDocumentParser.cpp"
......
......@@ -1218,7 +1218,7 @@ void Element::dispatchAttrRemovalEvent(Attribute*)
if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER))
return;
ExceptionCode ec = 0;
dispatchEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(),
dispatchScopedEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(),
attr->value(), document()->attrName(attr->id()), MutationEvent::REMOVAL), ec);
#endif
}
......@@ -1231,7 +1231,7 @@ void Element::dispatchAttrAdditionEvent(Attribute*)
if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER))
return;
ExceptionCode ec = 0;
dispatchEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(),
dispatchScopedEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(),
attr->value(), document()->attrName(attr->id()), MutationEvent::ADDITION), ec);
#endif
}
......
......@@ -70,6 +70,7 @@
#include "ProgressEvent.h"
#include "RegisteredEventListener.h"
#include "RenderBox.h"
#include "ScopedEventQueue.h"
#include "ScriptController.h"
#include "SelectorNodeList.h"
#include "StaticNodeList.h"
......@@ -2579,6 +2580,14 @@ bool Node::dispatchEvent(PassRefPtr<Event> prpEvent)
return dispatchGenericEvent(event.release());
}
void Node::dispatchScopedEvent(PassRefPtr<Event> event)
{
// We need to set the target here because it can go away by the time we actually fire the event.
event->setTarget(eventTargetRespectingSVGTargetRules(this));
ScopedEventQueue::instance()->enqueueEvent(event);
}
static const EventContext* topEventContext(const Vector<EventContext>& ancestors)
{
return ancestors.isEmpty() ? 0 : &ancestors.last();
......@@ -2693,7 +2702,7 @@ void Node::dispatchSubtreeModifiedEvent()
if (!document()->hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER))
return;
dispatchEvent(MutationEvent::create(eventNames().DOMSubtreeModifiedEvent, true));
dispatchScopedEvent(MutationEvent::create(eventNames().DOMSubtreeModifiedEvent, true));
}
void Node::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent)
......@@ -2703,10 +2712,10 @@ void Node::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr
eventType == eventNames().DOMFocusInEvent || eventType == eventNames().DOMFocusOutEvent || eventType == eventNames().DOMActivateEvent);
bool cancelable = eventType == eventNames().DOMActivateEvent;
RefPtr<UIEvent> event = UIEvent::create(eventType, true, cancelable, document()->defaultView(), detail);
event->setUnderlyingEvent(underlyingEvent);
dispatchEvent(event.release());
dispatchScopedEvent(event.release());
}
bool Node::dispatchKeyEvent(const PlatformKeyboardEvent& key)
......
......@@ -520,7 +520,8 @@ public:
virtual void postDispatchEventHandler(Event*, void* /*dataFromPreDispatch*/) { }
using EventTarget::dispatchEvent;
virtual bool dispatchEvent(PassRefPtr<Event>);
bool dispatchEvent(PassRefPtr<Event>);
void dispatchScopedEvent(PassRefPtr<Event>);
bool dispatchGenericEvent(PassRefPtr<Event>);
virtual void handleLocalEvents(Event*);
......
/*
* Copyright (C) 2010 Google, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ScopedEventQueue.h"
#include "Event.h"
#include "EventTarget.h"
namespace WebCore {
ScopedEventQueue* ScopedEventQueue::s_instance = 0;
ScopedEventQueue::ScopedEventQueue()
: m_scopingLevel(0)
{
}
ScopedEventQueue::~ScopedEventQueue()
{
ASSERT(!m_scopingLevel);
ASSERT(!m_queuedEvents.size());
}
void ScopedEventQueue::initialize()
{
ASSERT(!s_instance);
OwnPtr<ScopedEventQueue> instance = adoptPtr(new ScopedEventQueue);
s_instance = instance.leakPtr();
}
void ScopedEventQueue::enqueueEvent(PassRefPtr<Event> event)
{
if (m_scopingLevel)
m_queuedEvents.append(event);
else
dispatchEvent(event);
}
void ScopedEventQueue::dispatchAllEvents()
{
Vector<RefPtr<Event> > queuedEvents;
queuedEvents.swap(m_queuedEvents);
for (size_t i = 0; i < queuedEvents.size(); i++)
dispatchEvent(queuedEvents[i].release());
}
void ScopedEventQueue::dispatchEvent(PassRefPtr<Event> event) const
{
RefPtr<EventTarget> eventTarget = event->target();
eventTarget->dispatchEvent(event);
}
ScopedEventQueue* ScopedEventQueue::instance()
{
if (!s_instance)
initialize();
return s_instance;
}
void ScopedEventQueue::incrementScopingLevel()
{
m_scopingLevel++;
}
void ScopedEventQueue::decrementScopingLevel()
{
ASSERT(m_scopingLevel);
m_scopingLevel--;
if (!m_scopingLevel)
dispatchAllEvents();
}
}
/*
* Copyright (C) 2010 Google, Inc. All Rights Reserved.
*
* Redistribution and