Changeset 204611 in webkit
- Timestamp:
- Aug 18, 2016 3:19:59 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r204605 r204611 1 2016-08-17 Ryosuke Niwa <rniwa@webkit.org> 2 3 Add basic support for connected and disconnected callbacks 4 https://bugs.webkit.org/show_bug.cgi?id=160950 5 6 Reviewed by Chris Dumez. 7 8 Added W3C style testharness.js tests for connectedCallback and disconnectedCallback. 9 10 Four test cases are failing due to a bug in window-less document's custom element registry, 11 which will be addressed in a future patch. 12 13 * fast/custom-elements/connected-callbacks-expected.txt: Added. 14 * fast/custom-elements/connected-callbacks.html: Added. 15 * fast/custom-elements/disconnected-callbacks-expected.txt: Added. 16 * fast/custom-elements/disconnected-callbacks.html: Added. 17 1 18 2016-08-18 Chris Dumez <cdumez@apple.com> 2 19 -
trunk/Source/WebCore/ChangeLog
r204610 r204611 1 2016-08-17 Ryosuke Niwa <rniwa@webkit.org> 2 3 Add basic support for connected and disconnected callbacks 4 https://bugs.webkit.org/show_bug.cgi?id=160950 5 6 Reviewed by Chris Dumez. 7 8 Added the basic support for custom elements' connectedCallback and disconnectedCallback. These callbacks 9 are enqueued by inserting and removing a node as spec'ed by https://dom.spec.whatwg.org/#concept-node-insert 10 and https://dom.spec.whatwg.org/#concept-node-remove 11 12 For now, we only support callbacks on appendChild and removeChild to limit the amount of code changes and 13 tests that need to be included in this patch. 14 15 This patch also renames InvokesCustomElementLifecycleCallbacks IDL attribute to CEReactions to match 16 the latest specification: https://html.spec.whatwg.org/multipage/scripting.html#cereactions 17 18 Tests: fast/custom-elements/connected-callbacks.html 19 fast/custom-elements/disconnected-callbacks.html 20 21 * bindings/js/JSCustomElementInterface.cpp: 22 (WebCore::JSCustomElementInterface::invokeCallback): Extracted from invokeAttributeChangedCallback. 23 (WebCore::JSCustomElementInterface::setConnectedCallback): Added. 24 (WebCore::JSCustomElementInterface::invokeConnectedCallback): Added. 25 (WebCore::JSCustomElementInterface::setDisconnectedCallback): Added. 26 (WebCore::JSCustomElementInterface::invokeDisconnectedCallback): Added. 27 (WebCore::JSCustomElementInterface::setAttributeChangedCallback): 28 (WebCore::JSCustomElementInterface::invokeAttributeChangedCallback): Renamed from attributeChanged. 29 * bindings/js/JSCustomElementInterface.h: Added m_connectedCallback and m_disconnectedCallback as instance 30 variables. Also removed the superfluous mutable qualifier from m_constructor m_attributeChangedCallback. 31 * bindings/js/JSCustomElementsRegistryCustom.cpp: 32 (WebCore::JSCustomElementsRegistry::define): Store connectedCallback and disconnectedCallback. 33 * bindings/scripts/CodeGeneratorJS.pm: 34 (GenerateImplementation): 35 * bindings/scripts/IDLAttributes.txt: 36 * dom/CustomElementsRegistry.idl: 37 * dom/Element.cpp: 38 (WebCore::Element::insertedInto): Call enqueueConnectedCallbackIfNeeded. 39 (WebCore::Element::removedFrom): Call enqueueDisconnectedCallbackIfNeeded. 40 * dom/Element.idl: 41 * dom/LifecycleCallbackQueue.cpp: 42 (WebCore::LifecycleQueueItem::invoke): Added calls to invokeConnectedCallback and invokeDisconnectedCallback. 43 (WebCore::findInterfaceForCustomElement): Extracted from enqueueAttributeChangedCallbackIfNeeded. 44 (WebCore::LifecycleCallbackQueue::enqueueConnectedCallbackIfNeeded): Added. 45 (WebCore::LifecycleCallbackQueue::enqueueDisconnectedCallbackIfNeeded): Added. 46 (WebCore::LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded): 47 (WebCore::CustomElementLifecycleProcessingStack::ensureCurrentQueue): 48 * dom/LifecycleCallbackQueue.h: 49 * dom/Node.idl: 50 1 51 2016-08-18 Beth Dakin <bdakin@apple.com> 2 52 -
trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp
r204553 r204611 152 152 } 153 153 154 void JSCustomElementInterface::invokeCallback(Element& element, JSObject* callback, const Function<void(ExecState*, MarkedArgumentBuffer&)>& addArguments) 155 { 156 if (!canInvokeCallback()) 157 return; 158 159 auto* context = scriptExecutionContext(); 160 if (!context) 161 return; 162 163 Ref<JSCustomElementInterface> protectedThis(*this); 164 JSLockHolder lock(m_isolatedWorld->vm()); 165 166 ASSERT(context); 167 ASSERT(context->isDocument()); 168 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld); 169 ExecState* state = globalObject->globalExec(); 170 171 JSObject* jsElement = asObject(toJS(state, globalObject, element)); 172 173 CallData callData; 174 CallType callType = callback->methodTable()->getCallData(callback, callData); 175 ASSERT(callType != CallType::None); 176 177 MarkedArgumentBuffer args; 178 addArguments(state, args); 179 180 InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData); 181 182 NakedPtr<Exception> exception; 183 JSMainThreadExecState::call(state, callback, callType, callData, jsElement, args, exception); 184 185 InspectorInstrumentation::didCallFunction(cookie, context); 186 187 if (exception) 188 reportException(state, exception); 189 } 190 191 void JSCustomElementInterface::setConnectedCallback(JSC::JSObject* callback) 192 { 193 m_connectedCallback = callback; 194 } 195 196 void JSCustomElementInterface::invokeConnectedCallback(Element& element) 197 { 198 invokeCallback(element, m_connectedCallback.get()); 199 } 200 201 void JSCustomElementInterface::setDisconnectedCallback(JSC::JSObject* callback) 202 { 203 m_disconnectedCallback = callback; 204 } 205 206 void JSCustomElementInterface::invokeDisconnectedCallback(Element& element) 207 { 208 invokeCallback(element, m_disconnectedCallback.get()); 209 } 210 154 211 void JSCustomElementInterface::setAttributeChangedCallback(JSC::JSObject* callback, const Vector<String>& observedAttributes) 155 212 { … … 160 217 } 161 218 162 void JSCustomElementInterface::attributeChanged(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 163 { 164 if (!canInvokeCallback()) 165 return; 166 167 Ref<JSCustomElementInterface> protectedThis(*this); 168 169 JSLockHolder lock(m_isolatedWorld->vm()); 170 171 ScriptExecutionContext* context = scriptExecutionContext(); 172 if (!context) 173 return; 174 175 ASSERT(context->isDocument()); 176 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld); 177 ExecState* state = globalObject->globalExec(); 178 179 JSObject* jsElement = asObject(toJS(state, globalObject, element)); 180 181 CallData callData; 182 CallType callType = m_attributeChangedCallback->methodTable()->getCallData(m_attributeChangedCallback.get(), callData); 183 ASSERT(callType != CallType::None); 184 185 const AtomicString& namespaceURI = attributeName.namespaceURI(); 186 MarkedArgumentBuffer args; 187 args.append(jsStringWithCache(state, attributeName.localName())); 188 args.append(oldValue == nullAtom ? jsNull() : jsStringWithCache(state, oldValue)); 189 args.append(newValue == nullAtom ? jsNull() : jsStringWithCache(state, newValue)); 190 args.append(namespaceURI == nullAtom ? jsNull() : jsStringWithCache(state, attributeName.namespaceURI())); 191 192 InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData); 193 194 NakedPtr<Exception> exception; 195 JSMainThreadExecState::call(state, m_attributeChangedCallback.get(), callType, callData, jsElement, args, exception); 196 197 InspectorInstrumentation::didCallFunction(cookie, context); 198 199 if (exception) 200 reportException(state, exception); 201 } 202 219 void JSCustomElementInterface::invokeAttributeChangedCallback(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 220 { 221 invokeCallback(element, m_attributeChangedCallback.get(), [&](ExecState* state, MarkedArgumentBuffer& args) { 222 args.append(jsStringWithCache(state, attributeName.localName())); 223 args.append(jsStringOrNull(state, oldValue)); 224 args.append(jsStringOrNull(state, newValue)); 225 args.append(jsStringOrNull(state, attributeName.namespaceURI())); 226 }); 227 } 228 203 229 void JSCustomElementInterface::didUpgradeLastElementInConstructionStack() 204 230 { -
trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h
r204553 r204611 36 36 #include <runtime/JSObject.h> 37 37 #include <wtf/Forward.h> 38 #include <wtf/Function.h> 38 39 #include <wtf/RefCounted.h> 39 40 #include <wtf/RefPtr.h> … … 67 68 void upgradeElement(Element&); 68 69 70 void setConnectedCallback(JSC::JSObject*); 71 void invokeConnectedCallback(Element&); 72 73 void setDisconnectedCallback(JSC::JSObject*); 74 void invokeDisconnectedCallback(Element&); 75 69 76 void setAttributeChangedCallback(JSC::JSObject* callback, const Vector<String>& observedAttributes); 70 77 bool observesAttribute(const AtomicString& name) const { return m_observedAttributes.contains(name); } 71 void attributeChanged(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);78 void invokeAttributeChangedCallback(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); 72 79 73 80 ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); } … … 85 92 JSCustomElementInterface(const QualifiedName&, JSC::JSObject* callback, JSDOMGlobalObject*); 86 93 94 void invokeCallback(Element&, JSC::JSObject* callback, const Function<void(JSC::ExecState*, JSC::MarkedArgumentBuffer&)>& addArguments = {}); 95 87 96 QualifiedName m_name; 88 mutable JSC::Weak<JSC::JSObject> m_constructor; 89 mutable JSC::Weak<JSC::JSObject> m_attributeChangedCallback; 97 JSC::Weak<JSC::JSObject> m_constructor; 98 JSC::Weak<JSC::JSObject> m_connectedCallback; 99 JSC::Weak<JSC::JSObject> m_disconnectedCallback; 100 JSC::Weak<JSC::JSObject> m_attributeChangedCallback; 90 101 RefPtr<DOMWrapperWorld> m_isolatedWorld; 91 102 Vector<RefPtr<Element>, 1> m_constructionStack; -
trunk/Source/WebCore/bindings/js/JSCustomElementsRegistryCustom.cpp
r204553 r204611 105 105 JSObject& prototypeObject = *asObject(prototypeValue); 106 106 107 // FIXME: Add the support for connectedCallback. 108 getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback")); 107 QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI); 108 auto elementInterface = JSCustomElementInterface::create(name, constructor, globalObject()); 109 110 auto* connectedCallback = getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback")); 109 111 if (state.hadException()) 110 112 return jsUndefined(); 113 if (connectedCallback) 114 elementInterface->setConnectedCallback(connectedCallback); 111 115 112 // FIXME: Add the support for disconnectedCallback. 113 getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback")); 116 auto* disconnectedCallback = getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback")); 114 117 if (state.hadException()) 115 118 return jsUndefined(); 119 if (disconnectedCallback) 120 elementInterface->setDisconnectedCallback(disconnectedCallback); 116 121 117 122 // FIXME: Add the support for adoptedCallback. … … 119 124 if (state.hadException()) 120 125 return jsUndefined(); 121 122 QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI);123 auto elementInterface = JSCustomElementInterface::create(name, constructor, globalObject());124 126 125 127 auto* attributeChangedCallback = getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "attributeChangedCallback")); -
trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
r204589 r204611 3341 3341 $implIncludes{"<runtime/Error.h>"} = 1; 3342 3342 3343 if ($function->signature->extendedAttributes->{" InvokesCustomElementLifecycleCallbacks"}) {3343 if ($function->signature->extendedAttributes->{"CEReactions"}) { 3344 3344 push(@implContent, "#if ENABLE(CUSTOM_ELEMENTS)\n"); 3345 3345 push(@implContent, " CustomElementLifecycleProcessingStack customElementLifecycleProcessingStack;\n"); -
trunk/Source/WebCore/bindings/scripts/IDLAttributes.txt
r204481 r204611 22 22 AppleCopyright 23 23 AtomicString 24 CEReactions 24 25 CachedAttribute 25 26 CallbackNeedsOperatorEqual … … 92 93 MasqueradesAsUndefined 93 94 NamedConstructor=* 94 InvokesCustomElementLifecycleCallbacks95 95 NewImpurePropertyFiresWatchpoints 96 96 NewObject -
trunk/Source/WebCore/dom/CustomElementsRegistry.idl
r204367 r204611 30 30 ] interface CustomElementsRegistry { 31 31 32 [ InvokesCustomElementLifecycleCallbacks, Custom] void define(DOMString name, Function constructor);32 [CEReactions, Custom] void define(DOMString name, Function constructor); 33 33 34 34 }; -
trunk/Source/WebCore/dom/Element.cpp
r204594 r204611 1597 1597 } 1598 1598 1599 #if ENABLE(CUSTOM_ELEMENTS) 1600 if (newDocument && UNLIKELY(isCustomElement())) 1601 LifecycleCallbackQueue::enqueueConnectedCallbackIfNeeded(*this); 1602 #endif 1603 1599 1604 return InsertionDone; 1600 1605 } … … 1642 1647 updateLabel(*oldScope, attributeWithoutSynchronization(forAttr), nullAtom); 1643 1648 } 1649 1650 #if ENABLE(CUSTOM_ELEMENTS) 1651 if (oldDocument && UNLIKELY(isCustomElement())) 1652 LifecycleCallbackQueue::enqueueDisconnectedCallbackIfNeeded(*this); 1653 #endif 1644 1654 } 1645 1655 -
trunk/Source/WebCore/dom/Element.idl
r204594 r204611 32 32 DOMString? getAttribute(DOMString name); 33 33 34 [ObjCLegacyUnnamedParameters, RaisesException, InvokesCustomElementLifecycleCallbacks] 35 36 void setAttribute(DOMString name, DOMString value); 37 38 [InvokesCustomElementLifecycleCallbacks] void removeAttribute(DOMString name); 34 [ObjCLegacyUnnamedParameters, RaisesException, CEReactions] void setAttribute(DOMString name, DOMString value); 35 36 [CEReactions] void removeAttribute(DOMString name); 39 37 Attr? getAttributeNode(DOMString name); 40 38 41 [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr? setAttributeNode(Attr newAttr);42 [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr removeAttributeNode(Attr oldAttr);39 [RaisesException, CEReactions] Attr? setAttributeNode(Attr newAttr); 40 [RaisesException, CEReactions] Attr removeAttributeNode(Attr oldAttr); 43 41 44 42 HTMLCollection getElementsByTagName(DOMString name); … … 51 49 [ObjCLegacyUnnamedParameters] DOMString? getAttributeNS(DOMString? namespaceURI, DOMString localName); 52 50 53 [ObjCLegacyUnnamedParameters, RaisesException, InvokesCustomElementLifecycleCallbacks] 54 55 void setAttributeNS(DOMString? namespaceURI, DOMString qualifiedName, DOMString value); 56 57 [ObjCLegacyUnnamedParameters, InvokesCustomElementLifecycleCallbacks] void removeAttributeNS(DOMString? namespaceURI, DOMString localName); 51 [ObjCLegacyUnnamedParameters, RaisesException, CEReactions] void setAttributeNS(DOMString? namespaceURI, DOMString qualifiedName, DOMString value); 52 53 [ObjCLegacyUnnamedParameters, CEReactions] void removeAttributeNS(DOMString? namespaceURI, DOMString localName); 58 54 59 55 HTMLCollection getElementsByTagNameNS(DOMString? namespaceURI, DOMString localName); 60 56 61 57 [ObjCLegacyUnnamedParameters] Attr? getAttributeNodeNS(DOMString? namespaceURI, DOMString localName); 62 [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr? setAttributeNodeNS(Attr newAttr);58 [RaisesException, CEReactions] Attr? setAttributeNodeNS(Attr newAttr); 63 59 boolean hasAttribute(DOMString name); 64 60 -
trunk/Source/WebCore/dom/LifecycleCallbackQueue.cpp
r204553 r204611 45 45 enum class Type { 46 46 ElementUpgrade, 47 Connected, 48 Disconnected, 47 49 AttributeChanged, 48 50 }; … … 69 71 m_interface->upgradeElement(m_element.get()); 70 72 break; 73 case Type::Connected: 74 m_interface->invokeConnectedCallback(m_element.get()); 75 break; 76 case Type::Disconnected: 77 m_interface->invokeDisconnectedCallback(m_element.get()); 78 break; 71 79 case Type::AttributeChanged: 72 80 ASSERT(m_attributeName); 73 m_interface-> attributeChanged(m_element.get(), m_attributeName.value(), m_oldValue, m_newValue);81 m_interface->invokeAttributeChangedCallback(m_element.get(), m_attributeName.value(), m_oldValue, m_newValue); 74 82 break; 75 83 } … … 99 107 } 100 108 101 void LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)109 static JSCustomElementInterface* findInterfaceForCustomElement(Element& element) 102 110 { 103 111 ASSERT(element.isCustomElement()); 104 112 auto* window = element.document().domWindow(); 105 113 if (!window) 106 return ;114 return nullptr; 107 115 108 116 auto* registry = window->customElementsRegistry(); 109 117 if (!registry) 110 return ;118 return nullptr; 111 119 112 auto* elementInterface = registry->findInterface(element.tagQName()); 113 if (!elementInterface->observesAttribute(attributeName.localName())) 120 return registry->findInterface(element.tagQName()); 121 } 122 123 void LifecycleCallbackQueue::enqueueConnectedCallbackIfNeeded(Element& element) 124 { 125 auto* elementInterface = findInterfaceForCustomElement(element); 126 if (!elementInterface) 114 127 return; 115 128 116 129 if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue()) 117 queue->m_items.append(LifecycleQueueItem(element, *elementInterface, attributeName, oldValue, newValue)); 130 queue->m_items.append({LifecycleQueueItem::Type::Connected, element, *elementInterface}); 131 } 132 133 void LifecycleCallbackQueue::enqueueDisconnectedCallbackIfNeeded(Element& element) 134 { 135 auto* elementInterface = findInterfaceForCustomElement(element); 136 if (!elementInterface) 137 return; 138 139 if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue()) 140 queue->m_items.append({LifecycleQueueItem::Type::Disconnected, element, *elementInterface}); 141 } 142 143 void LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 144 { 145 auto* elementInterface = findInterfaceForCustomElement(element); 146 if (!elementInterface || !elementInterface->observesAttribute(attributeName.localName())) 147 return; 148 149 if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue()) 150 queue->m_items.append({element, *elementInterface, attributeName, oldValue, newValue}); 118 151 } 119 152 … … 128 161 LifecycleCallbackQueue* CustomElementLifecycleProcessingStack::ensureCurrentQueue() 129 162 { 130 // FIXME: This early exit indicates a bug that some DOM API is missing InvokesCustomElementLifecycleCallbacks163 // FIXME: This early exit indicates a bug that some DOM API is missing CEReactions 131 164 if (!s_currentProcessingStack) 132 165 return nullptr; -
trunk/Source/WebCore/dom/LifecycleCallbackQueue.h
r204553 r204611 47 47 48 48 static void enqueueElementUpgrade(Element&, JSCustomElementInterface&); 49 static void enqueueConnectedCallbackIfNeeded(Element&); 50 static void enqueueDisconnectedCallbackIfNeeded(Element&); 49 51 static void enqueueAttributeChangedCallbackIfNeeded(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); 50 52 -
trunk/Source/WebCore/dom/Node.idl
r204259 r204611 1 1 /* 2 * Copyright (C) 2006 , 2007, 2008, 2009Apple Inc. All rights reserved.2 * Copyright (C) 2006-2016 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> 4 4 * … … 58 58 [ObjCLegacyUnnamedParameters, Custom, RaisesException] Node insertBefore([CustomReturn] Node newChild, Node? refChild); 59 59 [ObjCLegacyUnnamedParameters, Custom, RaisesException] Node replaceChild(Node newChild, [CustomReturn] Node oldChild); 60 [Custom, RaisesException ] Node removeChild([CustomReturn] Node oldChild);61 [Custom, RaisesException ] Node appendChild([CustomReturn] Node newChild);60 [Custom, RaisesException, CEReactions] Node removeChild([CustomReturn] Node oldChild); 61 [Custom, RaisesException, CEReactions] Node appendChild([CustomReturn] Node newChild); 62 62 63 63 boolean hasChildNodes(); 64 64 65 [NewObject, RaisesException, ImplementedAs=cloneNodeForBindings, InvokesCustomElementLifecycleCallbacks] 66 Node cloneNode(optional boolean deep = false); 65 [NewObject, RaisesException, ImplementedAs=cloneNodeForBindings, CEReactions] Node cloneNode(optional boolean deep = false); 67 66 68 67 void normalize();
Note: See TracChangeset
for help on using the changeset viewer.