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 if (!scriptState->contextIsValid()) | |
haraken
2016/05/28 08:29:48
Nit: I'd remove this check.
We normally don't che
| |
41 return nullptr; | |
42 v8::Local<v8::Map> map = | |
43 ensureCustomElementsRegistryMap(scriptState, registry); | |
44 v8::Local<v8::Value> nameValue = v8CallOrCrash( | |
45 map->Get(scriptState->context(), constructor)); | |
46 if (!nameValue->IsString()) | |
47 return nullptr; | |
48 AtomicString name = toCoreAtomicString(nameValue.As<v8::String>()); | |
49 | |
50 // This downcast is safe because only | |
51 // ScriptCustomElementDefinitions have a name associated with a V8 | |
52 // constructor in the map; see | |
53 // ScriptCustomElementDefinition::create. This relies on three | |
54 // things: | |
55 // | |
56 // 1. Only ScriptCustomElementDefinition adds entries to the map. | |
57 // Audit the use of V8HiddenValue/hidden values in general and | |
58 // how the map is handled--it should never be leaked to script. | |
59 // | |
60 // 2. CustomElementsRegistry does not overwrite definitions with a | |
61 // given name--see the CHECK in CustomElementsRegistry::define | |
62 // --and adds ScriptCustomElementDefinitions to the map without | |
63 // fail. | |
64 // | |
65 // 3. The relationship between the CustomElementsRegistry and its | |
66 // map is never mixed up; this is guaranteed by the bindings | |
67 // system which provides a stable wrapper, and the map hangs | |
68 // off the wrapper. | |
69 // | |
70 // At a meta-level, this downcast is safe because there is | |
71 // currently only one implementation of CustomElementDefinition in | |
72 // product code and that is ScriptCustomElementDefinition. But | |
73 // that may change in the future. | |
74 CustomElementDefinition* definition = registry->definitionForName(name); | |
75 CHECK(definition); | |
76 return static_cast<ScriptCustomElementDefinition*>(definition); | |
77 } | |
78 | |
79 ScriptCustomElementDefinition* ScriptCustomElementDefinition::create( | |
80 ScriptState* scriptState, | |
81 CustomElementsRegistry* registry, | |
82 const CustomElementDescriptor& descriptor, | |
83 const v8::Local<v8::Object>& constructor, | |
84 const v8::Local<v8::Object>& prototype) | |
85 { | |
86 ScriptCustomElementDefinition* definition = | |
87 new ScriptCustomElementDefinition( | |
88 scriptState, | |
89 descriptor, | |
90 constructor, | |
91 prototype); | |
92 | |
93 // Add a constructor -> name mapping to the registry. | |
94 v8::Local<v8::Value> nameValue = | |
95 v8String(scriptState->isolate(), descriptor.name()); | |
96 v8::Local<v8::Map> map = | |
97 ensureCustomElementsRegistryMap(scriptState, registry); | |
98 v8CallOrCrash(map->Set(scriptState->context(), constructor, nameValue)); | |
99 // We add the prototype here to keep it alive; we make it a value | |
100 // not a key so authors cannot return another constructor as a | |
101 // prototype to overwrite a constructor in this map. We use the | |
102 // name because it is unique per-registry. | |
103 v8CallOrCrash(map->Set(scriptState->context(), nameValue, prototype)); | |
104 | |
105 return definition; | |
106 } | |
107 | |
108 ScriptCustomElementDefinition::ScriptCustomElementDefinition( | |
109 ScriptState* scriptState, | |
110 const CustomElementDescriptor& descriptor, | |
111 const v8::Local<v8::Object>& constructor, | |
112 const v8::Local<v8::Object>& prototype) | |
113 : CustomElementDefinition(descriptor) | |
114 , m_constructor(scriptState->isolate(), constructor) | |
115 , m_prototype(scriptState->isolate(), prototype) | |
116 { | |
117 // These objects are kept alive by references from the | |
118 // CustomElementsRegistry wrapper set up by | |
119 // ScriptCustomElementDefinition::create. | |
120 m_constructor.setPhantom(); | |
121 m_prototype.setPhantom(); | |
122 } | |
123 | |
124 v8::Local<v8::Object> ScriptCustomElementDefinition::constructor( | |
125 ScriptState* scriptState) const | |
126 { | |
127 DCHECK(!m_constructor.isEmpty()); | |
128 return m_constructor.newLocal(scriptState->isolate()); | |
129 } | |
130 | |
131 v8::Local<v8::Object> ScriptCustomElementDefinition::prototype( | |
132 ScriptState* scriptState) const | |
133 { | |
134 DCHECK(!m_prototype.isEmpty()); | |
135 return m_prototype.newLocal(scriptState->isolate()); | |
136 } | |
137 | |
138 } // namespace blink | |
OLD | NEW |