Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "bindings/core/v8/ScriptCustomElementDefinition.h" | 5 #include "bindings/core/v8/ScriptCustomElementDefinition.h" |
| 6 | 6 |
| 7 #include "bindings/core/v8/ScriptState.h" | 7 #include "bindings/core/v8/ScriptState.h" |
| 8 #include "bindings/core/v8/V8Binding.h" | 8 #include "bindings/core/v8/V8Binding.h" |
| 9 #include "bindings/core/v8/V8BindingMacros.h" | 9 #include "bindings/core/v8/V8BindingMacros.h" |
| 10 #include "bindings/core/v8/V8CustomElementsRegistry.h" | 10 #include "bindings/core/v8/V8CustomElementsRegistry.h" |
| 11 #include "bindings/core/v8/V8Element.h" | 11 #include "bindings/core/v8/V8Element.h" |
| 12 #include "bindings/core/v8/V8HiddenValue.h" | 12 #include "bindings/core/v8/V8HiddenValue.h" |
| 13 #include "bindings/core/v8/V8ScriptRunner.h" | 13 #include "bindings/core/v8/V8ScriptRunner.h" |
| 14 #include "bindings/core/v8/V8ThrowException.h" | 14 #include "bindings/core/v8/V8ThrowException.h" |
| 15 #include "core/dom/ExceptionCode.h" | 15 #include "core/dom/ExceptionCode.h" |
| 16 #include "core/html/HTMLElement.h" | |
| 17 #include "core/html/HTMLUnknownElement.h" | |
| 16 #include "v8.h" | 18 #include "v8.h" |
| 17 #include "wtf/Allocator.h" | 19 #include "wtf/Allocator.h" |
| 18 | 20 |
| 19 namespace blink { | 21 namespace blink { |
| 20 | 22 |
| 21 // Retrieves the custom elements constructor -> name map, creating it | 23 // Retrieves the custom elements constructor -> name map, creating it |
| 22 // if necessary. The same map is used to keep prototypes alive. | 24 // if necessary. The same map is used to keep prototypes alive. |
| 23 static v8::Local<v8::Map> ensureCustomElementsRegistryMap( | 25 static v8::Local<v8::Map> ensureCustomElementsRegistryMap( |
| 24 ScriptState* scriptState, | 26 ScriptState* scriptState, |
| 25 CustomElementsRegistry* registry) | 27 CustomElementsRegistry* registry) |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 145 const v8::Local<v8::Object>& disconnectedCallback, | 147 const v8::Local<v8::Object>& disconnectedCallback, |
| 146 const v8::Local<v8::Object>& attributeChangedCallback, | 148 const v8::Local<v8::Object>& attributeChangedCallback, |
| 147 const HashSet<AtomicString>& observedAttributes) | 149 const HashSet<AtomicString>& observedAttributes) |
| 148 : CustomElementDefinition(descriptor) | 150 : CustomElementDefinition(descriptor) |
| 149 , m_scriptState(scriptState) | 151 , m_scriptState(scriptState) |
| 150 , m_constructor(scriptState->isolate(), constructor) | 152 , m_constructor(scriptState->isolate(), constructor) |
| 151 , m_observedAttributes(observedAttributes) | 153 , m_observedAttributes(observedAttributes) |
| 152 { | 154 { |
| 153 } | 155 } |
| 154 | 156 |
| 157 static String errorForConstructorResult(Element* element, | |
| 158 Document& document, const QualifiedName& tagName) | |
| 159 { | |
| 160 // https://dom.spec.whatwg.org/#concept-create-element | |
| 161 // 6.1.4. If result's attribute list is not empty, then throw a NotSupported Error. | |
| 162 if (element->hasAttributes()) | |
| 163 return "The result must not have attributes"; | |
| 164 // 6.1.5. If result has children, then throw a NotSupportedError. | |
| 165 if (element->hasChildren()) | |
| 166 return "The result must not have children"; | |
| 167 // 6.1.6. If result's parent is not null, then throw a NotSupportedError. | |
| 168 if (element->parentNode()) | |
| 169 return "The result must not have a parent"; | |
| 170 // 6.1.7. If result's node document is not document, then throw a NotSupport edError. | |
| 171 if (&element->document() != &document) | |
| 172 return "The result must be in the same document"; | |
| 173 // 6.1.8. If result's namespace is not the HTML namespace, then throw a NotS upportedError. | |
| 174 if (element->namespaceURI() != HTMLNames::xhtmlNamespaceURI) | |
| 175 return "The result must have HTML namespace"; | |
| 176 // 6.1.9. If result's local name is not equal to localName, then throw a Not SupportedError. | |
| 177 if (element->localName() != tagName.localName()) | |
| 178 return "The result must have the same localName"; | |
| 179 return String(); | |
| 180 } | |
| 181 | |
| 182 HTMLElement* ScriptCustomElementDefinition::createCustomElement( | |
| 183 Document& document, const QualifiedName& tagName) | |
| 184 { | |
| 185 // Create an element | |
| 186 // https://dom.spec.whatwg.org/#concept-create-element | |
| 187 // 6. If definition is non-null | |
| 188 // 6.1. If the synchronous custom elements flag is set: | |
| 189 // 6.1.2. Set result to Construct(C). Rethrow any exceptions. | |
| 190 Element* element; | |
| 191 { | |
| 192 v8::TryCatch tryCatch(m_scriptState->isolate()); | |
| 193 element = runConstructor(); | |
| 194 if (tryCatch.HasCaught()) { | |
| 195 tryCatch.ReThrow(); | |
| 196 return nullptr; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 // 6.1.3. If result does not implement the HTMLElement interface, throw a Ty peError. | |
|
dominicc (has gone to gerrit)
2016/06/09 06:39:22
This might be a helpful reference:
https://cs.chr
kojii
2016/06/09 16:54:21
Ah, thanks for the pointer, this looks good.
It l
| |
| 201 // TODO(kojii): how to check interface? Is this correct way to do this? This | |
| 202 // can check HTMLXxxElement classes, but not duck-typing. | |
| 203 // https://github.com/whatwg/html/issues/1402 | |
| 204 if (!element || !element->isHTMLElement()) { | |
| 205 throwTypeError("The result must implement HTMLElement interface"); | |
|
dominicc (has gone to gerrit)
2016/06/09 06:39:22
Might not be worth adding a helper for one line? B
kojii
2016/06/09 16:54:21
Removed helpers as we switched to ExceptionState.
| |
| 206 return nullptr; | |
| 207 } | |
| 208 | |
| 209 // 6.1.4. through 6.1.9. | |
|
dominicc (has gone to gerrit)
2016/06/09 06:39:23
Could we make this a "template method pattern" (no
kojii
2016/06/09 16:54:21
Done.
| |
| 210 const String message = errorForConstructorResult(element, document, tagName) ; | |
| 211 if (!message.isEmpty()) { | |
| 212 throwDOMException(NotSupportedError, message); | |
| 213 return nullptr; | |
| 214 } | |
| 215 | |
| 216 DCHECK_EQ(element->getCustomElementState(), CustomElementState::Custom); | |
| 217 return toHTMLElement(element); | |
| 218 } | |
| 219 | |
| 220 HTMLElement* ScriptCustomElementDefinition::createCustomElementIgnoringErrors( | |
| 221 Document& document, const QualifiedName& tagName) | |
| 222 { | |
| 223 // When invoked from "create an element for a token": | |
| 224 // https://html.spec.whatwg.org/multipage/syntax.html#create-an-element-for- the-token | |
| 225 // 7. If this step throws an exception, then report the exception, and | |
| 226 // let element be instead a new element that implements HTMLUnknownElement, | |
| 227 // with no attributes, namespace set to given namespace, namespace prefix | |
| 228 // set to null, custom element state "undefined", and node document set to | |
| 229 // document. | |
| 230 v8::TryCatch tryCatch(m_scriptState->isolate()); | |
| 231 tryCatch.SetVerbose(true); | |
| 232 | |
| 233 HTMLElement* element = createCustomElement(document, tagName); | |
| 234 if (element && !tryCatch.HasCaught()) | |
| 235 return element; | |
| 236 | |
| 237 element = HTMLUnknownElement::create(tagName, document); | |
| 238 element->setCustomElementState(CustomElementState::Undefined); | |
| 239 return element; | |
| 240 } | |
| 241 | |
| 155 // https://html.spec.whatwg.org/multipage/scripting.html#upgrades | 242 // https://html.spec.whatwg.org/multipage/scripting.html#upgrades |
| 156 bool ScriptCustomElementDefinition::runConstructor(Element* element) | 243 bool ScriptCustomElementDefinition::runConstructor(Element* element) |
| 157 { | 244 { |
| 158 if (!m_scriptState->contextIsValid()) | 245 if (!m_scriptState->contextIsValid()) |
| 159 return false; | 246 return false; |
| 160 ScriptState::Scope scope(m_scriptState.get()); | |
| 161 v8::Isolate* isolate = m_scriptState->isolate(); | 247 v8::Isolate* isolate = m_scriptState->isolate(); |
| 162 | 248 |
| 163 // Step 5 says to rethrow the exception; but there is no one to | 249 // Step 5 says to rethrow the exception; but there is no one to |
| 164 // catch it. The side effect is to report the error. | 250 // catch it. The side effect is to report the error. |
| 165 v8::TryCatch tryCatch(isolate); | 251 v8::TryCatch tryCatch(isolate); |
| 166 tryCatch.SetVerbose(true); | 252 tryCatch.SetVerbose(true); |
| 167 | 253 |
| 254 Element* result = runConstructor(); | |
| 255 if (!result) | |
| 256 return false; | |
| 257 | |
| 258 if (result != element) { | |
| 259 throwDOMException( | |
| 260 InvalidStateError, | |
| 261 "custom element constructors must call super() first and must " | |
| 262 "not return a different object"); | |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 return true; | |
| 267 } | |
| 268 | |
| 269 Element* ScriptCustomElementDefinition::runConstructor() | |
| 270 { | |
| 271 if (!m_scriptState->contextIsValid()) | |
| 272 return nullptr; | |
| 273 ScriptState::Scope scope(m_scriptState.get()); | |
| 274 v8::Isolate* isolate = m_scriptState->isolate(); | |
| 168 ExecutionContext* executionContext = m_scriptState->getExecutionContext(); | 275 ExecutionContext* executionContext = m_scriptState->getExecutionContext(); |
| 169 v8::Local<v8::Value> result; | 276 v8::Local<v8::Value> result; |
| 170 if (!v8Call(V8ScriptRunner::callAsConstructor( | 277 if (!v8Call(V8ScriptRunner::callAsConstructor( |
| 171 isolate, | 278 isolate, |
| 172 constructor(), | 279 constructor(), |
| 173 executionContext, | 280 executionContext, |
| 174 0, | 281 0, |
| 175 nullptr), | 282 nullptr), |
| 176 result)) | 283 result)) { |
| 177 return false; | 284 return nullptr; |
| 178 | |
| 179 if (V8Element::toImplWithTypeCheck(isolate, result) != element) { | |
| 180 V8ThrowException::throwException( | |
| 181 V8ThrowException::createDOMException( | |
| 182 m_scriptState->isolate(), | |
| 183 InvalidStateError, | |
| 184 "custom element constructors must call super() first and must " | |
| 185 "not return a different object", | |
| 186 constructor()), | |
| 187 m_scriptState->isolate()); | |
| 188 return false; | |
| 189 } | 285 } |
| 190 | 286 return V8Element::toImplWithTypeCheck(isolate, result); |
| 191 return true; | |
| 192 } | 287 } |
| 193 | 288 |
| 194 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor() const | 289 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor() const |
| 195 { | 290 { |
| 196 DCHECK(!m_constructor.isEmpty()); | 291 DCHECK(!m_constructor.isEmpty()); |
| 197 return m_constructor.newLocal(m_scriptState->isolate()); | 292 return m_constructor.newLocal(m_scriptState->isolate()); |
| 198 } | 293 } |
| 199 | 294 |
| 200 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype() const | 295 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype() const |
| 201 { | 296 { |
| 202 DCHECK(!m_prototype.isEmpty()); | 297 DCHECK(!m_prototype.isEmpty()); |
| 203 return m_prototype.newLocal(m_scriptState->isolate()); | 298 return m_prototype.newLocal(m_scriptState->isolate()); |
| 204 } | 299 } |
| 205 | 300 |
| 206 // CustomElementDefinition | 301 // CustomElementDefinition |
| 207 ScriptValue ScriptCustomElementDefinition::getConstructorForScript() | 302 ScriptValue ScriptCustomElementDefinition::getConstructorForScript() |
| 208 { | 303 { |
| 209 return ScriptValue(m_scriptState.get(), constructor()); | 304 return ScriptValue(m_scriptState.get(), constructor()); |
| 210 } | 305 } |
| 211 | 306 |
| 307 void ScriptCustomElementDefinition::throwDOMException(int exceptionCode, const S tring& message) | |
| 308 { | |
| 309 V8ThrowException::throwException( | |
| 310 V8ThrowException::createDOMException( | |
| 311 m_scriptState->isolate(), | |
| 312 exceptionCode, | |
| 313 message, | |
| 314 constructor()), | |
| 315 m_scriptState->isolate()); | |
| 316 } | |
| 317 | |
| 318 void ScriptCustomElementDefinition::throwTypeError(const String& message) | |
| 319 { | |
| 320 V8ThrowException::throwTypeError(m_scriptState->isolate(), message); | |
| 321 } | |
| 322 | |
| 212 } // namespace blink | 323 } // namespace blink |
| OLD | NEW |