| 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
|
|
|