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

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

        Reviewed by Ojan Vafai.

        Make DOM Mutation Events Asynchronous
        https://bugs.webkit.org/show_bug.cgi?id=46936

        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.

        Make DOM Mutation Events Asynchronous
        https://bugs.webkit.org/show_bug.cgi?id=46936

        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/scoped: Added.
        * fast/events/scoped/editing-commands-expected.txt: Added.
        * fast/events/scoped/editing-commands.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@73690 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 1a796035
2010-12-09 Ryosuke Niwa <rniwa@webkit.org>
Reviewed by Ojan Vafai.
Make DOM Mutation Events Asynchronous
https://bugs.webkit.org/show_bug.cgi?id=46936
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/scoped: Added.
* fast/events/scoped/editing-commands-expected.txt: Added.
* fast/events/scoped/editing-commands.html: Added.
2010-12-09 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r73684.
......
......@@ -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.
Make DOM Mutation Events Asynchronous
https://bugs.webkit.org/show_bug.cgi?id=46936
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 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r73684.
......
......@@ -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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT
* OWNER 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()
{