blob: 928a8bea4cfd0e2876b80390cc147363a9058851 [file] [log] [blame]
// Copyright 2019 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/ng/custom/ng_custom_layout_algorithm.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_intrinsic_sizes_result_options.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/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h"
#include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h"
#include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
namespace blink {
NGCustomLayoutAlgorithm::NGCustomLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
: NGLayoutAlgorithm(params), params_(params) {
DCHECK(params.space.IsNewFormattingContext());
}
MinMaxSizesResult NGCustomLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& input) const {
if (!Node().IsCustomLayoutLoaded())
return FallbackMinMaxSizes(input);
ScriptForbiddenScope::AllowUserAgentScript allow_script;
CustomLayoutScope scope;
const AtomicString& name = Style().DisplayLayoutCustomName();
const Document& document = Node().GetDocument();
LayoutWorklet* worklet = LayoutWorklet::From(*document.domWindow());
CSSLayoutDefinition* definition = worklet->Proxy()->FindDefinition(name);
// TODO(ikilpatrick): Cache the instance of the layout class.
CSSLayoutDefinition::Instance* instance = definition->CreateInstance();
if (!instance) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackMinMaxSizes(input);
}
bool depends_on_percentage_block_size = false;
IntrinsicSizesResultOptions* intrinsic_sizes_result_options = nullptr;
LogicalSize border_box_size{
container_builder_.InlineSize(),
ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), BorderPadding(),
CalculateDefaultBlockSize(ConstraintSpace(), Node(),
BorderScrollbarPadding()),
container_builder_.InlineSize(),
Node().ShouldBeConsideredAsReplaced())};
if (!instance->IntrinsicSizes(ConstraintSpace(), document, Node(),
border_box_size, BorderScrollbarPadding(),
input.percentage_resolution_block_size, &scope,
&intrinsic_sizes_result_options,
&depends_on_percentage_block_size)) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackMinMaxSizes(input);
}
MinMaxSizes sizes;
sizes.max_size = LayoutUnit::FromDoubleRound(
intrinsic_sizes_result_options->maxContentSize());
sizes.min_size = std::min(
sizes.max_size, LayoutUnit::FromDoubleRound(
intrinsic_sizes_result_options->minContentSize()));
sizes.min_size.ClampNegativeToZero();
sizes.max_size.ClampNegativeToZero();
return MinMaxSizesResult(sizes, depends_on_percentage_block_size);
}
scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() {
DCHECK(!BreakToken());
if (!Node().IsCustomLayoutLoaded())
return FallbackLayout();
ScriptForbiddenScope::AllowUserAgentScript allow_script;
CustomLayoutScope scope;
const AtomicString& name = Style().DisplayLayoutCustomName();
const Document& document = Node().GetDocument();
LayoutWorklet* worklet = LayoutWorklet::From(*document.domWindow());
CSSLayoutDefinition* definition = worklet->Proxy()->FindDefinition(name);
// TODO(ikilpatrick): Cache the instance of the layout class.
CSSLayoutDefinition::Instance* instance = definition->CreateInstance();
if (!instance) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
}
FragmentResultOptions* fragment_result_options = nullptr;
scoped_refptr<SerializedScriptValue> fragment_result_data;
LogicalSize border_box_size{
container_builder_.InlineSize(),
ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), BorderPadding(),
CalculateDefaultBlockSize(ConstraintSpace(), Node(),
BorderScrollbarPadding()),
container_builder_.InlineSize(),
Node().ShouldBeConsideredAsReplaced())};
if (!instance->Layout(ConstraintSpace(), document, Node(), border_box_size,
BorderScrollbarPadding(), &scope,
fragment_result_options, &fragment_result_data)) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
}
const HeapVector<Member<CustomLayoutFragment>>& child_fragments =
fragment_result_options->childFragments();
NGLayoutInputNode child = Node().FirstChild();
for (auto fragment : child_fragments) {
if (!fragment->IsValid()) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
}
AddAnyOutOfFlowPositionedChildren(&child);
// TODO(ikilpatrick): Implement paint order. This should abort this loop,
// and go into a "slow" loop which allows developers to control the paint
// order of the children.
if (!child || child != fragment->GetLayoutNode()) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
}
// TODO(ikilpatrick): At this stage we may need to perform a re-layout on
// the given child. (The LayoutFragment may have been produced from a
// different LayoutFragmentRequest).
LayoutUnit inline_offset =
LayoutUnit::FromDoubleRound(fragment->inlineOffset());
LayoutUnit block_offset =
LayoutUnit::FromDoubleRound(fragment->blockOffset());
container_builder_.AddResult(fragment->GetLayoutResult(),
{inline_offset, block_offset});
child = child.NextSibling();
}
// We've exhausted the inflow fragments list, but we may still have
// OOF-positioned children to add to the fragment builder.
AddAnyOutOfFlowPositionedChildren(&child);
// Currently we only support exactly one LayoutFragment per LayoutChild.
if (child) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
}
// Compute the final block-size.
LayoutUnit auto_block_size = std::max(
BorderScrollbarPadding().BlockSum(),
LayoutUnit::FromDoubleRound(fragment_result_options->autoBlockSize()));
LayoutUnit block_size = ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), BorderPadding(), auto_block_size,
container_builder_.InitialBorderBoxSize().inline_size,
Node().ShouldBeConsideredAsReplaced());
if (fragment_result_options->hasBaseline()) {
LayoutUnit baseline =
LayoutUnit::FromDoubleRound(fragment_result_options->baseline());
container_builder_.SetBaseline(baseline);
}
container_builder_.SetCustomLayoutData(std::move(fragment_result_data));
container_builder_.SetIntrinsicBlockSize(auto_block_size);
container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
return container_builder_.ToBoxFragment();
}
// Seeks forward through any children starting at |child|. If any children are
// OOF-positioned, adds them as a candidate, then proceeds to the next child.
//
// |child| will end up being the next inflow child, or empty.
void NGCustomLayoutAlgorithm::AddAnyOutOfFlowPositionedChildren(
NGLayoutInputNode* child) {
DCHECK(child);
while (*child && child->IsOutOfFlowPositioned()) {
container_builder_.AddOutOfFlowChildCandidate(
To<NGBlockNode>(*child), BorderScrollbarPadding().StartOffset());
*child = child->NextSibling();
}
}
MinMaxSizesResult NGCustomLayoutAlgorithm::FallbackMinMaxSizes(
const MinMaxSizesInput& input) const {
return NGBlockLayoutAlgorithm(params_).ComputeMinMaxSizes(input);
}
scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::FallbackLayout() {
return NGBlockLayoutAlgorithm(params_).Layout();
}
} // namespace blink