blob: 5920b3d07cb188e9cbb5675d4909da27f55cb960 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_layout_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_no_argument_constructor.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_object_parser.h"
#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
#include "third_party/blink/renderer/core/layout/custom/css_layout_definition.h"
#include "third_party/blink/renderer/core/layout/custom/document_layout_definition.h"
#include "third_party/blink/renderer/core/layout/custom/layout_worklet.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
namespace blink {
// static
LayoutWorkletGlobalScope* LayoutWorkletGlobalScope::Create(
LocalFrame* frame,
std::unique_ptr<GlobalScopeCreationParams> creation_params,
WorkerReportingProxy& reporting_proxy,
PendingLayoutRegistry* pending_layout_registry) {
auto* global_scope = MakeGarbageCollected<LayoutWorkletGlobalScope>(
frame, std::move(creation_params), reporting_proxy,
pending_layout_registry);
// TODO(bashi): Handle a case where the script controller fails to initialize.
global_scope->ScriptController()->Initialize(NullURL());
MainThreadDebugger::Instance()->ContextCreated(
global_scope->ScriptController()->GetScriptState(),
global_scope->GetFrame(), global_scope->DocumentSecurityOrigin());
return global_scope;
}
LayoutWorkletGlobalScope::LayoutWorkletGlobalScope(
LocalFrame* frame,
std::unique_ptr<GlobalScopeCreationParams> creation_params,
WorkerReportingProxy& reporting_proxy,
PendingLayoutRegistry* pending_layout_registry)
: WorkletGlobalScope(std::move(creation_params), reporting_proxy, frame),
pending_layout_registry_(pending_layout_registry) {}
LayoutWorkletGlobalScope::~LayoutWorkletGlobalScope() = default;
void LayoutWorkletGlobalScope::Dispose() {
MainThreadDebugger::Instance()->ContextWillBeDestroyed(
ScriptController()->GetScriptState());
WorkletGlobalScope::Dispose();
}
// https://drafts.css-houdini.org/css-layout-api/#dom-layoutworkletglobalscope-registerlayout
void LayoutWorkletGlobalScope::registerLayout(
const AtomicString& name,
V8NoArgumentConstructor* layout_ctor,
ExceptionState& exception_state) {
if (name.IsEmpty()) {
exception_state.ThrowTypeError("The empty string is not a valid name.");
return;
}
if (layout_definitions_.Contains(name)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"A class with name:'" + name + "' is already registered.");
return;
}
if (!layout_ctor->IsConstructor()) {
exception_state.ThrowTypeError(
"The provided callback is not a constructor.");
return;
}
v8::Local<v8::Context> current_context =
layout_ctor->GetIsolate()->GetCurrentContext();
Vector<CSSPropertyID> native_invalidation_properties;
Vector<AtomicString> custom_invalidation_properties;
if (!V8ObjectParser::ParseCSSPropertyList(
current_context, layout_ctor->CallbackObject(), "inputProperties",
&native_invalidation_properties, &custom_invalidation_properties,
&exception_state))
return;
Vector<CSSPropertyID> child_native_invalidation_properties;
Vector<AtomicString> child_custom_invalidation_properties;
if (!V8ObjectParser::ParseCSSPropertyList(
current_context, layout_ctor->CallbackObject(),
"childInputProperties", &child_native_invalidation_properties,
&child_custom_invalidation_properties, &exception_state))
return;
CallbackMethodRetriever retriever(layout_ctor);
retriever.GetPrototypeObject(exception_state);
if (exception_state.HadException())
return;
v8::Local<v8::Function> v8_intrinsic_sizes =
retriever.GetMethodOrThrow("intrinsicSizes", exception_state);
if (exception_state.HadException())
return;
// TODO(ikilpatrick): Make it clear if we really need to check the function
// is a generator function or not. Non generator function can return an
// iterator.
if (!v8_intrinsic_sizes->IsGeneratorFunction()) {
exception_state.ThrowTypeError(
"The 'intrinsicSizes' property on the prototype is not a generator "
"function.");
return;
}
V8Function* intrinsic_sizes = V8Function::Create(v8_intrinsic_sizes);
v8::Local<v8::Function> v8_layout =
retriever.GetMethodOrThrow("layout", exception_state);
if (exception_state.HadException())
return;
// TODO(ikilpatrick): Make it clear if we really need to check the function
// is a generator function or not. Non generator function can return an
// iterator.
if (!v8_layout->IsGeneratorFunction()) {
exception_state.ThrowTypeError(
"The 'layout' property on the prototype is not a generator function.");
return;
}
V8LayoutCallback* layout = V8LayoutCallback::Create(v8_layout);
CSSLayoutDefinition* definition = MakeGarbageCollected<CSSLayoutDefinition>(
ScriptController()->GetScriptState(), layout_ctor, intrinsic_sizes,
layout, native_invalidation_properties, custom_invalidation_properties,
child_native_invalidation_properties,
child_custom_invalidation_properties);
layout_definitions_.Set(name, definition);
LayoutWorklet* layout_worklet =
LayoutWorklet::From(*GetFrame()->GetDocument()->domWindow());
LayoutWorklet::DocumentDefinitionMap* document_definition_map =
layout_worklet->GetDocumentDefinitionMap();
if (document_definition_map->Contains(name)) {
DocumentLayoutDefinition* existing_document_definition =
document_definition_map->at(name);
if (existing_document_definition == kInvalidDocumentLayoutDefinition)
return;
if (!existing_document_definition->RegisterAdditionalLayoutDefinition(
*definition)) {
document_definition_map->Set(name, kInvalidDocumentLayoutDefinition);
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"A class with name:'" + name +
"' was registered with a "
"different definition.");
return;
}
// Notify all of the pending layouts that all of the layout classes with
// |name| have been registered and are ready to use.
if (existing_document_definition->GetRegisteredDefinitionCount() ==
LayoutWorklet::kNumGlobalScopes)
pending_layout_registry_->NotifyLayoutReady(name);
} else {
DocumentLayoutDefinition* document_definition =
MakeGarbageCollected<DocumentLayoutDefinition>(definition);
document_definition_map->Set(name, document_definition);
}
}
CSSLayoutDefinition* LayoutWorkletGlobalScope::FindDefinition(
const AtomicString& name) {
return layout_definitions_.at(name);
}
void LayoutWorkletGlobalScope::Trace(blink::Visitor* visitor) {
visitor->Trace(layout_definitions_);
visitor->Trace(pending_layout_registry_);
WorkletGlobalScope::Trace(visitor);
}
} // namespace blink