Changeset 204540 in webkit
- Timestamp:
- Aug 16, 2016 4:55:49 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 11 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r204536 r204540 1 2016-08-16 Ryosuke Niwa <rniwa@webkit.org> 2 3 customElements.define should retrieve lifecycle callbacks 4 https://bugs.webkit.org/show_bug.cgi?id=160797 5 6 Reviewed by Chris Dumez. 7 8 Added test cases for CustomElementsRegistry.define to make sure it invokes Get(constructor, "prototype") 9 and Get(prototype, callbackName) for each lifecycle callback. 10 11 Also updated the tests to reflect the support for observedAttributes which specifies the list of attributes 12 for which attributeChangedCallback is invoked. 13 14 * fast/custom-elements/CustomElementsRegistry-expected.txt: Renamed from Document-defineElement-expected.txt. 15 * fast/custom-elements/CustomElementsRegistry.html: Renamed from Document-defineElement.html. 16 * fast/custom-elements/Document-defineElement-expected.txt: Removed. 17 * fast/custom-elements/Document-defineElement.html: Removed. 18 * fast/custom-elements/attribute-changed-callback-expected.txt: 19 * fast/custom-elements/attribute-changed-callback.html: Added test cases for "observedAttributes". 20 * fast/custom-elements/lifecycle-callback-timing.html: 21 1 22 2016-08-16 Chris Dumez <cdumez@apple.com> 2 23 -
trunk/LayoutTests/fast/custom-elements/CustomElementsRegistry-expected.txt
r204539 r204540 1 1 2 PASS Check the existence of CustomElementsRegistry.prototype.define on CustomElementsRegistry interface 3 PASS customElements.define should throw with an invalid name 4 PASS customElements.define should throw when there is already a custom element of the same name 5 PASS customElements.define should throw when there is already a custom element with the same class 6 PASS customElements.define should throw when the element interface is not a constructor 7 PASS customElements.define should define an instantiatable custom element 2 PASS CustomElementsRegistry interface must have define as a method 3 PASS customElements.define must throw with an invalid name 4 PASS customElements.define must throw when there is already a custom element of the same name 5 PASS customElements.define must throw when there is already a custom element with the same class 6 PASS customElements.define must throw when the element interface is not a constructor 7 PASS customElements.define must get "prototype" property of the constructor 8 PASS customElements.define must rethrow an exception thrown while getting "prototype" property of the constructor 9 PASS customElements.define must throw when "prototype" property of the constructor is not an object 10 PASS customElements.define must get callbacks of the constructor prototype 11 PASS customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype 12 PASS customElements.define must rethrow an exception thrown while converting a callback value to Function callback type 13 PASS customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present 14 PASS customElements.define must rethrow an exception thrown while getting observedAttributes on the constructor prototype 15 PASS customElements.define must rethrow an exception thrown while converting the value of observedAttributes to sequence<DOMString> 16 PASS customElements.define must rethrow an exception thrown while iterating over observedAttributes to sequence<DOMString> 17 PASS customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on observedAttributes 18 PASS customElements.define must not throw even if "observedAttributes" fails to convert if "attributeChangedCallback" is not defined 19 PASS customElements.define must define an instantiatable custom element 8 20 -
trunk/LayoutTests/fast/custom-elements/CustomElementsRegistry.html
r204539 r204540 2 2 <html> 3 3 <head> 4 <title>Custom Elements: Extensions to Documentinterface</title>4 <title>Custom Elements: CustomElementsRegistry interface</title> 5 5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> 6 <meta name="assert" content=" customElements.define should define a custom element">6 <meta name="assert" content="CustomElementsRegistry interface must exist"> 7 7 <script src="../../resources/testharness.js"></script> 8 8 <script src="../../resources/testharnessreport.js"></script> … … 16 16 assert_true('define' in CustomElementsRegistry.prototype, '"define" exists on CustomElementsRegistry.prototype'); 17 17 assert_true('define' in customElements, '"define" exists on window.customElements'); 18 }, 'C heck the existence of CustomElementsRegistry.prototype.define on CustomElementsRegistry interface');18 }, 'CustomElementsRegistry interface must have define as a method'); 19 19 20 20 test(function () { … … 46 46 } 47 47 48 }, 'customElements.define shouldthrow with an invalid name');48 }, 'customElements.define must throw with an invalid name'); 49 49 50 50 test(function () { … … 56 56 'customElements.define must throw a NotSupportedError if the specified tag name is already used'); 57 57 58 }, 'customElements.define shouldthrow when there is already a custom element of the same name');58 }, 'customElements.define must throw when there is already a custom element of the same name'); 59 59 60 60 test(function () { … … 65 65 'customElements.define must throw a NotSupportedError if the specified class already defines an element'); 66 66 67 }, 'customElements.define shouldthrow when there is already a custom element with the same class');67 }, 'customElements.define must throw when there is already a custom element with the same class'); 68 68 69 69 test(function () { … … 76 76 assert_throws({'name': 'TypeError'}, function () { customElements.define('invalid-element', []); }, 77 77 'customElements.define must throw a TypeError when the element interface is an array'); 78 }, 'customElements.define should throw when the element interface is not a constructor'); 78 }, 'customElements.define must throw when the element interface is not a constructor'); 79 80 test(function () { 81 var calls = []; 82 var proxy = new Proxy(class extends HTMLElement { }, { 83 get: function (target, name) { 84 calls.push(name); 85 return target[name]; 86 } 87 }); 88 customElements.define('proxy-element', proxy); 89 assert_array_equals(calls, ['prototype']); 90 }, 'customElements.define must get "prototype" property of the constructor'); 91 92 test(function () { 93 var proxy = new Proxy(class extends HTMLElement { }, { 94 get: function (target, name) { 95 throw {name: 'expectedError'}; 96 } 97 }); 98 assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-string-prototype', proxy); }); 99 }, 'customElements.define must rethrow an exception thrown while getting "prototype" property of the constructor'); 100 101 test(function () { 102 var returnedValue; 103 var proxy = new Proxy(class extends HTMLElement { }, { 104 get: function (target, name) { return returnedValue; } 105 }); 106 107 returnedValue = null; 108 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); }, 109 'customElements.define must throw when "prototype" property of the constructor is null'); 110 returnedValue = undefined; 111 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); }, 112 'customElements.define must throw when "prototype" property of the constructor is undefined'); 113 returnedValue = 'hello'; 114 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); }, 115 'customElements.define must throw when "prototype" property of the constructor is a string'); 116 returnedValue = 1; 117 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); }, 118 'customElements.define must throw when "prototype" property of the constructor is a number'); 119 120 }, 'customElements.define must throw when "prototype" property of the constructor is not an object'); 121 122 test(function () { 123 var constructor = function () {} 124 var calls = []; 125 constructor.prototype = new Proxy(constructor.prototype, { 126 get: function (target, name) { 127 calls.push(name); 128 return target[name]; 129 } 130 }); 131 customElements.define('element-with-proxy-prototype', constructor); 132 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'adoptedCallback', 'attributeChangedCallback']); 133 }, 'customElements.define must get callbacks of the constructor prototype'); 134 135 test(function () { 136 var constructor = function () {} 137 var calls = []; 138 constructor.prototype = new Proxy(constructor.prototype, { 139 get: function (target, name) { 140 calls.push(name); 141 if (name == 'disconnectedCallback') 142 throw {name: 'expectedError'}; 143 return target[name]; 144 } 145 }); 146 assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-throwing-callback', constructor); }); 147 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback'], 148 'customElements.define must not get callbacks after one of the get throws'); 149 }, 'customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype'); 150 151 test(function () { 152 var constructor = function () {} 153 var calls = []; 154 constructor.prototype = new Proxy(constructor.prototype, { 155 get: function (target, name) { 156 calls.push(name); 157 if (name == 'adoptedCallback') 158 return 1; 159 return target[name]; 160 } 161 }); 162 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-throwing-callback', constructor); }); 163 assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'adoptedCallback'], 164 'customElements.define must not get callbacks after one of the conversion throws'); 165 }, 'customElements.define must rethrow an exception thrown while converting a callback value to Function callback type'); 166 167 test(function () { 168 var constructor = function () {} 169 constructor.prototype.attributeChangedCallback = function () { }; 170 var prototypeCalls = []; 171 var callOrder = 0; 172 constructor.prototype = new Proxy(constructor.prototype, { 173 get: function (target, name) { 174 if (name == 'prototype' || name == 'observedAttributes') 175 throw 'Unexpected access to observedAttributes'; 176 prototypeCalls.push(callOrder++); 177 prototypeCalls.push(name); 178 return target[name]; 179 } 180 }); 181 var constructorCalls = []; 182 var proxy = new Proxy(constructor, { 183 get: function (target, name) { 184 constructorCalls.push(callOrder++); 185 constructorCalls.push(name); 186 return target[name]; 187 } 188 }); 189 customElements.define('element-with-attribute-changed-callback', proxy); 190 assert_array_equals(prototypeCalls, [1, 'connectedCallback', 2, 'disconnectedCallback', 3, 'adoptedCallback', 4, 'attributeChangedCallback']); 191 assert_array_equals(constructorCalls, [0, 'prototype', 5, 'observedAttributes']); 192 }, 'customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present'); 193 194 test(function () { 195 var constructor = function () {} 196 constructor.prototype.attributeChangedCallback = function () { }; 197 var calls = []; 198 var proxy = new Proxy(constructor, { 199 get: function (target, name) { 200 calls.push(name); 201 if (name == 'observedAttributes') 202 throw {name: 'expectedError'}; 203 return target[name]; 204 } 205 }); 206 assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-throwing-observed-attributes', proxy); }); 207 assert_array_equals(calls, ['prototype', 'observedAttributes'], 208 'customElements.define must get "prototype" and "observedAttributes" on the constructor'); 209 }, 'customElements.define must rethrow an exception thrown while getting observedAttributes on the constructor prototype'); 210 211 test(function () { 212 var constructor = function () {} 213 constructor.prototype.attributeChangedCallback = function () { }; 214 var calls = []; 215 var proxy = new Proxy(constructor, { 216 get: function (target, name) { 217 calls.push(name); 218 if (name == 'observedAttributes') 219 return 1; 220 return target[name]; 221 } 222 }); 223 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-invalid-observed-attributes', proxy); }); 224 assert_array_equals(calls, ['prototype', 'observedAttributes'], 225 'customElements.define must get "prototype" and "observedAttributes" on the constructor'); 226 }, 'customElements.define must rethrow an exception thrown while converting the value of observedAttributes to sequence<DOMString>'); 227 228 test(function () { 229 var constructor = function () {} 230 constructor.prototype.attributeChangedCallback = function () { }; 231 constructor.observedAttributes = {[Symbol.iterator]: function *() { 232 yield 'foo'; 233 throw {name: 'SomeError'}; 234 }}; 235 assert_throws({'name': 'SomeError'}, function () { customElements.define('element-with-generator-observed-attributes', constructor); }); 236 }, 'customElements.define must rethrow an exception thrown while iterating over observedAttributes to sequence<DOMString>'); 237 238 test(function () { 239 var constructor = function () {} 240 constructor.prototype.attributeChangedCallback = function () { }; 241 constructor.observedAttributes = {[Symbol.iterator]: 1}; 242 assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-observed-attributes-with-uncallable-iterator', constructor); }); 243 }, 'customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on observedAttributes'); 244 245 test(function () { 246 var constructor = function () {} 247 constructor.observedAttributes = 1; 248 customElements.define('element-without-callback-with-invalid-observed-attributes', constructor); 249 }, 'customElements.define must not throw even if "observedAttributes" fails to convert if "attributeChangedCallback" is not defined'); 79 250 80 251 test(function () { … … 95 266 'A custom element HTML must use HTML namespace'); 96 267 97 }, 'customElements.define shoulddefine an instantiatable custom element');268 }, 'customElements.define must define an instantiatable custom element'); 98 269 99 270 </script> -
trunk/LayoutTests/fast/custom-elements/attribute-changed-callback-expected.txt
r197611 r204540 4 4 PASS setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback 5 5 PASS setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback 6 PASS Mutating attributeChangedCallback after calling customElements.define must not affect the callback being invoked 7 PASS attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute. 8 PASS Mutating observedAttributes after calling customElements.define must not affect the set of attributes for which attributedChangedCallback is invoked 9 PASS attributedChangedCallback must be enqueued for attributes specified in a non-Array iterable observedAttributes 6 10 -
trunk/LayoutTests/fast/custom-elements/attribute-changed-callback.html
r204367 r204540 20 20 } 21 21 } 22 MyCustomElement.observedAttributes = ['title', 'id', 'r']; 22 23 customElements.define('my-custom-element', MyCustomElement); 23 24 … … 97 98 assert_equals(argumentList[1].value, null); 98 99 assert_array_equals(argumentList[1].arguments, ['r', '100', null, 'http://www.w3.org/2000/svg']); 100 }, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback'); 99 101 100 }, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback'); 102 test(function () { 103 var callsToOld = []; 104 var callsToNew = []; 105 class CustomElement extends HTMLElement { } 106 CustomElement.prototype.attributeChangedCallback = function () { 107 callsToOld.push(Array.from(arguments)); 108 } 109 CustomElement.observedAttributes = ['title']; 110 customElements.define('element-with-mutated-attribute-changed-callback', CustomElement); 111 CustomElement.prototype.attributeChangedCallback = function () { 112 callsToNew.push(Array.from(arguments)); 113 } 114 115 var instance = document.createElement('element-with-mutated-attribute-changed-callback'); 116 instance.setAttribute('title', 'hi'); 117 assert_equals(instance.getAttribute('title'), 'hi'); 118 assert_array_equals(callsToNew, []); 119 assert_equals(callsToOld.length, 1); 120 assert_array_equals(callsToOld[0], ['title', null, 'hi', null]); 121 }, 'Mutating attributeChangedCallback after calling customElements.define must not affect the callback being invoked'); 122 123 test(function () { 124 var calls = []; 125 class CustomElement extends HTMLElement { 126 attributeChangedCallback() { 127 calls.push(Array.from(arguments)); 128 } 129 } 130 CustomElement.observedAttributes = ['title']; 131 customElements.define('element-not-observing-id-attribute', CustomElement); 132 133 var instance = document.createElement('element-not-observing-id-attribute'); 134 instance.setAttribute('title', 'hi'); 135 assert_equals(calls.length, 1); 136 assert_array_equals(calls[0], ['title', null, 'hi', null]); 137 instance.setAttribute('id', 'some'); 138 assert_equals(calls.length, 1); 139 }, 'attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute.'); 140 141 test(function () { 142 var calls = []; 143 class CustomElement extends HTMLElement { } 144 CustomElement.prototype.attributeChangedCallback = function () { 145 calls.push(Array.from(arguments)); 146 } 147 CustomElement.observedAttributes = ['title', 'lang']; 148 customElements.define('element-with-mutated-observed-attributes', CustomElement); 149 CustomElement.observedAttributes = ['title', 'id']; 150 151 var instance = document.createElement('element-with-mutated-observed-attributes'); 152 instance.setAttribute('title', 'hi'); 153 assert_equals(calls.length, 1); 154 assert_array_equals(calls[0], ['title', null, 'hi', null]); 155 156 instance.setAttribute('id', 'some'); 157 assert_equals(calls.length, 1); 158 159 instance.setAttribute('lang', 'en'); 160 assert_equals(calls.length, 2); 161 assert_array_equals(calls[0], ['title', null, 'hi', null]); 162 assert_array_equals(calls[1], ['lang', null, 'en', null]); 163 }, 'Mutating observedAttributes after calling customElements.define must not affect the set of attributes for which attributedChangedCallback is invoked'); 164 165 test(function () { 166 var calls = []; 167 class CustomElement extends HTMLElement { } 168 CustomElement.prototype.attributeChangedCallback = function () { 169 calls.push(Array.from(arguments)); 170 } 171 CustomElement.observedAttributes = { [Symbol.iterator]: function *() { yield 'lang'; yield 'style'; } }; 172 customElements.define('element-with-generator-observed-attributes', CustomElement); 173 174 var instance = document.createElement('element-with-generator-observed-attributes'); 175 instance.setAttribute('lang', 'en'); 176 assert_equals(calls.length, 1); 177 assert_array_equals(calls[0], ['lang', null, 'en', null]); 178 179 instance.setAttribute('lang', 'ja'); 180 assert_equals(calls.length, 2); 181 assert_array_equals(calls[1], ['lang', 'en', 'ja', null]); 182 183 instance.setAttribute('title', 'hello'); 184 assert_equals(calls.length, 2); 185 186 instance.setAttribute('style', 'font-size: 2rem'); 187 assert_equals(calls.length, 3); 188 assert_array_equals(calls[2], ['style', null, 'font-size: 2rem', null]); 189 }, 'attributedChangedCallback must be enqueued for attributes specified in a non-Array iterable observedAttributes'); 101 190 102 191 </script> -
trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html
r204367 r204540 21 21 handler() { } 22 22 } 23 MyCustomElement.observedAttributes = ['data-title', 'title']; 23 24 customElements.define('my-custom-element', MyCustomElement); 24 25 -
trunk/Source/WebCore/ChangeLog
r204537 r204540 1 2016-08-16 Ryosuke Niwa <rniwa@webkit.org> 2 3 customElements.define should retrieve lifecycle callbacks 4 https://bugs.webkit.org/show_bug.cgi?id=160797 5 6 Reviewed by Chris Dumez. 7 8 Updated JSCustomElementInterface to invoke Get(constructor, "prototype") and Get(prototype, callbackName) 9 for each lifecycle callback as required by the latest specification: 10 https://html.spec.whatwg.org/#dom-customelementsregistry-define 11 12 Also added the support for "observedAttributes" property on the custom elements constructor which defines 13 the list of attributes for which attributeChangedCallback is invoked. 14 15 Test: fast/custom-elements/CustomElementsRegistry.html 16 17 * bindings/js/JSCustomElementInterface.cpp: 18 (WebCore::JSCustomElementInterface::setAttributeChangedCallback): Added. 19 (WebCore::JSCustomElementInterface::attributeChanged): Invoke m_attributeChangedCallback instead of on the 20 result of Get(element, "attributeChangedCallback"). 21 * bindings/js/JSCustomElementInterface.h: 22 (WebCore::JSCustomElementInterface::observesAttribute): Added. 23 24 * bindings/js/JSCustomElementsRegistryCustom.cpp: 25 (WebCore::getLifecycleCallback): Added. 26 (WebCore::JSCustomElementsRegistry::define): Invoke Get(prototype, callbackName) for each callback. Also 27 store attributedChangedCallback and observedAttributes to JSCustomElementInterface. Other callbacks will 28 be stored in the future when the support for those callbacks are added. 29 30 * dom/Element.cpp: 31 (WebCore::Element::attributeChanged): Moved more code into enqueueAttributeChangedCallbackIfNeeded. 32 33 * dom/LifecycleCallbackQueue.cpp: 34 (WebCore::LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded): Added an early exit for when 35 the given attribute is not observed by the custom element. Also moved the logic to retrieve 36 JSCustomElementInterface from Element::attributeChanged and renamed it from enqueueAttributeChangedCallback. 37 38 * bindings/js/JSDOMBinding.h: 39 (WebCore::toNativeArray): Throw a TypeError when the argument is not an object. 40 * bindings/js/JSDOMConvert.h: 41 (WebCore::Converter<Vector<T>>::convert): Removed a FIXME comment. 42 1 43 2016-08-16 Anders Carlsson <andersca@apple.com> 2 44 -
trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp
r200895 r204540 152 152 } 153 153 154 void JSCustomElementInterface::setAttributeChangedCallback(JSC::JSObject* callback, const Vector<String>& observedAttributes) 155 { 156 m_attributeChangedCallback = callback; 157 m_observedAttributes.clear(); 158 for (auto& name : observedAttributes) 159 m_observedAttributes.add(name); 160 } 161 154 162 void JSCustomElementInterface::attributeChanged(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 155 163 { … … 171 179 JSObject* jsElement = asObject(toJS(state, globalObject, element)); 172 180 173 PropertyName attributeChanged(Identifier::fromString(state, "attributeChangedCallback"));174 JSValue callback = jsElement->get(state, attributeChanged);175 181 CallData callData; 176 CallType callType = getCallData(callback, callData); 177 if (callType == CallType::None) 178 return; 182 CallType callType = m_attributeChangedCallback->methodTable()->getCallData(m_attributeChangedCallback.get(), callData); 183 ASSERT(callType != CallType::None); 179 184 180 185 const AtomicString& namespaceURI = attributeName.namespaceURI(); … … 188 193 189 194 NakedPtr<Exception> exception; 190 JSMainThreadExecState::call(state, callback, callType, callData, jsElement, args, exception);195 JSMainThreadExecState::call(state, m_attributeChangedCallback.get(), callType, callData, jsElement, args, exception); 191 196 192 197 InspectorInstrumentation::didCallFunction(cookie, context); -
trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h
r197634 r204540 38 38 #include <wtf/RefCounted.h> 39 39 #include <wtf/RefPtr.h> 40 #include <wtf/text/AtomicStringHash.h> 40 41 41 42 namespace JSC { … … 66 67 void upgradeElement(Element&); 67 68 69 void setAttributeChangedCallback(JSC::JSObject* callback, const Vector<String>& observedAttributes); 70 bool observesAttribute(const AtomicString& name) const { return m_observedAttributes.contains(name); } 68 71 void attributeChanged(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); 69 72 … … 84 87 QualifiedName m_name; 85 88 mutable JSC::Weak<JSC::JSObject> m_constructor; 89 mutable JSC::Weak<JSC::JSObject> m_attributeChangedCallback; 86 90 RefPtr<DOMWrapperWorld> m_isolatedWorld; 87 91 Vector<RefPtr<Element>, 1> m_constructionStack; 92 HashSet<AtomicString> m_observedAttributes; 88 93 }; 89 94 -
trunk/Source/WebCore/bindings/js/JSCustomElementsRegistryCustom.cpp
r204367 r204540 32 32 #include "JSCustomElementInterface.h" 33 33 #include "JSDOMBinding.h" 34 #include "JSDOMConvert.h" 34 35 35 36 using namespace JSC; … … 37 38 namespace WebCore { 38 39 39 40 40 #if ENABLE(CUSTOM_ELEMENTS) 41 42 static JSObject* getLifecycleCallback(ExecState& state, JSObject& prototype, const Identifier& id) 43 { 44 JSValue callback = prototype.get(&state, id); 45 if (state.hadException()) 46 return nullptr; 47 if (callback.isUndefined()) 48 return nullptr; 49 if (!callback.isFunction()) { 50 throwTypeError(&state, ASCIILiteral("A lifecycle callback must be a function")); 51 return nullptr; 52 } 53 return callback.getObject(); 54 } 55 56 // https://html.spec.whatwg.org/#dom-customelementsregistry-define 41 57 JSValue JSCustomElementsRegistry::define(ExecState& state) 42 58 { … … 54 70 55 71 // FIXME: Throw a TypeError if constructor doesn't inherit from HTMLElement. 72 // https://github.com/w3c/webcomponents/issues/541 56 73 57 74 switch (Document::validateCustomElementName(localName)) { … … 66 83 } 67 84 85 // FIXME: Check re-entrancy here. 86 // https://github.com/w3c/webcomponents/issues/545 87 68 88 CustomElementsRegistry& registry = wrapped(); 69 89 if (registry.findInterface(localName)) { … … 77 97 } 78 98 79 // FIXME: 10. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions. 80 // FIXME: 11. If Type(prototype) is not Object, throw a TypeError exception. 81 // FIXME: 12. Let attachedCallback be Get(prototype, "attachedCallback"). Rethrow any exceptions. 82 // FIXME: 13. Let detachedCallback be Get(prototype, "detachedCallback"). Rethrow any exceptions. 83 // FIXME: 14. Let attributeChangedCallback be Get(prototype, "attributeChangedCallback"). Rethrow any exceptions. 99 auto& vm = globalObject()->vm(); 100 JSValue prototypeValue = constructor->get(&state, vm.propertyNames->prototype); 101 if (state.hadException()) 102 return jsUndefined(); 103 if (!prototypeValue.isObject()) 104 return throwTypeError(&state, ASCIILiteral("Custom element constructor's prototype must be an object")); 105 JSObject& prototypeObject = *asObject(prototypeValue); 106 107 // FIXME: Add the support for connectedCallback. 108 getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback")); 109 if (state.hadException()) 110 return jsUndefined(); 111 112 // FIXME: Add the support for disconnectedCallback. 113 getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback")); 114 if (state.hadException()) 115 return jsUndefined(); 116 117 // FIXME: Add the support for adoptedCallback. 118 getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "adoptedCallback")); 119 if (state.hadException()) 120 return jsUndefined(); 121 122 QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI); 123 auto interface = JSCustomElementInterface::create(name, constructor, globalObject()); 124 125 auto* attributeChangedCallback = getLifecycleCallback(state, prototypeObject, Identifier::fromString(&vm, "attributeChangedCallback")); 126 if (state.hadException()) 127 return jsUndefined(); 128 if (attributeChangedCallback) { 129 auto value = convertOptional<Vector<String>>(state, constructor->get(&state, Identifier::fromString(&state, "observedAttributes"))); 130 if (state.hadException()) 131 return jsUndefined(); 132 if (value) 133 interface->setAttributeChangedCallback(attributeChangedCallback, *value); 134 } 84 135 85 136 PrivateName uniquePrivateName; 86 globalObject()->putDirect( globalObject()->vm(), uniquePrivateName, constructor);137 globalObject()->putDirect(vm, uniquePrivateName, constructor); 87 138 88 QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI); 89 registry.addElementDefinition(JSCustomElementInterface::create(name, constructor, globalObject())); 139 registry.addElementDefinition(WTFMove(interface)); 90 140 91 141 // FIXME: 17. Let map be registry's upgrade candidates map. 92 142 // FIXME: 18. Upgrade a newly-defined element given map and definition. 143 // FIXME: 19. Resolve whenDefined promise. 93 144 94 145 return jsUndefined(); -
trunk/Source/WebCore/dom/Element.cpp
r204466 r204540 1291 1291 1292 1292 #if ENABLE(CUSTOM_ELEMENTS) 1293 if (UNLIKELY(isCustomElement())) { 1294 if (auto* window = document().domWindow()) { 1295 if (auto* registry = window->customElementsRegistry()) { 1296 auto* elementInterface = registry->findInterface(tagQName()); 1297 RELEASE_ASSERT(elementInterface); 1298 LifecycleCallbackQueue::enqueueAttributeChangedCallback(*this, *elementInterface, name, oldValue, newValue); 1299 } 1300 } 1301 } 1293 if (UNLIKELY(isCustomElement())) 1294 LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded(*this, name, oldValue, newValue); 1302 1295 #endif 1303 1296 -
trunk/Source/WebCore/dom/LifecycleCallbackQueue.cpp
r202559 r204540 29 29 #if ENABLE(CUSTOM_ELEMENTS) 30 30 31 #include "CustomElementsRegistry.h" 32 #include "DOMWindow.h" 31 33 #include "Document.h" 32 34 #include "Element.h" … … 97 99 } 98 100 99 void LifecycleCallbackQueue::enqueueAttributeChangedCallback(Element& element, JSCustomElementInterface& elementInterface, 100 const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 101 void LifecycleCallbackQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) 101 102 { 103 ASSERT(element.isCustomElement()); 104 auto* window = element.document().domWindow(); 105 if (!window) 106 return; 107 108 auto* registry = window->customElementsRegistry(); 109 if (!registry) 110 return; 111 112 auto* elementInterface = registry->findInterface(element.tagQName()); 113 if (!elementInterface->observesAttribute(attributeName.localName())) 114 return; 115 102 116 if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue()) 103 queue->m_items.append(LifecycleQueueItem(element, elementInterface, attributeName, oldValue, newValue));117 queue->m_items.append(LifecycleQueueItem(element, *elementInterface, attributeName, oldValue, newValue)); 104 118 } 105 119 -
trunk/Source/WebCore/dom/LifecycleCallbackQueue.h
r197634 r204540 24 24 */ 25 25 26 #ifndef LifecycleCallbackQueue_h 27 #define LifecycleCallbackQueue_h 26 #pragma once 28 27 29 28 #if ENABLE(CUSTOM_ELEMENTS) … … 48 47 49 48 static void enqueueElementUpgrade(Element&, JSCustomElementInterface&); 50 51 static void enqueueAttributeChangedCallback(Element&, JSCustomElementInterface&, 52 const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); 49 static void enqueueAttributeChangedCallbackIfNeeded(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); 53 50 54 51 void invokeAll(); … … 90 87 91 88 #endif 92 93 #endif // LifecycleCallbackQueue_h
Note: See TracChangeset
for help on using the changeset viewer.