OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "bindings/core/v8/ScriptCustomElementDefinition.h" |
| 6 |
| 7 #include "bindings/core/v8/ScriptState.h" |
| 8 #include "bindings/core/v8/V8Binding.h" |
| 9 #include "bindings/core/v8/V8CustomElementsRegistry.h" |
| 10 #include "bindings/core/v8/V8HiddenValue.h" |
| 11 #include "wtf/Allocator.h" |
| 12 |
| 13 namespace blink { |
| 14 |
| 15 // Retrieves the custom elements constructor -> name map, creating it |
| 16 // if necessary. The same map is used to keep prototypes alive. |
| 17 static v8::Local<v8::Map> ensureCustomElementsRegistryMap( |
| 18 ScriptState* scriptState, |
| 19 CustomElementsRegistry* registry) |
| 20 { |
| 21 CHECK(scriptState->world().isMainWorld()); |
| 22 v8::Local<v8::String> name = V8HiddenValue::customElementsRegistryMap( |
| 23 scriptState->isolate()); |
| 24 v8::Local<v8::Object> wrapper = |
| 25 toV8(registry, scriptState).As<v8::Object>(); |
| 26 v8::Local<v8::Value> map = |
| 27 V8HiddenValue::getHiddenValue(scriptState, wrapper, name); |
| 28 if (map.IsEmpty()) { |
| 29 map = v8::Map::New(scriptState->isolate()); |
| 30 V8HiddenValue::setHiddenValue(scriptState, wrapper, name, map); |
| 31 } |
| 32 return map.As<v8::Map>(); |
| 33 } |
| 34 |
| 35 ScriptCustomElementDefinition* ScriptCustomElementDefinition::forConstructor( |
| 36 ScriptState* scriptState, |
| 37 CustomElementsRegistry* registry, |
| 38 const v8::Local<v8::Value>& constructor) |
| 39 { |
| 40 v8::Local<v8::Map> map = |
| 41 ensureCustomElementsRegistryMap(scriptState, registry); |
| 42 v8::Local<v8::Value> nameValue = v8CallOrCrash( |
| 43 map->Get(scriptState->context(), constructor)); |
| 44 if (!nameValue->IsString()) |
| 45 return nullptr; |
| 46 AtomicString name = toCoreAtomicString(nameValue.As<v8::String>()); |
| 47 |
| 48 // This downcast is safe because only |
| 49 // ScriptCustomElementDefinitions have a name associated with a V8 |
| 50 // constructor in the map; see |
| 51 // ScriptCustomElementDefinition::create. This relies on three |
| 52 // things: |
| 53 // |
| 54 // 1. Only ScriptCustomElementDefinition adds entries to the map. |
| 55 // Audit the use of V8HiddenValue/hidden values in general and |
| 56 // how the map is handled--it should never be leaked to script. |
| 57 // |
| 58 // 2. CustomElementsRegistry does not overwrite definitions with a |
| 59 // given name--see the CHECK in CustomElementsRegistry::define |
| 60 // --and adds ScriptCustomElementDefinitions to the map without |
| 61 // fail. |
| 62 // |
| 63 // 3. The relationship between the CustomElementsRegistry and its |
| 64 // map is never mixed up; this is guaranteed by the bindings |
| 65 // system which provides a stable wrapper, and the map hangs |
| 66 // off the wrapper. |
| 67 // |
| 68 // At a meta-level, this downcast is safe because there is |
| 69 // currently only one implementation of CustomElementDefinition in |
| 70 // product code and that is ScriptCustomElementDefinition. But |
| 71 // that may change in the future. |
| 72 CustomElementDefinition* definition = registry->definitionForName(name); |
| 73 CHECK(definition); |
| 74 return static_cast<ScriptCustomElementDefinition*>(definition); |
| 75 } |
| 76 |
| 77 ScriptCustomElementDefinition* ScriptCustomElementDefinition::create( |
| 78 ScriptState* scriptState, |
| 79 CustomElementsRegistry* registry, |
| 80 const CustomElementDescriptor& descriptor, |
| 81 const v8::Local<v8::Object>& constructor, |
| 82 const v8::Local<v8::Object>& prototype) |
| 83 { |
| 84 ScriptCustomElementDefinition* definition = |
| 85 new ScriptCustomElementDefinition( |
| 86 scriptState, |
| 87 descriptor, |
| 88 constructor, |
| 89 prototype); |
| 90 |
| 91 // Add a constructor -> name mapping to the registry. |
| 92 v8::Local<v8::Value> nameValue = |
| 93 v8String(scriptState->isolate(), descriptor.name()); |
| 94 v8::Local<v8::Map> map = |
| 95 ensureCustomElementsRegistryMap(scriptState, registry); |
| 96 v8CallOrCrash(map->Set(scriptState->context(), constructor, nameValue)); |
| 97 // We add the prototype here to keep it alive; we make it a value |
| 98 // not a key so authors cannot return another constructor as a |
| 99 // prototype to overwrite a constructor in this map. We use the |
| 100 // name because it is unique per-registry. |
| 101 v8CallOrCrash(map->Set(scriptState->context(), nameValue, prototype)); |
| 102 |
| 103 return definition; |
| 104 } |
| 105 |
| 106 ScriptCustomElementDefinition::ScriptCustomElementDefinition( |
| 107 ScriptState* scriptState, |
| 108 const CustomElementDescriptor& descriptor, |
| 109 const v8::Local<v8::Object>& constructor, |
| 110 const v8::Local<v8::Object>& prototype) |
| 111 : CustomElementDefinition(descriptor) |
| 112 , m_constructor(scriptState->isolate(), constructor) |
| 113 , m_prototype(scriptState->isolate(), prototype) |
| 114 { |
| 115 // These objects are kept alive by references from the |
| 116 // CustomElementsRegistry wrapper set up by |
| 117 // ScriptCustomElementDefinition::create. |
| 118 m_constructor.setPhantom(); |
| 119 m_prototype.setPhantom(); |
| 120 } |
| 121 |
| 122 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor( |
| 123 ScriptState* scriptState) const |
| 124 { |
| 125 DCHECK(!m_constructor.isEmpty()); |
| 126 return m_constructor.newLocal(scriptState->isolate()); |
| 127 } |
| 128 |
| 129 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype( |
| 130 ScriptState* scriptState) const |
| 131 { |
| 132 DCHECK(!m_prototype.isEmpty()); |
| 133 return m_prototype.newLocal(scriptState->isolate()); |
| 134 } |
| 135 |
| 136 } // namespace blink |
OLD | NEW |