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/V8BindingForCore.h" | 8 #include "bindings/core/v8/V8BindingForCore.h" |
| 9 #include "bindings/core/v8/V8BindingMacros.h" | 9 #include "bindings/core/v8/V8BindingMacros.h" |
| 10 #include "bindings/core/v8/V8CustomElementRegistry.h" | |
| 11 #include "bindings/core/v8/V8Element.h" | 10 #include "bindings/core/v8/V8Element.h" |
| 12 #include "bindings/core/v8/V8ErrorHandler.h" | |
| 13 #include "bindings/core/v8/V8PrivateProperty.h" | |
| 14 #include "bindings/core/v8/V8ScriptRunner.h" | 11 #include "bindings/core/v8/V8ScriptRunner.h" |
| 15 #include "bindings/core/v8/V8ThrowDOMException.h" | 12 #include "bindings/core/v8/V8ThrowDOMException.h" |
| 16 #include "core/dom/ExceptionCode.h" | 13 #include "core/dom/ExceptionCode.h" |
| 17 #include "core/dom/ExecutionContext.h" | 14 #include "core/dom/ExecutionContext.h" |
| 18 #include "core/dom/custom/CustomElement.h" | 15 #include "core/dom/custom/CustomElement.h" |
| 16 #include "core/dom/custom/CustomElementRegistry.h" | |
| 19 #include "core/events/ErrorEvent.h" | 17 #include "core/events/ErrorEvent.h" |
| 20 #include "core/html/HTMLElement.h" | 18 #include "core/html/HTMLElement.h" |
| 21 #include "core/html/imports/HTMLImportsController.h" | 19 #include "core/html/imports/HTMLImportsController.h" |
| 22 #include "platform/wtf/Allocator.h" | 20 #include "platform/wtf/Allocator.h" |
| 23 #include "v8.h" | 21 #include "v8.h" |
| 24 | 22 |
| 25 namespace blink { | 23 namespace blink { |
| 26 | 24 |
| 27 // Retrieves the custom elements constructor -> name map, creating it | |
| 28 // if necessary. | |
| 29 static v8::Local<v8::Map> EnsureCustomElementRegistryMap( | |
| 30 ScriptState* script_state, | |
| 31 CustomElementRegistry* registry) { | |
| 32 CHECK(script_state->World().IsMainWorld()); | |
| 33 v8::Isolate* isolate = script_state->GetIsolate(); | |
| 34 | |
| 35 V8PrivateProperty::Symbol symbol = | |
| 36 V8PrivateProperty::GetCustomElementRegistryMap(isolate); | |
| 37 v8::Local<v8::Object> wrapper = ToV8(registry, script_state).As<v8::Object>(); | |
| 38 v8::Local<v8::Value> map = symbol.GetOrUndefined(wrapper); | |
| 39 if (map->IsUndefined()) { | |
| 40 map = v8::Map::New(isolate); | |
| 41 symbol.Set(wrapper, map); | |
| 42 } | |
| 43 return map.As<v8::Map>(); | |
| 44 } | |
| 45 | |
| 46 ScriptCustomElementDefinition* ScriptCustomElementDefinition::ForConstructor( | 25 ScriptCustomElementDefinition* ScriptCustomElementDefinition::ForConstructor( |
| 47 ScriptState* script_state, | 26 ScriptState* script_state, |
| 48 CustomElementRegistry* registry, | 27 CustomElementRegistry* registry, |
| 49 const v8::Local<v8::Value>& constructor) { | 28 const v8::Local<v8::Value>& constructor) { |
| 50 v8::Local<v8::Map> map = | 29 if (!constructor->IsObject()) |
| 51 EnsureCustomElementRegistryMap(script_state, registry); | |
| 52 v8::Local<v8::Value> name_value = | |
| 53 map->Get(script_state->GetContext(), constructor).ToLocalChecked(); | |
| 54 if (!name_value->IsString()) | |
| 55 return nullptr; | 30 return nullptr; |
| 56 AtomicString name = ToCoreAtomicString(name_value.As<v8::String>()); | 31 auto private_id = |
| 32 script_state->PerContextData()->GetPrivateCustomElementDefinitionId(); | |
| 33 v8::Local<v8::Value> id_value; | |
| 34 if (!constructor.As<v8::Object>() | |
| 35 ->GetPrivate(script_state->GetContext(), private_id) | |
| 36 .ToLocal(&id_value)) | |
| 37 return nullptr; | |
| 38 if (!id_value->IsUint32()) | |
| 39 return nullptr; | |
| 40 uint32_t id = id_value.As<v8::Uint32>()->Value(); | |
| 57 | 41 |
| 58 // This downcast is safe because only | 42 // This downcast is safe because only ScriptCustomElementDefinitions |
| 59 // ScriptCustomElementDefinitions have a name associated with a V8 | 43 // have a ID associated with a V8 constructor; see |
| 60 // constructor in the map; see | |
| 61 // ScriptCustomElementDefinition::create. This relies on three | 44 // ScriptCustomElementDefinition::create. This relies on three |
| 62 // things: | 45 // things: |
| 63 // | 46 // |
| 64 // 1. Only ScriptCustomElementDefinition adds entries to the map. | 47 // 1. Only ScriptCustomElementDefinition sets the private property |
| 65 // Audit the use of private properties in general and how the | 48 // on a constructor. |
| 66 // map is handled--it should never be leaked to script. | |
| 67 // | 49 // |
| 68 // 2. CustomElementRegistry does not overwrite definitions with a | 50 // 2. CustomElementRegistry adds ScriptCustomElementDefinitions |
| 69 // given name--see the CHECK in CustomElementRegistry::define | 51 // assigned an ID to the list of definitions without fail. |
| 70 // --and adds ScriptCustomElementDefinitions to the map without | |
| 71 // fail. | |
| 72 // | 52 // |
| 73 // 3. The relationship between the CustomElementRegistry and its | 53 // 3. The relationship between the CustomElementRegistry and its |
| 74 // map is never mixed up; this is guaranteed by the bindings | 54 // private property is never mixed up; this is guaranteed by the |
| 75 // system which provides a stable wrapper, and the map hangs | 55 // bindings system because the registry is associated with its |
| 76 // off the wrapper. | 56 // context. |
| 77 // | 57 // |
| 78 // At a meta-level, this downcast is safe because there is | 58 // At a meta-level, this downcast is safe because there is |
| 79 // currently only one implementation of CustomElementDefinition in | 59 // currently only one implementation of CustomElementDefinition in |
| 80 // product code and that is ScriptCustomElementDefinition. But | 60 // product code and that is ScriptCustomElementDefinition. But |
| 81 // that may change in the future. | 61 // that may change in the future. |
| 82 CustomElementDefinition* definition = registry->DefinitionForName(name); | 62 CustomElementDefinition* definition = registry->DefinitionForId(id); |
| 83 CHECK(definition); | 63 CHECK(definition); |
| 84 return static_cast<ScriptCustomElementDefinition*>(definition); | 64 ScriptCustomElementDefinition* scriptDefinition = |
| 85 } | 65 static_cast<ScriptCustomElementDefinition*>(definition); |
| 86 | 66 // v8::Object::GetPrivate notes that private properties may be |
| 87 using SymbolGetter = V8PrivateProperty::Symbol (*)(v8::Isolate*); | 67 // inherited in future, so check that the definition's constructor |
| 88 | 68 // is exactly the passed object. |
| 89 template <typename T> | 69 return scriptDefinition->constructor_.Get() == constructor ? scriptDefinition |
| 90 static void KeepAlive(v8::Local<v8::Object>& object, | 70 : nullptr; |
| 91 SymbolGetter symbol_getter, | |
| 92 const v8::Local<T>& value, | |
| 93 ScopedPersistent<T>& persistent, | |
| 94 ScriptState* script_state) { | |
| 95 if (value.IsEmpty()) | |
| 96 return; | |
| 97 | |
| 98 v8::Isolate* isolate = script_state->GetIsolate(); | |
| 99 symbol_getter(isolate).Set(object, value); | |
| 100 persistent.Set(isolate, value); | |
| 101 persistent.SetPhantom(); | |
| 102 } | 71 } |
| 103 | 72 |
| 104 ScriptCustomElementDefinition* ScriptCustomElementDefinition::Create( | 73 ScriptCustomElementDefinition* ScriptCustomElementDefinition::Create( |
| 105 ScriptState* script_state, | 74 ScriptState* script_state, |
| 106 CustomElementRegistry* registry, | 75 CustomElementRegistry* registry, |
| 107 const CustomElementDescriptor& descriptor, | 76 const CustomElementDescriptor& descriptor, |
| 77 CustomElementDefinition::Id id, | |
| 108 const v8::Local<v8::Object>& constructor, | 78 const v8::Local<v8::Object>& constructor, |
| 109 const v8::Local<v8::Function>& connected_callback, | 79 const v8::Local<v8::Function>& connected_callback, |
| 110 const v8::Local<v8::Function>& disconnected_callback, | 80 const v8::Local<v8::Function>& disconnected_callback, |
| 111 const v8::Local<v8::Function>& adopted_callback, | 81 const v8::Local<v8::Function>& adopted_callback, |
| 112 const v8::Local<v8::Function>& attribute_changed_callback, | 82 const v8::Local<v8::Function>& attribute_changed_callback, |
| 113 const HashSet<AtomicString>& observed_attributes) { | 83 HashSet<AtomicString>&& observed_attributes) { |
| 114 ScriptCustomElementDefinition* definition = new ScriptCustomElementDefinition( | 84 auto private_id = |
| 85 script_state->PerContextData()->GetPrivateCustomElementDefinitionId(); | |
| 86 CHECK(constructor | |
| 87 ->SetPrivate( | |
| 88 script_state->GetContext(), private_id, | |
| 89 v8::Integer::NewFromUnsigned(script_state->GetIsolate(), id)) | |
| 90 .ToChecked()); | |
| 91 return new ScriptCustomElementDefinition( | |
| 115 script_state, descriptor, constructor, connected_callback, | 92 script_state, descriptor, constructor, connected_callback, |
| 116 disconnected_callback, adopted_callback, attribute_changed_callback, | 93 disconnected_callback, adopted_callback, attribute_changed_callback, |
| 117 observed_attributes); | 94 std::move(observed_attributes)); |
| 118 | |
| 119 // Add a constructor -> name mapping to the registry. | |
| 120 v8::Local<v8::Value> name_value = | |
| 121 V8String(script_state->GetIsolate(), descriptor.GetName()); | |
| 122 v8::Local<v8::Map> map = | |
| 123 EnsureCustomElementRegistryMap(script_state, registry); | |
| 124 map->Set(script_state->GetContext(), constructor, name_value) | |
| 125 .ToLocalChecked(); | |
| 126 definition->constructor_.SetPhantom(); | |
| 127 | |
| 128 // We add the callbacks here to keep them alive. We use the name as | |
| 129 // the key because it is unique per-registry. | |
| 130 v8::Local<v8::Object> object = v8::Object::New(script_state->GetIsolate()); | |
| 131 KeepAlive(object, V8PrivateProperty::GetCustomElementConnectedCallback, | |
| 132 connected_callback, definition->connected_callback_, script_state); | |
| 133 KeepAlive(object, V8PrivateProperty::GetCustomElementDisconnectedCallback, | |
| 134 disconnected_callback, definition->disconnected_callback_, | |
| 135 script_state); | |
| 136 KeepAlive(object, V8PrivateProperty::GetCustomElementAdoptedCallback, | |
| 137 adopted_callback, definition->adopted_callback_, script_state); | |
| 138 KeepAlive(object, V8PrivateProperty::GetCustomElementAttributeChangedCallback, | |
| 139 attribute_changed_callback, definition->attribute_changed_callback_, | |
| 140 script_state); | |
| 141 map->Set(script_state->GetContext(), name_value, object).ToLocalChecked(); | |
| 142 | |
| 143 return definition; | |
| 144 } | 95 } |
| 145 | 96 |
| 146 ScriptCustomElementDefinition::ScriptCustomElementDefinition( | 97 ScriptCustomElementDefinition::ScriptCustomElementDefinition( |
| 147 ScriptState* script_state, | 98 ScriptState* script_state, |
| 148 const CustomElementDescriptor& descriptor, | 99 const CustomElementDescriptor& descriptor, |
| 149 const v8::Local<v8::Object>& constructor, | 100 const v8::Local<v8::Object>& constructor, |
| 150 const v8::Local<v8::Function>& connected_callback, | 101 const v8::Local<v8::Function>& connected_callback, |
| 151 const v8::Local<v8::Function>& disconnected_callback, | 102 const v8::Local<v8::Function>& disconnected_callback, |
| 152 const v8::Local<v8::Function>& adopted_callback, | 103 const v8::Local<v8::Function>& adopted_callback, |
| 153 const v8::Local<v8::Function>& attribute_changed_callback, | 104 const v8::Local<v8::Function>& attribute_changed_callback, |
| 154 const HashSet<AtomicString>& observed_attributes) | 105 HashSet<AtomicString>&& observed_attributes) |
| 155 : CustomElementDefinition(descriptor, observed_attributes), | 106 : CustomElementDefinition(descriptor, std::move(observed_attributes)), |
| 156 script_state_(script_state), | 107 script_state_(script_state), |
| 157 constructor_(script_state->GetIsolate(), constructor) {} | 108 constructor_(script_state->GetIsolate(), this, constructor), |
| 109 connected_callback_(this), | |
| 110 disconnected_callback_(this), | |
| 111 adopted_callback_(this), | |
| 112 attribute_changed_callback_(this) { | |
| 113 v8::Isolate* isolate = script_state->GetIsolate(); | |
| 114 if (!connected_callback.IsEmpty()) | |
| 115 connected_callback_.Set(isolate, connected_callback); | |
| 116 if (!disconnected_callback.IsEmpty()) | |
| 117 disconnected_callback_.Set(isolate, disconnected_callback); | |
| 118 if (!adopted_callback.IsEmpty()) | |
| 119 adopted_callback_.Set(isolate, adopted_callback); | |
| 120 if (!attribute_changed_callback.IsEmpty()) | |
| 121 attribute_changed_callback_.Set(isolate, attribute_changed_callback); | |
| 122 } | |
| 158 | 123 |
| 159 static void DispatchErrorEvent(v8::Isolate* isolate, | 124 static void DispatchErrorEvent(v8::Isolate* isolate, |
| 160 v8::Local<v8::Value> exception, | 125 v8::Local<v8::Value> exception, |
| 161 v8::Local<v8::Object> constructor) { | 126 v8::Local<v8::Object> constructor) { |
| 162 v8::TryCatch try_catch(isolate); | 127 v8::TryCatch try_catch(isolate); |
| 163 try_catch.SetVerbose(true); | 128 try_catch.SetVerbose(true); |
| 164 V8ScriptRunner::ThrowException( | 129 V8ScriptRunner::ThrowException( |
| 165 isolate, exception, constructor.As<v8::Function>()->GetScriptOrigin()); | 130 isolate, exception, constructor.As<v8::Function>()->GetScriptOrigin()); |
| 166 } | 131 } |
| 167 | 132 |
| 133 DEFINE_TRACE_WRAPPERS(ScriptCustomElementDefinition) { | |
| 134 visitor->TraceWrappers(constructor_.Cast<v8::Value>()); | |
|
haraken
2017/04/28 08:49:30
Won't visitor->TraceWrappers(constructor_) work?
| |
| 135 visitor->TraceWrappers(connected_callback_.Cast<v8::Value>()); | |
| 136 visitor->TraceWrappers(disconnected_callback_.Cast<v8::Value>()); | |
| 137 visitor->TraceWrappers(adopted_callback_.Cast<v8::Value>()); | |
| 138 visitor->TraceWrappers(attribute_changed_callback_.Cast<v8::Value>()); | |
| 139 } | |
| 140 | |
| 168 HTMLElement* ScriptCustomElementDefinition::HandleCreateElementSyncException( | 141 HTMLElement* ScriptCustomElementDefinition::HandleCreateElementSyncException( |
| 169 Document& document, | 142 Document& document, |
| 170 const QualifiedName& tag_name, | 143 const QualifiedName& tag_name, |
| 171 v8::Isolate* isolate, | 144 v8::Isolate* isolate, |
| 172 ExceptionState& exception_state) { | 145 ExceptionState& exception_state) { |
| 173 DCHECK(exception_state.HadException()); | 146 DCHECK(exception_state.HadException()); |
| 174 // 6.1."If any of these subsubsteps threw an exception".1 | 147 // 6.1."If any of these subsubsteps threw an exception".1 |
| 175 // Report the exception. | 148 // Report the exception. |
| 176 DispatchErrorEvent(isolate, exception_state.GetException(), Constructor()); | 149 DispatchErrorEvent(isolate, exception_state.GetException(), Constructor()); |
| 177 exception_state.ClearException(); | 150 exception_state.ClearException(); |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 v8::Local<v8::Value> argv[] = { | 341 v8::Local<v8::Value> argv[] = { |
| 369 V8String(isolate, name.LocalName()), V8StringOrNull(isolate, old_value), | 342 V8String(isolate, name.LocalName()), V8StringOrNull(isolate, old_value), |
| 370 V8StringOrNull(isolate, new_value), | 343 V8StringOrNull(isolate, new_value), |
| 371 V8StringOrNull(isolate, name.NamespaceURI()), | 344 V8StringOrNull(isolate, name.NamespaceURI()), |
| 372 }; | 345 }; |
| 373 RunCallback(attribute_changed_callback_.NewLocal(isolate), element, | 346 RunCallback(attribute_changed_callback_.NewLocal(isolate), element, |
| 374 WTF_ARRAY_LENGTH(argv), argv); | 347 WTF_ARRAY_LENGTH(argv), argv); |
| 375 } | 348 } |
| 376 | 349 |
| 377 } // namespace blink | 350 } // namespace blink |
| OLD | NEW |