Index: third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp |
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d0d3bd051180dce7ef6a8ad89f2a005361ff7fe8 |
--- /dev/null |
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp |
@@ -0,0 +1,136 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "bindings/core/v8/ScriptCustomElementDefinition.h" |
+ |
+#include "bindings/core/v8/ScriptState.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "bindings/core/v8/V8CustomElementsRegistry.h" |
+#include "bindings/core/v8/V8HiddenValue.h" |
+#include "wtf/Allocator.h" |
+ |
+namespace blink { |
+ |
+// Retrieves the custom elements constructor -> name map, creating it |
+// if necessary. The same map is used to keep prototypes alive. |
+static v8::Local<v8::Map> ensureCustomElementsRegistryMap( |
+ ScriptState* scriptState, |
+ CustomElementsRegistry* registry) |
+{ |
+ CHECK(scriptState->world().isMainWorld()); |
+ v8::Local<v8::String> name = V8HiddenValue::customElementsRegistryMap( |
+ scriptState->isolate()); |
+ v8::Local<v8::Object> wrapper = |
+ toV8(registry, scriptState).As<v8::Object>(); |
+ v8::Local<v8::Value> map = |
+ V8HiddenValue::getHiddenValue(scriptState, wrapper, name); |
+ if (map.IsEmpty()) { |
+ map = v8::Map::New(scriptState->isolate()); |
+ V8HiddenValue::setHiddenValue(scriptState, wrapper, name, map); |
+ } |
+ return map.As<v8::Map>(); |
+} |
+ |
+ScriptCustomElementDefinition* ScriptCustomElementDefinition::forConstructor( |
+ ScriptState* scriptState, |
+ CustomElementsRegistry* registry, |
+ const v8::Local<v8::Value>& constructor) |
+{ |
+ v8::Local<v8::Map> map = |
+ ensureCustomElementsRegistryMap(scriptState, registry); |
+ v8::Local<v8::Value> nameValue = v8CallOrCrash( |
+ map->Get(scriptState->context(), constructor)); |
+ if (!nameValue->IsString()) |
+ return nullptr; |
+ AtomicString name = toCoreAtomicString(nameValue.As<v8::String>()); |
+ |
+ // This downcast is safe because only |
+ // ScriptCustomElementDefinitions have a name associated with a V8 |
+ // constructor in the map; see |
+ // ScriptCustomElementDefinition::create. This relies on three |
+ // things: |
+ // |
+ // 1. Only ScriptCustomElementDefinition adds entries to the map. |
+ // Audit the use of V8HiddenValue/hidden values in general and |
+ // how the map is handled--it should never be leaked to script. |
+ // |
+ // 2. CustomElementsRegistry does not overwrite definitions with a |
+ // given name--see the CHECK in CustomElementsRegistry::define |
+ // --and adds ScriptCustomElementDefinitions to the map without |
+ // fail. |
+ // |
+ // 3. The relationship between the CustomElementsRegistry and its |
+ // map is never mixed up; this is guaranteed by the bindings |
+ // system which provides a stable wrapper, and the map hangs |
+ // off the wrapper. |
+ // |
+ // At a meta-level, this downcast is safe because there is |
+ // currently only one implementation of CustomElementDefinition in |
+ // product code and that is ScriptCustomElementDefinition. But |
+ // that may change in the future. |
+ CustomElementDefinition* definition = registry->definitionForName(name); |
+ CHECK(definition); |
+ return static_cast<ScriptCustomElementDefinition*>(definition); |
+} |
+ |
+ScriptCustomElementDefinition* ScriptCustomElementDefinition::create( |
+ ScriptState* scriptState, |
+ CustomElementsRegistry* registry, |
+ const CustomElementDescriptor& descriptor, |
+ const v8::Local<v8::Object>& constructor, |
+ const v8::Local<v8::Object>& prototype) |
+{ |
+ ScriptCustomElementDefinition* definition = |
+ new ScriptCustomElementDefinition( |
+ scriptState, |
+ descriptor, |
+ constructor, |
+ prototype); |
+ |
+ // Add a constructor -> name mapping to the registry. |
+ v8::Local<v8::Value> nameValue = |
+ v8String(scriptState->isolate(), descriptor.name()); |
+ v8::Local<v8::Map> map = |
+ ensureCustomElementsRegistryMap(scriptState, registry); |
+ v8CallOrCrash(map->Set(scriptState->context(), constructor, nameValue)); |
+ // We add the prototype here to keep it alive; we make it a value |
+ // not a key so authors cannot return another constructor as a |
+ // prototype to overwrite a constructor in this map. We use the |
+ // name because it is unique per-registry. |
+ v8CallOrCrash(map->Set(scriptState->context(), nameValue, prototype)); |
+ |
+ return definition; |
+} |
+ |
+ScriptCustomElementDefinition::ScriptCustomElementDefinition( |
+ ScriptState* scriptState, |
+ const CustomElementDescriptor& descriptor, |
+ const v8::Local<v8::Object>& constructor, |
+ const v8::Local<v8::Object>& prototype) |
+ : CustomElementDefinition(descriptor) |
+ , m_constructor(scriptState->isolate(), constructor) |
+ , m_prototype(scriptState->isolate(), prototype) |
+{ |
+ // These objects are kept alive by references from the |
+ // CustomElementsRegistry wrapper set up by |
+ // ScriptCustomElementDefinition::create. |
+ m_constructor.setPhantom(); |
+ m_prototype.setPhantom(); |
+} |
+ |
+v8::Local<v8::Object> ScriptCustomElementDefinition::constructor( |
+ ScriptState* scriptState) const |
+{ |
+ DCHECK(!m_constructor.isEmpty()); |
+ return m_constructor.newLocal(scriptState->isolate()); |
+} |
+ |
+v8::Local<v8::Object> ScriptCustomElementDefinition::prototype( |
+ ScriptState* scriptState) const |
+{ |
+ DCHECK(!m_prototype.isEmpty()); |
+ return m_prototype.newLocal(scriptState->isolate()); |
+} |
+ |
+} // namespace blink |