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/V8ErrorHandler.h" | |
| 12 #include "bindings/core/v8/V8HiddenValue.h" | 13 #include "bindings/core/v8/V8HiddenValue.h" |
| 13 #include "bindings/core/v8/V8ScriptRunner.h" | 14 #include "bindings/core/v8/V8ScriptRunner.h" |
| 14 #include "bindings/core/v8/V8ThrowException.h" | 15 #include "bindings/core/v8/V8ThrowException.h" |
| 15 #include "core/dom/ExceptionCode.h" | 16 #include "core/dom/ExceptionCode.h" |
| 16 #include "core/dom/custom/CustomElement.h" | 17 #include "core/dom/custom/CustomElement.h" |
| 18 #include "core/events/ErrorEvent.h" | |
| 17 #include "core/html/HTMLElement.h" | 19 #include "core/html/HTMLElement.h" |
| 18 #include "v8.h" | 20 #include "v8.h" |
| 19 #include "wtf/Allocator.h" | 21 #include "wtf/Allocator.h" |
| 20 | 22 |
| 21 namespace blink { | 23 namespace blink { |
| 22 | 24 |
| 23 // Retrieves the custom elements constructor -> name map, creating it | 25 // Retrieves the custom elements constructor -> name map, creating it |
| 24 // if necessary. The same map is used to keep prototypes alive. | 26 // if necessary. The same map is used to keep prototypes alive. |
| 25 static v8::Local<v8::Map> ensureCustomElementsRegistryMap( | 27 static v8::Local<v8::Map> ensureCustomElementsRegistryMap( |
| 26 ScriptState* scriptState, | 28 ScriptState* scriptState, |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 } | 100 } |
| 99 | 101 |
| 100 ScriptCustomElementDefinition* ScriptCustomElementDefinition::create( | 102 ScriptCustomElementDefinition* ScriptCustomElementDefinition::create( |
| 101 ScriptState* scriptState, | 103 ScriptState* scriptState, |
| 102 CustomElementsRegistry* registry, | 104 CustomElementsRegistry* registry, |
| 103 const CustomElementDescriptor& descriptor, | 105 const CustomElementDescriptor& descriptor, |
| 104 const v8::Local<v8::Object>& constructor, | 106 const v8::Local<v8::Object>& constructor, |
| 105 const v8::Local<v8::Object>& prototype, | 107 const v8::Local<v8::Object>& prototype, |
| 106 const v8::Local<v8::Function>& connectedCallback, | 108 const v8::Local<v8::Function>& connectedCallback, |
| 107 const v8::Local<v8::Function>& disconnectedCallback, | 109 const v8::Local<v8::Function>& disconnectedCallback, |
| 110 const v8::Local<v8::Function>& adoptedCallback, | |
| 108 const v8::Local<v8::Function>& attributeChangedCallback, | 111 const v8::Local<v8::Function>& attributeChangedCallback, |
| 109 const HashSet<AtomicString>& observedAttributes) | 112 const HashSet<AtomicString>& observedAttributes) |
| 110 { | 113 { |
| 111 ScriptCustomElementDefinition* definition = | 114 ScriptCustomElementDefinition* definition = |
| 112 new ScriptCustomElementDefinition( | 115 new ScriptCustomElementDefinition( |
| 113 scriptState, | 116 scriptState, |
| 114 descriptor, | 117 descriptor, |
| 115 constructor, | 118 constructor, |
| 116 prototype, | 119 prototype, |
| 117 connectedCallback, | 120 connectedCallback, |
| 118 disconnectedCallback, | 121 disconnectedCallback, |
| 122 adoptedCallback, | |
| 119 attributeChangedCallback, | 123 attributeChangedCallback, |
| 120 observedAttributes); | 124 observedAttributes); |
| 121 | 125 |
| 122 // Add a constructor -> name mapping to the registry. | 126 // Add a constructor -> name mapping to the registry. |
| 123 v8::Local<v8::Value> nameValue = | 127 v8::Local<v8::Value> nameValue = |
| 124 v8String(scriptState->isolate(), descriptor.name()); | 128 v8String(scriptState->isolate(), descriptor.name()); |
| 125 v8::Local<v8::Map> map = | 129 v8::Local<v8::Map> map = |
| 126 ensureCustomElementsRegistryMap(scriptState, registry); | 130 ensureCustomElementsRegistryMap(scriptState, registry); |
| 127 v8CallOrCrash(map->Set(scriptState->context(), constructor, nameValue)); | 131 v8CallOrCrash(map->Set(scriptState->context(), constructor, nameValue)); |
| 128 definition->m_constructor.setPhantom(); | 132 definition->m_constructor.setPhantom(); |
| 129 | 133 |
| 130 // We add the prototype and callbacks here to keep them alive. We use the | 134 // We add the prototype and callbacks here to keep them alive. We use the |
| 131 // name as the key because it is unique per-registry. | 135 // name as the key because it is unique per-registry. |
| 132 v8::Local<v8::Array> array = v8::Array::New(scriptState->isolate(), 4); | 136 v8::Local<v8::Array> array = v8::Array::New(scriptState->isolate(), 5); |
| 133 keepAlive(array, 0, prototype, definition->m_prototype, scriptState); | 137 keepAlive(array, 0, prototype, definition->m_prototype, scriptState); |
| 134 keepAlive(array, 1, connectedCallback, definition->m_connectedCallback, scri ptState); | 138 keepAlive(array, 1, connectedCallback, definition->m_connectedCallback, scri ptState); |
| 135 keepAlive(array, 2, disconnectedCallback, definition->m_disconnectedCallback , scriptState); | 139 keepAlive(array, 2, disconnectedCallback, definition->m_disconnectedCallback , scriptState); |
| 136 keepAlive(array, 3, attributeChangedCallback, definition->m_attributeChanged Callback, scriptState); | 140 keepAlive(array, 3, attributeChangedCallback, definition->m_attributeChanged Callback, scriptState); |
| 141 keepAlive(array, 4, adoptedCallback, definition->m_adoptedCallback, scriptSt ate); | |
| 137 v8CallOrCrash(map->Set(scriptState->context(), nameValue, array)); | 142 v8CallOrCrash(map->Set(scriptState->context(), nameValue, array)); |
| 138 | 143 |
| 139 return definition; | 144 return definition; |
| 140 } | 145 } |
| 141 | 146 |
| 142 ScriptCustomElementDefinition::ScriptCustomElementDefinition( | 147 ScriptCustomElementDefinition::ScriptCustomElementDefinition( |
| 143 ScriptState* scriptState, | 148 ScriptState* scriptState, |
| 144 const CustomElementDescriptor& descriptor, | 149 const CustomElementDescriptor& descriptor, |
| 145 const v8::Local<v8::Object>& constructor, | 150 const v8::Local<v8::Object>& constructor, |
| 146 const v8::Local<v8::Object>& prototype, | 151 const v8::Local<v8::Object>& prototype, |
| 147 const v8::Local<v8::Function>& connectedCallback, | 152 const v8::Local<v8::Function>& connectedCallback, |
| 148 const v8::Local<v8::Function>& disconnectedCallback, | 153 const v8::Local<v8::Function>& disconnectedCallback, |
| 154 const v8::Local<v8::Function>& adoptedCallback, | |
| 149 const v8::Local<v8::Function>& attributeChangedCallback, | 155 const v8::Local<v8::Function>& attributeChangedCallback, |
| 150 const HashSet<AtomicString>& observedAttributes) | 156 const HashSet<AtomicString>& observedAttributes) |
| 151 : CustomElementDefinition(descriptor, observedAttributes) | 157 : CustomElementDefinition(descriptor, observedAttributes) |
| 152 , m_scriptState(scriptState) | 158 , m_scriptState(scriptState) |
| 153 , m_constructor(scriptState->isolate(), constructor) | 159 , m_constructor(scriptState->isolate(), constructor) |
| 154 { | 160 { |
| 155 } | 161 } |
| 156 | 162 |
| 157 HTMLElement* ScriptCustomElementDefinition::createElementSync( | 163 HTMLElement* ScriptCustomElementDefinition::createElementSync( |
| 158 Document& document, const QualifiedName& tagName, | 164 Document& document, const QualifiedName& tagName, |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 return false; | 223 return false; |
| 218 ScriptState::Scope scope(m_scriptState.get()); | 224 ScriptState::Scope scope(m_scriptState.get()); |
| 219 v8::Isolate* isolate = m_scriptState->isolate(); | 225 v8::Isolate* isolate = m_scriptState->isolate(); |
| 220 | 226 |
| 221 // Step 5 says to rethrow the exception; but there is no one to | 227 // Step 5 says to rethrow the exception; but there is no one to |
| 222 // catch it. The side effect is to report the error. | 228 // catch it. The side effect is to report the error. |
| 223 v8::TryCatch tryCatch(isolate); | 229 v8::TryCatch tryCatch(isolate); |
| 224 tryCatch.SetVerbose(true); | 230 tryCatch.SetVerbose(true); |
| 225 | 231 |
| 226 Element* result = runConstructor(); | 232 Element* result = runConstructor(); |
| 227 if (!result) | 233 |
| 234 // To report exception thrown from runConstructor() | |
| 235 if (tryCatch.HasCaught()) | |
| 228 return false; | 236 return false; |
| 229 | 237 |
| 238 // To report InvalidStateError Exception, when the constructor returns some differnt object | |
|
dominicc (has gone to gerrit)
2016/08/02 07:48:57
Spelling: different
| |
| 230 if (result != element) { | 239 if (result != element) { |
| 231 V8ThrowException::throwException( | 240 const String& message = "custom element constructors must call super() f irst and must " |
| 232 V8ThrowException::createDOMException( | 241 "not return a different object"; |
| 233 m_scriptState->isolate(), | 242 |
|
dominicc (has gone to gerrit)
2016/08/02 07:48:57
Maybe delete this blank line; since the message an
| |
| 234 InvalidStateError, | 243 std::unique_ptr<SourceLocation> location = SourceLocation::fromFunction( constructor().As<v8::Function>()); |
| 235 "custom element constructors must call super() first and must " | 244 v8::Local<v8::Value> exception = V8ThrowException::createDOMException( |
| 236 "not return a different object", | 245 m_scriptState->isolate(), |
| 237 constructor()), | 246 InvalidStateError, |
| 238 m_scriptState->isolate()); | 247 message, |
| 248 constructor()); | |
| 249 fireErrorEvent(m_scriptState.get(), message, exception, std::move(locati on)); | |
| 239 return false; | 250 return false; |
| 240 } | 251 } |
| 241 | 252 |
| 242 return true; | 253 return true; |
| 243 } | 254 } |
| 244 | 255 |
| 245 Element* ScriptCustomElementDefinition::runConstructor() | 256 Element* ScriptCustomElementDefinition::runConstructor() |
| 246 { | 257 { |
| 247 v8::Isolate* isolate = m_scriptState->isolate(); | 258 v8::Isolate* isolate = m_scriptState->isolate(); |
| 248 DCHECK(ScriptState::current(isolate) == m_scriptState); | 259 DCHECK(ScriptState::current(isolate) == m_scriptState); |
| 249 ExecutionContext* executionContext = m_scriptState->getExecutionContext(); | 260 ExecutionContext* executionContext = m_scriptState->getExecutionContext(); |
| 250 v8::Local<v8::Value> result; | 261 v8::Local<v8::Value> result; |
| 251 if (!v8Call(V8ScriptRunner::callAsConstructor( | 262 if (!v8Call(V8ScriptRunner::callAsConstructor( |
| 252 isolate, | 263 isolate, |
| 253 constructor(), | 264 constructor(), |
| 254 executionContext, | 265 executionContext, |
| 255 0, | 266 0, |
| 256 nullptr), | 267 nullptr), |
| 257 result)) { | 268 result)) { |
| 258 return nullptr; | 269 return nullptr; |
| 259 } | 270 } |
| 260 return V8Element::toImplWithTypeCheck(isolate, result); | 271 return V8Element::toImplWithTypeCheck(isolate, result); |
| 261 } | 272 } |
| 262 | 273 |
| 274 void ScriptCustomElementDefinition::fireErrorEvent(ScriptState* scriptState, con st String& message, v8::Local<v8::Value> exception, std::unique_ptr<SourceLocati on> location) | |
| 275 { | |
| 276 ErrorEvent* event = ErrorEvent::create(message, std::move(location), &script State->world()); | |
| 277 V8ErrorHandler::storeExceptionOnErrorEventWrapper(scriptState, event, except ion, scriptState->context()->Global()); | |
| 278 ExecutionContext* executionContext = scriptState->getExecutionContext(); | |
| 279 executionContext->reportException(event, NotSharableCrossOrigin); | |
| 280 } | |
| 281 | |
| 263 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor() const | 282 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor() const |
| 264 { | 283 { |
| 265 DCHECK(!m_constructor.isEmpty()); | 284 DCHECK(!m_constructor.isEmpty()); |
| 266 return m_constructor.newLocal(m_scriptState->isolate()); | 285 return m_constructor.newLocal(m_scriptState->isolate()); |
| 267 } | 286 } |
| 268 | 287 |
| 269 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype() const | 288 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype() const |
| 270 { | 289 { |
| 271 DCHECK(!m_prototype.isEmpty()); | 290 DCHECK(!m_prototype.isEmpty()); |
| 272 return m_prototype.newLocal(m_scriptState->isolate()); | 291 return m_prototype.newLocal(m_scriptState->isolate()); |
| 273 } | 292 } |
| 274 | 293 |
| 275 // CustomElementDefinition | 294 // CustomElementDefinition |
| 276 ScriptValue ScriptCustomElementDefinition::getConstructorForScript() | 295 ScriptValue ScriptCustomElementDefinition::getConstructorForScript() |
| 277 { | 296 { |
| 278 return ScriptValue(m_scriptState.get(), constructor()); | 297 return ScriptValue(m_scriptState.get(), constructor()); |
| 279 } | 298 } |
| 280 | 299 |
| 281 bool ScriptCustomElementDefinition::hasConnectedCallback() const | 300 bool ScriptCustomElementDefinition::hasConnectedCallback() const |
| 282 { | 301 { |
| 283 return !m_connectedCallback.isEmpty(); | 302 return !m_connectedCallback.isEmpty(); |
| 284 } | 303 } |
| 285 | 304 |
| 286 bool ScriptCustomElementDefinition::hasDisconnectedCallback() const | 305 bool ScriptCustomElementDefinition::hasDisconnectedCallback() const |
| 287 { | 306 { |
| 288 return !m_disconnectedCallback.isEmpty(); | 307 return !m_disconnectedCallback.isEmpty(); |
| 289 } | 308 } |
| 290 | 309 |
| 310 bool ScriptCustomElementDefinition::hasAdoptedCallback() const | |
| 311 { | |
| 312 return !m_adoptedCallback.isEmpty(); | |
| 313 } | |
| 314 | |
| 291 void ScriptCustomElementDefinition::runCallback( | 315 void ScriptCustomElementDefinition::runCallback( |
| 292 v8::Local<v8::Function> callback, | 316 v8::Local<v8::Function> callback, |
| 293 Element* element, int argc, v8::Local<v8::Value> argv[]) | 317 Element* element, int argc, v8::Local<v8::Value> argv[]) |
| 294 { | 318 { |
| 295 DCHECK(ScriptState::current(m_scriptState->isolate()) == m_scriptState); | 319 DCHECK(ScriptState::current(m_scriptState->isolate()) == m_scriptState); |
| 296 v8::Isolate* isolate = m_scriptState->isolate(); | 320 v8::Isolate* isolate = m_scriptState->isolate(); |
| 297 | 321 |
| 298 // Invoke custom element reactions | 322 // Invoke custom element reactions |
| 299 // https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-eleme nt-reactions | 323 // https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-eleme nt-reactions |
| 300 // If this throws any exception, then report the exception. | 324 // If this throws any exception, then report the exception. |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 322 | 346 |
| 323 void ScriptCustomElementDefinition::runDisconnectedCallback(Element* element) | 347 void ScriptCustomElementDefinition::runDisconnectedCallback(Element* element) |
| 324 { | 348 { |
| 325 if (!m_scriptState->contextIsValid()) | 349 if (!m_scriptState->contextIsValid()) |
| 326 return; | 350 return; |
| 327 ScriptState::Scope scope(m_scriptState.get()); | 351 ScriptState::Scope scope(m_scriptState.get()); |
| 328 v8::Isolate* isolate = m_scriptState->isolate(); | 352 v8::Isolate* isolate = m_scriptState->isolate(); |
| 329 runCallback(m_disconnectedCallback.newLocal(isolate), element); | 353 runCallback(m_disconnectedCallback.newLocal(isolate), element); |
| 330 } | 354 } |
| 331 | 355 |
| 356 void ScriptCustomElementDefinition::runAdoptedCallback(Element* element) | |
| 357 { | |
| 358 if (!m_scriptState->contextIsValid()) | |
| 359 return; | |
| 360 ScriptState::Scope scope(m_scriptState.get()); | |
| 361 v8::Isolate* isolate = m_scriptState->isolate(); | |
| 362 runCallback(m_adoptedCallback.newLocal(isolate), element); | |
| 363 } | |
| 364 | |
| 332 void ScriptCustomElementDefinition::runAttributeChangedCallback( | 365 void ScriptCustomElementDefinition::runAttributeChangedCallback( |
| 333 Element* element, const QualifiedName& name, | 366 Element* element, const QualifiedName& name, |
| 334 const AtomicString& oldValue, const AtomicString& newValue) | 367 const AtomicString& oldValue, const AtomicString& newValue) |
| 335 { | 368 { |
| 336 if (!m_scriptState->contextIsValid()) | 369 if (!m_scriptState->contextIsValid()) |
| 337 return; | 370 return; |
| 338 ScriptState::Scope scope(m_scriptState.get()); | 371 ScriptState::Scope scope(m_scriptState.get()); |
| 339 v8::Isolate* isolate = m_scriptState->isolate(); | 372 v8::Isolate* isolate = m_scriptState->isolate(); |
| 340 const int argc = 4; | 373 const int argc = 4; |
| 341 v8::Local<v8::Value> argv[argc] = { | 374 v8::Local<v8::Value> argv[argc] = { |
| 342 v8String(isolate, name.localName()), | 375 v8String(isolate, name.localName()), |
| 343 v8StringOrNull(isolate, oldValue), | 376 v8StringOrNull(isolate, oldValue), |
| 344 v8StringOrNull(isolate, newValue), | 377 v8StringOrNull(isolate, newValue), |
| 345 v8String(isolate, name.namespaceURI()), | 378 v8String(isolate, name.namespaceURI()), |
| 346 }; | 379 }; |
| 347 runCallback(m_attributeChangedCallback.newLocal(isolate), element, | 380 runCallback(m_attributeChangedCallback.newLocal(isolate), element, |
| 348 argc, argv); | 381 argc, argv); |
| 349 } | 382 } |
| 350 | 383 |
| 351 } // namespace blink | 384 } // namespace blink |
| OLD | NEW |