blob: d0d3bd051180dce7ef6a8ad89f2a005361ff7fe8 [file] [log] [blame]
// 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