WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-68956-20111017152249.patch (text/plain), 35.60 KB, created by
Rafael Weinstein
on 2011-10-17 15:22:50 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Rafael Weinstein
Created:
2011-10-17 15:22:50 PDT
Size:
35.60 KB
patch
obsolete
>Subversion Revision: 97635 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index bafd4c94847cdff0faa660b414736b046c848364..c1468a33db2436f12205ff44ecb6359aa9b90362 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,55 @@ >+2011-10-13 Rafael Weinstein <rafaelw@chromium.org> >+ >+ [MutationObservers] Implement WebKitMutationObserver.observe for attributes >+ https://bugs.webkit.org/show_bug.cgi?id=68956 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Test: fast/mutation/observe-attributes.html >+ >+ This adds an initial implementation for registering mutation observers on nodes, >+ delivering mutation records at the end of the outer-most script invokation and >+ observing mutations to element attributes. >+ >+ Note that the outer-most script invokation only works in V8. >+ >+ Note also that support for observing changes to the style attribute when updated >+ via the style property is not implemented here. >+ >+ * bindings/v8/V8Proxy.cpp: >+ (WebCore::V8Proxy::didLeaveScriptContext): >+ * dom/Element.cpp: >+ (WebCore::enqueueAttributesMutationRecord): >+ (WebCore::Element::setAttribute): >+ * dom/MutationRecord.cpp: >+ (WebCore::MutationRecord::createAttributes): >+ * dom/MutationRecord.h: >+ * dom/MutationRecord.idl: >+ * dom/Node.cpp: >+ (WebCore::Node::clearRareData): >+ (WebCore::Node::mutationObserverData): >+ (WebCore::Node::ensureMutationObserverData): >+ (WebCore::Node::registeredMutationObserversOfType): >+ (WebCore::Node::registerMutationObserver): >+ (WebCore::Node::deregisterMutationObserver): >+ * dom/Node.h: >+ * dom/NodeRareData.h: >+ (WebCore::MutationObserverRegistration::MutationObserverRegistration): >+ (WebCore::MutationObserverRegistration::operator==): >+ (WebCore::MutationObserverData::MutationObserverData): >+ (WebCore::MutationObserverData::~MutationObserverData): >+ (WebCore::NodeRareData::mutationObserverData): >+ (WebCore::NodeRareData::ensureMutationObserverData): >+ * dom/WebKitMutationObserver.cpp: >+ (WebCore::WebKitMutationObserver::observe): >+ (WebCore::WebKitMutationObserver::disconnect): >+ (WebCore::WebKitMutationObserver::wasDeregistered): >+ (WebCore::activeMutationObservers): >+ (WebCore::WebKitMutationObserver::enqueueMutationRecord): >+ (WebCore::WebKitMutationObserver::deliverAllMutations): >+ (WebCore::WebKitMutationObserver::deliver): >+ * dom/WebKitMutationObserver.h: >+ > 2011-10-17 Dan Bernstein <mitz@apple.com> > > <rdar://problem/10293929> REGRESSION (r97525): iChat transcript has horizontal scroll bar when the conversation includes a wide image >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index 6782182d8a72a908a4040493eef21f6669000442..814be4cb1b5395f94ce143e887bb28f3b2009def 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -5464,7 +5464,7 @@ > C6F08FCA1431000D00685849 /* JSMutationRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F08FC81431000D00685849 /* JSMutationRecord.h */; }; > C6F0900A14327B6100685849 /* MutationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900114327B6100685849 /* MutationCallback.h */; }; > C6F0900E14327B6100685849 /* WebKitMutationObserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0900514327B6100685849 /* WebKitMutationObserver.cpp */; }; >- C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; }; >+ C6F0900F14327B6100685849 /* WebKitMutationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900614327B6100685849 /* WebKitMutationObserver.h */; settings = {ATTRIBUTES = (Private, ); }; }; > C6F0901114327B6100685849 /* MutationObserverOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0900814327B6100685849 /* MutationObserverOptions.h */; }; > C6F0902814327D4F00685849 /* JSMutationCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6F0902014327D4F00685849 /* JSMutationCallback.cpp */; }; > C6F0902914327D4F00685849 /* JSMutationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F0902114327D4F00685849 /* JSMutationCallback.h */; }; >diff --git a/Source/WebCore/bindings/v8/V8Proxy.cpp b/Source/WebCore/bindings/v8/V8Proxy.cpp >index 0bf48e8341d04b55536ffb636766c7050e20c066..80a21a0ff0c2d725a4c887c3b60f03a4c1a5dfe1 100644 >--- a/Source/WebCore/bindings/v8/V8Proxy.cpp >+++ b/Source/WebCore/bindings/v8/V8Proxy.cpp >@@ -65,6 +65,7 @@ > #include "V8SQLException.h" > #include "V8XMLHttpRequestException.h" > #include "V8XPathException.h" >+#include "WebKitMutationObserver.h" > #include "WorkerContext.h" > #include "WorkerContextExecutionProxy.h" > >@@ -620,6 +621,10 @@ void V8Proxy::didLeaveScriptContext() > #endif // ENABLE(INDEXED_DATABASE) > if (page->group().hasLocalStorage()) > page->group().localStorage()->unlock(); >+ >+#if ENABLE(MUTATION_OBSERVERS) >+ WebCore::WebKitMutationObserver::deliverAllMutations(); >+#endif > } > > void V8Proxy::resetIsolatedWorlds() >diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp >index e8033e352d37af3b7b00c227ee732cb4d0bdf42e..ddde51904c910f0b4ba119662d68e6edda6a256d 100644 >--- a/Source/WebCore/dom/Element.cpp >+++ b/Source/WebCore/dom/Element.cpp >@@ -48,6 +48,7 @@ > #include "HTMLNames.h" > #include "HTMLParserIdioms.h" > #include "InspectorInstrumentation.h" >+#include "MutationRecord.h" > #include "NodeList.h" > #include "NodeRenderStyle.h" > #include "NodeRenderingContext.h" >@@ -59,6 +60,7 @@ > #include "ShadowRoot.h" > #include "Text.h" > #include "TextIterator.h" >+#include "WebKitMutationObserver.h" > #include "WebKitAnimationList.h" > #include "XMLNames.h" > #include "htmlediting.h" >@@ -610,6 +612,20 @@ const AtomicString& Element::getAttributeNS(const String& namespaceURI, const St > return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); > } > >+#if ENABLE(MUTATION_OBSERVERS) >+static void enqueueAttributesMutationRecord(Element* element, const QualifiedName& name) >+{ >+ Vector<WebKitMutationObserver*> observers; >+ element->registeredMutationObserversOfType(observers, WebKitMutationObserver::Attributes); >+ if (observers.isEmpty()) >+ return; >+ >+ RefPtr<MutationRecord> mutation = MutationRecord::createAttributes(element, name); >+ for (Vector<WebKitMutationObserver*>::iterator iter = observers.begin(); iter != observers.end(); ++iter) >+ (*iter)->enqueueMutationRecord(mutation); >+} >+#endif >+ > void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec) > { > if (!Document::isValidName(name)) { >@@ -630,6 +646,11 @@ void Element::setAttribute(const AtomicString& name, const AtomicString& value, > > document()->incDOMTreeVersion(); > >+#if ENABLE(MUTATION_OBSERVERS) >+ // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now. >+ enqueueAttributesMutationRecord(this, attributeName); >+#endif >+ > if (isIdAttributeName(old ? old->name() : attributeName)) > updateId(old ? old->value() : nullAtom, value); > >@@ -663,6 +684,11 @@ void Element::setAttribute(const QualifiedName& name, const AtomicString& value, > // Allocate attribute map if necessary. > Attribute* old = attributes(false)->getAttributeItem(name); > >+#if ENABLE(MUTATION_OBSERVERS) >+ // The call to attributeChanged below may dispatch DOMSubtreeModified, so it's important to enqueue a MutationRecord now. >+ enqueueAttributesMutationRecord(this, name); >+#endif >+ > if (isIdAttributeName(name)) > updateId(old ? old->value() : nullAtom, value); > >diff --git a/Source/WebCore/dom/MutationRecord.cpp b/Source/WebCore/dom/MutationRecord.cpp >index 84b298b0f79394667cf4926de669a34c0d8282b5..c36eca6dde71deee9cd2cfd24c884f58ba7bbfb9 100644 >--- a/Source/WebCore/dom/MutationRecord.cpp >+++ b/Source/WebCore/dom/MutationRecord.cpp >@@ -36,6 +36,7 @@ > > #include "Node.h" > #include "NodeList.h" >+#include "QualifiedName.h" > #include <wtf/Assertions.h> > #include <wtf/StdLibExtras.h> > >@@ -69,17 +70,17 @@ private: > > class AttributesRecord : public MutationRecord { > public: >- AttributesRecord(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace) >+ AttributesRecord(PassRefPtr<Node> target, const QualifiedName& name) > : MutationRecord(target) >- , m_attributeName(attributeName) >- , m_attributeNamespace(attributeNamespace) >+ , m_attributeName(name.localName()) >+ , m_attributeNamespace(name.namespaceURI()) > { > } > > private: > virtual const AtomicString& type(); > virtual const AtomicString& attributeName() { return m_attributeName; } >- virtual const AtomicString& attributeNamespace() { return m_attributeName; } >+ virtual const AtomicString& attributeNamespace() { return m_attributeNamespace; } > virtual String oldValue() { return m_oldValue; } > virtual void setOldValue(const String& value) { m_oldValue = value; } > >@@ -128,9 +129,9 @@ PassRefPtr<MutationRecord> MutationRecord::createChildList(PassRefPtr<Node> targ > return adoptRef(static_cast<MutationRecord*>(new ChildListRecord(target, added, removed, previousSibling, nextSibling))); > } > >-PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace) >+PassRefPtr<MutationRecord> MutationRecord::createAttributes(PassRefPtr<Node> target, const QualifiedName& name) > { >- return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, attributeName, attributeNamespace))); >+ return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name))); > } > > PassRefPtr<MutationRecord> MutationRecord::createCharacterData(PassRefPtr<Node> target) >diff --git a/Source/WebCore/dom/MutationRecord.h b/Source/WebCore/dom/MutationRecord.h >index f0cec606687defa215edecc48f32e5dcdd572e99..25af4d3ea3a1799dfa0e34186e18881582236145 100644 >--- a/Source/WebCore/dom/MutationRecord.h >+++ b/Source/WebCore/dom/MutationRecord.h >@@ -42,11 +42,12 @@ namespace WebCore { > > class Node; > class NodeList; >+class QualifiedName; > > class MutationRecord : public RefCounted<MutationRecord> { > public: > static PassRefPtr<MutationRecord> createChildList(PassRefPtr<Node> target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling); >- static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const AtomicString& attributeName, const AtomicString& attributeNamespace); >+ static PassRefPtr<MutationRecord> createAttributes(PassRefPtr<Node> target, const QualifiedName&); > static PassRefPtr<MutationRecord> createCharacterData(PassRefPtr<Node> target); > > virtual ~MutationRecord(); >diff --git a/Source/WebCore/dom/MutationRecord.idl b/Source/WebCore/dom/MutationRecord.idl >index 46e7a29bbb84526b4290d9046680038ce0cc5b29..c19b41f216e4a52c46f2184785c36f8802fd2b54 100644 >--- a/Source/WebCore/dom/MutationRecord.idl >+++ b/Source/WebCore/dom/MutationRecord.idl >@@ -41,8 +41,8 @@ module core { > readonly attribute Node nextSibling; > > readonly attribute DOMString attributeName; >- readonly attribute DOMString attributeNamespace; >+ readonly attribute [ConvertNullStringTo=Null] DOMString attributeNamespace; > >- readonly attribute DOMString oldValue; >+ readonly attribute [ConvertNullStringTo=Null] DOMString oldValue; > }; > } >diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp >index f5e47f4d88bb27b153209117d6766ab82a818a3c..d0210d9df2bc2613d3a367848db4b8f40d564aff 100644 >--- a/Source/WebCore/dom/Node.cpp >+++ b/Source/WebCore/dom/Node.cpp >@@ -100,6 +100,7 @@ > #include <wtf/PassOwnPtr.h> > #include <wtf/RefCountedLeakCounter.h> > #include <wtf/UnusedParam.h> >+#include <wtf/Vector.h> > #include <wtf/text/CString.h> > #include <wtf/text/StringBuilder.h> > >@@ -546,6 +547,14 @@ void Node::clearRareData() > if (treeScope() && rareData()->nodeLists()) > treeScope()->removeNodeListCache(); > >+#if ENABLE(MUTATION_OBSERVERS) >+ Vector<MutationObserverEntry>* observerEnties = mutationObserverEntries(); >+ if (observerEnties) { >+ for (Vector<MutationObserverEntry>::iterator iter = observerEnties->begin(); iter != observerEnties->end(); ++iter) >+ iter->observer->observedNodeDestructed(this); >+ } >+#endif >+ > NodeRareData::NodeRareDataMap& dataMap = NodeRareData::rareDataMap(); > NodeRareData::NodeRareDataMap::iterator it = dataMap.find(this); > ASSERT(it != dataMap.end()); >@@ -2673,6 +2682,62 @@ EventTargetData* Node::ensureEventTargetData() > return ensureRareData()->ensureEventTargetData(); > } > >+#if ENABLE(MUTATION_OBSERVERS) >+Vector<MutationObserverEntry>* Node::mutationObserverEntries() >+{ >+ return hasRareData() ? rareData()->mutationObserverEntries() : 0; >+} >+ >+Vector<MutationObserverEntry>* Node::ensureMutationObserverEntries() >+{ >+ return ensureRareData()->ensureMutationObserverEntries(); >+} >+ >+void Node::registeredMutationObserversOfType(Vector<WebKitMutationObserver*>& observers, WebKitMutationObserver::MutationType type) >+{ >+ Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries(); >+ if (!observerEntries || observerEntries->isEmpty()) >+ return; >+ >+ for (Vector<MutationObserverEntry>::iterator iter = observerEntries->begin(); iter != observerEntries->end(); ++iter) { >+ if (iter->matches(type)) >+ observers.append(iter->observer.get()); >+ } >+} >+ >+Node::MutationRegistrationResult Node::registerMutationObserver(PassRefPtr<WebKitMutationObserver> observer, unsigned char options) >+{ >+ Vector<MutationObserverEntry>* observerEntries = ensureMutationObserverEntries(); >+ MutationObserverEntry entry(observer, options); >+ >+ size_t index = observerEntries->find(entry); >+ if (index == notFound) { >+ observerEntries->append(entry); >+ return MutationObserverRegistered; >+ } >+ >+ (*observerEntries)[index].options = entry.options; >+ return MutationRegistrationOptionsReset; >+} >+ >+void Node::unregisterMutationObserver(PassRefPtr<WebKitMutationObserver> observer) >+{ >+ Vector<MutationObserverEntry>* observerEntries = mutationObserverEntries(); >+ ASSERT(observerEntries); >+ if (!observerEntries) >+ return; >+ >+ MutationObserverEntry entry(observer, 0); >+ size_t index = observerEntries->find(entry); >+ ASSERT(index != notFound); >+ if (index == notFound) >+ return; >+ >+ observerEntries->remove(index); >+} >+#endif // ENABLE(MUTATION_OBSERVERS) >+ >+ > void Node::handleLocalEvents(Event* event) > { > if (!hasRareData() || !rareData()->eventTargetData()) >diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h >index 163caeea40385379b134f99dbbe44e5ebbe3b8e0..edc00e1b67d1fe904192968a8b807b6267f4265c 100644 >--- a/Source/WebCore/dom/Node.h >+++ b/Source/WebCore/dom/Node.h >@@ -31,6 +31,7 @@ > #include "RenderStyleConstants.h" > #include "ScriptWrappable.h" > #include "TreeShared.h" >+#include "WebKitMutationObserver.h" > #include <wtf/Forward.h> > #include <wtf/ListHashSet.h> > >@@ -58,6 +59,7 @@ class Frame; > class HTMLInputElement; > class IntRect; > class KeyboardEvent; >+class MutationObserverEntry; > class NSResolver; > class NamedNodeMap; > class NameNodeList; >@@ -585,6 +587,22 @@ public: > void itemTypeAttributeChanged(); > #endif > >+#if ENABLE(MUTATION_OBSERVERS) >+ Vector<MutationObserverEntry>* mutationObserverEntries(); >+ Vector<MutationObserverEntry>* ensureMutationObserverEntries(); >+ >+ void registeredMutationObserversOfType(Vector<WebKitMutationObserver*>&, WebKitMutationObserver::MutationType); >+ >+ // Returns true if the observer wasn't already registered on this node. >+ enum MutationRegistrationResult { >+ MutationObserverRegistered, >+ MutationRegistrationOptionsReset >+ }; >+ MutationRegistrationResult registerMutationObserver(PassRefPtr<WebKitMutationObserver>, unsigned char options); >+ >+ void unregisterMutationObserver(PassRefPtr<WebKitMutationObserver>); >+#endif // ENABLE(MUTATION_OBSERVERS) >+ > private: > enum NodeFlags { > IsTextFlag = 1, >diff --git a/Source/WebCore/dom/NodeRareData.h b/Source/WebCore/dom/NodeRareData.h >index 4cf5b996d93c640f6a781321ab84cc7f948f20b2..6908d81caa9a6d1668645e579e859fb82c0cc28b 100644 >--- a/Source/WebCore/dom/NodeRareData.h >+++ b/Source/WebCore/dom/NodeRareData.h >@@ -32,6 +32,7 @@ > #include "NameNodeList.h" > #include "QualifiedName.h" > #include "TagNodeList.h" >+#include "WebKitMutationObserver.h" > #include <wtf/HashSet.h> > #include <wtf/OwnPtr.h> > #include <wtf/PassOwnPtr.h> >@@ -86,7 +87,31 @@ public: > private: > NodeListsNodeData() : m_labelsNodeListCache(0) {} > }; >- >+ >+#if ENABLE(MUTATION_OBSERVERS) >+struct MutationObserverEntry { >+ MutationObserverEntry(PassRefPtr<WebKitMutationObserver> observer, unsigned char options) >+ : observer(observer) >+ , options(options) >+ { >+ } >+ >+ bool operator==(const MutationObserverEntry& other) const >+ { >+ return observer == other.observer; >+ } >+ >+ bool matches(unsigned char options) const >+ { >+ return this->options & options; >+ } >+ >+ RefPtr<WebKitMutationObserver> observer; >+ unsigned char options; >+}; >+ >+#endif // ENABLE(MUTATION_OBSERVERS) >+ > class NodeRareData { > WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED; > public: >@@ -136,6 +161,16 @@ public: > return m_eventTargetData.get(); > } > >+#if ENABLE(MUTATION_OBSERVERS) >+ Vector<MutationObserverEntry>* mutationObserverEntries() { return m_mutationObservers.get(); } >+ Vector<MutationObserverEntry>* ensureMutationObserverEntries() >+ { >+ if (!m_mutationObservers) >+ m_mutationObservers = adoptPtr(new Vector<MutationObserverEntry>); >+ return m_mutationObservers.get(); >+ } >+#endif >+ > bool isFocused() const { return m_isFocused; } > void setFocused(bool focused) { m_isFocused = focused; } > >@@ -152,6 +187,10 @@ private: > bool m_tabIndexWasSetExplicitly : 1; > bool m_isFocused : 1; > bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1; >+ >+#if ENABLE(MUTATION_OBSERVERS) >+ OwnPtr<Vector<MutationObserverEntry> > m_mutationObservers; >+#endif > }; > > } // namespace WebCore >diff --git a/Source/WebCore/dom/WebKitMutationObserver.cpp b/Source/WebCore/dom/WebKitMutationObserver.cpp >index 1487364c818294b66e4fe656c0ca90ba84569366..bbfe4cb9f4a9acbd3927fc5823f856f3c1427477 100644 >--- a/Source/WebCore/dom/WebKitMutationObserver.cpp >+++ b/Source/WebCore/dom/WebKitMutationObserver.cpp >@@ -34,8 +34,11 @@ > > #include "WebKitMutationObserver.h" > >+#include "MutationObserverOptions.h" > #include "MutationCallback.h" >-#include "NotImplemented.h" >+#include "MutationRecord.h" >+#include "Node.h" >+#include <wtf/ListHashSet.h> > > namespace WebCore { > >@@ -53,14 +56,70 @@ WebKitMutationObserver::~WebKitMutationObserver() > { > } > >-void WebKitMutationObserver::observe(Node*, MutationObserverOptions*) >+void WebKitMutationObserver::observe(Node* node, MutationObserverOptions* options) > { >- notImplemented(); >+ unsigned char optionFlags = 0; >+ >+ // FIXME: Push composition of the optionFlags into the custom binding. >+ if (options->childList()) >+ optionFlags |= ChildList; >+ if (options->attributes()) >+ optionFlags |= Attributes; >+ if (options->characterData()) >+ optionFlags |= CharacterData; >+ // FIXME: More options composition work needs to be done here. >+ >+ if (node->registerMutationObserver(this, optionFlags) == Node::MutationObserverRegistered) >+ m_observedNodes.append(node); > } > > void WebKitMutationObserver::disconnect() > { >- notImplemented(); >+ for (Vector<Node*>::iterator iter = m_observedNodes.begin(); iter != m_observedNodes.end(); ++iter) >+ (*iter)->unregisterMutationObserver(this); >+ >+ m_observedNodes.clear(); >+} >+ >+void WebKitMutationObserver::observedNodeDestructed(Node* node) >+{ >+ size_t index = m_observedNodes.find(node); >+ ASSERT(index != notFound); >+ if (index == notFound) >+ return; >+ >+ m_observedNodes.remove(index); >+} >+ >+typedef ListHashSet<RefPtr<WebKitMutationObserver> > MutationObserverSet; >+ >+static MutationObserverSet& activeMutationObservers() >+{ >+ DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ()); >+ return activeObservers; >+} >+ >+void WebKitMutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation) >+{ >+ m_records.append(mutation); >+ activeMutationObservers().add(this); >+} >+ >+void WebKitMutationObserver::deliver() >+{ >+ MutationRecordArray records; >+ records.swap(m_records); >+ m_callback->handleEvent(&records, this); >+} >+ >+void WebKitMutationObserver::deliverAllMutations() >+{ >+ while (!activeMutationObservers().isEmpty()) { >+ MutationObserverSet::iterator iter = activeMutationObservers().begin(); >+ RefPtr<WebKitMutationObserver> observer = *iter; >+ activeMutationObservers().remove(iter); >+ observer->deliver(); >+ } > } > > } // namespace WebCore >diff --git a/Source/WebCore/dom/WebKitMutationObserver.h b/Source/WebCore/dom/WebKitMutationObserver.h >index 087944756b2fc26d144bafa043ac7e82d500b0db..6c195feccecf7f2f12b8ebee22f13c3e688939e5 100644 >--- a/Source/WebCore/dom/WebKitMutationObserver.h >+++ b/Source/WebCore/dom/WebKitMutationObserver.h >@@ -36,26 +36,48 @@ > #include <wtf/PassOwnPtr.h> > #include <wtf/RefCounted.h> > #include <wtf/RefPtr.h> >+#include <wtf/Vector.h> > > namespace WebCore { > > class MutationCallback; > class MutationObserverOptions; >+class MutationRecord; > class Node; > > class WebKitMutationObserver : public RefCounted<WebKitMutationObserver> { > public: >+ enum MutationType { >+ ChildList = 1 << 0, >+ Attributes = 1 << 1, >+ CharacterData = 1 << 2 >+ }; >+ >+ enum OptionFlags { >+ Subtree = 1 << 3, >+ AttributeOldValue = 1 << 4, >+ CharacterDataOldValue = 1 << 5, >+ AttributeFilter = 1 << 6 >+ }; >+ > static PassRefPtr<WebKitMutationObserver> create(PassRefPtr<MutationCallback>); >+ static void deliverAllMutations(); > > ~WebKitMutationObserver(); > > void observe(Node*, MutationObserverOptions*); > void disconnect(); >+ void observedNodeDestructed(Node*); >+ void enqueueMutationRecord(PassRefPtr<MutationRecord>); > > private: > WebKitMutationObserver(PassRefPtr<MutationCallback>); > >+ void deliver(); >+ > RefPtr<MutationCallback> m_callback; >+ Vector<RefPtr<MutationRecord> > m_records; >+ Vector<Node*> m_observedNodes; // NodeRareData has a RefPtr to this, so use a weak pointer to avoid a cycle. > }; > > } >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index f8848eaabe0ebb6cd4aec422d5549caf8d3a5f0f..3e326d72da5fc6357a80959d1db3c7c25cd07c41 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,13 @@ >+2011-10-13 Rafael Weinstein <rafaelw@chromium.org> >+ >+ [MutationObservers] Implement WebKitMutationObserver.observe for attributes >+ https://bugs.webkit.org/show_bug.cgi?id=68956 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * fast/mutation/observe-attributes-expected.txt: Added. >+ * fast/mutation/observe-attributes.html: Added. >+ > 2011-10-17 Dan Bernstein <mitz@apple.com> > > <rdar://problem/10293929> REGRESSION (r97525): iChat transcript has horizontal scroll bar when the conversation includes a wide image >diff --git a/LayoutTests/fast/mutation/observe-attributes-expected.txt b/LayoutTests/fast/mutation/observe-attributes-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..c8b2f683f9cfb71ae948968401129f295d2cdc26 >--- /dev/null >+++ b/LayoutTests/fast/mutation/observe-attributes-expected.txt >@@ -0,0 +1,64 @@ >+Test the constructor of WebKitMutationObserver >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+Testing basic aspects of attribute observation. >+...can attribute changes be observed at all >+PASS mutations.length is 1 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations[0].attributeNamespace is null >+...observer.disconnect() should prevent further delivery of mutations. >+PASS mutations is null >+...re-observing after disconnect works with the same observer. >+PASS mutations.length is 2 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations[0].attributeNamespace is null >+PASS mutations[1].type is "attributes" >+PASS mutations[1].attributeName is "bar" >+PASS mutations[1].attributeNamespace is null >+ >+Testing that observing without specifying "attributes" does not result in hearing about attribute changes. >+PASS mutations is null >+ >+Testing that re-observing the same node with the same observing has the effect of resetting the options. >+PASS calls is 1 >+PASS mutations.length is 1 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations is null >+ >+Testing that multiple observers can be registered to a given node and both receive mutations. >+PASS mutations.length is 1 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations2.length is 1 >+PASS mutations2[0].type is "attributes" >+PASS mutations2[0].attributeName is "foo" >+ >+Testing that "attributeNamespace" value is delivered properly. >+PASS mutations.length is 1 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations[0].attributeNamespace is "http://www.foo.com/bar" >+ >+Testing that modifications to node properties which delegate to attribute storage deliver mutations. >+PASS mutations.length is 2 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "src" >+PASS mutations[1].type is "attributes" >+PASS mutations[1].attributeName is "href" >+ >+Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched. >+PASS mutations.length is 2 >+PASS mutations[0].type is "attributes" >+PASS mutations[0].attributeName is "foo" >+PASS mutations[1].type is "attributes" >+PASS mutations[1].attributeName is "baz" >+ >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/fast/mutation/observe-attributes.html b/LayoutTests/fast/mutation/observe-attributes.html >new file mode 100644 >index 0000000000000000000000000000000000000000..58eab818e88176adff3bb5ba27ec805d5e9fef5b >--- /dev/null >+++ b/LayoutTests/fast/mutation/observe-attributes.html >@@ -0,0 +1,322 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<meta charset="utf-8"> >+<link rel="stylesheet" href="../js/resources/js-test-style.css"> >+<script src="../js/resources/js-test-pre.js"></script> >+<title></title> >+</head> >+<body> >+<p id=description></p> >+<div id="console"></div> >+<script> >+ >+window.jsTestIsAsync = true; >+var mutations; >+var mutations2; >+var calls; >+ >+function testBasic() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing basic aspects of attribute observation.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { attributes: true, characterData: true }); >+ div.setAttribute('foo', 'bar'); >+ setTimeout(checkDisconnectAndMutate, 0); >+ } >+ >+ function checkDisconnectAndMutate() { >+ debug('...can attribute changes be observed at all'); >+ >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ shouldBe('mutations[0].attributeNamespace', 'null'); >+ >+ mutations = null; >+ observer.disconnect(); >+ div.setAttribute('foo', 'baz'); >+ setTimeout(checkNotDeliveredAndMutateMultiple, 0); >+ } >+ >+ function checkNotDeliveredAndMutateMultiple() { >+ debug('...observer.disconnect() should prevent further delivery of mutations.'); >+ >+ shouldBe('mutations', 'null'); >+ observer.observe(div, { attributes: true }); >+ div.setAttribute('foo', 'bat'); >+ div.setAttribute('bar', 'foo'); >+ setTimeout(finish); >+ } >+ >+ function finish() { >+ debug('...re-observing after disconnect works with the same observer.'); >+ >+ shouldBe('mutations.length', '2'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ shouldBe('mutations[0].attributeNamespace', 'null'); >+ shouldBe('mutations[1].type', '"attributes"'); >+ shouldBe('mutations[1].attributeName', '"bar"'); >+ shouldBe('mutations[1].attributeNamespace', 'null'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testWrongType() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that observing without specifying "attributes" does not result in hearing about attribute changes.'); >+ >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { childList: true, characterData: true }); >+ div.setAttribute('foo', 'bar'); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations', 'null'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testMultipleRegistration() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that re-observing the same node with the same observing has the effect of resetting the options.'); >+ >+ calls = 0; >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ calls++; >+ }); >+ >+ observer.observe(div, { attributes: true, characterData: true }); >+ observer.observe(div, { attributes: true }); >+ div.setAttribute('foo', 'bar'); >+ setTimeout(checkDisconnectAndMutate, 0); >+ } >+ >+ function checkDisconnectAndMutate() { >+ shouldBe('calls', '1'); >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ mutations = null; >+ observer.observe(div, { attributes: true, characterData: true }); >+ observer.observe(div, { childList: true }); >+ div.setAttribute('foo', 'baz'); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations', 'null'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testMultipleObservers() { >+ var div; >+ var observer; >+ var observer2; >+ >+ function start() { >+ debug('Testing that multiple observers can be registered to a given node and both receive mutations.'); >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ observer2 = new WebKitMutationObserver(function(m) { >+ mutations2 = m; >+ }); >+ observer.observe(div, { attributes: true }); >+ observer2.observe(div, { attributes: true }); >+ div.setAttribute('foo', 'bar'); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ shouldBe('mutations2.length', '1'); >+ shouldBe('mutations2[0].type', '"attributes"'); >+ shouldBe('mutations2[0].attributeName', '"foo"'); >+ observer.disconnect(); >+ observer2.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testNamespaceURI() { >+ var div; >+ var observer; >+ >+ function start() { >+ debug('Testing that "attributeNamespace" value is delivered properly.'); >+ mutations = null; >+ div = document.createElement('div'); >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(div, { attributes: true, childList: true }); >+ div.setAttributeNS('http://www.foo.com/bar', 'foo', 'bar'); >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '1'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ shouldBe('mutations[0].attributeNamespace', '"http://www.foo.com/bar"'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testPropertyAccess() { >+ var img, a; >+ var observer; >+ >+ function start() { >+ debug('Testing that modifications to node properties which delegate to attribute storage deliver mutations.'); >+ mutations = null; >+ img = document.createElement('img'); >+ a = document.createElement('a'); >+ >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ observer.observe(img, { attributes: true }); >+ observer.observe(a, { attributes: true }); >+ >+ img.src = 'baz.png'; >+ a.href = 'foo.html'; >+ >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '2'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"src"'); >+ shouldBe('mutations[1].type', '"attributes"'); >+ shouldBe('mutations[1].attributeName', '"href"'); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+ >+function testOrderingWrtDOMSubtreeModified() { >+ var div, div2, subDiv; >+ var observer; >+ var listener; >+ >+ function start() { >+ debug('Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.'); >+ >+ mutations = null; >+ div = document.body.appendChild(document.createElement('div')); >+ div2 = document.body.appendChild(document.createElement('div')); >+ >+ subDiv = div.appendChild(document.createElement('div')); >+ >+ observer = new WebKitMutationObserver(function(m) { >+ mutations = m; >+ }); >+ >+ listener = function(e) { >+ div2.setAttribute('baz', 'bat'); >+ } >+ >+ div.addEventListener('DOMSubtreeModified', listener); >+ observer.observe(subDiv, { attributes: true }); >+ observer.observe(div2, { attributes: true }); >+ >+ subDiv.setAttribute('foo', 'bar'); >+ >+ setTimeout(finish, 0); >+ } >+ >+ function finish() { >+ shouldBe('mutations.length', '2'); >+ shouldBe('mutations[0].type', '"attributes"'); >+ shouldBe('mutations[0].attributeName', '"foo"'); >+ shouldBe('mutations[1].type', '"attributes"'); >+ shouldBe('mutations[1].attributeName', '"baz"'); >+ div.removeEventListener(listener); >+ document.body.removeChild(div); >+ observer.disconnect(); >+ debug(''); >+ runNextTest(); >+ } >+ >+ start(); >+} >+var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testNamespaceURI, testPropertyAccess, testOrderingWrtDOMSubtreeModified]; >+var testIndex = 0; >+ >+function runNextTest() { >+ if (testIndex < tests.length) >+ tests[testIndex++](); >+ else >+ finishJSTest(); >+} >+ >+description('Test the constructor of WebKitMutationObserver'); >+ >+if (!window.WebKitMutationObserver) >+ testFailed('This test requires ENABLE(MUTATION_OBSERVERS)'); >+else >+ runNextTest(); >+ >+var successfullyParsed = true; >+ >+</script> >+<script src="../js/resources/js-test-post.js"></script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 68956
:
110865
|
110906
|
110952
|
111047
|
111301
|
111326
| 111330