| Index: third_party/WebKit/Source/bindings/core/v8/V8SnapshotCreator.cpp
|
| diff --git a/third_party/WebKit/Source/bindings/core/v8/V8SnapshotCreator.cpp b/third_party/WebKit/Source/bindings/core/v8/V8SnapshotCreator.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..eb479347e29108cb5582b7eb361704ab7d462f65
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/bindings/core/v8/V8SnapshotCreator.cpp
|
| @@ -0,0 +1,418 @@
|
| +// Copyright 2017 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/V8SnapshotCreator.h"
|
| +
|
| +#include <cstring>
|
| +
|
| +#include "bindings/core/v8/GeneratedCodeHelper.h"
|
| +#include "bindings/core/v8/V8Document.h"
|
| +#include "bindings/core/v8/V8EventTarget.h"
|
| +#include "bindings/core/v8/V8HTMLDocument.h"
|
| +#include "bindings/core/v8/V8Initializer.h"
|
| +#include "bindings/core/v8/V8Node.h"
|
| +#include "bindings/core/v8/V8Window.h"
|
| +#include "platform/bindings/DOMWrapperWorld.h"
|
| +#include "platform/bindings/V8ObjectConstructor.h"
|
| +#include "platform/bindings/V8PerIsolateData.h"
|
| +#include "platform/bindings/V8PrivateProperty.h"
|
| +#include "v8/include/v8.h"
|
| +
|
| +namespace blink {
|
| +
|
| +namespace {
|
| +
|
| +intptr_t* g_snapshot_reference_table = nullptr;
|
| +
|
| +// TODO(peria): This method is almost a copy of
|
| +// V8PerContext::ConstructorForTypeSlowCase(), so merge with it.
|
| +v8::Local<v8::Function> ConstructPlainType(v8::Isolate* isolate,
|
| + const DOMWrapperWorld& world,
|
| + v8::Local<v8::Context> context,
|
| + const WrapperTypeInfo* type) {
|
| + v8::Context::Scope scope(context);
|
| + // We shouldn't reach this point for the types that are implemented in v8 such
|
| + // as typed arrays and hence don't have domTemplateFunction.
|
| + DCHECK(type->dom_template_function);
|
| + v8::Local<v8::FunctionTemplate> interface_template =
|
| + type->domTemplate(isolate, world);
|
| + // Getting the function might fail if we're running out of stack or memory.
|
| + v8::Local<v8::Function> interface_object;
|
| + if (!interface_template->GetFunction(context).ToLocal(&interface_object))
|
| + return v8::Local<v8::Function>();
|
| +
|
| + if (type->parent_class) {
|
| + v8::Local<v8::Object> prototype_template =
|
| + ConstructPlainType(isolate, world, context, type->parent_class);
|
| + CHECK(interface_object->SetPrototype(context, prototype_template)
|
| + .ToChecked());
|
| + }
|
| +
|
| + v8::Local<v8::Value> prototype_value;
|
| + CHECK(interface_object->Get(context, V8AtomicString(isolate, "prototype"))
|
| + .ToLocal(&prototype_value));
|
| + CHECK(prototype_value->IsObject());
|
| + v8::Local<v8::Object> prototype_object = prototype_value.As<v8::Object>();
|
| + if (prototype_object->InternalFieldCount() ==
|
| + kV8PrototypeInternalFieldcount &&
|
| + type->wrapper_type_prototype ==
|
| + WrapperTypeInfo::kWrapperTypeObjectPrototype) {
|
| + prototype_object->SetAlignedPointerInInternalField(
|
| + kV8PrototypeTypeIndex, const_cast<WrapperTypeInfo*>(type));
|
| + }
|
| + type->PreparePrototypeAndInterfaceObject(
|
| + context, world, prototype_object, interface_object, interface_template);
|
| +
|
| + return interface_object;
|
| +}
|
| +
|
| +// TODO(peria): This method is almost a copy of
|
| +// V8PerContext::CreateWrapperFromCacheSlowCase(), so merge with it.
|
| +v8::Local<v8::Object> CreatePlainWrapper(v8::Isolate* isolate,
|
| + const DOMWrapperWorld& world,
|
| + v8::Local<v8::Context> context,
|
| + const WrapperTypeInfo* type) {
|
| + CHECK(V8HTMLDocument::wrapperTypeInfo.Equals(type));
|
| +
|
| + v8::Context::Scope scope(context);
|
| + v8::Local<v8::Function> interface_object =
|
| + ConstructPlainType(isolate, world, context, type);
|
| + CHECK(!interface_object.IsEmpty());
|
| + v8::Local<v8::Object> instance_template;
|
| + CHECK(V8ObjectConstructor::NewInstance(isolate, interface_object)
|
| + .ToLocal(&instance_template));
|
| + v8::Local<v8::Object> wrapper = instance_template->Clone();
|
| + wrapper->SetAlignedPointerInInternalField(kV8DOMWrapperTypeIndex,
|
| + const_cast<WrapperTypeInfo*>(type));
|
| + return wrapper;
|
| +}
|
| +
|
| +constexpr int kWorldIdForNonMainWorld =
|
| + DOMWrapperWorld::WorldId::kIsolatedWorldIdLimit - 1;
|
| +
|
| +int GetSnapshotIndexForWorld(const DOMWrapperWorld& world) {
|
| + return world.IsMainWorld() ? 0 : 1;
|
| +}
|
| +
|
| +constexpr const WrapperTypeInfo* kSnapshotWrapperTypes[] = {
|
| + &V8Window::wrapperTypeInfo, &V8HTMLDocument::wrapperTypeInfo,
|
| + &V8EventTarget::wrapperTypeInfo, &V8Node::wrapperTypeInfo,
|
| + &V8Document::wrapperTypeInfo,
|
| +};
|
| +
|
| +enum class InternalFieldType : uint8_t {
|
| + kNone,
|
| + kNodeType,
|
| + kDocumentType,
|
| + kHTMLDocumentType,
|
| + kHTMLDocumentObject,
|
| +};
|
| +
|
| +const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(InternalFieldType type) {
|
| + switch (type) {
|
| + case InternalFieldType::kNone:
|
| + NOTREACHED();
|
| + break;
|
| + case InternalFieldType::kNodeType:
|
| + return &V8Node::wrapperTypeInfo;
|
| + case InternalFieldType::kDocumentType:
|
| + return &V8Document::wrapperTypeInfo;
|
| + case InternalFieldType::kHTMLDocumentType:
|
| + return &V8HTMLDocument::wrapperTypeInfo;
|
| + case InternalFieldType::kHTMLDocumentObject:
|
| + return &V8HTMLDocument::wrapperTypeInfo;
|
| + }
|
| + NOTREACHED();
|
| + return nullptr;
|
| +}
|
| +
|
| +struct DataForDeserializer {
|
| + STACK_ALLOCATED();
|
| + Member<Document> document;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +v8::StartupData V8SnapshotCreator::TakeSnapshot() {
|
| + v8::SnapshotCreator* creator =
|
| + V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
|
| + ->GetSnapshotCreator();
|
| + v8::Isolate* isolate = creator->GetIsolate();
|
| + CHECK_EQ(isolate, v8::Isolate::GetCurrent());
|
| +
|
| + // Disable all runtime enabled featuers
|
| + RuntimeEnabledFeatures::setStableFeaturesEnabled(false);
|
| + RuntimeEnabledFeatures::setExperimentalFeaturesEnabled(false);
|
| + RuntimeEnabledFeatures::setTestFeaturesEnabled(false);
|
| +
|
| + {
|
| + v8::HandleScope handleScope(isolate);
|
| + creator->SetDefaultContext(v8::Context::New(isolate));
|
| +
|
| + TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld());
|
| + // For non main worlds, we can use any type to create a context.
|
| + TakeSnapshotForWorld(creator, *DOMWrapperWorld::EnsureIsolatedWorld(
|
| + isolate, kWorldIdForNonMainWorld));
|
| + }
|
| +
|
| + // Snapshot is taken on the main thread, but it can be used on other threads.
|
| + // So we remove a message handler for the main thread.
|
| + isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread);
|
| +
|
| + return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
| +}
|
| +
|
| +void V8SnapshotCreator::TakeSnapshotForWorld(v8::SnapshotCreator* creator,
|
| + const DOMWrapperWorld& world) {
|
| + v8::Isolate* isolate = creator->GetIsolate();
|
| + CHECK_EQ(isolate, v8::Isolate::GetCurrent());
|
| +
|
| + // Function templates
|
| + v8::HandleScope handleScope(isolate);
|
| + Vector<v8::Local<v8::FunctionTemplate>> interface_templates;
|
| + for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
|
| + v8::Local<v8::FunctionTemplate> interface_template =
|
| + wrapper_type_info->domTemplate(isolate, world);
|
| + CHECK(!interface_template.IsEmpty());
|
| + interface_templates.push_back(interface_template);
|
| + }
|
| +
|
| + // FIXME: Confirm interface_tempaltes[0] is a template of V8Window.
|
| + v8::Local<v8::ObjectTemplate> window_template =
|
| + interface_templates[0]->InstanceTemplate();
|
| + CHECK(!window_template.IsEmpty());
|
| +
|
| + v8::Local<v8::Context> context;
|
| + {
|
| + V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
|
| + V8PerIsolateData::From(isolate));
|
| + context = v8::Context::New(isolate, nullptr, window_template);
|
| + }
|
| + CHECK(!context.IsEmpty());
|
| +
|
| + if (world.IsMainWorld()) {
|
| + v8::Context::Scope scope(context);
|
| + v8::Local<v8::Object> document_wrapper = CreatePlainWrapper(
|
| + isolate, world, context, &V8HTMLDocument::wrapperTypeInfo);
|
| + int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
|
| + void* values[] = {nullptr, const_cast<WrapperTypeInfo*>(
|
| + &V8HTMLDocument::wrapperTypeInfo)};
|
| + document_wrapper->SetAlignedPointerInInternalFields(
|
| + WTF_ARRAY_LENGTH(indices), indices, values);
|
| +
|
| + // Update the cached accessor for window.document.
|
| + CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set(
|
| + context->Global(), document_wrapper));
|
| + }
|
| +
|
| + for (auto& interface_template : interface_templates) {
|
| + creator->AddTemplate(interface_template);
|
| + }
|
| + creator->AddContext(context, SerializeInternalField);
|
| +
|
| + V8PerIsolateData::From(isolate)->ClearPersistentsForV8Snapshot();
|
| +}
|
| +
|
| +void V8SnapshotCreator::EnsureInterfaceTemplates(v8::Isolate* isolate) {
|
| + if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
|
| + V8PerIsolateData::V8ContextMode::kUseSnapshot) {
|
| + return;
|
| + }
|
| +
|
| + EnsureInterfaceTemplatesForWorld(isolate, DOMWrapperWorld::MainWorld());
|
| + EnsureInterfaceTemplatesForWorld(
|
| + isolate,
|
| + *DOMWrapperWorld::EnsureIsolatedWorld(isolate, kWorldIdForNonMainWorld));
|
| +}
|
| +
|
| +void V8SnapshotCreator::EnsureInterfaceTemplatesForWorld(
|
| + v8::Isolate* isolate,
|
| + const DOMWrapperWorld& world) {
|
| + for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
|
| + v8::Local<v8::FunctionTemplate> interface =
|
| + wrapper_type_info->domTemplate(isolate, world);
|
| + CHECK(!interface.IsEmpty());
|
| + }
|
| +}
|
| +
|
| +v8::Local<v8::FunctionTemplate>
|
| +V8SnapshotCreator::InterfaceTemplateFromSnapshot(
|
| + v8::Isolate* isolate,
|
| + const DOMWrapperWorld& world,
|
| + WrapperTypeInfo* wrapper_type_info) {
|
| + if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
|
| + V8PerIsolateData::V8ContextMode::kUseSnapshot) {
|
| + return v8::Local<v8::FunctionTemplate>();
|
| + }
|
| +
|
| + const int index_offset =
|
| + world.IsMainWorld() ? 0 : WTF_ARRAY_LENGTH(kSnapshotWrapperTypes);
|
| +
|
| + // Snapshotted templates are expected to be used just to get
|
| + // wrapper_type_info.
|
| + for (size_t i = 0; i < WTF_ARRAY_LENGTH(kSnapshotWrapperTypes); ++i) {
|
| + if (kSnapshotWrapperTypes[i]->Equals(wrapper_type_info)) {
|
| + return v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i)
|
| + .ToLocalChecked();
|
| + }
|
| + }
|
| + return v8::Local<v8::FunctionTemplate>();
|
| +}
|
| +
|
| +v8::StartupData V8SnapshotCreator::SerializeInternalField(
|
| + v8::Local<v8::Object> holder,
|
| + int index,
|
| + void*) {
|
| + InternalFieldType field_type = InternalFieldType::kNone;
|
| + const WrapperTypeInfo* wrapper_type = ToWrapperTypeInfo(holder);
|
| + if (kV8DOMWrapperObjectIndex == index) {
|
| + if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
|
| + field_type = InternalFieldType::kHTMLDocumentObject;
|
| + }
|
| + } else if (kV8DOMWrapperTypeIndex == index) {
|
| + if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
|
| + field_type = InternalFieldType::kHTMLDocumentType;
|
| + } else if (blink::V8Document::wrapperTypeInfo.Equals(wrapper_type)) {
|
| + field_type = InternalFieldType::kDocumentType;
|
| + } else if (blink::V8Node::wrapperTypeInfo.Equals(wrapper_type)) {
|
| + field_type = InternalFieldType::kNodeType;
|
| + }
|
| + }
|
| + CHECK_NE(field_type, InternalFieldType::kNone);
|
| +
|
| + int size = sizeof(InternalFieldType);
|
| + char* data = new char[size];
|
| + std::memcpy(data, &field_type, size);
|
| +
|
| + return {data, size};
|
| +}
|
| +
|
| +void V8SnapshotCreator::DeserializeInternalField(v8::Local<v8::Object> holder,
|
| + int index,
|
| + v8::StartupData payload,
|
| + void* ptr) {
|
| + // DeserializeInternalField() expects to be called in the main world
|
| + // with |document| being HTMLDocument.
|
| + CHECK_EQ(payload.raw_size, static_cast<int>(sizeof(InternalFieldType)));
|
| + InternalFieldType type =
|
| + *reinterpret_cast<const InternalFieldType*>(payload.data);
|
| +
|
| + const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type);
|
| + switch (type) {
|
| + case InternalFieldType::kNodeType:
|
| + case InternalFieldType::kDocumentType:
|
| + case InternalFieldType::kHTMLDocumentType: {
|
| + CHECK_EQ(index, kV8DOMWrapperTypeIndex);
|
| + holder->SetAlignedPointerInInternalField(
|
| + index, const_cast<WrapperTypeInfo*>(wrapper_type_info));
|
| + return;
|
| + }
|
| + case InternalFieldType::kHTMLDocumentObject: {
|
| + CHECK_EQ(index, kV8DOMWrapperObjectIndex);
|
| + v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
| + DataForDeserializer* data = static_cast<DataForDeserializer*>(ptr);
|
| + ScriptWrappable* document = data->document;
|
| + DCHECK(document);
|
| +
|
| + // Make reference from wrapper to document
|
| + holder->SetAlignedPointerInInternalField(index, document);
|
| + // Make reference from document to wrapper
|
| + CHECK(document->SetWrapper(isolate, wrapper_type_info, holder));
|
| + WrapperTypeInfo::WrapperCreated();
|
| + return;
|
| + }
|
| + case InternalFieldType::kNone:
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + NOTREACHED();
|
| +}
|
| +
|
| +v8::Local<v8::Context> V8SnapshotCreator::CreateContext(
|
| + v8::Isolate* isolate,
|
| + const DOMWrapperWorld& world,
|
| + v8::ExtensionConfiguration* extension_configuration,
|
| + v8::Local<v8::Object> global_proxy,
|
| + Document* document) {
|
| + if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
|
| + V8PerIsolateData::V8ContextMode::kUseSnapshot) {
|
| + return v8::Local<v8::Context>();
|
| + }
|
| + // For main world frames, a HTMLDocument object is required to be
|
| + // deserialized.
|
| + if (world.IsMainWorld() && !(document && document->IsHTMLDocument())) {
|
| + return v8::Local<v8::Context>();
|
| + }
|
| + v8::Local<v8::Context> context;
|
| + const int index = GetSnapshotIndexForWorld(world);
|
| + DataForDeserializer data{document};
|
| + v8::DeserializeInternalFieldsCallback callback =
|
| + v8::DeserializeInternalFieldsCallback(&DeserializeInternalField, &data);
|
| + return v8::Context::FromSnapshot(isolate, index, callback,
|
| + extension_configuration, global_proxy)
|
| + .ToLocalChecked();
|
| +}
|
| +
|
| +void V8SnapshotCreator::InstallRuntimeEnabledFeaturesOnDocument(
|
| + ScriptState* script_state,
|
| + v8::Local<v8::Object> wrapper) {
|
| + v8::Isolate* isolate = script_state->GetIsolate();
|
| + if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
|
| + V8PerIsolateData::V8ContextMode::kUseSnapshot) {
|
| + return;
|
| + }
|
| +
|
| + v8::Local<v8::Context> context = script_state->GetContext();
|
| + DOMWrapperWorld& world = script_state->World();
|
| +
|
| + v8::Local<v8::Object> htmldocument_prototype =
|
| + wrapper->GetPrototype().As<v8::Object>();
|
| + v8::Local<v8::Function> htmldocument_interface =
|
| + V8HTMLDocument::domTemplate(isolate, world)->GetFunction();
|
| + V8HTMLDocument::installRuntimeEnabledFeatures(
|
| + isolate, world, wrapper, htmldocument_prototype, htmldocument_interface);
|
| +
|
| + v8::Local<v8::Object> document_prototype =
|
| + htmldocument_prototype->GetPrototype().As<v8::Object>();
|
| + v8::Local<v8::Function> document_interface =
|
| + V8Document::domTemplate(isolate, world)->GetFunction();
|
| + V8Document::installRuntimeEnabledFeatures(
|
| + isolate, world, wrapper, document_prototype, document_interface);
|
| +
|
| + V8Document::preparePrototypeAndInterfaceObject(
|
| + context, world, document_prototype, document_interface,
|
| + v8::Local<v8::FunctionTemplate>());
|
| +}
|
| +
|
| +void V8SnapshotCreator::InstallRuntimeEnabledFeaturesOnGlobal(
|
| + ScriptState* script_state,
|
| + v8::Local<v8::Object> global) {
|
| + v8::Isolate* isolate = script_state->GetIsolate();
|
| + if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
|
| + V8PerIsolateData::V8ContextMode::kUseSnapshot) {
|
| + return;
|
| + }
|
| +
|
| + DOMWrapperWorld& world = script_state->World();
|
| +
|
| + v8::Local<v8::Object> window_prototype =
|
| + global->GetPrototype().As<v8::Object>();
|
| + v8::Local<v8::Function> window_interface =
|
| + V8Window::domTemplate(isolate, world)->GetFunction();
|
| + V8Window::installV8WindowRuntimeEnabledFunction(
|
| + isolate, world, global, window_prototype, window_interface);
|
| +}
|
| +
|
| +void V8SnapshotCreator::SetReferenceTable(intptr_t* table) {
|
| + DCHECK(!g_snapshot_reference_table);
|
| + g_snapshot_reference_table = table;
|
| +}
|
| +
|
| +intptr_t* V8SnapshotCreator::GetReferenceTable() {
|
| + DCHECK(g_snapshot_reference_table);
|
| + return g_snapshot_reference_table;
|
| +}
|
| +
|
| +} // namespace blink
|
|
|