diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-24 12:15:48 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:30:04 +0000 |
commit | b014812705fc80bff0a5c120dfcef88f349816dc (patch) | |
tree | 25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/third_party/blink/renderer/core/layout | |
parent | 9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff) | |
download | qtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz |
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout')
316 files changed, 8973 insertions, 4903 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/BUILD.gn b/chromium/third_party/blink/renderer/core/layout/BUILD.gn index 1651d24669c..052db1932e7 100644 --- a/chromium/third_party/blink/renderer/core/layout/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/layout/BUILD.gn @@ -92,6 +92,8 @@ blink_core_sources("layout") { "hit_testing_transform_state.h", "intersection_geometry.cc", "intersection_geometry.h", + "jank_tracker.cc", + "jank_tracker.h", "layout_analyzer.cc", "layout_analyzer.h", "layout_block.cc", @@ -197,8 +199,6 @@ blink_core_sources("layout") { "layout_slider.h", "layout_slider_container.cc", "layout_slider_container.h", - "layout_slider_thumb.cc", - "layout_slider_thumb.h", "layout_state.cc", "layout_state.h", "layout_table.cc", @@ -268,6 +268,8 @@ blink_core_sources("layout") { "line/line_breaker.h", "line/line_info.h", "line/line_layout_state.h", + "line/line_orientation_utils.cc", + "line/line_orientation_utils.h", "line/line_width.cc", "line/line_width.h", "line/root_inline_box.cc", @@ -284,6 +286,10 @@ blink_core_sources("layout") { "multi_column_fragmentainer_group.h", "ng/exclusions/ng_exclusion_space.cc", "ng/exclusions/ng_exclusion_space.h", + "ng/exclusions/ng_layout_opportunity.cc", + "ng/exclusions/ng_layout_opportunity.h", + "ng/exclusions/ng_line_layout_opportunity.h", + "ng/exclusions/ng_shape_exclusions.h", "ng/geometry/ng_bfc_offset.cc", "ng/geometry/ng_bfc_offset.h", "ng/geometry/ng_bfc_rect.cc", @@ -292,7 +298,6 @@ blink_core_sources("layout") { "ng/geometry/ng_border_edges.h", "ng/geometry/ng_box_strut.cc", "ng/geometry/ng_box_strut.h", - "ng/geometry/ng_edge.h", "ng/geometry/ng_logical_offset.cc", "ng/geometry/ng_logical_offset.h", "ng/geometry/ng_logical_rect.cc", @@ -318,6 +323,8 @@ blink_core_sources("layout") { "ng/inline/ng_baseline.h", "ng/inline/ng_bidi_paragraph.cc", "ng/inline/ng_bidi_paragraph.h", + "ng/inline/ng_caret_position.cc", + "ng/inline/ng_caret_position.h", "ng/inline/ng_caret_rect.cc", "ng/inline/ng_caret_rect.h", "ng/inline/ng_inline_box_state.cc", @@ -346,6 +353,10 @@ blink_core_sources("layout") { "ng/inline/ng_line_breaker.h", "ng/inline/ng_line_height_metrics.cc", "ng/inline/ng_line_height_metrics.h", + "ng/inline/ng_line_truncator.cc", + "ng/inline/ng_line_truncator.h", + "ng/inline/ng_line_utils.cc", + "ng/inline/ng_line_utils.h", "ng/inline/ng_offset_mapping.cc", "ng/inline/ng_offset_mapping.h", "ng/inline/ng_offset_mapping_builder.cc", @@ -360,8 +371,12 @@ blink_core_sources("layout") { "ng/inline/ng_text_fragment_builder.h", "ng/layout_ng_block_flow.cc", "ng/layout_ng_block_flow.h", + "ng/layout_ng_flexible_box.cc", + "ng/layout_ng_flexible_box.h", "ng/layout_ng_mixin.cc", "ng/layout_ng_mixin.h", + "ng/layout_ng_table_caption.cc", + "ng/layout_ng_table_caption.h", "ng/layout_ng_table_cell.cc", "ng/layout_ng_table_cell.h", "ng/legacy_layout_tree_walking.cc", @@ -415,11 +430,15 @@ blink_core_sources("layout") { "ng/ng_layout_input_node.h", "ng/ng_layout_result.cc", "ng/ng_layout_result.h", + "ng/ng_layout_utils.cc", + "ng/ng_layout_utils.h", "ng/ng_length_utils.cc", "ng/ng_length_utils.h", "ng/ng_out_of_flow_layout_part.cc", "ng/ng_out_of_flow_layout_part.h", "ng/ng_out_of_flow_positioned_descendant.h", + "ng/ng_outline_utils.cc", + "ng/ng_outline_utils.h", "ng/ng_page_layout_algorithm.cc", "ng/ng_page_layout_algorithm.h", "ng/ng_physical_box_fragment.cc", diff --git a/chromium/third_party/blink/renderer/core/layout/DEPS b/chromium/third_party/blink/renderer/core/layout/DEPS new file mode 100644 index 00000000000..3b34fd02b70 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/DEPS @@ -0,0 +1,5 @@ +specific_include_rules = { + "layout_theme\.cc": [ + "+ui/native_theme/native_theme.h", + ], +} diff --git a/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc b/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc index 804ffe2869e..6e690135457 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc @@ -5,9 +5,9 @@ #include "third_party/blink/renderer/core/layout/custom/css_layout_definition.h" #include <memory> -#include "third_party/blink/renderer/bindings/core/v8/dictionary_iterator.h" #include "third_party/blink/renderer/bindings/core/v8/idl_types.h" #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" +#include "third_party/blink/renderer/bindings/core/v8/script_iterator.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_layout_fragment_request.h" @@ -121,7 +121,7 @@ bool CSSLayoutDefinition::Instance::Layout( v8::Local<v8::Object> generator = v8::Local<v8::Object>::Cast(generator_value); - DictionaryIterator iterator(generator, isolate); + ScriptIterator iterator(generator, isolate); v8::Local<v8::Value> next_value; // We run the generator until it's exhausted. @@ -274,8 +274,7 @@ void CSSLayoutDefinition::Instance::Trace(blink::Visitor* visitor) { visitor->Trace(definition_); } -void CSSLayoutDefinition::TraceWrappers( - const ScriptWrappableVisitor* visitor) const { +void CSSLayoutDefinition::TraceWrappers(ScriptWrappableVisitor* visitor) const { visitor->TraceWrappers(constructor_.Cast<v8::Value>()); visitor->TraceWrappers(intrinsic_sizes_.Cast<v8::Value>()); visitor->TraceWrappers(layout_.Cast<v8::Value>()); diff --git a/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.h b/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.h index cae5fa26f58..c0ff5e73cfc 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/css_layout_definition.h @@ -81,7 +81,7 @@ class CSSLayoutDefinition final } void Trace(blink::Visitor* visitor) {} - void TraceWrappers(const ScriptWrappableVisitor*) const override; + void TraceWrappers(ScriptWrappableVisitor*) const override; const char* NameInHeapSnapshot() const override { return "CSSLayoutDefinition"; } diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_child.h b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_child.h index 3049da7f000..4e1193e15b3 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_child.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_child.h @@ -27,7 +27,7 @@ class CustomLayoutChild : public ScriptWrappable { public: CustomLayoutChild(const CSSLayoutDefinition&, LayoutBox*); - virtual ~CustomLayoutChild() = default; + ~CustomLayoutChild() override = default; // LayoutChild.idl PrepopulatedComputedStylePropertyMap* styleMap() const { return style_map_; } diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints.h b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints.h index f45f5da75ae..7c3d6150412 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints.h @@ -18,7 +18,7 @@ class CustomLayoutConstraints : public ScriptWrappable { public: CustomLayoutConstraints(LayoutUnit fixed_inline_size) : fixed_inline_size_(fixed_inline_size.ToDouble()) {} - virtual ~CustomLayoutConstraints() = default; + ~CustomLayoutConstraints() override = default; // LayoutConstraints.idl double fixedInlineSize() const { return fixed_inline_size_; } diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints_options.idl b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints_options.idl index 47c198f857f..8742a7752a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints_options.idl +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_constraints_options.idl @@ -8,8 +8,8 @@ // don't want the resulting generated class to have a Layout* prefix. // This name is fine, as it doesn't appear to javascript at all. dictionary CustomLayoutConstraintsOptions { - // double availableInlineSize = 0; - // double availableBlockSize = 0; + double availableInlineSize = 0; + double availableBlockSize = 0; double fixedInlineSize; double fixedBlockSize; diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment.h b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment.h index 4970f13030a..8c18e76bb2c 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment.h @@ -31,7 +31,7 @@ class CustomLayoutFragment : public ScriptWrappable { CustomLayoutFragment(CustomLayoutFragmentRequest*, const LayoutUnit inline_size, const LayoutUnit block_size); - virtual ~CustomLayoutFragment() = default; + ~CustomLayoutFragment() override = default; double inlineSize() const { return inline_size_; } double blockSize() const { return block_size_; } diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.cc b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.cc index cf4371e864b..b401bbaf7ce 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.cc @@ -25,12 +25,6 @@ CustomLayoutFragment* CustomLayoutFragmentRequest::PerformLayout() { LayoutBox* box = child_->GetLayoutBox(); const ComputedStyle& style = box->StyleRef(); - // TODO(ikilpatrick): At the moment we just pretend that we are being sized - // off something which is 0x0. Additional fields inside the constraints - // object will allow the developer to override this. - box->SetOverrideContainingBlockContentLogicalWidth(LayoutUnit()); - box->SetOverrideContainingBlockContentLogicalHeight(LayoutUnit()); - DCHECK(box->Parent()); DCHECK(box->Parent()->IsLayoutCustom()); DCHECK(box->Parent() == box->ContainingBlock()); @@ -42,35 +36,43 @@ CustomLayoutFragment* CustomLayoutFragmentRequest::PerformLayout() { if (options_.hasFixedInlineSize()) { if (is_parallel_writing_mode) { - box->SetOverrideLogicalContentWidth( - (LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) - - box->BorderAndPaddingLogicalWidth()) - .ClampNegativeToZero()); + box->SetOverrideLogicalWidth( + LayoutUnit::FromDoubleRound(options_.fixedInlineSize())); + } else { + box->SetOverrideLogicalHeight( + LayoutUnit::FromDoubleRound(options_.fixedInlineSize())); + } + } else { + if (is_parallel_writing_mode) { + box->SetOverrideContainingBlockContentLogicalWidth( + LayoutUnit::FromDoubleRound(options_.availableInlineSize())); } else { - box->SetOverrideLogicalContentHeight( - (LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) - - box->BorderAndPaddingLogicalHeight()) - .ClampNegativeToZero()); + box->SetOverrideContainingBlockContentLogicalHeight( + LayoutUnit::FromDoubleRound(options_.availableInlineSize())); } } if (options_.hasFixedBlockSize()) { if (is_parallel_writing_mode) { - box->SetOverrideLogicalContentHeight( - (LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) - - box->BorderAndPaddingLogicalHeight()) - .ClampNegativeToZero()); + box->SetOverrideLogicalHeight( + LayoutUnit::FromDoubleRound(options_.fixedBlockSize())); + } else { + box->SetOverrideLogicalWidth( + LayoutUnit::FromDoubleRound(options_.fixedBlockSize())); + } + } else { + if (is_parallel_writing_mode) { + box->SetOverrideContainingBlockContentLogicalHeight( + LayoutUnit::FromDoubleRound(options_.availableBlockSize())); } else { - box->SetOverrideLogicalContentWidth( - (LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) - - box->BorderAndPaddingLogicalWidth()) - .ClampNegativeToZero()); + box->SetOverrideContainingBlockContentLogicalWidth( + LayoutUnit::FromDoubleRound(options_.availableBlockSize())); } } box->ForceLayout(); - box->ClearContainingBlockOverrideSize(); + box->ClearOverrideContainingBlockContentSize(); box->ClearOverrideSize(); LayoutUnit fragment_inline_size = diff --git a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.h b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.h index 2c310e4ef4f..60353643e9c 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/custom_layout_fragment_request.h @@ -23,7 +23,7 @@ class CustomLayoutFragmentRequest : public ScriptWrappable { public: CustomLayoutFragmentRequest(CustomLayoutChild*, const CustomLayoutConstraintsOptions&); - virtual ~CustomLayoutFragmentRequest() = default; + ~CustomLayoutFragmentRequest() override = default; // Produces a CustomLayoutFragment from this CustomLayoutFragmentRequest. This // may fail if the underlying LayoutBox represented by the CustomLayoutChild diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.cc b/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.cc index b194b7df0eb..cead5acc48f 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.cc @@ -18,7 +18,27 @@ namespace blink { -LayoutCustom::LayoutCustom(Element* element) : LayoutBlockFlow(element) { +// This scope should be added when about to perform a web-developer defined +// layout. This sets the phase_ flag which changes how children are sized. +class LayoutCustomPhaseScope { + STACK_ALLOCATED(); + + public: + explicit LayoutCustomPhaseScope(LayoutCustom& layout_custom) + : layout_custom_(layout_custom) { + layout_custom_.phase_ = LayoutCustomPhase::kCustom; + } + + ~LayoutCustomPhaseScope() { + layout_custom_.phase_ = LayoutCustomPhase::kFallback; + } + + private: + LayoutCustom& layout_custom_; +}; + +LayoutCustom::LayoutCustom(Element* element) + : LayoutBlockFlow(element), phase_(LayoutCustomPhase::kFallback) { DCHECK(element); } @@ -96,6 +116,8 @@ void LayoutCustom::UpdateBlockLayout(bool relayout_children) { bool LayoutCustom::PerformLayout(bool relayout_children, SubtreeLayoutScope* layout_scope) { + LayoutCustomPhaseScope phase_scope(*this); + // We need to fallback to block layout if we don't have a registered // definition yet. if (state_ == kUnloaded) diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.h b/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.h index 0f8e0f8ca6b..16ac0499d45 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_custom.h @@ -10,11 +10,18 @@ namespace blink { +class LayoutCustomPhaseScope; + // NOTE: In the future there may be a third state "normal", this will mean that // not everything is blockified, (e.g. root inline boxes, so that line-by-line // layout can be performed). enum LayoutCustomState { kUnloaded, kBlock }; +// This enum is used to determine if the current layout is under control of web +// developer defined script, or during a fallback layout pass. +// Sizing of children is different between these two phases. +enum LayoutCustomPhase { kCustom, kFallback }; + // The LayoutObject for elements which have "display: layout(foo);" specified. // https://drafts.css-houdini.org/css-layout-api/ // @@ -27,6 +34,7 @@ class LayoutCustom final : public LayoutBlockFlow { const char* GetName() const override { return "LayoutCustom"; } LayoutCustomState State() const { return state_; } + LayoutCustomPhase Phase() const { return phase_; } bool CreatesNewFormattingContext() const override { return true; } @@ -37,6 +45,8 @@ class LayoutCustom final : public LayoutBlockFlow { void UpdateBlockLayout(bool relayout_children) override; private: + friend class LayoutCustomPhaseScope; + bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectLayoutCustom || LayoutBlockFlow::IsOfType(type); } @@ -44,6 +54,7 @@ class LayoutCustom final : public LayoutBlockFlow { bool PerformLayout(bool relayout_children, SubtreeLayoutScope*); LayoutCustomState state_; + LayoutCustomPhase phase_; Persistent<CSSLayoutDefinition::Instance> instance_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc index 44d03820b1b..06874831cc0 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc @@ -163,7 +163,7 @@ void LayoutWorkletGlobalScope::Trace(blink::Visitor* visitor) { } void LayoutWorkletGlobalScope::TraceWrappers( - const ScriptWrappableVisitor* visitor) const { + ScriptWrappableVisitor* visitor) const { for (auto definition : layout_definitions_) visitor->TraceWrappers(definition.value); MainThreadWorkletGlobalScope::TraceWrappers(visitor); diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h index 6645721c900..a8e7fe0510c 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h @@ -42,7 +42,7 @@ class CORE_EXPORT LayoutWorkletGlobalScope final CSSLayoutDefinition* FindDefinition(const AtomicString& name); void Trace(blink::Visitor*) override; - void TraceWrappers(const ScriptWrappableVisitor*) const override; + void TraceWrappers(ScriptWrappableVisitor*) const override; private: LayoutWorkletGlobalScope(LocalFrame*, diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc index 95a832de401..dad9635a1b0 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.cc @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h" +#include "third_party/blink/renderer/core/script/script.h" #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h" #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -32,7 +33,7 @@ LayoutWorkletGlobalScopeProxy::LayoutWorkletGlobalScopeProxy( std::make_unique<MainThreadWorkletReportingProxy>(document); auto creation_params = std::make_unique<GlobalScopeCreationParams>( - document->Url(), document->UserAgent(), + document->Url(), ScriptType::kModule, document->UserAgent(), document->GetContentSecurityPolicy()->Headers().get(), document->GetReferrerPolicy(), document->GetSecurityOrigin(), document->IsSecureContext(), nullptr /* worker_clients */, diff --git a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.h b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.h index 376eab411d9..a4d1f72a375 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.h +++ b/chromium/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_CUSTOM_LAYOUT_WORKLET_GLOBAL_SCOPE_PROXY_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_CUSTOM_LAYOUT_WORKLET_GLOBAL_SCOPE_PROXY_H_ +#include "base/single_thread_task_runner.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.h" #include "third_party/blink/renderer/core/workers/main_thread_worklet_reporting_proxy.h" diff --git a/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.cc b/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.cc index afef423888f..7161b41cf78 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.cc @@ -168,14 +168,14 @@ LayoutUnit GridBaselineAlignment::BaselineOffsetForChild( return LayoutUnit(); } -Optional<LayoutUnit> GridBaselineAlignment::ExtentForBaselineAlignment( +base::Optional<LayoutUnit> GridBaselineAlignment::ExtentForBaselineAlignment( ItemPosition preference, unsigned shared_context, const LayoutBox& child, GridAxis baseline_axis) const { DCHECK(IsBaselinePosition(preference)); if (!IsBaselineContextComputed(baseline_axis)) - return WTF::nullopt; + return base::nullopt; auto& group = GetBaselineGroupForChild(preference, shared_context, child, baseline_axis); diff --git a/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.h b/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.h index 58cd74e61c9..a585074e82b 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.h +++ b/chromium/third_party/blink/renderer/core/layout/grid_baseline_alignment.h @@ -156,10 +156,10 @@ class GridBaselineAlignment { // Returns the sum of the 'max-ascent' and 'max-descent' of a particular // item's baseline-sharing group. - Optional<LayoutUnit> ExtentForBaselineAlignment(ItemPosition, - unsigned shared_context, - const LayoutBox&, - GridAxis) const; + base::Optional<LayoutUnit> ExtentForBaselineAlignment(ItemPosition, + unsigned shared_context, + const LayoutBox&, + GridAxis) const; // Determines whether baseline algnment may affect the intrinsic // size of the grid container. diff --git a/chromium/third_party/blink/renderer/core/layout/grid_layout_utils.cc b/chromium/third_party/blink/renderer/core/layout/grid_layout_utils.cc index a3a4494622d..016f9c577a3 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_layout_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid_layout_utils.cc @@ -112,8 +112,8 @@ bool GridLayoutUtils::HasOverrideContainingBlockContentSizeForChild( const LayoutBox& child, GridTrackSizingDirection direction) { return direction == kForColumns - ? child.HasOverrideContainingBlockLogicalWidth() - : child.HasOverrideContainingBlockLogicalHeight(); + ? child.HasOverrideContainingBlockContentLogicalWidth() + : child.HasOverrideContainingBlockContentLogicalHeight(); } LayoutUnit GridLayoutUtils::OverrideContainingBlockContentSizeForChild( diff --git a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc index 19ca6991f23..950974a81c5 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc @@ -64,7 +64,7 @@ void GridTrack::SetInfinitelyGrowable(bool infinitely_growable) { infinitely_growable_ = infinitely_growable; } -void GridTrack::SetGrowthLimitCap(Optional<LayoutUnit> growth_limit_cap) { +void GridTrack::SetGrowthLimitCap(base::Optional<LayoutUnit> growth_limit_cap) { DCHECK(!growth_limit_cap || *growth_limit_cap >= 0); growth_limit_cap_ = growth_limit_cap; } @@ -88,16 +88,19 @@ class IndefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { LayoutBox&, bool override_size_has_changed) const override; void MaximizeTracks(Vector<GridTrack>&, - Optional<LayoutUnit>& free_space) override; - double FindUsedFlexFraction(Vector<size_t>& flexible_sized_tracks_index, - GridTrackSizingDirection, - Optional<LayoutUnit> free_space) const override; + base::Optional<LayoutUnit>& free_space) override; + double FindUsedFlexFraction( + Vector<size_t>& flexible_sized_tracks_index, + GridTrackSizingDirection, + base::Optional<LayoutUnit> free_space) const override; bool RecomputeUsedFlexFractionIfNeeded( Vector<size_t>& flexible_sized_tracks_index, double& flex_fraction, Vector<LayoutUnit>& increments, LayoutUnit& total_growth) const override; LayoutUnit FreeSpaceForStretchAutoTracksStep() const override; + LayoutUnit MinContentForChild(LayoutBox&) const override; + LayoutUnit MaxContentForChild(LayoutBox&) const override; }; class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { @@ -110,10 +113,11 @@ class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { LayoutBox&, bool override_size_has_changed) const override; void MaximizeTracks(Vector<GridTrack>&, - Optional<LayoutUnit>& free_space) override; - double FindUsedFlexFraction(Vector<size_t>& flexible_sized_tracks_index, - GridTrackSizingDirection, - Optional<LayoutUnit> free_space) const override; + base::Optional<LayoutUnit>& free_space) override; + double FindUsedFlexFraction( + Vector<size_t>& flexible_sized_tracks_index, + GridTrackSizingDirection, + base::Optional<LayoutUnit> free_space) const override; bool RecomputeUsedFlexFractionIfNeeded( Vector<size_t>& flexible_sized_tracks_index, double& flex_fraction, @@ -124,13 +128,6 @@ class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { LayoutUnit FreeSpaceForStretchAutoTracksStep() const override; }; -void GridTrackSizingAlgorithmStrategy::SetNeedsLayoutForChild( - LayoutBox& child) const { - if (algorithm_.is_in_perform_layout_) { - child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); - } -} - GridTrackSizingAlgorithmStrategy::~GridTrackSizingAlgorithmStrategy() = default; bool GridTrackSizingAlgorithmStrategy:: @@ -159,40 +156,14 @@ void GridTrackSizingAlgorithmStrategy:: child.SetOverrideContainingBlockContentLogicalHeight(size); } -LayoutUnit GridTrackSizingAlgorithm::AssumedRowsSizeForOrthogonalChild( - const LayoutBox& child) const { - DCHECK(GridLayoutUtils::IsOrthogonalChild(*layout_grid_, child)); - const GridSpan& span = grid_.GridItemSpan(child, kForRows); - LayoutUnit grid_area_size; - bool grid_area_is_indefinite = false; - LayoutUnit containing_block_available_size = - layout_grid_->ContainingBlockLogicalHeightForContent( - kExcludeMarginBorderPadding); - for (auto track_position : span) { - GridLength max_track_size = - GetGridTrackSize(kForRows, track_position).MaxTrackBreadth(); - if (max_track_size.IsContentSized() || max_track_size.IsFlex()) { - grid_area_is_indefinite = true; - } else { - grid_area_size += ValueForLength(max_track_size.length(), - containing_block_available_size); - } - } - - grid_area_size += - layout_grid_->GuttersSize(grid_, kForRows, span.StartLine(), - span.IntegerSpan(), AvailableSpace(kForRows)); - - return grid_area_is_indefinite - ? std::max(child.MaxPreferredLogicalWidth(), grid_area_size) - : grid_area_size; -} - LayoutUnit GridTrackSizingAlgorithm::GridAreaBreadthForChild( const LayoutBox& child, GridTrackSizingDirection direction) { - if (direction == kForRows && sizing_state_ == kColumnSizingFirstIteration) - return AssumedRowsSizeForOrthogonalChild(child); + if (direction == kForRows && sizing_state_ == kColumnSizingFirstIteration) { + DCHECK(GridLayoutUtils::IsOrthogonalChild(*layout_grid_, child)); + return layout_grid_->EstimatedGridAreaBreadthForChild(grid_, child, + kForRows); + } Vector<GridTrack>& all_tracks = Tracks(direction); const GridSpan& span = grid_.GridItemSpan(child, direction); @@ -211,12 +182,10 @@ bool GridTrackSizingAlgorithmStrategy:: UpdateOverrideContainingBlockContentSizeForChild( LayoutBox& child, GridTrackSizingDirection direction, - Optional<LayoutUnit> override_size) const { + base::Optional<LayoutUnit> override_size) const { if (!override_size) override_size = algorithm_.GridAreaBreadthForChild(child, direction); - if (GridLayoutUtils::HasOverrideContainingBlockContentSizeForChild( - child, direction) && - GridLayoutUtils::OverrideContainingBlockContentSizeForChild( + if (GridLayoutUtils::OverrideContainingBlockContentSizeForChild( child, direction) == override_size.value()) return false; @@ -225,7 +194,7 @@ bool GridTrackSizingAlgorithmStrategy:: return true; } -Optional<LayoutUnit> +base::Optional<LayoutUnit> GridTrackSizingAlgorithmStrategy::ExtentForBaselineAlignment( const LayoutBox& child) const { const LayoutGrid& layout_grid = *GetLayoutGrid(); @@ -234,7 +203,7 @@ GridTrackSizingAlgorithmStrategy::ExtentForBaselineAlignment( ? kGridRowAxis : kGridColumnAxis; if (!layout_grid.IsBaselineAlignmentForChild(child, baseline_axis)) - return WTF::nullopt; + return base::nullopt; ItemPosition align = layout_grid.SelfAlignmentForChild(baseline_axis, child).GetPosition(); @@ -260,7 +229,7 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::LogicalHeightForChild( *GetLayoutGrid(), child, child_block_direction)) { SetOverrideContainingBlockContentSizeForChild(child, child_block_direction, LayoutUnit(-1)); - SetNeedsLayoutForChild(child); + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); } child.LayoutIfNeeded(); @@ -294,7 +263,7 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::MinContentForChild( if (UpdateOverrideContainingBlockContentSizeForChild( child, child_inline_direction)) { - SetNeedsLayoutForChild(child); + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); } return LogicalHeightForChild(child); } @@ -315,7 +284,7 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::MaxContentForChild( if (UpdateOverrideContainingBlockContentSizeForChild( child, child_inline_direction)) { - SetNeedsLayoutForChild(child); + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); } return LogicalHeightForChild(child); } @@ -444,13 +413,15 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::MinLogicalWidthForChild( void DefiniteSizeStrategy::LayoutGridItemForMinSizeComputation( LayoutBox& child, bool override_size_has_changed) const { - if (override_size_has_changed) - SetNeedsLayoutForChild(child); - child.LayoutIfNeeded(); + if (override_size_has_changed) { + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); + child.LayoutIfNeeded(); + } } -void DefiniteSizeStrategy::MaximizeTracks(Vector<GridTrack>& tracks, - Optional<LayoutUnit>& free_space) { +void DefiniteSizeStrategy::MaximizeTracks( + Vector<GridTrack>& tracks, + base::Optional<LayoutUnit>& free_space) { size_t tracks_size = tracks.size(); Vector<GridTrack*> tracks_for_distribution(tracks_size); for (size_t i = 0; i < tracks_size; ++i) { @@ -469,7 +440,7 @@ void DefiniteSizeStrategy::MaximizeTracks(Vector<GridTrack>& tracks, double DefiniteSizeStrategy::FindUsedFlexFraction( Vector<size_t>& flexible_sized_tracks_index, GridTrackSizingDirection direction, - Optional<LayoutUnit> free_space) const { + base::Optional<LayoutUnit> free_space) const { GridSpan all_tracks_span = GridSpan::TranslatedDefiniteGridSpan( 0, algorithm_.Tracks(direction).size()); DCHECK(free_space); @@ -484,13 +455,14 @@ LayoutUnit DefiniteSizeStrategy::FreeSpaceForStretchAutoTracksStep() const { void IndefiniteSizeStrategy::LayoutGridItemForMinSizeComputation( LayoutBox& child, bool override_size_has_changed) const { - if (override_size_has_changed && Direction() != kForColumns) - SetNeedsLayoutForChild(child); - child.LayoutIfNeeded(); + if (override_size_has_changed && Direction() != kForColumns) { + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); + child.LayoutIfNeeded(); + } } void IndefiniteSizeStrategy::MaximizeTracks(Vector<GridTrack>& tracks, - Optional<LayoutUnit>&) { + base::Optional<LayoutUnit>&) { for (auto& track : tracks) track.SetBaseSize(track.GrowthLimit()); } @@ -503,7 +475,7 @@ static inline double NormalizedFlexFraction(const GridTrack& track, double IndefiniteSizeStrategy::FindUsedFlexFraction( Vector<size_t>& flexible_sized_tracks_index, GridTrackSizingDirection direction, - Optional<LayoutUnit>) const { + base::Optional<LayoutUnit>) const { auto all_tracks = algorithm_.Tracks(direction); double flex_fraction = 0; @@ -593,24 +565,61 @@ LayoutUnit IndefiniteSizeStrategy::FreeSpaceForStretchAutoTracksStep() const { return min_size - ComputeTrackBasedSize(); } -Optional<LayoutUnit> GridTrackSizingAlgorithm::FreeSpace( +DISABLE_CFI_PERF +LayoutUnit IndefiniteSizeStrategy::MinContentForChild(LayoutBox& child) const { + GridTrackSizingDirection child_inline_direction = + GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, + kForColumns); + if (Direction() == child_inline_direction || Direction() == kForRows) + return GridTrackSizingAlgorithmStrategy::MinContentForChild(child); + + // This code is executed only when computing the grid's intrinsic + // width based on an orthogonal child. We rely on the pre-layout + // performed in LayoutGrid::LayoutOrthogonalWritingModeRoots. + DCHECK(GridLayoutUtils::IsOrthogonalChild(*GetLayoutGrid(), child)); + + if (auto baseline_extent = ExtentForBaselineAlignment(child)) + return baseline_extent.value(); + + return child.LogicalHeight() + + GridLayoutUtils::MarginLogicalHeightForChild(*GetLayoutGrid(), child); +} + +DISABLE_CFI_PERF +LayoutUnit IndefiniteSizeStrategy::MaxContentForChild(LayoutBox& child) const { + GridTrackSizingDirection child_inline_direction = + GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, + kForColumns); + if (Direction() == child_inline_direction || Direction() == kForRows) + return GridTrackSizingAlgorithmStrategy::MaxContentForChild(child); + + // This code is executed only when computing the grid's intrinsic + // width based on an orthogonal child. We rely on the pre-layout + // performed in LayoutGrid::LayoutOrthogonalWritingModeRoots. + DCHECK(GridLayoutUtils::IsOrthogonalChild(*GetLayoutGrid(), child)); + + return child.LogicalHeight() + + GridLayoutUtils::MarginLogicalHeightForChild(*GetLayoutGrid(), child); +} + +base::Optional<LayoutUnit> GridTrackSizingAlgorithm::FreeSpace( GridTrackSizingDirection direction) const { return direction == kForRows ? free_space_rows_ : free_space_columns_; } -Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace( +base::Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace( GridTrackSizingDirection direction) const { return direction == kForRows ? available_space_rows_ : available_space_columns_; } -Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace() const { +base::Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace() const { return AvailableSpace(direction_); } void GridTrackSizingAlgorithm::SetAvailableSpace( GridTrackSizingDirection direction, - Optional<LayoutUnit> available_space) { + base::Optional<LayoutUnit> available_space) { if (direction == kForColumns) available_space_columns_ = available_space; else @@ -627,8 +636,9 @@ const Vector<GridTrack>& GridTrackSizingAlgorithm::Tracks( return direction == kForColumns ? columns_ : rows_; } -void GridTrackSizingAlgorithm::SetFreeSpace(GridTrackSizingDirection direction, - Optional<LayoutUnit> free_space) { +void GridTrackSizingAlgorithm::SetFreeSpace( + GridTrackSizingDirection direction, + base::Optional<LayoutUnit> free_space) { if (direction == kForColumns) free_space_columns_ = free_space; else @@ -1242,7 +1252,7 @@ void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() { max_content_size_ += track.GrowthLimit(); // The growth limit caps must be cleared now in order to properly sort // tracks by growth potential on an eventual "Maximize Tracks". - track.SetGrowthLimitCap(WTF::nullopt); + track.SetGrowthLimitCap(base::nullopt); } } @@ -1351,7 +1361,7 @@ void GridTrackSizingAlgorithm::ComputeFlexSizedTracksGrowth( } void GridTrackSizingAlgorithm::StretchFlexibleTracks( - Optional<LayoutUnit> free_space) { + base::Optional<LayoutUnit> free_space) { if (flexible_sized_tracks_index_.IsEmpty()) return; @@ -1434,22 +1444,20 @@ bool GridTrackSizingAlgorithm::IsValidTransition() const { return false; } -void GridTrackSizingAlgorithm::Setup(GridTrackSizingDirection direction, - size_t num_tracks, - Optional<LayoutUnit> available_space) { +void GridTrackSizingAlgorithm::Setup( + GridTrackSizingDirection direction, + size_t num_tracks, + base::Optional<LayoutUnit> available_space) { DCHECK(needs_setup_); direction_ = direction; SetAvailableSpace( direction, available_space ? available_space.value().ClampNegativeToZero() : available_space); - if (available_space) { + if (available_space) strategy_ = std::make_unique<DefiniteSizeStrategy>(*this); - } else { + else strategy_ = std::make_unique<IndefiniteSizeStrategy>(*this); - is_in_perform_layout_ = - layout_grid_->GetDocument().View()->IsInPerformLayout(); - } content_sized_tracks_index_.Shrink(0); flexible_sized_tracks_index_.Shrink(0); @@ -1460,7 +1468,7 @@ void GridTrackSizingAlgorithm::Setup(GridTrackSizingDirection direction, grid_, direction, 0, grid_.NumTracks(direction), available_space); SetFreeSpace(direction, available_space.value() - gutters_size); } else { - SetFreeSpace(direction, WTF::nullopt); + SetFreeSpace(direction, base::nullopt); } Tracks(direction).resize(num_tracks); @@ -1474,7 +1482,7 @@ void GridTrackSizingAlgorithm::Run() { StateMachine state_machine(*this); // Step 1. - Optional<LayoutUnit> initial_free_space = FreeSpace(direction_); + base::Optional<LayoutUnit> initial_free_space = FreeSpace(direction_); InitializeTrackSizes(); // Step 2. @@ -1514,8 +1522,8 @@ void GridTrackSizingAlgorithm::Reset() { content_sized_tracks_index_.Shrink(0); flexible_sized_tracks_index_.Shrink(0); auto_sized_tracks_for_stretch_index_.Shrink(0); - SetAvailableSpace(kForRows, WTF::nullopt); - SetAvailableSpace(kForColumns, WTF::nullopt); + SetAvailableSpace(kForRows, base::nullopt); + SetAvailableSpace(kForColumns, base::nullopt); } #if DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h index 0b471f6eb70..07622f71df3 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h @@ -7,13 +7,13 @@ #include <memory> #include "base/macros.h" +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/grid_baseline_alignment.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/style/grid_positions_resolver.h" #include "third_party/blink/renderer/core/style/grid_track_size.h" #include "third_party/blink/renderer/platform/layout_unit.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { @@ -58,8 +58,10 @@ class GridTrack { bool InfinitelyGrowable() const { return infinitely_growable_; } void SetInfinitelyGrowable(bool); - Optional<LayoutUnit> GrowthLimitCap() const { return growth_limit_cap_; } - void SetGrowthLimitCap(Optional<LayoutUnit>); + base::Optional<LayoutUnit> GrowthLimitCap() const { + return growth_limit_cap_; + } + void SetGrowthLimitCap(base::Optional<LayoutUnit>); private: bool IsGrowthLimitBiggerThanBaseSize() const; @@ -69,7 +71,7 @@ class GridTrack { LayoutUnit growth_limit_; LayoutUnit planned_size_; LayoutUnit size_during_distribution_; - Optional<LayoutUnit> growth_limit_cap_; + base::Optional<LayoutUnit> growth_limit_cap_; bool infinitely_growable_; }; @@ -86,7 +88,7 @@ class GridTrackSizingAlgorithm final { // the algorithm. void Setup(GridTrackSizingDirection, size_t num_tracks, - Optional<LayoutUnit> available_space); + base::Optional<LayoutUnit> available_space); void Run(); void Reset(); @@ -108,21 +110,20 @@ class GridTrackSizingAlgorithm final { Vector<GridTrack>& Tracks(GridTrackSizingDirection); const Vector<GridTrack>& Tracks(GridTrackSizingDirection) const; - Optional<LayoutUnit> FreeSpace(GridTrackSizingDirection) const; - void SetFreeSpace(GridTrackSizingDirection, Optional<LayoutUnit>); + base::Optional<LayoutUnit> FreeSpace(GridTrackSizingDirection) const; + void SetFreeSpace(GridTrackSizingDirection, base::Optional<LayoutUnit>); - Optional<LayoutUnit> AvailableSpace(GridTrackSizingDirection) const; - void SetAvailableSpace(GridTrackSizingDirection, Optional<LayoutUnit>); + base::Optional<LayoutUnit> AvailableSpace(GridTrackSizingDirection) const; + void SetAvailableSpace(GridTrackSizingDirection, base::Optional<LayoutUnit>); #if DCHECK_IS_ON() bool TracksAreWiderThanMinTrackBreadth() const; #endif private: - Optional<LayoutUnit> AvailableSpace() const; + base::Optional<LayoutUnit> AvailableSpace() const; GridTrackSize RawGridTrackSize(GridTrackSizingDirection, size_t translated_index) const; - LayoutUnit AssumedRowsSizeForOrthogonalChild(const LayoutBox&) const; LayoutUnit ComputeTrackBasedSize() const; // Helper methods for step 1. initializeTrackSizes(). @@ -174,7 +175,7 @@ class GridTrackSizingAlgorithm final { // method at thise level. void InitializeTrackSizes(); void ResolveIntrinsicTrackSizes(); - void StretchFlexibleTracks(Optional<LayoutUnit> free_space); + void StretchFlexibleTracks(base::Optional<LayoutUnit> free_space); void StretchAutoTracks(); // State machine. @@ -183,12 +184,11 @@ class GridTrackSizingAlgorithm final { // Data. bool needs_setup_{true}; - bool is_in_perform_layout_{true}; - Optional<LayoutUnit> available_space_columns_; - Optional<LayoutUnit> available_space_rows_; + base::Optional<LayoutUnit> available_space_columns_; + base::Optional<LayoutUnit> available_space_rows_; - Optional<LayoutUnit> free_space_columns_; - Optional<LayoutUnit> free_space_rows_; + base::Optional<LayoutUnit> free_space_columns_; + base::Optional<LayoutUnit> free_space_rows_; // We need to keep both alive in order to properly size grids with orthogonal // writing modes. @@ -243,16 +243,16 @@ class GridTrackSizingAlgorithmStrategy { public: virtual ~GridTrackSizingAlgorithmStrategy(); - LayoutUnit MinContentForChild(LayoutBox&) const; - LayoutUnit MaxContentForChild(LayoutBox&) const; + virtual LayoutUnit MinContentForChild(LayoutBox&) const; + virtual LayoutUnit MaxContentForChild(LayoutBox&) const; LayoutUnit MinSizeForChild(LayoutBox&) const; virtual void MaximizeTracks(Vector<GridTrack>&, - Optional<LayoutUnit>& free_space) = 0; + base::Optional<LayoutUnit>& free_space) = 0; virtual double FindUsedFlexFraction( Vector<size_t>& flexible_sized_tracks_index, GridTrackSizingDirection, - Optional<LayoutUnit> initial_free_space) const = 0; + base::Optional<LayoutUnit> initial_free_space) const = 0; virtual bool RecomputeUsedFlexFractionIfNeeded( Vector<size_t>& flexible_sized_tracks_index, double& flex_fraction, @@ -276,10 +276,11 @@ class GridTrackSizingAlgorithmStrategy { bool UpdateOverrideContainingBlockContentSizeForChild( LayoutBox&, GridTrackSizingDirection, - Optional<LayoutUnit> = WTF::nullopt) const; + base::Optional<LayoutUnit> = base::nullopt) const; LayoutUnit ComputeTrackBasedSize() const; - Optional<LayoutUnit> ExtentForBaselineAlignment(const LayoutBox& child) const; + base::Optional<LayoutUnit> ExtentForBaselineAlignment( + const LayoutBox& child) const; GridTrackSizingDirection Direction() const { return algorithm_.direction_; } double FindFrUnitSize(const GridSpan& tracks_span, @@ -287,18 +288,11 @@ class GridTrackSizingAlgorithmStrategy { void DistributeSpaceToTracks(Vector<GridTrack*>& tracks, LayoutUnit& available_logical_space) const; const LayoutGrid* GetLayoutGrid() const { return algorithm_.layout_grid_; } - Optional<LayoutUnit> AvailableSpace() const { + base::Optional<LayoutUnit> AvailableSpace() const { return algorithm_.AvailableSpace(); } - void SetNeedsLayoutForChild(LayoutBox&) const; // Helper functions - static bool HasOverrideContainingBlockContentSizeForChild( - const LayoutBox& child, - GridTrackSizingDirection); - static LayoutUnit OverrideContainingBlockContentSizeForChild( - const LayoutBox& child, - GridTrackSizingDirection); static bool ShouldClearOverrideContainingBlockContentSizeForChild( const LayoutGrid&, const LayoutBox& child, @@ -307,10 +301,6 @@ class GridTrackSizingAlgorithmStrategy { LayoutBox& child, GridTrackSizingDirection, LayoutUnit size); - static GridTrackSizingDirection FlowAwareDirectionForChild( - const LayoutGrid*, - const LayoutBox& child, - GridTrackSizingDirection); GridTrackSizingAlgorithm& algorithm_; diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_request.h b/chromium/third_party/blink/renderer/core/layout/hit_test_request.h index b7b41677e1f..e7626ce8bb2 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_request.h +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_request.h @@ -28,6 +28,8 @@ namespace blink { +class LayoutObject; + class HitTestRequest { DISALLOW_NEW(); @@ -54,8 +56,9 @@ class HitTestRequest { typedef unsigned HitTestRequestType; - HitTestRequest(HitTestRequestType request_type) - : request_type_(request_type) { + HitTestRequest(HitTestRequestType request_type, + const LayoutObject* stop_node = nullptr) + : request_type_(request_type), stop_node_(stop_node) { // Penetrating lists should also be list-based. DCHECK(!(request_type & kPenetratingList) || (request_type & kListBased)); } @@ -84,6 +87,7 @@ class HitTestRequest { bool TouchMove() const { return Move() && TouchEvent(); } HitTestRequestType GetType() const { return request_type_; } + const LayoutObject* GetStopNode() const { return stop_node_; } // The Cacheability bits don't affect hit testing computation. // TODO(dtapuska): These bits really shouldn't be fields on the HitTestRequest @@ -93,11 +97,14 @@ class HitTestRequest { kReadOnly | kActive | kMove | kRelease | kTouchEvent; bool EqualForCacheability(const HitTestRequest& value) const { return (request_type_ | kCacheabilityBits) == - (value.request_type_ | kCacheabilityBits); + (value.request_type_ | kCacheabilityBits) && + stop_node_ == value.stop_node_; } private: HitTestRequestType request_type_; + // If non-null, do not hit test the children of this object. + const LayoutObject* stop_node_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc b/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc index 3a3d4859ffd..2a15d5471d7 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc @@ -252,7 +252,7 @@ String HitTestResult::Title(TextDirection& dir) const { // For <area> tags in image maps, walk the tree for the <area>, not the <img> // using it. if (inner_node_.Get()) - inner_node_->UpdateDistribution(); + inner_node_->UpdateDistributionForFlatTreeTraversal(); for (Node* title_node = inner_node_.Get(); title_node; title_node = FlatTreeTraversal::Parent(*title_node)) { if (title_node->IsElementNode()) { diff --git a/chromium/third_party/blink/renderer/core/layout/intersection_geometry.cc b/chromium/third_party/blink/renderer/core/layout/intersection_geometry.cc index 71e2985900e..c1e890d17fc 100644 --- a/chromium/third_party/blink/renderer/core/layout/intersection_geometry.cc +++ b/chromium/third_party/blink/renderer/core/layout/intersection_geometry.cc @@ -113,14 +113,9 @@ void IntersectionGeometry::InitializeTargetRect() { } void IntersectionGeometry::InitializeRootRect() { - if (root_->IsLayoutView() && - !RuntimeEnabledFeatures::RootLayerScrollingEnabled()) { - root_rect_ = LayoutRect(root_->GetFrameView()->VisibleContentRect()); - root_->MapToVisualRectInAncestorSpace(nullptr, root_rect_); - } else if (root_->IsLayoutView() && root_->GetDocument().IsInMainFrame()) { - // The main frame is a bit special (even after RLS) as the scrolling - // viewport can differ in size from the LayoutView itself. There's two - // situations this occurs in: + if (root_->IsLayoutView() && root_->GetDocument().IsInMainFrame()) { + // The main frame is a bit special as the scrolling viewport can differ in + // size from the LayoutView itself. There's two situations this occurs in: // 1) The ForceZeroLayoutHeight quirk setting is used in Android WebView for // compatibility and sets the initial-containing-block's (a.k.a. // LayoutView) height to 0. Thus, we can't use its size for intersection @@ -160,8 +155,12 @@ void IntersectionGeometry::ClipToRoot() { LayoutBox* local_ancestor = nullptr; if (!RootIsImplicit() || root_->GetDocument().IsInMainFrame()) local_ancestor = ToLayoutBox(root_); + VisualRectFlags flags = static_cast<VisualRectFlags>( + RuntimeEnabledFeatures::IntersectionObserverGeometryMapperEnabled() + ? (kUseGeometryMapper | kEdgeInclusive) + : kEdgeInclusive); does_intersect_ = target_->MapToVisualRectInAncestorSpace( - local_ancestor, intersection_rect_, kEdgeInclusive); + local_ancestor, intersection_rect_, flags); if (!does_intersect_ || !local_ancestor) return; if (local_ancestor->HasOverflowClip()) diff --git a/chromium/third_party/blink/renderer/core/layout/jank_tracker.cc b/chromium/third_party/blink/renderer/core/layout/jank_tracker.cc new file mode 100644 index 00000000000..3bb95502bab --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/jank_tracker.cc @@ -0,0 +1,161 @@ +// 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/jank_tracker.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/frame/location.h" +#include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" + +namespace blink { + +static const float kTimerDelay = 3.0; + +static FloatPoint LogicalStart(const FloatRect& rect, + const LayoutObject& object) { + const ComputedStyle* style = object.Style(); + DCHECK(style); + auto logical = + PhysicalToLogical<float>(style->GetWritingMode(), style->Direction(), + rect.Y(), rect.MaxX(), rect.MaxY(), rect.X()); + return FloatPoint(logical.InlineStart(), logical.BlockStart()); +} + +static float GetMoveDistance(const FloatRect& old_rect, + const FloatRect& new_rect, + const LayoutObject& object) { + FloatSize location_delta = + LogicalStart(new_rect, object) - LogicalStart(old_rect, object); + return std::max(fabs(location_delta.Width()), fabs(location_delta.Height())); +} + +JankTracker::JankTracker(LocalFrameView* frame_view) + : frame_view_(frame_view), + score_(0.0), + timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault), + this, + &JankTracker::TimerFired), + has_fired_(false), + max_distance_(0.0) {} + +void JankTracker::NotifyObjectPrePaint(const LayoutObject& object, + const LayoutRect& old_visual_rect, + const PaintLayer& painting_layer) { + if (!IsActive()) + return; + + LayoutRect new_visual_rect = object.FirstFragment().VisualRect(); + if (old_visual_rect.IsEmpty() || new_visual_rect.IsEmpty()) + return; + + if (LogicalStart(FloatRect(old_visual_rect), object) == + LogicalStart(FloatRect(new_visual_rect), object)) + return; + + const auto* local_transform = painting_layer.GetLayoutObject() + .FirstFragment() + .LocalBorderBoxProperties() + .Transform(); + const auto* ancestor_transform = painting_layer.GetLayoutObject() + .View() + ->FirstFragment() + .LocalBorderBoxProperties() + .Transform(); + + FloatRect old_visual_rect_abs = FloatRect(old_visual_rect); + GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform, + old_visual_rect_abs); + + FloatRect new_visual_rect_abs = FloatRect(new_visual_rect); + GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform, + new_visual_rect_abs); + + // TOOD(crbug.com/842282): Consider tracking a separate jank score for each + // transform space to avoid these local-to-absolute conversions, once we have + // a better idea of how to aggregate multiple scores for a page. + // See review thread of http://crrev.com/c/1046155 for more details. + + IntRect viewport = frame_view_->GetScrollableArea()->VisibleContentRect(); + if (!old_visual_rect_abs.Intersects(viewport) && + !new_visual_rect_abs.Intersects(viewport)) + return; + + DVLOG(2) << object.DebugName() << " moved from " + << old_visual_rect_abs.ToString() << " to " + << new_visual_rect_abs.ToString(); + + max_distance_ = std::max( + max_distance_, + GetMoveDistance(old_visual_rect_abs, new_visual_rect_abs, object)); + + IntRect visible_old_visual_rect = RoundedIntRect(old_visual_rect_abs); + visible_old_visual_rect.Intersect(viewport); + + IntRect visible_new_visual_rect = RoundedIntRect(new_visual_rect_abs); + visible_new_visual_rect.Intersect(viewport); + + region_.Unite(Region(visible_old_visual_rect)); + region_.Unite(Region(visible_new_visual_rect)); +} + +void JankTracker::NotifyPrePaintFinished() { + if (!IsActive()) + return; + + if (region_.IsEmpty()) { + if (!timer_.IsActive()) + timer_.StartOneShot(kTimerDelay, FROM_HERE); + return; + } + + IntRect viewport = frame_view_->GetScrollableArea()->VisibleContentRect(); + double viewport_area = double(viewport.Width()) * double(viewport.Height()); + + double jank_fraction = region_.Area() / viewport_area; + score_ += jank_fraction; + + DVLOG(1) << "viewport " << (jank_fraction * 100) + << "% janked, raising score to " << score_; + + TRACE_EVENT_INSTANT1("blink", "FrameLayoutJank", TRACE_EVENT_SCOPE_THREAD, + "viewportFraction", jank_fraction); + + region_ = Region(); + + // This cancels any previously scheduled task from the same timer. + timer_.StartOneShot(kTimerDelay, FROM_HERE); +} + +bool JankTracker::IsActive() { + // This eliminates noise from the private Page object created by + // SVGImage::DataChanged. + if (frame_view_->GetFrame().GetChromeClient().IsSVGImageChromeClient()) + return false; + + if (has_fired_) + return false; + return true; +} + +void JankTracker::TimerFired(TimerBase* timer) { + has_fired_ = true; + + // TODO(skobes): Aggregate jank scores from iframes. + if (!frame_view_->GetFrame().IsMainFrame()) + return; + + DVLOG(1) << "final jank score for " + << frame_view_->GetFrame().DomWindow()->location()->toString() + << " is " << score_ << " with max move distance of " + << max_distance_; + + TRACE_EVENT_INSTANT2("blink", "TotalLayoutJank", TRACE_EVENT_SCOPE_THREAD, + "score", score_, "maxDistance", max_distance_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/jank_tracker.h b/chromium/third_party/blink/renderer/core/layout/jank_tracker.h new file mode 100644 index 00000000000..ee8a533d9a5 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/jank_tracker.h @@ -0,0 +1,58 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_JANK_TRACKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_JANK_TRACKER_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/geometry/region.h" +#include "third_party/blink/renderer/platform/timer.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" + +namespace blink { + +class LayoutObject; +class LayoutRect; +class LocalFrameView; +class PaintLayer; + +// Tracks "jank" from layout objects changing their visual location between +// animation frames. +class CORE_EXPORT JankTracker { + DISALLOW_NEW(); + + public: + JankTracker(LocalFrameView*); + ~JankTracker() {} + void NotifyObjectPrePaint(const LayoutObject& object, + const LayoutRect& old_visual_rect, + const PaintLayer& painting_layer); + void NotifyPrePaintFinished(); + bool IsActive(); + double Score() const { return score_; } + float MaxDistance() const { return max_distance_; } + + private: + void TimerFired(TimerBase*); + + // This owns us. + UntracedMember<LocalFrameView> frame_view_; + + // The global jank score. + double score_; + + // The per-frame jank region. + Region region_; + + // Timer that fires the first time we've had no layout jank for a few seconds. + TaskRunnerTimer<JankTracker> timer_; + bool has_fired_; + + // The maximum distance any layout object has moved in any frame. + float max_distance_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_JANK_TRACKER_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/jank_tracker_test.cc b/chromium/third_party/blink/renderer/core/layout/jank_tracker_test.cc new file mode 100644 index 00000000000..bf258a2d320 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/jank_tracker_test.cc @@ -0,0 +1,68 @@ +// 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/jank_tracker.h" + +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" + +namespace blink { + +class JankTrackerTest : public RenderingTest { + protected: + LocalFrameView& GetFrameView() { return *GetFrame().View(); } + JankTracker& GetJankTracker() { return GetFrameView().GetJankTracker(); } +}; + +TEST_F(JankTrackerTest, SimpleBlockMovement) { + SetBodyInnerHTML(R"HTML( + <style> + #j { position: relative; width: 300px; height: 100px; } + </style> + <div id='j'></div> + )HTML"); + + EXPECT_EQ(0.0, GetJankTracker().Score()); + EXPECT_EQ(0.0, GetJankTracker().MaxDistance()); + + GetDocument().getElementById("j")->setAttribute(HTMLNames::styleAttr, + AtomicString("top: 60px")); + GetFrameView().UpdateAllLifecyclePhases(); + // 300 * (100 + 60) / (default viewport size 800 * 600) + EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score()); + EXPECT_FLOAT_EQ(60.0, GetJankTracker().MaxDistance()); +} + +TEST_F(JankTrackerTest, Transform) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #c { transform: translateX(-300px) translateY(-40px); } + #j { position: relative; width: 600px; height: 140px; } + </style> + <div id='c'> + <div id='j'></div> + </div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(HTMLNames::styleAttr, + AtomicString("top: 60px")); + GetFrameView().UpdateAllLifecyclePhases(); + // (600 - 300) * (140 - 40 + 60) / (default viewport size 800 * 600) + EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score()); +} + +TEST_F(JankTrackerTest, RtlDistance) { + SetBodyInnerHTML(R"HTML( + <style> + #j { position: relative; width: 100px; height: 100px; direction: rtl; } + </style> + <div id='j'></div> + )HTML"); + GetDocument().getElementById("j")->setAttribute( + HTMLNames::styleAttr, AtomicString("width: 70px; left: 10px")); + GetFrameView().UpdateAllLifecyclePhases(); + EXPECT_FLOAT_EQ(20.0, GetJankTracker().MaxDistance()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block.cc b/chromium/third_party/blink/renderer/core/layout/layout_block.cc index 3171762e48a..c39c3d143a4 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block.cc @@ -36,6 +36,7 @@ #include "third_party/blink/renderer/core/editing/drag_caret.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" +#include "third_party/blink/renderer/core/editing/text_affinity.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/html/html_marquee_element.h" @@ -157,12 +158,12 @@ void LayoutBlock::StyleWillChange(StyleDifference diff, if (old_style && Parent()) { bool old_style_contains_fixed_position = - old_style->CanContainFixedPositionObjects(); + old_style->CanContainFixedPositionObjects(IsDocumentElement()); bool old_style_contains_absolute_position = old_style_contains_fixed_position || old_style->CanContainAbsolutePositionObjects(); bool new_style_contains_fixed_position = - new_style.CanContainFixedPositionObjects(); + new_style.CanContainFixedPositionObjects(IsDocumentElement()); bool new_style_contains_absolute_position = new_style_contains_fixed_position || new_style.CanContainAbsolutePositionObjects(); @@ -256,9 +257,9 @@ void LayoutBlock::StyleDidChange(StyleDifference diff, // text control. So just make sure this is the case. Finally, computed style // may turn us into a container of all things, e.g. if the element is // transformed, or contain:paint is specified. - SetCanContainFixedPositionObjects(IsLayoutView() || IsSVGForeignObject() || - IsTextControl() || - new_style.CanContainFixedPositionObjects()); + SetCanContainFixedPositionObjects( + IsLayoutView() || IsSVGForeignObject() || IsTextControl() || + new_style.CanContainFixedPositionObjects(IsDocumentElement())); // It's possible for our border/padding to change, but for the overall logical // width or height of the block to end up being the same. We keep track of @@ -418,9 +419,6 @@ void LayoutBlock::RemoveLeftoverAnonymousBlock(LayoutBlock* child) { void LayoutBlock::UpdateAfterLayout() { InvalidateStickyConstraints(); - if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() && GetNode()) - GetDocument().GetRootScrollerController().ConsiderForImplicit(*GetNode()); - LayoutBox::UpdateAfterLayout(); } @@ -928,7 +926,7 @@ void LayoutBlock::InsertPositionedObject(LayoutBox* o) { DCHECK(!IsAnonymousBlock()); DCHECK_EQ(o->ContainingBlock(), this); - o->ClearContainingBlockOverrideSize(); + o->ClearOverrideContainingBlockContentSize(); if (g_positioned_container_map) { auto container_map_it = g_positioned_container_map->find(o); @@ -1257,10 +1255,15 @@ PositionWithAffinity LayoutBlock::PositionForPointIfOutsideAtomicInlineLevel( LayoutUnit point_logical_top = IsHorizontalWritingMode() ? point.Y() : point.X(); - if (point_logical_left < 0) - return CreatePositionWithAffinity(CaretMinOffset()); - if (point_logical_left >= LogicalWidth()) - return CreatePositionWithAffinity(CaretMaxOffset()); + const bool is_ltr = IsLtr(ResolvedDirection()); + if (point_logical_left < 0) { + return CreatePositionWithAffinity(is_ltr ? CaretMinOffset() + : CaretMaxOffset()); + } + if (point_logical_left >= LogicalWidth()) { + return CreatePositionWithAffinity(is_ltr ? CaretMaxOffset() + : CaretMinOffset()); + } if (point_logical_top < 0) return CreatePositionWithAffinity(CaretMinOffset()); if (point_logical_top >= LogicalHeight()) @@ -1469,17 +1472,17 @@ void LayoutBlock::ComputeBlockPreferredLogicalWidths( child->SetPreferredLogicalWidthsDirty(); } - const ComputedStyle& child_style = child->StyleRef(); + scoped_refptr<const ComputedStyle> child_style = child->Style(); if (child->IsFloating() || (child->IsBox() && ToLayoutBox(child)->AvoidsFloats())) { LayoutUnit float_total_width = float_left_width + float_right_width; - if (child_style.Clear() == EClear::kBoth || - child_style.Clear() == EClear::kLeft) { + if (child_style->Clear() == EClear::kBoth || + child_style->Clear() == EClear::kLeft) { max_logical_width = std::max(float_total_width, max_logical_width); float_left_width = LayoutUnit(); } - if (child_style.Clear() == EClear::kBoth || - child_style.Clear() == EClear::kRight) { + if (child_style->Clear() == EClear::kBoth || + child_style->Clear() == EClear::kRight) { max_logical_width = std::max(float_total_width, max_logical_width); float_right_width = LayoutUnit(); } @@ -1489,8 +1492,8 @@ void LayoutBlock::ComputeBlockPreferredLogicalWidths( // (variable). // Auto and percentage margins simply become 0 when computing min/max width. // Fixed margins can be added in as is. - Length start_margin_length = child_style.MarginStartUsing(style_to_use); - Length end_margin_length = child_style.MarginEndUsing(style_to_use); + Length start_margin_length = child_style->MarginStartUsing(style_to_use); + Length end_margin_length = child_style->MarginEndUsing(style_to_use); LayoutUnit margin; LayoutUnit margin_start; LayoutUnit margin_end; @@ -1544,7 +1547,7 @@ void LayoutBlock::ComputeBlockPreferredLogicalWidths( } if (child->IsFloating()) { - if (child_style.Floating() == EFloat::kLeft) + if (child_style->Floating() == EFloat::kLeft) float_left_width += w; else float_right_width += w; @@ -2145,15 +2148,15 @@ LayoutUnit LayoutBlock::AvailableLogicalHeightForPercentageComputation() const { (!style.LogicalTop().IsAuto() && !style.LogicalBottom().IsAuto())); LayoutUnit stretched_flex_height(-1); - if (IsFlexItem()) - stretched_flex_height = - ToLayoutFlexibleBox(Parent()) - ->ChildLogicalHeightForPercentageResolution(*this); - + if (IsFlexItem()) { + const LayoutFlexibleBox* flex_box = ToLayoutFlexibleBox(Parent()); + if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*this)) + stretched_flex_height = OverrideContentLogicalHeight(); + } if (stretched_flex_height != LayoutUnit(-1)) { available_height = stretched_flex_height; - } else if (IsGridItem() && HasOverrideLogicalContentHeight()) { - available_height = OverrideLogicalContentHeight(); + } else if (IsGridItem() && HasOverrideLogicalHeight()) { + available_height = OverrideContentLogicalHeight(); } else if (style.LogicalHeight().IsFixed()) { LayoutUnit content_box_height = AdjustContentBoxLogicalHeightForBoxSizing( style.LogicalHeight().Value()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block.h b/chromium/third_party/blink/renderer/core/layout/layout_block.h index 6d0383c5579..b37858d572a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_block.h @@ -452,6 +452,11 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { bool NeedsPreferredWidthsRecalculation() const override; + bool IsInSelfHitTestingPhase(HitTestAction hit_test_action) const final { + return hit_test_action == kHitTestBlockBackground || + hit_test_action == kHitTestChildBlockBackground; + } + private: LayoutObjectChildList* VirtualChildren() final { return Children(); } const LayoutObjectChildList* VirtualChildren() const final { @@ -468,11 +473,6 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { // Returns true if the positioned movement-only layout succeeded. bool TryLayoutDoingPositionedMovementOnly(); - bool IsInSelfHitTestingPhase(HitTestAction hit_test_action) const final { - return hit_test_action == kHitTestBlockBackground || - hit_test_action == kHitTestChildBlockBackground; - } - bool IsPointInOverflowControl(HitTestResult&, const LayoutPoint& location_in_container, const LayoutPoint& accumulated_offset) const; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc index 3b53d9da415..4d707008deb 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc @@ -51,13 +51,17 @@ #include "third_party/blink/renderer/core/layout/line/inline_iterator.h" #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" #include "third_party/blink/renderer/core/layout/line/line_width.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" #include "third_party/blink/renderer/core/layout/text_autosizer.h" #include "third_party/blink/renderer/core/paint/block_flow_paint_invalidator.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -2512,6 +2516,10 @@ scoped_refptr<NGLayoutResult> LayoutBlockFlow::CachedLayoutResult( return nullptr; } +const NGConstraintSpace* LayoutBlockFlow::CachedConstraintSpace() const { + return nullptr; +} + scoped_refptr<NGLayoutResult> LayoutBlockFlow::CachedLayoutResultForTesting() { return nullptr; } @@ -2523,11 +2531,6 @@ void LayoutBlockFlow::SetCachedLayoutResult(const NGConstraintSpace&, void LayoutBlockFlow::SetPaintFragment( scoped_refptr<const NGPhysicalFragment>) {} -Vector<NGPaintFragment*> LayoutBlockFlow::GetPaintFragments( - const LayoutObject&) const { - return Vector<NGPaintFragment*>(); -} - void LayoutBlockFlow::ComputeOverflow(LayoutUnit old_client_after_edge, bool recompute_floats) { LayoutBlock::ComputeOverflow(old_client_after_edge, recompute_floats); @@ -2664,6 +2667,19 @@ LayoutUnit LayoutBlockFlow::FirstLineBoxBaseline() const { return FirstLineBox()->LogicalTop() + font_data->GetFontMetrics().Ascent(FirstRootBox()->BaselineType()); } + if (RuntimeEnabledFeatures::LayoutNGEnabled()) { + if (const NGPaintFragment* paint_fragment = PaintFragment()) { + NGBoxFragment box_fragment( + StyleRef().GetWritingMode(), + ToNGPhysicalBoxFragment(paint_fragment->PhysicalFragment())); + NGLineHeightMetrics metrics = + box_fragment.BaselineMetricsWithoutSynthesize( + {NGBaselineAlgorithmType::kFirstLine, + StyleRef().GetFontBaseline()}); + if (!metrics.IsEmpty()) + return metrics.ascent; + } + } return LayoutUnit(-1); } @@ -4767,10 +4783,12 @@ PositionWithAffinity LayoutBlockFlow::PositionForPoint( // We hit this case for Mac behavior when the Y coordinate is below the last // box. DCHECK(move_caret_to_boundary); - InlineBox* logically_last_box; - if (last_root_box_with_children->GetLogicalEndBoxWithNode( - logically_last_box)) - return PositionWithAffinity(PositionForBox(logically_last_box, false)); + if (const InlineBox* logically_last_box = + last_root_box_with_children->GetLogicalEndNonPseudoBox()) { + // TODO(layout-dev): Change |PositionForBox()| to take |const InlineBox*|. + return PositionWithAffinity( + PositionForBox(const_cast<InlineBox*>(logically_last_box), false)); + } } // Can't reach this. We have a root line box, but it has no kids. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h index 5eeb9a9defd..6b0832a038b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h @@ -450,14 +450,15 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { // still working on LayoutNG. void AddOverflowFromFloats(); + virtual NGInlineNodeData* TakeNGInlineNodeData() { return nullptr; } virtual NGInlineNodeData* GetNGInlineNodeData() const { return nullptr; } virtual void ResetNGInlineNodeData() {} virtual bool HasNGInlineNodeData() const { return false; } virtual NGPaintFragment* PaintFragment() const { return nullptr; } - virtual Vector<NGPaintFragment*> GetPaintFragments(const LayoutObject&) const; virtual scoped_refptr<NGLayoutResult> CachedLayoutResult( const NGConstraintSpace&, NGBreakToken*) const; + virtual const NGConstraintSpace* CachedConstraintSpace() const; virtual scoped_refptr<NGLayoutResult> CachedLayoutResultForTesting(); virtual void SetCachedLayoutResult(const NGConstraintSpace&, NGBreakToken*, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc index 8516d623408..9d80ecffbf2 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc @@ -38,6 +38,7 @@ #include "third_party/blink/renderer/core/layout/line/word_measurement.h" #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h" #include "third_party/blink/renderer/core/layout/vertical_position_cache.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/platform/text/bidi_resolver.h" #include "third_party/blink/renderer/platform/text/character.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -2654,6 +2655,8 @@ void LayoutBlockFlow::SetShouldDoFullPaintInvalidationForFirstLine() { DCHECK(ChildrenInline()); if (RootInlineBox* first_root_box = FirstRootBox()) first_root_box->SetShouldDoFullPaintInvalidationRecursively(); + else if (NGPaintFragment* paint_fragment = PaintFragment()) + paint_fragment->SetShouldDoFullPaintInvalidationForFirstLine(); } bool LayoutBlockFlow::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_box.cc index 4f724b59448..d56be3429c1 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box.cc @@ -66,10 +66,12 @@ #include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h" #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h" +#include "third_party/blink/renderer/core/paint/adjust_paint_offset_scope.h" #include "third_party/blink/renderer/core/paint/background_image_geometry.h" #include "third_party/blink/renderer/core/paint/box_paint_invalidator.h" #include "third_party/blink/renderer/core/paint/box_painter.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/style/shadow_list.h" #include "third_party/blink/renderer/platform/geometry/double_rect.h" @@ -127,7 +129,7 @@ PaintLayerType LayoutBox::LayerTypeRequired() const { void LayoutBox::WillBeDestroyed() { ClearOverrideSize(); - ClearContainingBlockOverrideSize(); + ClearOverrideContainingBlockContentSize(); if (IsOutOfFlowPositioned()) LayoutBlock::RemovePositionedObject(this); @@ -198,10 +200,6 @@ void LayoutBox::StyleWillChange(StyleDifference diff, if ((diff.NeedsFullPaintInvalidation() || diff.NeedsLayout()) && GetNode() && (IsDocumentElement() || IsHTMLBodyElement(*GetNode()))) { View()->SetShouldDoFullPaintInvalidation(); - - if (old_style->HasEntirelyFixedBackground() != - new_style.HasEntirelyFixedBackground()) - View()->Compositor()->SetNeedsUpdateFixedBackground(); } // When a layout hint happens and an object's position style changes, we @@ -686,7 +684,7 @@ LayoutRect LayoutBox::ScrollRectToVisibleRecursive( // end of the IPC call. HTMLFrameOwnerElement* owner_element = GetDocument().LocalOwner(); if (owner_element && owner_element->GetLayoutObject() && - GetFrameView()->SafeToPropagateScrollToParent()) { + AllowedToPropageRecursiveScrollToParentFrame(params)) { parent_box = owner_element->GetLayoutObject()->EnclosingBox(); LayoutView* parent_view = owner_element->GetLayoutObject()->View(); absolute_rect_for_parent = EnclosingLayoutRect( @@ -709,7 +707,7 @@ LayoutRect LayoutBox::ScrollRectToVisibleRecursive( return parent_box->ScrollRectToVisibleRecursive(absolute_rect_for_parent, params); } else if (GetFrame()->IsLocalRoot() && !GetFrame()->IsMainFrame()) { - if (GetFrameView()->SafeToPropagateScrollToParent()) { + if (AllowedToPropageRecursiveScrollToParentFrame(params)) { GetFrameView()->ScrollRectToVisibleInRemoteParent( absolute_rect_for_parent, params); } @@ -879,7 +877,7 @@ LayoutRect LayoutBox::BackgroundRect(BackgroundRectType rect_type) const { const FillLayer* cur = current; current = current->Next(); if (rect_type == kBackgroundKnownOpaqueRect) { - if (cur->BlendMode() != WebBlendMode::kNormal || + if (cur->GetBlendMode() != BlendMode::kNormal || cur->Composite() != kCompositeSourceOver) continue; @@ -998,17 +996,6 @@ LayoutUnit LayoutBox::VerticalScrollbarWidthClampedToContentBox() const { return width; } -ScrollResult LayoutBox::Scroll(ScrollGranularity granularity, - const FloatSize& delta) { - // Presumably the same issue as in setScrollTop. See crbug.com/343132. - DisableCompositingQueryAsserts disabler; - - if (!GetScrollableArea()) - return ScrollResult(); - - return GetScrollableArea()->UserScroll(granularity, delta); -} - bool LayoutBox::CanBeScrolledAndHasScrollableArea() const { return CanBeProgramaticallyScrolled() && (PixelSnappedScrollHeight() != PixelSnappedClientHeight() || @@ -1070,19 +1057,12 @@ IntSize LayoutBox::CalculateAutoscrollDirection( if (!frame_view) return IntSize(); - LayoutRect absolute_scrolling_box; + LayoutRect absolute_scrolling_box = LayoutRect(AbsoluteBoundingBoxRect()); - if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled() && IsLayoutView()) { - absolute_scrolling_box = - LayoutRect(frame_view->VisibleContentRect(kExcludeScrollbars)); - } else { - absolute_scrolling_box = LayoutRect(AbsoluteBoundingBoxRect()); - - // Exclude scrollbars so the border belt (activation area) starts from the - // scrollbar-content edge rather than the window edge. - ExcludeScrollbars(absolute_scrolling_box, - kExcludeOverlayScrollbarSizeForHitTesting); - } + // Exclude scrollbars so the border belt (activation area) starts from the + // scrollbar-content edge rather than the window edge. + ExcludeScrollbars(absolute_scrolling_box, + kExcludeOverlayScrollbarSizeForHitTesting); IntRect belt_box = View()->GetFrameView()->AbsoluteToRootFrame( PixelSnappedIntRect(absolute_scrolling_box)); @@ -1387,73 +1367,85 @@ LayoutUnit LayoutBox::MaxPreferredLogicalWidth() const { return max_preferred_logical_width_; } -bool LayoutBox::HasOverrideLogicalContentHeight() const { - return rare_data_ && rare_data_->override_logical_content_height_ != -1; +LayoutUnit LayoutBox::OverrideLogicalWidth() const { + DCHECK(HasOverrideLogicalWidth()); + return rare_data_->override_logical_width_; +} + +LayoutUnit LayoutBox::OverrideLogicalHeight() const { + DCHECK(HasOverrideLogicalHeight()); + return rare_data_->override_logical_height_; +} + +bool LayoutBox::HasOverrideLogicalHeight() const { + return rare_data_ && rare_data_->override_logical_height_ != -1; } -bool LayoutBox::HasOverrideLogicalContentWidth() const { - return rare_data_ && rare_data_->override_logical_content_width_ != -1; +bool LayoutBox::HasOverrideLogicalWidth() const { + return rare_data_ && rare_data_->override_logical_width_ != -1; } -void LayoutBox::SetOverrideLogicalContentHeight(LayoutUnit height) { +void LayoutBox::SetOverrideLogicalHeight(LayoutUnit height) { DCHECK_GE(height, 0); - EnsureRareData().override_logical_content_height_ = height; + EnsureRareData().override_logical_height_ = height; } -void LayoutBox::SetOverrideLogicalContentWidth(LayoutUnit width) { +void LayoutBox::SetOverrideLogicalWidth(LayoutUnit width) { DCHECK_GE(width, 0); - EnsureRareData().override_logical_content_width_ = width; + EnsureRareData().override_logical_width_ = width; } -void LayoutBox::ClearOverrideLogicalContentHeight() { +void LayoutBox::ClearOverrideLogicalHeight() { if (rare_data_) - rare_data_->override_logical_content_height_ = LayoutUnit(-1); + rare_data_->override_logical_height_ = LayoutUnit(-1); } -void LayoutBox::ClearOverrideLogicalContentWidth() { +void LayoutBox::ClearOverrideLogicalWidth() { if (rare_data_) - rare_data_->override_logical_content_width_ = LayoutUnit(-1); + rare_data_->override_logical_width_ = LayoutUnit(-1); } void LayoutBox::ClearOverrideSize() { - ClearOverrideLogicalContentHeight(); - ClearOverrideLogicalContentWidth(); + ClearOverrideLogicalHeight(); + ClearOverrideLogicalWidth(); } -LayoutUnit LayoutBox::OverrideLogicalContentWidth() const { - DCHECK(HasOverrideLogicalContentWidth()); - return rare_data_->override_logical_content_width_; +LayoutUnit LayoutBox::OverrideContentLogicalWidth() const { + return (OverrideLogicalWidth() - BorderAndPaddingLogicalWidth() - + ScrollbarLogicalWidth()) + .ClampNegativeToZero(); } -LayoutUnit LayoutBox::OverrideLogicalContentHeight() const { - DCHECK(HasOverrideLogicalContentHeight()); - return rare_data_->override_logical_content_height_; +LayoutUnit LayoutBox::OverrideContentLogicalHeight() const { + return (OverrideLogicalHeight() - BorderAndPaddingLogicalHeight() - + ScrollbarLogicalHeight()) + .ClampNegativeToZero(); } // TODO (lajava) Shouldn't we implement these functions based on physical // direction ?. LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalWidth() const { - DCHECK(HasOverrideContainingBlockLogicalWidth()); + DCHECK(HasOverrideContainingBlockContentLogicalWidth()); return rare_data_->override_containing_block_content_logical_width_; } // TODO (lajava) Shouldn't we implement these functions based on physical // direction ?. LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalHeight() const { - DCHECK(HasOverrideContainingBlockLogicalHeight()); + DCHECK(HasOverrideContainingBlockContentLogicalHeight()); return rare_data_->override_containing_block_content_logical_height_; } // TODO (lajava) Shouldn't we implement these functions based on physical // direction ?. -bool LayoutBox::HasOverrideContainingBlockLogicalWidth() const { +bool LayoutBox::HasOverrideContainingBlockContentLogicalWidth() const { return rare_data_ && rare_data_->has_override_containing_block_content_logical_width_; } // TODO (lajava) Shouldn't we implement these functions based on physical // direction ?. -bool LayoutBox::HasOverrideContainingBlockLogicalHeight() const { +bool LayoutBox::HasOverrideContainingBlockContentLogicalHeight() const { return rare_data_ && rare_data_->has_override_containing_block_content_logical_height_; } @@ -1480,7 +1472,7 @@ void LayoutBox::SetOverrideContainingBlockContentLogicalHeight( // TODO (lajava) Shouldn't we implement these functions based on physical // direction ?. -void LayoutBox::ClearContainingBlockOverrideSize() { +void LayoutBox::ClearOverrideContainingBlockContentSize() { if (!rare_data_) return; EnsureRareData().has_override_containing_block_content_logical_width_ = false; @@ -1488,15 +1480,6 @@ void LayoutBox::ClearContainingBlockOverrideSize() { false; } -// TODO (lajava) Shouldn't we implement these functions based on physical -// direction ?. -void LayoutBox::ClearOverrideContainingBlockContentLogicalHeight() { - if (!rare_data_) - return; - EnsureRareData().has_override_containing_block_content_logical_height_ = - false; -} - LayoutUnit LayoutBox::AdjustBorderBoxLogicalWidthForBoxSizing( float width) const { LayoutUnit borders_plus_padding = CollapsedBorderAndCSSPaddingLogicalWidth(); @@ -1540,8 +1523,9 @@ bool LayoutBox::HitTestAllPhases(HitTestResult& result, // If we have clipping, then we can't have any spillout. // TODO(pdr): Why is this optimization not valid for the effective root? if (!RootScrollerUtil::IsEffective(*this)) { - LayoutRect overflow_box = - HasOverflowClip() ? BorderBoxRect() : VisualOverflowRect(); + LayoutRect overflow_box = (HasOverflowClip() || Style()->ContainsPaint()) + ? BorderBoxRect() + : VisualOverflowRect(); FlipForWritingMode(overflow_box); LayoutPoint adjusted_location = accumulated_offset + Location(); overflow_box.MoveBy(adjusted_location); @@ -1564,8 +1548,8 @@ bool LayoutBox::NodeAtPoint(HitTestResult& result, HitTestOverflowControl(result, location_in_container, adjusted_location)) return true; - bool skip_children = false; - if (ShouldClipOverflow()) { + bool skip_children = (result.GetHitTestRequest().GetStopNode() == this); + if (!skip_children && ShouldClipOverflow()) { // PaintLayer::HitTestContentsForFragments checked the fragments' // foreground rect for intersection if a layer is self painting, // so only do the overflow clip check here for non-self-painting layers. @@ -1942,16 +1926,24 @@ PaintInvalidationReason LayoutBox::InvalidatePaint( LayoutRect LayoutBox::OverflowClipRect( const LayoutPoint& location, OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const { - if (RootScrollerUtil::IsEffective(*this)) - return View()->ViewRect(); - - // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property - // here. - LayoutRect clip_rect = BorderBoxRect(); - clip_rect.SetLocation(location + clip_rect.Location() + - LayoutSize(BorderLeft(), BorderTop())); - clip_rect.SetSize(clip_rect.Size() - - LayoutSize(BorderWidth(), BorderHeight())); + LayoutRect clip_rect; + + if (RootScrollerUtil::IsEffective(*this)) { + // If this box is the effective root scroller, use the viewport clipping + // rect since it will account for the URL bar correctly which the border + // box does not. We can do this because the effective root scroller is + // restricted such that it exactly fills the viewport. See + // RootScrollerController::IsValidRootScroller() + clip_rect = LayoutRect(location, View()->ViewRect().Size()); + } else { + // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the + // property here. + clip_rect = BorderBoxRect(); + clip_rect.SetLocation(location + clip_rect.Location() + + LayoutSize(BorderLeft(), BorderTop())); + clip_rect.SetSize(clip_rect.Size() - + LayoutSize(BorderWidth(), BorderHeight())); + } if (HasOverflowClip()) ExcludeScrollbars(clip_rect, overlay_scrollbar_clip_behavior); @@ -2066,7 +2058,7 @@ LayoutUnit LayoutBox::ShrinkLogicalWidthToAvoidFloats( } LayoutUnit LayoutBox::ContainingBlockLogicalHeightForGetComputedStyle() const { - if (HasOverrideContainingBlockLogicalHeight()) + if (HasOverrideContainingBlockContentLogicalHeight()) return OverrideContainingBlockContentLogicalHeight(); if (!IsPositioned()) @@ -2080,7 +2072,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalHeightForGetComputedStyle() const { } LayoutUnit LayoutBox::ContainingBlockLogicalWidthForContent() const { - if (HasOverrideContainingBlockLogicalWidth()) + if (HasOverrideContainingBlockContentLogicalWidth()) return OverrideContainingBlockContentLogicalWidth(); LayoutBlock* cb = ContainingBlock(); @@ -2091,7 +2083,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalWidthForContent() const { LayoutUnit LayoutBox::ContainingBlockLogicalHeightForContent( AvailableLogicalHeightType height_type) const { - if (HasOverrideContainingBlockLogicalHeight()) + if (HasOverrideContainingBlockContentLogicalHeight()) return OverrideContainingBlockContentLogicalHeight(); LayoutBlock* cb = ContainingBlock(); @@ -2108,12 +2100,12 @@ LayoutUnit LayoutBox::ContainingBlockAvailableLineWidth() const { } LayoutUnit LayoutBox::PerpendicularContainingBlockLogicalHeight() const { - if (HasOverrideContainingBlockLogicalHeight()) + if (HasOverrideContainingBlockContentLogicalHeight()) return OverrideContainingBlockContentLogicalHeight(); LayoutBlock* cb = ContainingBlock(); - if (cb->HasOverrideLogicalContentHeight()) - return cb->OverrideLogicalContentHeight(); + if (cb->HasOverrideLogicalHeight()) + return cb->OverrideContentLogicalHeight(); const ComputedStyle& containing_block_style = cb->StyleRef(); Length logical_height_length = containing_block_style.LogicalHeight(); @@ -2172,7 +2164,9 @@ void LayoutBox::MapAncestorToLocal(const LayoutBoxModelObject* ancestor, LayoutBoxModelObject::MapAncestorToLocal(ancestor, transform_state, mode); } -LayoutSize LayoutBox::OffsetFromContainer(const LayoutObject* o) const { +LayoutSize LayoutBox::OffsetFromContainerInternal( + const LayoutObject* o, + bool ignore_scroll_offset) const { DCHECK_EQ(o, Container()); LayoutSize offset; @@ -2182,7 +2176,7 @@ LayoutSize LayoutBox::OffsetFromContainer(const LayoutObject* o) const { offset += PhysicalLocationOffset(); if (o->HasOverflowClip()) - offset -= ToLayoutBox(o)->ScrolledContentOffset(); + offset += OffsetFromScrollableContainer(o, ignore_scroll_offset); if (IsOutOfFlowPositioned() && o->IsLayoutInline() && o->CanContainOutOfFlowPositionedElement(Style()->GetPosition())) { @@ -2538,26 +2532,12 @@ bool LayoutBox::MapToVisualRectInAncestorSpaceInternal( LayoutSize container_offset = ancestor->OffsetFromAncestorContainer(container); transform_state.Move(-container_offset, accumulation); - - if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) { - // If the ancestor is fixed, then the rect is already in its coordinates - // so doesn't need viewport-adjusting. - if (ancestor->Style()->GetPosition() != EPosition::kFixed && - container->IsLayoutView() && position == EPosition::kFixed) { - transform_state.Move( - ToLayoutView(container)->OffsetForFixedPosition(true), - accumulation); - } - } - return true; } if (container->IsLayoutView()) { bool use_fixed_position_adjustment = - position == EPosition::kFixed && - (!RuntimeEnabledFeatures::RootLayerScrollingEnabled() || - container == ancestor); + position == EPosition::kFixed && container == ancestor; return ToLayoutView(container)->MapToVisualRectInAncestorSpaceInternal( ancestor, transform_state, use_fixed_position_adjustment ? kIsFixed : 0, visual_rect_flags); @@ -2671,9 +2651,8 @@ void LayoutBox::ComputeLogicalWidth( // The parent box is flexing us, so it has increased or decreased our // width. Use the width from the style context. - if (HasOverrideLogicalContentWidth()) { - computed_values.extent_ = - OverrideLogicalContentWidth() + BorderAndPaddingLogicalWidth(); + if (HasOverrideLogicalWidth()) { + computed_values.extent_ = OverrideLogicalWidth(); return; } @@ -2721,9 +2700,13 @@ void LayoutBox::ComputeLogicalWidth( } LayoutUnit container_width_in_inline_direction = container_logical_width; - if (has_perpendicular_containing_block) + if (has_perpendicular_containing_block) { + // PerpendicularContainingBlockLogicalHeight() can return -1 in some + // situations but we cannot have a negative width, that's why we clamp it to + // zero. container_width_in_inline_direction = - PerpendicularContainingBlockLogicalHeight(); + PerpendicularContainingBlockLogicalHeight().ClampNegativeToZero(); + } // Width calculations if (treat_as_replaced) { @@ -2987,6 +2970,9 @@ bool LayoutBox::SizesLogicalWidthToFitContent( if (IsHorizontalWritingMode() != ContainingBlock()->IsHorizontalWritingMode()) return true; + if (IsCustomItem()) + return IsCustomItemShrinkToFit(); + return false; } @@ -3152,10 +3138,8 @@ void LayoutBox::ComputeLogicalHeight( Length h; if (IsOutOfFlowPositioned()) { ComputePositionedLogicalHeight(computed_values); - if (HasOverrideLogicalContentHeight()) { - computed_values.extent_ = - OverrideLogicalContentHeight() + BorderAndPaddingLogicalHeight(); - } + if (HasOverrideLogicalHeight()) + computed_values.extent_ = OverrideLogicalHeight(); } else { LayoutBlock* cb = ContainingBlock(); @@ -3195,10 +3179,12 @@ void LayoutBox::ComputeLogicalHeight( // The parent box is flexing us, so it has increased or decreased our // height. We have to grab our cached flexible height. - if (HasOverrideLogicalContentHeight()) { - h = Length(OverrideLogicalContentHeight(), kFixed); + if (HasOverrideLogicalHeight()) { + h = Length(OverrideLogicalHeight(), kFixed); } else if (treat_as_replaced) { - h = Length(ComputeReplacedLogicalHeight(), kFixed); + h = Length( + ComputeReplacedLogicalHeight() + BorderAndPaddingLogicalHeight(), + kFixed); } else { h = Style()->LogicalHeight(); check_min_max_height = true; @@ -3209,9 +3195,9 @@ void LayoutBox::ComputeLogicalHeight( // https://bugs.webkit.org/show_bug.cgi?id=46418 if (h.IsAuto() && in_horizontal_box && ToLayoutDeprecatedFlexibleBox(Parent())->IsStretchingChildren()) { - h = Length(ParentBox()->ContentLogicalHeight() - MarginBefore() - - MarginAfter() - BorderAndPaddingLogicalHeight(), - kFixed); + h = Length( + ParentBox()->ContentLogicalHeight() - MarginBefore() - MarginAfter(), + kFixed); check_min_max_height = false; } @@ -3226,11 +3212,8 @@ void LayoutBox::ComputeLogicalHeight( height_result, computed_values.extent_ - BorderAndPaddingLogicalHeight()); } else { - // The only times we don't check min/max height are when a fixed length - // has been given as an override. Just use that. The value has already - // been adjusted for box-sizing. DCHECK(h.IsFixed()); - height_result = LayoutUnit(h.Value()) + BorderAndPaddingLogicalHeight(); + height_result = LayoutUnit(h.Value()); } computed_values.extent_ = height_result; @@ -3417,7 +3400,7 @@ LayoutUnit LayoutBox::ComputePercentageLogicalHeight( if (IsHorizontalWritingMode() != cb->IsHorizontalWritingMode()) { available_height = containing_block_child->ContainingBlockLogicalWidthForContent(); - } else if (HasOverrideContainingBlockLogicalHeight()) { + } else if (HasOverrideContainingBlockContentLogicalHeight()) { available_height = OverrideContainingBlockContentLogicalHeight(); } else if (cb->IsTableCell()) { if (!skipped_auto_height_containing_block) { @@ -3425,7 +3408,7 @@ LayoutUnit LayoutBox::ComputePercentageLogicalHeight( // Basically we don't care if the cell specified a height or not. We just // always make ourselves be a percentage of the cell's current content // height. - if (!cb->HasOverrideLogicalContentHeight()) { + if (!cb->HasOverrideLogicalHeight()) { // https://drafts.csswg.org/css-tables-3/#row-layout: // For the purpose of calculating [the minimum height of a row], // descendants of table cells whose height depends on percentages @@ -3441,7 +3424,9 @@ LayoutUnit LayoutBox::ComputePercentageLogicalHeight( return LayoutUnit(); return LayoutUnit(-1); } - available_height = cb->OverrideLogicalContentHeight(); + available_height = cb->OverrideLogicalHeight() - + cb->CollapsedBorderAndCSSPaddingLogicalHeight() - + cb->ScrollbarLogicalHeight(); } } else { available_height = cb->AvailableLogicalHeightForPercentageComputation(); @@ -3456,16 +3441,16 @@ LayoutUnit LayoutBox::ComputePercentageLogicalHeight( available_height += cb->PaddingLogicalHeight(); LayoutUnit result = ValueForLength(height, available_height); - // |overrideLogicalContentHeight| is the maximum height made available by the + // |OverrideLogicalHeight| is the maximum height made available by the // cell to its percent height children when we decide they can determine the // height of the cell. If the percent height child is box-sizing:content-box // then we must subtract the border and padding from the cell's - // |availableHeight| (given by |overrideLogicalContentHeight|) to arrive + // |available_height| (given by |OverrideLogicalHeight|) to arrive // at the child's computed height. bool subtract_border_and_padding = IsTable() || (cb->IsTableCell() && !skipped_auto_height_containing_block && - cb->HasOverrideLogicalContentHeight() && + cb->HasOverrideLogicalHeight() && Style()->BoxSizing() == EBoxSizing::kContentBox); if (subtract_border_and_padding) { result -= BorderAndPaddingLogicalHeight(); @@ -3526,13 +3511,16 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalWidthUsing( case kFillAvailable: case kPercent: case kCalculated: { - // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced - // element's writing-mode is perpendicular to the containing block's - // writing-mode. https://bugs.webkit.org/show_bug.cgi?id=46496 - const LayoutUnit cw = IsOutOfFlowPositioned() - ? ContainingBlockLogicalWidthForPositioned( - ToLayoutBoxModelObject(Container())) - : ContainingBlockLogicalWidthForContent(); + LayoutUnit cw; + if (IsOutOfFlowPositioned()) { + cw = ContainingBlockLogicalWidthForPositioned( + ToLayoutBoxModelObject(Container())); + } else { + cw = IsHorizontalWritingMode() == + ContainingBlock()->IsHorizontalWritingMode() + ? ContainingBlockLogicalWidthForContent() + : PerpendicularContainingBlockLogicalHeight(); + } Length container_logical_width = ContainingBlock()->Style()->LogicalWidth(); // FIXME: Handle cases when containing block width is calculated or @@ -3621,17 +3609,21 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalHeightUsing( IsOutOfFlowPositioned() ? Container() : ContainingBlock(); while (cb->IsAnonymous()) cb = cb->ContainingBlock(); + bool has_perpendicular_containing_block = + cb->IsHorizontalWritingMode() != IsHorizontalWritingMode(); LayoutUnit stretched_height(-1); if (cb->IsLayoutBlock()) { LayoutBlock* block = ToLayoutBlock(cb); block->AddPercentHeightDescendant(const_cast<LayoutBox*>(this)); - if (block->IsFlexItem()) - stretched_height = - ToLayoutFlexibleBox(block->Parent()) - ->ChildLogicalHeightForPercentageResolution(*block); - else if (block->IsGridItem() && - block->HasOverrideLogicalContentHeight()) - stretched_height = block->OverrideLogicalContentHeight(); + if (block->IsFlexItem()) { + const LayoutFlexibleBox* flex_box = + ToLayoutFlexibleBox(block->Parent()); + if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*block)) + stretched_height = block->OverrideContentLogicalHeight(); + } else if (block->IsGridItem() && block->HasOverrideLogicalHeight() && + !has_perpendicular_containing_block) { + stretched_height = block->OverrideContentLogicalHeight(); + } } if (cb->IsOutOfFlowPositioned() && cb->Style()->Height().IsAuto() && @@ -3648,20 +3640,17 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalHeightUsing( ValueForLength(logical_height, new_content_height)); } - // FIXME: availableLogicalHeight() is wrong if the replaced element's - // writing-mode is perpendicular to the containing block's writing-mode. - // https://bugs.webkit.org/show_bug.cgi?id=46496 LayoutUnit available_height; if (IsOutOfFlowPositioned()) { available_height = ContainingBlockLogicalHeightForPositioned( ToLayoutBoxModelObject(cb)); } else if (stretched_height != -1) { available_height = stretched_height; - } else if (HasOverrideContainingBlockLogicalHeight()) { - available_height = OverrideContainingBlockContentLogicalHeight(); } else { - available_height = - ContainingBlockLogicalHeightForContent(kIncludeMarginBorderPadding); + available_height = has_perpendicular_containing_block + ? ContainingBlockLogicalWidthForContent() + : ContainingBlockLogicalHeightForContent( + kIncludeMarginBorderPadding); // It is necessary to use the border-box to match WinIE's broken // box model. This is essential for sizing inside // table cells using percentage heights. @@ -3726,17 +3715,18 @@ LayoutUnit LayoutBox::AvailableLogicalHeightUsing( // some new height, and then when we lay out again we'll use the calculation // below. if (IsTableCell() && (h.IsAuto() || h.IsPercentOrCalc())) { - if (HasOverrideLogicalContentHeight()) - return OverrideLogicalContentHeight(); + if (HasOverrideLogicalHeight()) { + return OverrideLogicalHeight() - + CollapsedBorderAndCSSPaddingLogicalHeight() - + ScrollbarLogicalHeight(); + } return LogicalHeight() - BorderAndPaddingLogicalHeight(); } if (IsFlexItem()) { - LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); - LayoutUnit stretched_height = - flex_box.ChildLogicalHeightForPercentageResolution(*this); - if (stretched_height != LayoutUnit(-1)) - return stretched_height; + const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); + if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this)) + return OverrideContentLogicalHeight(); } if (h.IsPercentOrCalc() && IsOutOfFlowPositioned()) { @@ -3825,7 +3815,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalWidthForPositioned( } } - if (HasOverrideContainingBlockLogicalWidth()) + if (HasOverrideContainingBlockContentLogicalWidth()) return OverrideContainingBlockContentLogicalWidth(); // Ensure we compute our width based on the width of our rel-pos inline @@ -3889,7 +3879,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPositioned( } } - if (HasOverrideContainingBlockLogicalHeight()) + if (HasOverrideContainingBlockContentLogicalHeight()) return OverrideContainingBlockContentLogicalHeight(); if (containing_block->IsBox()) @@ -3998,6 +3988,12 @@ void LayoutBox::ComputeInlineStaticDistance( LayoutUnit static_position = child->Layer()->StaticInlinePosition() + container_logical_width + container_block->BorderLogicalLeft(); + if (container_block->IsBox() && + ToLayoutBox(container_block) + ->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { + static_position += + ToLayoutBox(container_block)->OriginAdjustmentForScrollbars().Width(); + } for (LayoutObject* curr = child->Parent(); curr; curr = curr->Container()) { if (curr->IsBox()) { if (curr == enclosing_box) @@ -5065,6 +5061,13 @@ bool LayoutBox::IsCustomItem() const { ToLayoutCustom(Parent())->State() == LayoutCustomState::kBlock; } +// LayoutCustom items are only shrink-to-fit during the web-developer defined +// layout phase (not during fallback). +bool LayoutBox::IsCustomItemShrinkToFit() const { + DCHECK(IsCustomItem()); + return ToLayoutCustom(Parent())->Phase() == LayoutCustomPhase::kCustom; +} + bool LayoutBox::IsRenderedLegend() const { if (!IsHTMLLegendElement(GetNode())) return false; @@ -5486,6 +5489,20 @@ LayoutPoint LayoutBox::FlipForWritingModeForChild( point.Y()); } +LayoutPoint LayoutBox::FlipForWritingModeForChildForPaint( + const LayoutBox* child, + const LayoutPoint& point) const { + // Do nothing unless in FlippedBlocks(). Fast path optimization + if (!Style()->IsFlippedBlocksWritingMode()) + return point; + // If child will be painted by LayoutNG, and will use fragment.Offset(), + // flip is not needed. + if (!AdjustPaintOffsetScope::WillUseLegacyLocation(child)) + return point; + + return FlipForWritingModeForChild(child, point); +} + LayoutBox* LayoutBox::LocationContainer() const { // Location of a non-root SVG object derived from LayoutBox should not be // affected by writing-mode of the containing box (SVGRoot). @@ -5953,6 +5970,22 @@ void LayoutBox::RemoveSnapArea(const LayoutBox& snap_area) { } } +bool LayoutBox::AllowedToPropageRecursiveScrollToParentFrame( + const WebScrollIntoViewParams& params) { + if (!GetFrameView()->SafeToPropagateScrollToParent()) + return false; + + if (params.GetScrollType() != kProgrammaticScroll) + return true; + + if (!IsSupportedInFeaturePolicy( + mojom::FeaturePolicyFeature::kVerticalScroll)) { + return true; + } + return GetFrame()->IsFeatureEnabled( + mojom::FeaturePolicyFeature::kVerticalScroll); +} + SnapAreaSet* LayoutBox::SnapAreas() const { return rare_data_ ? rare_data_->snap_areas_.get() : nullptr; } @@ -6026,4 +6059,19 @@ float LayoutBox::VisualRectOutsetForRasterEffects() const { return overflow_ && overflow_->HasSubpixelVisualEffectOutsets() ? 1 : 0; } +TextDirection LayoutBox::ResolvedDirection() const { + if (IsInline() && IsAtomicInlineLevel()) { + const auto fragments = NGPaintFragment::InlineFragmentsFor(this); + if (fragments.IsInLayoutNGInlineFormattingContext()) { + DCHECK(*fragments.begin()) << this; + const NGPaintFragment* fragment = *fragments.begin(); + return fragment->PhysicalFragment().ResolvedDirection(); + } + + if (InlineBoxWrapper()) + return InlineBoxWrapper()->Direction(); + } + return Style()->Direction(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box.h b/chromium/third_party/blink/renderer/core/layout/layout_box.h index ff4159964b4..924dcac603a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_box.h @@ -65,8 +65,8 @@ struct LayoutBoxRareData { public: LayoutBoxRareData() : spanner_placeholder_(nullptr), - override_logical_content_width_(-1), - override_logical_content_height_(-1), + override_logical_width_(-1), + override_logical_height_(-1), has_override_containing_block_content_logical_width_(false), has_override_containing_block_content_logical_height_(false), has_previous_content_box_size_and_layout_overflow_rect_(false), @@ -78,8 +78,8 @@ struct LayoutBoxRareData { // container. LayoutMultiColumnSpannerPlaceholder* spanner_placeholder_; - LayoutUnit override_logical_content_width_; - LayoutUnit override_logical_content_height_; + LayoutUnit override_logical_width_; + LayoutUnit override_logical_height_; bool has_override_containing_block_content_logical_width_ : 1; bool has_override_containing_block_content_logical_height_ : 1; @@ -689,30 +689,26 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutUnit MinPreferredLogicalWidth() const override; LayoutUnit MaxPreferredLogicalWidth() const override; - // FIXME: We should rename these back to overrideLogicalHeight/Width and have - // them store the border-box height/width like the regular height/width - // accessors on LayoutBox. Right now, these are different than contentHeight/ - // contentWidth because they still include the scrollbar height/width. - LayoutUnit OverrideLogicalContentWidth() const; - LayoutUnit OverrideLogicalContentHeight() const; - bool HasOverrideLogicalContentHeight() const; - bool HasOverrideLogicalContentWidth() const; - void SetOverrideLogicalContentHeight(LayoutUnit); - void SetOverrideLogicalContentWidth(LayoutUnit); + LayoutUnit OverrideLogicalHeight() const; + LayoutUnit OverrideLogicalWidth() const; + bool HasOverrideLogicalHeight() const; + bool HasOverrideLogicalWidth() const; + void SetOverrideLogicalHeight(LayoutUnit); + void SetOverrideLogicalWidth(LayoutUnit); + void ClearOverrideLogicalHeight(); + void ClearOverrideLogicalWidth(); void ClearOverrideSize(); - void ClearOverrideLogicalContentHeight(); - void ClearOverrideLogicalContentWidth(); + + LayoutUnit OverrideContentLogicalWidth() const; + LayoutUnit OverrideContentLogicalHeight() const; LayoutUnit OverrideContainingBlockContentLogicalWidth() const; LayoutUnit OverrideContainingBlockContentLogicalHeight() const; - bool HasOverrideContainingBlockLogicalWidth() const; - bool HasOverrideContainingBlockLogicalHeight() const; + bool HasOverrideContainingBlockContentLogicalWidth() const; + bool HasOverrideContainingBlockContentLogicalHeight() const; void SetOverrideContainingBlockContentLogicalWidth(LayoutUnit); void SetOverrideContainingBlockContentLogicalHeight(LayoutUnit); - void ClearContainingBlockOverrideSize(); - void ClearOverrideContainingBlockContentLogicalHeight(); - - LayoutSize OffsetFromContainer(const LayoutObject*) const override; + void ClearOverrideContainingBlockContentSize(); LayoutUnit AdjustBorderBoxLogicalWidthForBoxSizing(float width) const; LayoutUnit AdjustBorderBoxLogicalHeightForBoxSizing(float height) const; @@ -998,7 +994,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { // scrollWidth. For the full story, visit crbug.com/724255 LayoutUnit VerticalScrollbarWidthClampedToContentBox() const; - virtual ScrollResult Scroll(ScrollGranularity, const FloatSize&); bool CanBeScrolledAndHasScrollableArea() const; virtual bool CanBeProgramaticallyScrolled() const; virtual void Autoscroll(const IntPoint&); @@ -1120,6 +1115,7 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void UnmarkOrthogonalWritingModeRoot(); bool IsCustomItem() const; + bool IsCustomItemShrinkToFit() const; bool IsDeprecatedFlexItem() const { return !IsInline() && !IsFloatingOrOutOfFlowPositioned() && Parent() && @@ -1157,6 +1153,12 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutPoint FlipForWritingModeForChild(const LayoutBox* child, const LayoutPoint&) const; + + // NG: Like FlipForWritingModeForChild, except that it will not flip + // if LayoutBox will be painted by NG using fragment.Offset. + LayoutPoint FlipForWritingModeForChildForPaint(const LayoutBox* child, + const LayoutPoint&) const; + WARN_UNUSED_RESULT LayoutUnit FlipForWritingMode(LayoutUnit position) const { // The offset is in the block direction (y for horizontal writing modes, x // for vertical writing modes). @@ -1529,6 +1531,15 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutRect LocalVisualRectIgnoringVisibility() const override; + LayoutSize OffsetFromContainerInternal( + const LayoutObject*, + bool ignore_scroll_offset) const override; + + // For atomic inlines, returns its resolved direction in text flow. Not to be + // confused with the CSS property 'direction'. + // Returns the CSS 'direction' property value when it is not atomic inline. + TextDirection ResolvedDirection() const; + private: void UpdateShapeOutsideInfoAfterStyleChange(const ComputedStyle&, const ComputedStyle* old_style); @@ -1629,6 +1640,11 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void AddSnapArea(const LayoutBox&); void RemoveSnapArea(const LayoutBox&); + // Returns true when the current recursive scroll into visible could propagate + // to parent frame. + bool AllowedToPropageRecursiveScrollToParentFrame( + const WebScrollIntoViewParams&); + LayoutRect DebugRect() const override; float VisualRectOutsetForRasterEffects() const override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc index 3fc35e5d67b..9cedc31e5c3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc @@ -38,9 +38,9 @@ #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/style/shadow_list.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" #include "third_party/blink/renderer/platform/length_functions.h" #include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { @@ -306,10 +306,11 @@ void LayoutBoxModelObject::StyleDidChange(StyleDifference diff, } } - if (old_style && (old_style->CanContainFixedPositionObjects() != - StyleRef().CanContainFixedPositionObjects() || - old_style->GetPosition() != StyleRef().GetPosition() || - had_layer != HasLayer())) { + if (old_style && + (old_style->CanContainFixedPositionObjects(IsDocumentElement()) != + StyleRef().CanContainFixedPositionObjects(IsDocumentElement()) || + old_style->GetPosition() != StyleRef().GetPosition() || + had_layer != HasLayer())) { // This may affect paint properties of the current object, and descendants // even if paint properties of the current object won't change. E.g. the // stacking context and/or containing block of descendants may change. @@ -430,11 +431,30 @@ void LayoutBoxModelObject::StyleDidChange(StyleDifference diff, } if (old_style && RuntimeEnabledFeatures::SlimmingPaintV175Enabled() && - old_style->BackfaceVisibility() != StyleRef().BackfaceVisibility() && - HasLayer()) { - // We need to repaint the layer to update the backface visibility value of - // the paint chunk. - Layer()->SetNeedsRepaint(); + HasLayer() && !Layer()->NeedsRepaint()) { + if (old_style->BackfaceVisibility() != StyleRef().BackfaceVisibility()) { + // We need to repaint the layer to update the backface visibility value of + // the paint chunk. + Layer()->SetNeedsRepaint(); + } else if (diff.TransformChanged() && + (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() || + !Layer()->HasStyleDeterminedDirectCompositingReasons())) { + // PaintLayerPainter::PaintLayerWithAdjustedRoot skips painting of a layer + // whose transform is not invertible, so we need to repaint the layer when + // invertible status changes. + TransformationMatrix old_transform; + TransformationMatrix new_transform; + old_style->ApplyTransform( + old_transform, LayoutSize(), ComputedStyle::kExcludeTransformOrigin, + ComputedStyle::kExcludeMotionPath, + ComputedStyle::kIncludeIndependentTransformProperties); + StyleRef().ApplyTransform( + new_transform, LayoutSize(), ComputedStyle::kExcludeTransformOrigin, + ComputedStyle::kExcludeMotionPath, + ComputedStyle::kIncludeIndependentTransformProperties); + if (old_transform.IsInvertible() != new_transform.IsInvertible()) + Layer()->SetNeedsRepaint(); + } } } @@ -677,13 +697,12 @@ bool LayoutBoxModelObject::HasAutoHeightOrContainingBlockWithAutoHeight() if (logical_height_length.IsPercentOrCalc() && cb && IsBox()) cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(ToLayoutBox(this))); if (this_box && this_box->IsFlexItem()) { - LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); - if (flex_box.ChildLogicalHeightForPercentageResolution(*this_box) != - LayoutUnit(-1)) + const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); + if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box)) return false; } if (this_box && this_box->IsGridItem() && - this_box->HasOverrideContainingBlockLogicalHeight()) + this_box->HasOverrideContainingBlockContentLogicalHeight()) return false; if (logical_height_length.IsAuto() && !IsOutOfFlowPositionedWithImplicitHeight(this)) @@ -711,8 +730,8 @@ LayoutSize LayoutBoxModelObject::RelativePositionOffset() const { // don't use containingBlockLogicalWidthForContent() here, but instead // explicitly call availableWidth on our containing block. // https://drafts.csswg.org/css-position-3/#rel-pos - Optional<LayoutUnit> left; - Optional<LayoutUnit> right; + base::Optional<LayoutUnit> left; + base::Optional<LayoutUnit> right; if (!Style()->Left().IsAuto()) left = ValueForLength(Style()->Left(), containing_block->AvailableWidth()); if (!Style()->Right().IsAuto()) @@ -753,8 +772,8 @@ LayoutSize LayoutBoxModelObject::RelativePositionOffset() const { // the percent offset based on this height. // See <https://bugs.webkit.org/show_bug.cgi?id=26396>. - Optional<LayoutUnit> top; - Optional<LayoutUnit> bottom; + base::Optional<LayoutUnit> top; + base::Optional<LayoutUnit> bottom; if (!Style()->Top().IsAuto() && (!containing_block->HasAutoHeightOrContainingBlockWithAutoHeight() || !Style()->Top().IsPercentOrCalc() || @@ -812,18 +831,19 @@ void LayoutBoxModelObject::UpdateStickyPositionConstraints() const { while (containing_block->IsAnonymous()) { containing_block = containing_block->ContainingBlock(); } - MapCoordinatesFlags flags = kIgnoreStickyOffset; + + // The sticky position constraint rects should be independent of the current + // scroll position therefore we should ignore the scroll offset when + // calculating the quad. + MapCoordinatesFlags flags = kIgnoreScrollOffset | kIgnoreStickyOffset; skipped_containers_offset = ToFloatSize(location_container ->LocalToAncestorQuadWithoutTransforms( FloatQuad(), containing_block, flags) .BoundingBox() .Location()); - LayoutBox* scroll_ancestor = - Layer()->AncestorOverflowLayer()->IsRootLayer() && - !RuntimeEnabledFeatures::RootLayerScrollingEnabled() - ? nullptr - : &ToLayoutBox(Layer()->AncestorOverflowLayer()->GetLayoutObject()); + LayoutBox& scroll_ancestor = + ToLayoutBox(Layer()->AncestorOverflowLayer()->GetLayoutObject()); LayoutUnit max_container_width = containing_block->IsLayoutView() @@ -840,29 +860,15 @@ void LayoutBoxModelObject::UpdateStickyPositionConstraints() const { // transforms. FloatRect scroll_container_relative_padding_box_rect( containing_block->LayoutOverflowRect()); - FloatSize scroll_container_border_offset; - if (scroll_ancestor) { - scroll_container_border_offset = - FloatSize(scroll_ancestor->BorderLeft(), scroll_ancestor->BorderTop()); - } - if (containing_block != scroll_ancestor) { + FloatSize scroll_container_border_offset = + FloatSize(scroll_ancestor.BorderLeft(), scroll_ancestor.BorderTop()); + if (containing_block != &scroll_ancestor) { FloatQuad local_quad(FloatRect(containing_block->PaddingBoxRect())); scroll_container_relative_padding_box_rect = containing_block - ->LocalToAncestorQuadWithoutTransforms(local_quad, scroll_ancestor, + ->LocalToAncestorQuadWithoutTransforms(local_quad, &scroll_ancestor, flags) .BoundingBox(); - - // The sticky position constraint rects should be independent of the current - // scroll position, so after mapping we add in the scroll position to get - // the container's position within the ancestor scroller's unscrolled layout - // overflow. - ScrollOffset scroll_offset( - scroll_ancestor - ? ToFloatSize( - scroll_ancestor->GetScrollableArea()->ScrollPosition()) - : FloatSize()); - scroll_container_relative_padding_box_rect.Move(scroll_offset); } // Remove top-left border offset from overflow scroller. scroll_container_relative_padding_box_rect.Move( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc index 2805438d13f..ed71dfef77d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc @@ -118,6 +118,115 @@ TEST_F(LayoutBoxModelObjectTest, StickyPositionVerticalRLConstraints) { EnclosingIntRect(sticky->ComputeStickyConstrainingRect())); } +// Verifies that the sticky constraints are correctly computed for inline. +TEST_F(LayoutBoxModelObjectTest, StickyPositionInlineConstraints) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { overflow: scroll; width: 100px; height: 100px; top: 100px; + position: absolute; } + .container { position: relative; top: 100px; height: 400px; + width: 200px; } + .sticky_box { width: 10px; height: 10px; top: 10px; position: sticky; } + .inline { display: inline-block; } + .spacer { height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='container'> + <div class='inline sticky_box' id='sticky'></div> + </div> + <div class='spacer'></div> + </div> + )HTML"); + + LayoutBoxModelObject* scroller = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); + PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); + scrollable_area->ScrollToAbsolutePosition( + FloatPoint(scrollable_area->ScrollOffsetInt().Width(), 50)); + EXPECT_EQ(50.f, scrollable_area->ScrollPosition().Y()); + LayoutBoxModelObject* sticky = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky")); + + sticky->UpdateStickyPositionConstraints(); + + EXPECT_EQ(scroller->Layer(), sticky->Layer()->AncestorOverflowLayer()); + + const StickyPositionScrollingConstraints& constraints = + scrollable_area->GetStickyConstraintsMap().at(sticky->Layer()); + + EXPECT_EQ(10.f, constraints.TopOffset()); + + // The coordinates of the constraint rects should all be with respect to the + // unscrolled scroller. + EXPECT_EQ(IntRect(0, 100, 200, 400), + EnclosingIntRect( + GetScrollContainerRelativeContainingBlockRect(constraints))); + EXPECT_EQ( + IntRect(0, 100, 10, 10), + EnclosingIntRect(GetScrollContainerRelativeStickyBoxRect(constraints))); + EXPECT_EQ(IntRect(0, 50, 100, 100), sticky->ComputeStickyConstrainingRect()); +} + +// Verifies that the sticky constraints are correctly computed for sticky with +// writing mode. +TEST_F(LayoutBoxModelObjectTest, StickyPositionVerticalRLInlineConstraints) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { writing-mode: vertical-rl; overflow: scroll; width: 100px; + height: 100px; top: 100px; position: absolute; } + .container { position: relative; top: 100px; height: 400px; + width: 200px; } + .sticky_box { width: 10px; height: 10px; top: 10px; position: sticky; } + .inline { display: inline-block; } + .spacer { width: 2000px; height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='container'> + <div class='inline sticky_box' id='sticky'></div> + </div> + <div class='spacer'></div> + </div> + )HTML"); + // Initial layout: + // 0---------------2000----2200 + // -----spacer----- + // container--- + // ----2100---- + // scroller + // ----2190 + // sticky + LayoutBoxModelObject* scroller = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); + PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); + scrollable_area->ScrollToAbsolutePosition( + FloatPoint(scrollable_area->ScrollPosition().X(), 50)); + EXPECT_EQ(50.f, scrollable_area->ScrollPosition().Y()); + LayoutBoxModelObject* sticky = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky")); + + sticky->UpdateStickyPositionConstraints(); + + EXPECT_EQ(scroller->Layer(), sticky->Layer()->AncestorOverflowLayer()); + + const StickyPositionScrollingConstraints& constraints = + scrollable_area->GetStickyConstraintsMap().at(sticky->Layer()); + + EXPECT_EQ(10.f, constraints.TopOffset()); + + // The coordinates of the constraint rects should all be with respect to the + // unscrolled scroller. + EXPECT_EQ(IntRect(2000, 100, 200, 400), + EnclosingIntRect( + GetScrollContainerRelativeContainingBlockRect(constraints))); + EXPECT_EQ( + IntRect(2190, 100, 10, 10), + EnclosingIntRect(GetScrollContainerRelativeStickyBoxRect(constraints))); + EXPECT_EQ(IntRect(2100, 50, 100, 100), + sticky->ComputeStickyConstrainingRect()); +} + // Verifies that the sticky constraints are not affected by transforms TEST_F(LayoutBoxModelObjectTest, StickyPositionTransforms) { SetBodyInnerHTML(R"HTML( @@ -950,12 +1059,11 @@ TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedFixedPos) { FloatPoint(scrollable_area->ScrollPosition().X(), 100)); ASSERT_EQ(100.0, scrollable_area->ScrollPosition().Y()); - // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update + // TODO(smcgruer): Until http://crbug.com/686164 is fixed, the sticky position + // offset of the inner sticky stays 75 instead of 25. // the constraints here before calculations will be correct. - inner_sticky->UpdateStickyPositionConstraints(); - EXPECT_EQ(LayoutSize(0, 100), outer_sticky->StickyPositionOffset()); - EXPECT_EQ(LayoutSize(0, 25), inner_sticky->StickyPositionOffset()); + EXPECT_EQ(LayoutSize(0, 75), inner_sticky->StickyPositionOffset()); } TEST_F(LayoutBoxModelObjectTest, NoCrashStackingContextChangeNonRooted) { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc index 1ddb6e110ad..30156c6b95e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc @@ -6,6 +6,7 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/html/html_body_element.h" #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" @@ -433,6 +434,20 @@ TEST_F(LayoutBoxTest, ContentsVisualOverflowPropagation) { EXPECT_EQ(LayoutRect(-70, 0, 230, 210), a->VisualOverflowRect()); } +TEST_F(LayoutBoxTest, HitTestContainPaint) { + SetBodyInnerHTML(R"HTML( + <div id='container' style='width: 100px; height: 200px; contain: paint'> + <div id='child' style='width: 300px; height: 400px;'></div> + </div> + )HTML"); + + auto* child = GetDocument().getElementById("child"); + EXPECT_EQ(GetDocument().documentElement(), HitTest(1, 1)); + EXPECT_EQ(child, HitTest(10, 10)); + EXPECT_EQ(GetDocument().FirstBodyElement(), HitTest(150, 10)); + EXPECT_EQ(GetDocument().documentElement(), HitTest(10, 250)); +} + class AnimatedImage : public StubImage { public: bool MaybeAnimated() override { return true; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_br.cc b/chromium/third_party/blink/renderer/core/layout/layout_br.cc index 9d390cbb251..094d44a8e54 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_br.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_br.cc @@ -60,4 +60,19 @@ PositionWithAffinity LayoutBR::PositionForPoint(const LayoutPoint&) const { return CreatePositionWithAffinity(0); } +Position LayoutBR::PositionForCaretOffset(unsigned offset) const { + DCHECK_LE(offset, 1u); + DCHECK(GetNode()); + return offset ? Position::AfterNode(*GetNode()) + : Position::BeforeNode(*GetNode()); +} + +base::Optional<unsigned> LayoutBR::CaretOffsetForPosition( + const Position& position) const { + if (position.IsNull() || position.AnchorNode() != GetNode()) + return base::nullopt; + DCHECK(position.IsBeforeAnchor() || position.IsAfterAnchor()) << position; + return position.IsBeforeAnchor() ? 0 : 1; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_br.h b/chromium/third_party/blink/renderer/core/layout/layout_br.h index cfafddccd6b..0c11848d9c3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_br.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_br.h @@ -70,6 +70,9 @@ class LayoutBR final : public LayoutText { PositionWithAffinity PositionForPoint(const LayoutPoint&) const final; + Position PositionForCaretOffset(unsigned) const final; + base::Optional<unsigned> CaretOffsetForPosition(const Position&) const final; + protected: void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_count_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_count_test.cc index 84dbe96284a..f6500919773 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_count_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_count_test.cc @@ -5,7 +5,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { @@ -13,7 +12,6 @@ class LayoutCountTest : public RenderingTest {}; TEST_F(LayoutCountTest, SimpleBlockLayoutIsOnePass) { ScopedTrackLayoutPassesPerBlockForTest track_layout_passes_per_block(true); - ScopedRootLayerScrollingForTest root_layer_scrolling(true); SetBodyInnerHTML( "<!DOCTYPE html>" " <div id='block' style='height:1000px'>Item</div>"); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc index 72b845650c2..e9ef81e07e7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc @@ -318,16 +318,28 @@ static bool ChildDoesNotAffectWidthOrFlexing(LayoutObject* child) { child->Style()->Visibility() == EVisibility::kCollapse; } +static LayoutUnit WidthForChild(LayoutBox* child) { + if (child->HasOverrideLogicalWidth()) + return child->OverrideLogicalWidth(); + return child->LogicalWidth(); +} + static LayoutUnit ContentWidthForChild(LayoutBox* child) { - if (child->HasOverrideLogicalContentWidth()) - return child->OverrideLogicalContentWidth(); - return child->LogicalWidth() - child->BorderAndPaddingLogicalWidth(); + // TODO(rego): Shouldn't we subtract the scrollbar width too? + return (WidthForChild(child) - child->BorderAndPaddingLogicalWidth()) + .ClampNegativeToZero(); +} + +static LayoutUnit HeightForChild(LayoutBox* child) { + if (child->HasOverrideLogicalHeight()) + return child->OverrideLogicalHeight(); + return child->LogicalHeight(); } static LayoutUnit ContentHeightForChild(LayoutBox* child) { - if (child->HasOverrideLogicalContentHeight()) - return child->OverrideLogicalContentHeight(); - return child->LogicalHeight() - child->BorderAndPaddingLogicalHeight(); + // TODO(rego): Shouldn't we subtract the scrollbar height too? + return (HeightForChild(child) - child->BorderAndPaddingLogicalHeight()) + .ClampNegativeToZero(); } void LayoutDeprecatedFlexibleBox::StyleWillChange( @@ -712,8 +724,7 @@ void LayoutDeprecatedFlexibleBox::LayoutHorizontalBox(bool relayout_children) { LayoutUnit(space_available_this_pass * (child->Style()->BoxFlex() / total_flex)); if (space_add) { - child->SetOverrideLogicalContentWidth( - ContentWidthForChild(child) + space_add); + child->SetOverrideLogicalWidth(WidthForChild(child) + space_add); flexing_children = true; relayout_children = true; } @@ -731,8 +742,7 @@ void LayoutDeprecatedFlexibleBox::LayoutHorizontalBox(bool relayout_children) { for (LayoutBox* child = iterator.First(); child && remaining_space; child = iterator.Next()) { if (AllowedChildFlex(child, expanding)) { - child->SetOverrideLogicalContentWidth( - ContentWidthForChild(child) + space_add); + child->SetOverrideLogicalWidth(WidthForChild(child) + space_add); flexing_children = true; relayout_children = true; remaining_space -= space_add; @@ -998,8 +1008,8 @@ void LayoutDeprecatedFlexibleBox::LayoutVerticalBox(bool relayout_children) { space_available_this_pass * (child->Style()->BoxFlex() / total_flex)); if (space_add) { - child->SetOverrideLogicalContentHeight( - ContentHeightForChild(child) + space_add); + child->SetOverrideLogicalHeight(HeightForChild(child) + + space_add); flexing_children = true; relayout_children = true; } @@ -1017,8 +1027,8 @@ void LayoutDeprecatedFlexibleBox::LayoutVerticalBox(bool relayout_children) { for (LayoutBox* child = iterator.First(); child && remaining_space; child = iterator.Next()) { if (AllowedChildFlex(child, expanding)) { - child->SetOverrideLogicalContentHeight( - ContentHeightForChild(child) + space_add); + child->SetOverrideLogicalHeight(HeightForChild(child) + + space_add); flexing_children = true; relayout_children = true; remaining_space -= space_add; @@ -1162,8 +1172,7 @@ void LayoutDeprecatedFlexibleBox::ApplyLineClamp(FlexBoxIterator& iterator, if (new_height == child->Size().Height()) continue; - child->SetOverrideLogicalContentHeight(new_height - - child->BorderAndPaddingHeight()); + child->SetOverrideLogicalHeight(new_height); child->ForceChildLayout(); // FIXME: For now don't support RTL. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc index da2f38366ed..ce2fcd590c5 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc @@ -124,7 +124,7 @@ bool LayoutEmbeddedContent::RequiresAcceleratedCompositing() const { // Second, if this is a LayoutObject with a contentDocument and that document // needs a layer, then we need a layer. WebPluginContainerImpl* plugin_view = Plugin(); - if (plugin_view && plugin_view->PlatformLayer()) + if (plugin_view && plugin_view->CcLayer()) return true; if (!GetNode() || !GetNode()->IsFrameOwnerElement()) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.cc b/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.cc index 9ee0e68fd40..df7644aec9a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.cc @@ -142,17 +142,28 @@ void LayoutEmbeddedObject::UpdateLayout() { ClearNeedsLayout(); } -ScrollResult LayoutEmbeddedObject::Scroll(ScrollGranularity granularity, - const FloatSize&) { - return ScrollResult(); -} - CompositingReasons LayoutEmbeddedObject::AdditionalCompositingReasons() const { if (RequiresAcceleratedCompositing()) return CompositingReason::kPlugin; return CompositingReason::kNone; } +void LayoutEmbeddedObject::ComputeIntrinsicSizingInfo( + IntrinsicSizingInfo& intrinsic_sizing_info) const { + FrameView* frame_view = ChildFrameView(); + if (frame_view && frame_view->GetIntrinsicSizingInfo(intrinsic_sizing_info)) { + // Handle zoom & vertical writing modes here, as the embedded document + // doesn't know about them. + intrinsic_sizing_info.size.Scale(Style()->EffectiveZoom()); + + if (!IsHorizontalWritingMode()) + intrinsic_sizing_info.Transpose(); + return; + } + + LayoutEmbeddedContent::ComputeIntrinsicSizingInfo(intrinsic_sizing_info); +} + bool LayoutEmbeddedObject::NeedsPreferredWidthsRecalculation() const { if (LayoutEmbeddedContent::NeedsPreferredWidthsRecalculation()) return true; @@ -160,11 +171,4 @@ bool LayoutEmbeddedObject::NeedsPreferredWidthsRecalculation() const { return frame_view && frame_view->HasIntrinsicSizingInfo(); } -bool LayoutEmbeddedObject::GetNestedIntrinsicSizingInfo( - IntrinsicSizingInfo& intrinsic_sizing_info) const { - if (FrameView* frame_view = ChildFrameView()) - return frame_view->GetIntrinsicSizingInfo(intrinsic_sizing_info); - return false; -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.h b/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.h index 180fb40684d..8b196e9db68 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_embedded_object.h @@ -62,13 +62,11 @@ class LayoutEmbeddedObject final : public LayoutEmbeddedContent { return type == kLayoutObjectEmbeddedObject || LayoutEmbeddedContent::IsOfType(type); } + void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; bool NeedsPreferredWidthsRecalculation() const override; - bool GetNestedIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; PaintLayerType LayerTypeRequired() const final; - ScrollResult Scroll(ScrollGranularity, const FloatSize&) final; - CompositingReasons AdditionalCompositingReasons() const override; PluginAvailability plugin_availability_ = kPluginAvailable; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc index c80e8f984a1..baf3a07f6f0 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc @@ -31,17 +31,19 @@ #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" #include <limits> +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h" #include "third_party/blink/renderer/core/layout/layout_state.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/text_autosizer.h" #include "third_party/blink/renderer/core/paint/block_painter.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/length_functions.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { @@ -370,7 +372,7 @@ void LayoutFlexibleBox::UpdateBlockLayout(bool relayout_children) { return; relaid_out_children_.clear(); - WTF::AutoReset<bool> reset1(&in_layout_, true); + base::AutoReset<bool> reset1(&in_layout_, true); DCHECK_EQ(has_definite_height_, SizeDefiniteness::kUnknown); if (UpdateLogicalWidthAndColumnWidth()) @@ -825,6 +827,30 @@ void LayoutFlexibleBox::ClearCachedMainSizeForChild(const LayoutBox& child) { intrinsic_size_along_main_axis_.erase(&child); } +bool LayoutFlexibleBox::ShouldForceLayoutForNGChild( + const LayoutBlockFlow& child) const { + // If the last layout was done with a different override size, + // or different definite-ness, we need to force-relayout so + // that percentage sizes are resolved correctly. + const NGConstraintSpace* old_space = child.CachedConstraintSpace(); + if (!old_space) + return true; + if (old_space->IsFixedSizeInline() != child.HasOverrideLogicalWidth()) + return true; + if (old_space->IsFixedSizeBlock() != child.HasOverrideLogicalHeight()) + return true; + if (old_space->FixedSizeBlockIsDefinite() != + UseOverrideLogicalHeightForPerentageResolution(child)) + return true; + if (child.HasOverrideLogicalWidth() && + old_space->AvailableSize().inline_size != child.OverrideLogicalWidth()) + return true; + if (child.HasOverrideLogicalHeight() && + old_space->AvailableSize().block_size != child.OverrideLogicalHeight()) + return true; + return false; +} + DISABLE_CFI_PERF LayoutUnit LayoutFlexibleBox::ComputeInnerFlexBaseSizeForChild( LayoutBox& child, @@ -1119,19 +1145,17 @@ MinMaxSize LayoutFlexibleBox::ComputeMinAndMaxSizesForChild( return sizes; } -LayoutUnit LayoutFlexibleBox::CrossSizeForPercentageResolution( - const LayoutBox& child) { +bool LayoutFlexibleBox::CrossSizeIsDefiniteForPercentageResolution( + const LayoutBox& child) const { if (FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child.StyleRef()) != ItemPosition::kStretch) - return LayoutUnit(-1); + return false; // Here we implement https://drafts.csswg.org/css-flexbox/#algo-stretch - if (HasOrthogonalFlow(child) && child.HasOverrideLogicalContentWidth()) - return child.OverrideLogicalContentWidth() - child.ScrollbarLogicalWidth(); - if (!HasOrthogonalFlow(child) && child.HasOverrideLogicalContentHeight()) { - return child.OverrideLogicalContentHeight() - - child.ScrollbarLogicalHeight(); - } + if (HasOrthogonalFlow(child) && child.HasOverrideLogicalWidth()) + return true; + if (!HasOrthogonalFlow(child) && child.HasOverrideLogicalHeight()) + return true; // We don't currently implement the optimization from // https://drafts.csswg.org/css-flexbox/#definite-sizes case 1. While that @@ -1139,42 +1163,36 @@ LayoutUnit LayoutFlexibleBox::CrossSizeForPercentageResolution( // definite size, which itself is not cheap. We can consider implementing it // at a later time. (The correctness is ensured by redoing layout in // applyStretchAlignmentToChild) - return LayoutUnit(-1); + return false; } -LayoutUnit LayoutFlexibleBox::MainSizeForPercentageResolution( - const LayoutBox& child) { +bool LayoutFlexibleBox::MainSizeIsDefiniteForPercentageResolution( + const LayoutBox& child) const { // This function implements section 9.8. Definite and Indefinite Sizes, case // 2) of the flexbox spec. // We need to check for the flexbox to have a definite main size, and for the // flex item to have a definite flex basis. const Length& flex_basis = FlexBasisForChild(child); if (!MainAxisLengthIsDefinite(child, flex_basis)) - return LayoutUnit(-1); + return false; if (!flex_basis.IsPercentOrCalc()) { // If flex basis had a percentage, our size is guaranteed to be definite or // the flex item's size could not be definite. Otherwise, we make up a // percentage to check whether we have a definite size. if (!MainAxisLengthIsDefinite(child, Length(0, kPercent))) - return LayoutUnit(-1); + return false; } if (HasOrthogonalFlow(child)) - return child.HasOverrideLogicalContentHeight() - ? child.OverrideLogicalContentHeight() - - child.ScrollbarLogicalHeight() - : LayoutUnit(-1); - return child.HasOverrideLogicalContentWidth() - ? child.OverrideLogicalContentWidth() - - child.ScrollbarLogicalWidth() - : LayoutUnit(-1); + return child.HasOverrideLogicalHeight(); + return child.HasOverrideLogicalWidth(); } -LayoutUnit LayoutFlexibleBox::ChildLogicalHeightForPercentageResolution( - const LayoutBox& child) { +bool LayoutFlexibleBox::UseOverrideLogicalHeightForPerentageResolution( + const LayoutBox& child) const { if (!HasOrthogonalFlow(child)) - return CrossSizeForPercentageResolution(child); - return MainSizeForPercentageResolution(child); + return CrossSizeIsDefiniteForPercentageResolution(child); + return MainSizeIsDefiniteForPercentageResolution(child); } LayoutUnit LayoutFlexibleBox::AdjustChildSizeForAspectRatioCrossAxisMinAndMax( @@ -1283,10 +1301,15 @@ static LayoutUnit AlignmentOffset(LayoutUnit available_free_space, void LayoutFlexibleBox::SetOverrideMainAxisContentSizeForChild( LayoutBox& child, LayoutUnit child_preferred_size) { - if (HasOrthogonalFlow(child)) - child.SetOverrideLogicalContentHeight(child_preferred_size); - else - child.SetOverrideLogicalContentWidth(child_preferred_size); + if (HasOrthogonalFlow(child)) { + // TODO(rego): Shouldn't we add the scrollbar height too? + child.SetOverrideLogicalHeight(child_preferred_size + + child.BorderAndPaddingLogicalHeight()); + } else { + // TODO(rego): Shouldn't we add the scrollbar width too? + child.SetOverrideLogicalWidth(child_preferred_size + + child.BorderAndPaddingLogicalWidth()); + } } LayoutUnit LayoutFlexibleBox::StaticMainAxisPositionForPositionedChild( @@ -1457,7 +1480,6 @@ void LayoutFlexibleBox::LayoutLineItems(FlexLine* current_line, flex_item.flexed_content_size); // The flexed content size and the override size include the scrollbar // width, so we need to compare to the size including the scrollbar. - // TODO(cbiesinger): Should it include the scrollbar? if (flex_item.flexed_content_size != MainAxisContentExtentForChildIncludingScrollbar(*child)) { child->SetChildNeedsLayout(kMarkOnlyThis); @@ -1470,8 +1492,12 @@ void LayoutFlexibleBox::LayoutLineItems(FlexLine* current_line, // computeInnerFlexBaseSizeForChild. bool force_child_relayout = relayout_children && !relaid_out_children_.Contains(child); - if (child->IsLayoutBlock() && - ToLayoutBlock(*child).HasPercentHeightDescendants()) { + // TODO(dgrogan): Broaden the NG part of this check once NG types other + // than Mixin derivatives are cached. + if ((child->IsLayoutNGMixin() && + ShouldForceLayoutForNGChild(ToLayoutBlockFlow(*child))) || + (child->IsLayoutBlock() && + ToLayoutBlock(*child).HasPercentHeightDescendants())) { // Have to force another relayout even though the child is sized // correctly, because its descendants are not sized correctly yet. Our // previous layout of the child was done without an override height set. @@ -1713,9 +1739,8 @@ void LayoutFlexibleBox::ApplyStretchAlignmentToChild( // So, redo it here. child_needs_relayout = true; } - if (child_needs_relayout || !child.HasOverrideLogicalContentHeight()) - child.SetOverrideLogicalContentHeight( - desired_logical_height - child.BorderAndPaddingLogicalHeight()); + if (child_needs_relayout || !child.HasOverrideLogicalHeight()) + child.SetOverrideLogicalHeight(desired_logical_height); if (child_needs_relayout) { child.SetLogicalHeight(LayoutUnit()); // We cache the child's intrinsic content logical height to avoid it being @@ -1739,8 +1764,7 @@ void LayoutFlexibleBox::ApplyStretchAlignmentToChild( flex_item.cross_axis_size = child_width; if (child_width != child.LogicalWidth()) { - child.SetOverrideLogicalContentWidth( - child_width - child.BorderAndPaddingLogicalWidth()); + child.SetOverrideLogicalWidth(child_width); child.ForceChildLayout(); } } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h index 9e783233ce4..def9538873b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h @@ -75,9 +75,14 @@ class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { const OrderIterator& GetOrderIterator() const { return order_iterator_; } - LayoutUnit CrossSizeForPercentageResolution(const LayoutBox& child); - LayoutUnit MainSizeForPercentageResolution(const LayoutBox& child); - LayoutUnit ChildLogicalHeightForPercentageResolution(const LayoutBox& child); + // These three functions are used when resolving percentages against a + // flex item's logical height. In flexbox, sometimes a logical height + // should be considered definite even though it normally shouldn't be, + // and these functions implement that logic. + bool CrossSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const; + bool MainSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const; + bool UseOverrideLogicalHeightForPerentageResolution( + const LayoutBox& child) const; void ClearCachedMainSizeForChild(const LayoutBox& child); @@ -153,6 +158,7 @@ class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { EOverflow MainAxisOverflowForChild(const LayoutBox& child) const; EOverflow CrossAxisOverflowForChild(const LayoutBox& child) const; void CacheChildMainSize(const LayoutBox& child); + bool ShouldForceLayoutForNGChild(const LayoutBlockFlow& child) const; void LayoutFlexItems(bool relayout_children, SubtreeLayoutScope&); LayoutUnit AutoMarginOffsetInMainAxis(const Vector<FlexItem>&, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_full_screen.cc b/chromium/third_party/blink/renderer/core/layout/layout_full_screen.cc index 8a1f7b6fde6..a90fabbc6e6 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_full_screen.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_full_screen.cc @@ -25,13 +25,12 @@ #include "third_party/blink/renderer/core/layout/layout_full_screen.h" +#include "third_party/blink/public/platform/web_screen_info.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/fullscreen/fullscreen.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/page/page.h" -#include "third_party/blink/public/platform/web_screen_info.h" - namespace blink { namespace { @@ -43,9 +42,11 @@ class LayoutFullScreenPlaceholder final : public LayoutBlockFlow { SetDocumentForAnonymous(&owner->GetDocument()); } - // Must call setStyleWithWritingModeOfParent() instead. + // Must call SetStyleWithWritingModeOfParent() instead. void SetStyle(scoped_refptr<ComputedStyle>) = delete; + bool CreatesNewFormattingContext() const override { return true; } + private: bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectLayoutFullScreenPlaceholder || @@ -54,6 +55,7 @@ class LayoutFullScreenPlaceholder final : public LayoutBlockFlow { bool AnonymousHasStylePropagationOverride() override { return true; } void WillBeDestroyed() override; + LayoutFullScreen* owner_; }; @@ -95,7 +97,7 @@ void LayoutFullScreen::WillBeDestroyed() { LayoutFlexibleBox::WillBeDestroyed(); } -void LayoutFullScreen::UpdateStyle(LayoutObject* parent) { +scoped_refptr<ComputedStyle> LayoutFullScreen::CreateAnonymousStyle() { scoped_refptr<ComputedStyle> fullscreen_style = ComputedStyle::Create(); // Create a stacking context: @@ -121,19 +123,19 @@ void LayoutFullScreen::UpdateStyle(LayoutObject* parent) { fullscreen_style->SetHeight(Length(viewport_size.Height(), blink::kFixed)); fullscreen_style->SetBackgroundColor(StyleColor(Color::kBlack)); - - SetStyleWithWritingModeOf(fullscreen_style, parent); + return fullscreen_style; } void LayoutFullScreen::UpdateStyle() { - UpdateStyle(Parent()); + scoped_refptr<ComputedStyle> style = CreateAnonymousStyle(); + SetStyleWithWritingModeOf(style, Parent()); } LayoutObject* LayoutFullScreen::WrapLayoutObject(LayoutObject* object, LayoutObject* parent, Document* document) { - // FIXME: We should not modify the structure of the layout tree during - // layout. crbug.com/370459 + // TODO: We should not modify the structure of the layout tree during layout. + // crbug.com/370459 DeprecatedDisableModifyLayoutTreeStructureAsserts disabler; // A fullscreen <html> element should not be wrapped (see crbug.com/676432). @@ -141,37 +143,41 @@ LayoutObject* LayoutFullScreen::WrapLayoutObject(LayoutObject* object, LayoutFullScreen* fullscreen_layout_object = LayoutFullScreen::CreateAnonymous(document); - fullscreen_layout_object->UpdateStyle(parent); - if (parent && !parent->IsChildAllowed(fullscreen_layout_object, - fullscreen_layout_object->StyleRef())) { + scoped_refptr<ComputedStyle> fullscreen_style = + fullscreen_layout_object->CreateAnonymousStyle(); + + if (parent && + !parent->IsChildAllowed(fullscreen_layout_object, *fullscreen_style)) { fullscreen_layout_object->Destroy(); return nullptr; } + parent = object ? object->Parent() : nullptr; + fullscreen_layout_object->SetStyleWithWritingModeOf(fullscreen_style, parent); + + // |object->Parent()| can be null if the object is not yet attached + // to |parent|. + if (parent) { + LayoutBlock* containing_block = object->ContainingBlock(); + DCHECK(containing_block); + // Since we are moving the |object| to a new parent + // |fullscreen_layout_object|, the line box tree underneath our + // |containing_block| is not longer valid. + if (containing_block->IsLayoutBlockFlow()) + ToLayoutBlockFlow(containing_block)->DeleteLineBoxTree(); + + parent->AddChild(fullscreen_layout_object, object); + object->Remove(); + + // Always just do a full layout to ensure that line boxes get deleted + // properly. + // Because objects moved from |parent| to |fullscreen_layout_object|, we + // want to make new line boxes instead of leaving the old ones around. + parent->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + LayoutInvalidationReason::kFullscreen); + containing_block->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + LayoutInvalidationReason::kFullscreen); + } if (object) { - // |object->parent()| can be null if the object is not yet attached - // to |parent|. - if (LayoutObject* parent = object->Parent()) { - LayoutBlock* containing_block = object->ContainingBlock(); - DCHECK(containing_block); - // Since we are moving the |object| to a new parent - // |fullscreenLayoutObject|, the line box tree underneath our - // |containingBlock| is not longer valid. - if (containing_block->IsLayoutBlockFlow()) - ToLayoutBlockFlow(containing_block)->DeleteLineBoxTree(); - - parent->AddChildWithWritingModeOfParent(fullscreen_layout_object, object); - object->Remove(); - - // Always just do a full layout to ensure that line boxes get deleted - // properly. - // Because objects moved from |parent| to |fullscreenLayoutObject|, we - // want to make new line boxes instead of leaving the old ones around. - parent->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - LayoutInvalidationReason::kFullscreen); - containing_block - ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - LayoutInvalidationReason::kFullscreen); - } fullscreen_layout_object->AddChild(object); fullscreen_layout_object ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( @@ -215,17 +221,17 @@ void LayoutFullScreen::CreatePlaceholder(scoped_refptr<ComputedStyle> style, if (style->Height().IsAuto()) style->SetHeight(Length(frame_rect.Height(), kFixed)); - if (!placeholder_) { - placeholder_ = new LayoutFullScreenPlaceholder(this); - placeholder_->SetStyleWithWritingModeOfParent(std::move(style)); - if (Parent()) { - Parent()->AddChildWithWritingModeOfParent(placeholder_, this); - Parent()->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - LayoutInvalidationReason::kFullscreen); - } - } else { - placeholder_->SetStyle(std::move(style)); + if (placeholder_) { placeholder_->SetStyleWithWritingModeOfParent(std::move(style)); + return; + } + + placeholder_ = new LayoutFullScreenPlaceholder(this); + placeholder_->SetStyleWithWritingModeOf(std::move(style), Parent()); + if (Parent()) { + Parent()->AddChild(placeholder_, this); + Parent()->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + LayoutInvalidationReason::kFullscreen); } } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_full_screen.h b/chromium/third_party/blink/renderer/core/layout/layout_full_screen.h index c1056940301..63534f4f06b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_full_screen.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_full_screen.h @@ -54,7 +54,6 @@ class CORE_EXPORT LayoutFullScreen final : public LayoutFlexibleBox { void UnwrapLayoutObject(); void UpdateStyle(); - void UpdateStyle(LayoutObject* parent); bool AnonymousHasStylePropagationOverride() override { return true; } // Must call setStyleWithWritingModeOfParent() instead. @@ -63,6 +62,7 @@ class CORE_EXPORT LayoutFullScreen final : public LayoutFlexibleBox { private: LayoutFullScreen(); void WillBeDestroyed() override; + scoped_refptr<ComputedStyle> CreateAnonymousStyle(); protected: LayoutBlockFlow* placeholder_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc index 176f44d4312..c9948bff78c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc @@ -25,10 +25,10 @@ #include "third_party/blink/renderer/core/layout/layout_geometry_map.h" +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" #define LAYOUT_GEOMETRY_MAP_LOGGING 0 @@ -196,7 +196,8 @@ void LayoutGeometryMap::PushMappingsToAncestor( const LayoutBoxModelObject* ancestor_layout_object) { // We need to push mappings in reverse order here, so do insertions rather // than appends. - AutoReset<size_t> position_change(&insertion_position_, mapping_.size()); + base::AutoReset<size_t> position_change(&insertion_position_, + mapping_.size()); do { layout_object = layout_object->PushMappingToContainer(ancestor_layout_object, *this); @@ -265,7 +266,8 @@ void LayoutGeometryMap::PushMappingsToAncestor( PushMappingsToAncestor(&ancestor_layer->GetLayoutObject(), nullptr); } - AutoReset<size_t> position_change(&insertion_position_, mapping_.size()); + base::AutoReset<size_t> position_change(&insertion_position_, + mapping_.size()); bool accumulating_transform = layout_object.Style()->Preserves3D() || ancestor_layer->GetLayoutObject().Style()->Preserves3D(); @@ -398,7 +400,7 @@ bool LayoutGeometryMap::IsTopmostLayoutView( if (!(map_coordinates_flags_ & kTraverseDocumentBoundaries)) return true; - return layout_object->GetFrame()->IsMainFrame(); + return layout_object->GetFrame()->IsLocalRoot(); } #endif diff --git a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc index 894ebba5615..c5d0da335da 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc @@ -42,21 +42,14 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" namespace blink { -typedef bool TestParamRootLayerScrolling; -class LayoutGeometryMapTest - : public testing::Test, - public testing::WithParamInterface<TestParamRootLayerScrolling>, - private ScopedRootLayerScrollingForTest { +class LayoutGeometryMapTest : public testing::Test { public: - LayoutGeometryMapTest() - : ScopedRootLayerScrollingForTest(GetParam()), - base_url_("http://www.test.com/") {} + LayoutGeometryMapTest() : base_url_("http://www.test.com/") {} void TearDown() override { Platform::Current() @@ -171,9 +164,7 @@ class LayoutGeometryMapTest const std::string base_url_; }; -INSTANTIATE_TEST_CASE_P(All, LayoutGeometryMapTest, testing::Bool()); - -TEST_P(LayoutGeometryMapTest, SimpleGeometryMapTest) { +TEST_F(LayoutGeometryMapTest, SimpleGeometryMapTest) { RegisterMockedHttpURLLoad("rgm_test.html"); FrameTestHelpers::WebViewHelper web_view_helper; WebView* web_view = @@ -213,9 +204,9 @@ TEST_P(LayoutGeometryMapTest, SimpleGeometryMapTest) { // Fails on Windows due to crbug.com/391457. When run through the transform the // position on windows differs by a pixel #if defined(OS_WIN) -TEST_P(LayoutGeometryMapTest, DISABLED_TransformedGeometryTest) +TEST_F(LayoutGeometryMapTest, DISABLED_TransformedGeometryTest) #else -TEST_P(LayoutGeometryMapTest, TransformedGeometryTest) +TEST_F(LayoutGeometryMapTest, TransformedGeometryTest) #endif { RegisterMockedHttpURLLoad("rgm_transformed_test.html"); @@ -276,7 +267,7 @@ TEST_P(LayoutGeometryMapTest, TransformedGeometryTest) rgm.MapToAncestor(rect, nullptr).BoundingBox()); } -TEST_P(LayoutGeometryMapTest, FixedGeometryTest) { +TEST_F(LayoutGeometryMapTest, FixedGeometryTest) { RegisterMockedHttpURLLoad("rgm_fixed_position_test.html"); FrameTestHelpers::WebViewHelper web_view_helper; WebView* web_view = web_view_helper.InitializeAndLoad( @@ -314,7 +305,7 @@ TEST_P(LayoutGeometryMapTest, FixedGeometryTest) { rgm.MapToAncestor(rect, nullptr)); } -TEST_P(LayoutGeometryMapTest, ContainsFixedPositionTest) { +TEST_F(LayoutGeometryMapTest, ContainsFixedPositionTest) { RegisterMockedHttpURLLoad("rgm_contains_fixed_position_test.html"); FrameTestHelpers::WebViewHelper web_view_helper; WebView* web_view = web_view_helper.InitializeAndLoad( @@ -361,7 +352,7 @@ TEST_P(LayoutGeometryMapTest, ContainsFixedPositionTest) { rgm.PopMappingsToAncestor(static_cast<PaintLayer*>(nullptr)); } -TEST_P(LayoutGeometryMapTest, IframeTest) { +TEST_F(LayoutGeometryMapTest, IframeTest) { RegisterMockedHttpURLLoad("rgm_iframe_test.html"); RegisterMockedHttpURLLoad("rgm_test.html"); FrameTestHelpers::WebViewHelper web_view_helper; @@ -458,7 +449,7 @@ TEST_P(LayoutGeometryMapTest, IframeTest) { rgm_no_frame.MapToAncestor(rect, nullptr)); } -TEST_P(LayoutGeometryMapTest, ColumnTest) { +TEST_F(LayoutGeometryMapTest, ColumnTest) { RegisterMockedHttpURLLoad("rgm_column_test.html"); FrameTestHelpers::WebViewHelper web_view_helper; WebView* web_view = @@ -507,7 +498,7 @@ TEST_P(LayoutGeometryMapTest, ColumnTest) { EXPECT_EQ(3.0f, RectFromQuad(rgm.MapToAncestor(rect, nullptr)).Height()); } -TEST_P(LayoutGeometryMapTest, FloatUnderInlineLayer) { +TEST_F(LayoutGeometryMapTest, FloatUnderInlineLayer) { RegisterMockedHttpURLLoad("rgm_float_under_inline.html"); FrameTestHelpers::WebViewHelper web_view_helper; WebView* web_view = web_view_helper.InitializeAndLoad( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_grid.cc b/chromium/third_party/blink/renderer/core/layout/layout_grid.cc index d2a17280639..20b0f690b67 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_grid.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_grid.cc @@ -212,14 +212,14 @@ bool LayoutGrid::NamedGridLinesDefinitionDidChange( // This method optimizes the gutters computation by skiping the available size // call if gaps are fixed size (it's only needed for percentages). -Optional<LayoutUnit> LayoutGrid::AvailableSpaceForGutters( +base::Optional<LayoutUnit> LayoutGrid::AvailableSpaceForGutters( GridTrackSizingDirection direction) const { bool is_row_axis = direction == kForColumns; const GapLength& gap = is_row_axis ? StyleRef().ColumnGap() : StyleRef().RowGap(); if (!gap.IsNormal() && !gap.GetLength().IsPercent()) - return WTF::nullopt; + return base::nullopt; return is_row_axis ? AvailableLogicalWidth() : AvailableLogicalHeightForPercentageComputation(); @@ -319,15 +319,13 @@ void LayoutGrid::UpdateBlockLayout(bool relayout_children) { LayoutSize previous_size = Size(); has_definite_logical_height_ = HasDefiniteLogicalHeight(); - // Grid's layout logic controls the grid item's override size, hence - // we need to clear any override size set previously, so it doesn't + // Grid's layout logic controls the grid item's override height, hence + // we need to clear any override height set previously, so it doesn't // interfere in current layout execution. + // Grid never uses the override width, that's why we don't need to clear it. for (auto* child = FirstInFlowChildBox(); child; - child = child->NextInFlowSiblingBox()) { - child->ClearOverrideSize(); - child->SetOverrideContainingBlockContentLogicalWidth(LayoutUnit()); - child->SetOverrideContainingBlockContentLogicalHeight(LayoutUnit()); - } + child = child->NextInFlowSiblingBox()) + child->ClearOverrideLogicalHeight(); UpdateLogicalWidth(); @@ -407,8 +405,9 @@ void LayoutGrid::UpdateBlockLayout(bool relayout_children) { ClearNeedsLayout(); } -LayoutUnit LayoutGrid::GridGap(GridTrackSizingDirection direction, - Optional<LayoutUnit> available_size) const { +LayoutUnit LayoutGrid::GridGap( + GridTrackSizingDirection direction, + base::Optional<LayoutUnit> available_size) const { const GapLength& gap = direction == kForColumns ? StyleRef().ColumnGap() : StyleRef().RowGap(); if (gap.IsNormal()) @@ -436,11 +435,12 @@ LayoutUnit LayoutGrid::GridGap(GridTrackSizingDirection direction) const { return ValueForLength(gap.GetLength(), available_size); } -LayoutUnit LayoutGrid::GuttersSize(const Grid& grid, - GridTrackSizingDirection direction, - size_t start_line, - size_t span, - Optional<LayoutUnit> available_size) const { +LayoutUnit LayoutGrid::GuttersSize( + const Grid& grid, + GridTrackSizingDirection direction, + size_t start_line, + size_t span, + base::Optional<LayoutUnit> available_size) const { if (span <= 1) return LayoutUnit(); @@ -520,7 +520,7 @@ void LayoutGrid::ComputeIntrinsicLogicalWidths( LayoutUnit& min_logical_width, LayoutUnit& max_logical_width) const { Grid grid(this); - PlaceItemsOnGrid(grid, WTF::nullopt); + PlaceItemsOnGrid(grid, base::nullopt); GridTrackSizingAlgorithm algorithm(this, grid); ComputeTrackSizesForIndefiniteSize(algorithm, kForColumns, grid, @@ -537,7 +537,7 @@ void LayoutGrid::ComputeTrackSizesForIndefiniteSize( Grid& grid, LayoutUnit& min_intrinsic_size, LayoutUnit& max_intrinsic_size) const { - algo.Setup(direction, NumTracks(direction, grid), WTF::nullopt); + algo.Setup(direction, NumTracks(direction, grid), base::nullopt); algo.Run(); min_intrinsic_size = algo.MinContentSize(); @@ -545,7 +545,7 @@ void LayoutGrid::ComputeTrackSizesForIndefiniteSize( size_t number_of_tracks = algo.Tracks(direction).size(); LayoutUnit total_gutters_size = - GuttersSize(grid, direction, 0, number_of_tracks, WTF::nullopt); + GuttersSize(grid, direction, 0, number_of_tracks, base::nullopt); min_intrinsic_size += total_gutters_size; max_intrinsic_size += total_gutters_size; @@ -591,16 +591,16 @@ LayoutUnit LayoutGrid::OverrideContainingBlockContentSizeForChild( } // Unfortunately there are still many layout methods that return -1 for -// non-resolvable sizes. We prefer to represent them with WTF::nullopt. -static Optional<LayoutUnit> ConvertLayoutUnitToOptional(LayoutUnit size) { +// non-resolvable sizes. We prefer to represent them with base::nullopt. +static base::Optional<LayoutUnit> ConvertLayoutUnitToOptional(LayoutUnit size) { if (size == -1) - return WTF::nullopt; + return base::nullopt; return size; } size_t LayoutGrid::ComputeAutoRepeatTracksCount( GridTrackSizingDirection direction, - Optional<LayoutUnit> available_size) const { + base::Optional<LayoutUnit> available_size) const { DCHECK(!available_size || available_size.value() != -1); bool is_row_axis = direction == kForColumns; const auto& auto_repeat_tracks = is_row_axis @@ -768,7 +768,7 @@ size_t LayoutGrid::ClampAutoRepeatTracks(GridTrackSizingDirection direction, // does know whether the available logical width is indefinite or not. void LayoutGrid::PlaceItemsOnGrid( Grid& grid, - Optional<LayoutUnit> available_logical_width) const { + base::Optional<LayoutUnit> available_logical_width) const { size_t auto_repeat_rows = ComputeAutoRepeatTracksCount( kForRows, ConvertLayoutUnitToOptional( AvailableLogicalHeightForPercentageComputation())); @@ -792,20 +792,27 @@ void LayoutGrid::PlaceItemsOnGrid( Vector<LayoutBox*> auto_major_axis_auto_grid_items; Vector<LayoutBox*> specified_major_axis_auto_grid_items; + Vector<LayoutBox*> orthogonal_grid_items; #if DCHECK_IS_ON() DCHECK(!grid.HasAnyGridItemPaintOrder()); #endif DCHECK(!grid.HasAnyOrthogonalGridItem()); - bool has_any_orthogonal_grid_item = false; size_t child_index = 0; for (LayoutBox* child = grid.GetOrderIterator().First(); child; child = grid.GetOrderIterator().Next()) { if (child->IsOutOfFlowPositioned()) continue; - has_any_orthogonal_grid_item = - has_any_orthogonal_grid_item || - GridLayoutUtils::IsOrthogonalChild(*this, *child); + // Grid items should use the grid area sizes instead of the containing block + // (grid container) sizes, we initialize the overrides here if needed to + // ensure it. + if (!child->HasOverrideContainingBlockContentLogicalWidth()) + child->SetOverrideContainingBlockContentLogicalWidth(LayoutUnit()); + if (!child->HasOverrideContainingBlockContentLogicalHeight()) + child->SetOverrideContainingBlockContentLogicalHeight(LayoutUnit(-1)); + + if (GridLayoutUtils::IsOrthogonalChild(*this, *child)) + orthogonal_grid_items.push_back(child); grid.SetGridItemPaintOrder(*child, child_index++); GridArea area = grid.GridItemArea(*child); @@ -827,7 +834,7 @@ void LayoutGrid::PlaceItemsOnGrid( } grid.insert(*child, area); } - grid.SetHasAnyOrthogonalGridItem(has_any_orthogonal_grid_item); + grid.SetHasAnyOrthogonalGridItem(!orthogonal_grid_items.IsEmpty()); #if DCHECK_IS_ON() if (grid.HasGridItems()) { @@ -851,6 +858,14 @@ void LayoutGrid::PlaceItemsOnGrid( grid.SetNeedsItemsPlacement(false); + // Blink does a pre-layout of all the orthogonal boxes in the layout + // tree (see how LocalFrameView::PerformLayout calls its + // LayoutOrthogonalWritingModeRoots function). However, grid items + // don't participate in this process (see the function + // PrepareOrthogonalWritingModeRootForLayout) because it's useless + // and even wrong if they don't have their corresponding Grid Area. + LayoutOrthogonalWritingModeRoots(grid, orthogonal_grid_items); + #if DCHECK_IS_ON() for (LayoutBox* child = grid.GetOrderIterator().First(); child; child = grid.GetOrderIterator().Next()) { @@ -864,6 +879,37 @@ void LayoutGrid::PlaceItemsOnGrid( #endif } +// TODO(lajava): Consider rafactoring this code with +// LocalFrameView::PrepareOrthogonalWritingModeRootForLayout +static bool PrepareOrthogonalWritingModeRootForLayout(LayoutObject& root) { + DCHECK(root.IsBox() && ToLayoutBox(root).IsOrthogonalWritingModeRoot()); + if (!root.NeedsLayout() || root.IsOutOfFlowPositioned() || + root.IsColumnSpanAll() || root.IsTablePart()) + return false; + + return true; +} + +// TODO(lajava): Consider rafactoring this code with +// LocalFrameView::LayoutOrthogonalWritingModeRoots +void LayoutGrid::LayoutOrthogonalWritingModeRoots( + const Grid& grid, + const Vector<LayoutBox*>& orthogonal_grid_items) const { + if (!GetDocument().View()->IsInPerformLayout()) + return; + + for (auto* root : orthogonal_grid_items) { + DCHECK(GridLayoutUtils::IsOrthogonalChild(*this, *root)); + if (PrepareOrthogonalWritingModeRootForLayout(*root)) { + UpdateGridAreaLogicalSize( + *root, + LayoutSize(EstimatedGridAreaBreadthForChild(grid, *root, kForColumns), + EstimatedGridAreaBreadthForChild(grid, *root, kForRows))); + root->LayoutIfNeeded(); + } + } +} + void LayoutGrid::PopulateExplicitGridAndOrderIterator(Grid& grid) const { OrderIteratorPopulator populator(grid.GetOrderIterator()); int smallest_row_start = 0; @@ -1171,6 +1217,26 @@ const StyleContentAlignmentData& LayoutGrid::ContentAlignmentNormalBehavior() { return kNormalBehavior; } +void LayoutGrid::UpdateGridAreaLogicalSize( + LayoutBox& child, + LayoutSize grid_area_logical_size) const { + // Because the grid area cannot be styled, we don't need to adjust + // the grid breadth to account for 'box-sizing'. + if (child.OverrideContainingBlockContentLogicalWidth() != + grid_area_logical_size.Width() || + (child.OverrideContainingBlockContentLogicalHeight() != + grid_area_logical_size.Height() && + (child.HasRelativeLogicalHeight() || + GridLayoutUtils::IsOrthogonalChild(*this, child)))) { + child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); + } + + child.SetOverrideContainingBlockContentLogicalWidth( + grid_area_logical_size.Width()); + child.SetOverrideContainingBlockContentLogicalHeight( + grid_area_logical_size.Height()); +} + void LayoutGrid::LayoutGridItems() { PopulateGridPositionsForDirection(kForColumns); PopulateGridPositionsForDirection(kForRows); @@ -1183,24 +1249,14 @@ void LayoutGrid::LayoutGridItems() { continue; } - // Because the grid area cannot be styled, we don't need to adjust - // the grid breadth to account for 'box-sizing'. - LayoutUnit override_containing_block_content_logical_width = - GridAreaBreadthForChildIncludingAlignmentOffsets(*child, kForColumns); - LayoutUnit override_containing_block_content_logical_height = - GridAreaBreadthForChildIncludingAlignmentOffsets(*child, kForRows); - - if (child->OverrideContainingBlockContentLogicalWidth() != - override_containing_block_content_logical_width || - (child->OverrideContainingBlockContentLogicalHeight() != - override_containing_block_content_logical_height && - child->HasRelativeLogicalHeight())) - child->SetNeedsLayout(LayoutInvalidationReason::kGridChanged); - - child->SetOverrideContainingBlockContentLogicalWidth( - override_containing_block_content_logical_width); - child->SetOverrideContainingBlockContentLogicalHeight( - override_containing_block_content_logical_height); + // Setting the definite grid area's sizes. It may imply that the + // item must perform a layout if its area differs from the one + // used during the track sizing algorithm. + UpdateGridAreaLogicalSize( + *child, LayoutSize(GridAreaBreadthForChildIncludingAlignmentOffsets( + *child, kForColumns), + GridAreaBreadthForChildIncludingAlignmentOffsets( + *child, kForRows))); // Stretching logic might force a child layout, so we need to run it before // the layoutIfNeeded call to avoid unnecessary relayouts. This might imply @@ -1229,12 +1285,12 @@ void LayoutGrid::LayoutGridItems() { // dimensions for simplicity, so we can forget about orthogonalty. LayoutUnit child_grid_area_height = IsHorizontalWritingMode() - ? override_containing_block_content_logical_height - : override_containing_block_content_logical_width; + ? child->OverrideContainingBlockContentLogicalHeight() + : child->OverrideContainingBlockContentLogicalWidth(); LayoutUnit child_grid_area_width = IsHorizontalWritingMode() - ? override_containing_block_content_logical_width - : override_containing_block_content_logical_height; + ? child->OverrideContainingBlockContentLogicalWidth() + : child->OverrideContainingBlockContentLogicalHeight(); LayoutRect grid_area_rect( GridAreaLogicalPosition(area), LayoutSize(child_grid_area_width, child_grid_area_height)); @@ -1295,6 +1351,40 @@ void LayoutGrid::LayoutPositionedObjects(bool relayout_children, } } +LayoutUnit LayoutGrid::EstimatedGridAreaBreadthForChild( + const Grid& grid, + const LayoutBox& child, + GridTrackSizingDirection direction) const { + const GridSpan& span = grid.GridItemSpan(child, direction); + LayoutUnit grid_area_size; + bool grid_area_is_indefinite = false; + base::Optional<LayoutUnit> available_size = + track_sizing_algorithm_.AvailableSpace(direction); + for (auto track_position : span) { + GridLength max_track_size = + track_sizing_algorithm_.GetGridTrackSize(direction, track_position) + .MaxTrackBreadth(); + if (max_track_size.IsContentSized() || max_track_size.IsFlex()) { + grid_area_is_indefinite = true; + } else { + grid_area_size += ValueForLength(max_track_size.length(), + available_size.value_or(LayoutUnit())); + } + } + + grid_area_size += GuttersSize(grid_, direction, span.StartLine(), + span.IntegerSpan(), available_size); + + GridTrackSizingDirection child_inline_direction = + GridLayoutUtils::FlowAwareDirectionForChild(*this, child, kForColumns); + if (grid_area_is_indefinite) { + return direction == child_inline_direction + ? std::max(child.MaxPreferredLogicalWidth(), grid_area_size) + : LayoutUnit(-1); + } + return grid_area_size; +} + LayoutUnit LayoutGrid::GridAreaBreadthForChildIncludingAlignmentOffsets( const LayoutBox& child, GridTrackSizingDirection direction) const { @@ -1453,9 +1543,7 @@ void LayoutGrid::ApplyStretchAlignmentToChildIfNeeded(LayoutBox& child) { child); LayoutUnit desired_logical_height = child.ConstrainLogicalHeightByMinMax( stretched_logical_height, LayoutUnit(-1)); - child.SetOverrideLogicalContentHeight( - (desired_logical_height - child.BorderAndPaddingLogicalHeight()) - .ClampNegativeToZero()); + child.SetOverrideLogicalHeight(desired_logical_height); if (desired_logical_height != child.LogicalHeight()) { // TODO (lajava): Can avoid laying out here in some cases. See // https://webkit.org/b/87905. @@ -1976,7 +2064,7 @@ LayoutUnit LayoutGrid::GridAreaBreadthForOutOfFlowChild( end = positions[end_line]; // These vectors store line positions including gaps, but we shouldn't // consider them for the edges of the grid. - Optional<LayoutUnit> available_size_for_gutters = + base::Optional<LayoutUnit> available_size_for_gutters = AvailableSpaceForGutters(direction); if (end_line > 0 && end_line < last_line) { DCHECK(!grid_.NeedsItemsPlacement()); @@ -2030,7 +2118,7 @@ void LayoutGrid::GridAreaPositionForOutOfFlowChild( auto& line_of_positioned_item = is_row_axis ? column_of_positioned_item_ : row_of_positioned_item_; start = is_row_axis ? BorderLogicalLeft() : BorderBefore(); - if (Optional<size_t> line = line_of_positioned_item.at(&child)) { + if (base::Optional<size_t> line = line_of_positioned_item.at(&child)) { auto& positions = is_row_axis ? column_positions_ : row_positions_; start = positions[line.value()]; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_grid.h b/chromium/third_party/blink/renderer/core/layout/layout_grid.h index 4cdeaf6665b..929e6954470 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_grid.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_grid.h @@ -95,7 +95,7 @@ class LayoutGrid final : public LayoutBlock { GridTrackSizingDirection, size_t start_line, size_t span, - Optional<LayoutUnit> available_size) const; + base::Optional<LayoutUnit> available_size) const; bool CachedHasDefiniteLogicalHeight() const; bool IsBaselineAlignmentForChild(const LayoutBox& child, GridAxis = kGridColumnAxis) const; @@ -108,6 +108,10 @@ class LayoutGrid final : public LayoutBlock { LayoutUnit GridGap(GridTrackSizingDirection) const; LayoutUnit GridItemOffset(GridTrackSizingDirection) const; + LayoutUnit EstimatedGridAreaBreadthForChild(const Grid&, + const LayoutBox&, + GridTrackSizingDirection) const; + StyleContentAlignmentData ContentAlignment(GridTrackSizingDirection) const; protected: @@ -144,14 +148,15 @@ class LayoutGrid final : public LayoutBlock { const ComputedStyle& new_style) const; void StyleDidChange(StyleDifference, const ComputedStyle*) override; - Optional<LayoutUnit> AvailableSpaceForGutters(GridTrackSizingDirection) const; + base::Optional<LayoutUnit> AvailableSpaceForGutters( + GridTrackSizingDirection) const; bool ExplicitGridDidResize(const ComputedStyle&) const; bool NamedGridLinesDefinitionDidChange(const ComputedStyle&) const; size_t ComputeAutoRepeatTracksCount( GridTrackSizingDirection, - Optional<LayoutUnit> available_size) const; + base::Optional<LayoutUnit> available_size) const; size_t ClampAutoRepeatTracks(GridTrackSizingDirection, size_t auto_repeat_tracks) const; @@ -159,8 +164,12 @@ class LayoutGrid final : public LayoutBlock { Grid&, GridTrackSizingDirection) const; - void PlaceItemsOnGrid(Grid&, - Optional<LayoutUnit> available_logical_width) const; + void LayoutOrthogonalWritingModeRoots(const Grid&, + const Vector<LayoutBox*>&) const; + + void PlaceItemsOnGrid( + Grid&, + base::Optional<LayoutUnit> available_logical_width) const; void PopulateExplicitGridAndOrderIterator(Grid&) const; std::unique_ptr<GridArea> CreateEmptyGridAreaAtSpecifiedPositionsOutsideGrid( const Grid&, @@ -189,6 +198,7 @@ class LayoutGrid final : public LayoutBlock { void RepeatTracksSizingIfNeeded(LayoutUnit available_space_for_columns, LayoutUnit available_space_for_rows); + void UpdateGridAreaLogicalSize(LayoutBox&, LayoutSize) const; void LayoutGridItems(); void PrepareChildForPositionedLayout(LayoutBox&); bool HasStaticPositionForChild(const LayoutBox&, @@ -284,7 +294,7 @@ class LayoutGrid final : public LayoutBlock { LayoutUnit RowAxisBaselineOffsetForChild(const LayoutBox&) const; LayoutUnit GridGap(GridTrackSizingDirection, - Optional<LayoutUnit> available_size) const; + base::Optional<LayoutUnit> available_size) const; size_t GridItemSpan(const LayoutBox&, GridTrackSizingDirection); @@ -308,14 +318,15 @@ class LayoutGrid final : public LayoutBlock { LayoutUnit offset_between_rows_; Vector<LayoutBox*> grid_items_overflowing_grid_area_; - typedef HashMap<const LayoutBox*, Optional<size_t>> OutOfFlowPositionsMap; + typedef HashMap<const LayoutBox*, base::Optional<size_t>> + OutOfFlowPositionsMap; OutOfFlowPositionsMap column_of_positioned_item_; OutOfFlowPositionsMap row_of_positioned_item_; LayoutUnit min_content_height_{-1}; LayoutUnit max_content_height_{-1}; - Optional<bool> has_definite_logical_height_; + base::Optional<bool> has_definite_logical_height_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutGrid, IsLayoutGrid()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc index af913d3cc72..6de2e94ed6d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc @@ -70,8 +70,8 @@ void LayoutHTMLCanvas::CanvasSizeChanged() { LayoutSize old_size = Size(); UpdateLogicalWidth(); UpdateLogicalHeight(); - if (old_size == Size() && !HasOverrideLogicalContentWidth() && - !HasOverrideLogicalContentHeight()) { + if (old_size == Size() && !HasOverrideLogicalWidth() && + !HasOverrideLogicalHeight()) { // If we have an override size, then we're probably a flex item, and the // check above is insufficient because updateLogical{Width,Height} just // used the override size. We actually have to mark ourselves as needing diff --git a/chromium/third_party/blink/renderer/core/layout/layout_iframe.cc b/chromium/third_party/blink/renderer/core/layout/layout_iframe.cc index 549a6576530..b0c1de439bb 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_iframe.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_iframe.cc @@ -61,11 +61,4 @@ void LayoutIFrame::UpdateLayout() { ClearNeedsLayout(); } -void LayoutIFrame::UpdateAfterLayout() { - if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() && GetNode()) - GetDocument().GetRootScrollerController().ConsiderForImplicit(*GetNode()); - - LayoutEmbeddedContent::UpdateAfterLayout(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_iframe.h b/chromium/third_party/blink/renderer/core/layout/layout_iframe.h index 27a912413d9..33904fe895e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_iframe.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_iframe.h @@ -41,7 +41,6 @@ class LayoutIFrame final : public LayoutEmbeddedContent { bool IsInlineBlockOrInlineTable() const override; void UpdateLayout() override; - void UpdateAfterLayout() override; bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectLayoutIFrame || diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image.cc b/chromium/third_party/blink/renderer/core/layout/layout_image.cc index 3b33eee410d..976366b60e1 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_image.cc @@ -39,17 +39,75 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/core/paint/image_painter.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/svg/graphics/svg_image.h" +#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h" namespace blink { +namespace { +constexpr float kmax_downscaling_ratio = 2.0f; + +bool CheckForOptimizedImagePolicy(const LocalFrame& frame, + LayoutImage* layout_image, + ImageResourceContent* new_image) { + // Invert the image if the document does not have the 'legacy-image-formats' + // feature enabled, and the image is not one of the allowed formats. + if (IsSupportedInFeaturePolicy( + mojom::FeaturePolicyFeature::kLegacyImageFormats) && + !frame.IsFeatureEnabled( + mojom::FeaturePolicyFeature::kLegacyImageFormats)) { + if (!new_image->IsAcceptableContentType()) { + return true; + } + } + // Invert the image if the document does not have the image-compression' + // feature enabled and the image is not sufficiently-well-compressed. + if (IsSupportedInFeaturePolicy( + mojom::FeaturePolicyFeature::kImageCompression) && + !frame.IsFeatureEnabled(mojom::FeaturePolicyFeature::kImageCompression)) { + if (!new_image->IsAcceptableCompressionRatio()) + return true; + } + return false; +} + +bool CheckForMaxDownscalingImagePolicy(const LocalFrame& frame, + HTMLImageElement* element, + LayoutImage* layout_image) { + if (!IsSupportedInFeaturePolicy( + mojom::FeaturePolicyFeature::kMaxDownscalingImage) || + frame.IsFeatureEnabled(mojom::FeaturePolicyFeature::kMaxDownscalingImage)) + return false; + // Invert the image if the image's size is more than 2 times bigger than the + // size it is being laid-out by. + LayoutUnit layout_width = layout_image->ContentBoxRect().Width(); + LayoutUnit layout_height = layout_image->ContentBoxRect().Height(); + auto image_width = element->naturalWidth(); + auto image_height = element->naturalHeight(); + if (layout_width > 0 && layout_height > 0 && image_width > 0 && + image_height > 0) { + double device_pixel_ratio = frame.DevicePixelRatio(); + if (LayoutUnit(image_width / kmax_downscaling_ratio * device_pixel_ratio) > + layout_width || + LayoutUnit(image_height / kmax_downscaling_ratio * device_pixel_ratio) > + layout_height) + return true; + } + return false; +} + +} // namespace + using namespace HTMLNames; LayoutImage::LayoutImage(Element* element) : LayoutReplaced(element, LayoutSize()), did_increment_visually_non_empty_pixel_count_(false), is_generated_content_(false), - image_device_pixel_ratio_(1.0f) {} + image_device_pixel_ratio_(1.0f), + is_legacy_format_or_compressed_image_(false), + is_downscaled_image_(false) {} LayoutImage* LayoutImage::CreateAnonymous(PseudoElement& pseudo) { LayoutImage* image = new LayoutImage(nullptr); @@ -197,6 +255,15 @@ void LayoutImage::ImageNotifyFinished(ImageResourceContent* new_image) { InvalidateBackgroundObscurationStatus(); + // Check for optimized image policies. + if (View() && View()->GetFrameView()) { + bool old_flag = ShouldInvertColor(); + is_legacy_format_or_compressed_image_ = CheckForOptimizedImagePolicy( + View()->GetFrameView()->GetFrame(), this, new_image); + if (old_flag != ShouldInvertColor()) + UpdateShouldInvertColor(); + } + if (new_image == image_resource_->CachedImage()) { // tell any potential compositing layers // that the image is done and they can reference it directly. @@ -297,6 +364,20 @@ bool LayoutImage::NodeAtPoint(HitTestResult& result, void LayoutImage::ComputeIntrinsicSizingInfo( IntrinsicSizingInfo& intrinsic_sizing_info) const { + if (SVGImage* svg_image = EmbeddedSVGImage()) { + svg_image->GetIntrinsicSizingInfo(intrinsic_sizing_info); + + // Handle zoom & vertical writing modes here, as the embedded SVG document + // doesn't know about them. + intrinsic_sizing_info.size.Scale(Style()->EffectiveZoom()); + if (Style()->GetObjectFit() != EObjectFit::kScaleDown) + intrinsic_sizing_info.size.Scale(ImageDevicePixelRatio()); + + if (!IsHorizontalWritingMode()) + intrinsic_sizing_info.Transpose(); + return; + } + LayoutReplaced::ComputeIntrinsicSizingInfo(intrinsic_sizing_info); // Our intrinsicSize is empty if we're laying out generated images with @@ -326,19 +407,11 @@ void LayoutImage::ComputeIntrinsicSizingInfo( bool LayoutImage::NeedsPreferredWidthsRecalculation() const { if (LayoutReplaced::NeedsPreferredWidthsRecalculation()) return true; - return EmbeddedReplacedContent(); + SVGImage* svg_image = EmbeddedSVGImage(); + return svg_image && svg_image->HasIntrinsicSizingInfo(); } -bool LayoutImage::GetNestedIntrinsicSizingInfo( - IntrinsicSizingInfo& intrinsic_sizing_info) const { - if (LayoutReplaced* content_layout_object = EmbeddedReplacedContent()) { - content_layout_object->ComputeIntrinsicSizingInfo(intrinsic_sizing_info); - return true; - } - return false; -} - -LayoutReplaced* LayoutImage::EmbeddedReplacedContent() const { +SVGImage* LayoutImage::EmbeddedSVGImage() const { if (!image_resource_) return nullptr; ImageResourceContent* cached_image = image_resource_->CachedImage(); @@ -346,10 +419,37 @@ LayoutReplaced* LayoutImage::EmbeddedReplacedContent() const { // https://crbug.com/761026 if (!cached_image || cached_image->IsCacheValidator()) return nullptr; - Image* image = cached_image->GetImage(); - if (!image->IsSVGImage()) - return nullptr; - return ToSVGImage(image)->EmbeddedReplacedContent(); + return ToSVGImageOrNull(cached_image->GetImage()); +} + +bool LayoutImage::ShouldInvertColor() const { + return is_downscaled_image_ || is_legacy_format_or_compressed_image_; +} + +void LayoutImage::UpdateShouldInvertColor() { + SetNeedsPaintPropertyUpdate(); + // If composited image, update compositing layer. + if (Layer()) + Layer()->SetNeedsCompositingInputsUpdate(); +} + +void LayoutImage::UpdateShouldInvertColorForTest(bool value) { + is_downscaled_image_ = value; + is_legacy_format_or_compressed_image_ = value; + UpdateShouldInvertColor(); +} + +void LayoutImage::UpdateAfterLayout() { + LayoutBox::UpdateAfterLayout(); + Node* node = GetNode(); + // Check for optimized image policies. + if (IsHTMLImageElement(node) && View() && View()->GetFrameView()) { + bool old_flag = ShouldInvertColor(); + is_downscaled_image_ = CheckForMaxDownscalingImagePolicy( + View()->GetFrameView()->GetFrame(), ToHTMLImageElement(node), this); + if (old_flag != ShouldInvertColor()) + UpdateShouldInvertColor(); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image.h b/chromium/third_party/blink/renderer/core/layout/layout_image.h index e59f84ff12c..021c5035e57 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_image.h @@ -35,6 +35,7 @@ namespace blink { class HTMLAreaElement; class HTMLMapElement; +class SVGImage; // LayoutImage is used to display any image type. // @@ -87,11 +88,19 @@ class CORE_EXPORT LayoutImage : public LayoutReplaced { const char* GetName() const override { return "LayoutImage"; } + // When an image element violates feature policy optimized image policies, it + // should be rendered with inverted color. + // https://github.com/WICG/feature-policy/blob/gh-pages/policies/optimized-images.md + bool ShouldInvertColor() const; + void UpdateShouldInvertColor(); + void UpdateShouldInvertColorForTest(bool); + + void UpdateAfterLayout() override; + protected: bool NeedsPreferredWidthsRecalculation() const final; - LayoutReplaced* EmbeddedReplacedContent() const; - void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const final; - bool GetNestedIntrinsicSizingInfo(IntrinsicSizingInfo&) const; + SVGImage* EmbeddedSVGImage() const; + void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; void ImageChanged(WrappedImagePtr, CanDeferInvalidation, @@ -147,6 +156,12 @@ class CORE_EXPORT LayoutImage : public LayoutReplaced { // This field stores whether this image is generated with 'content'. bool is_generated_content_; float image_device_pixel_ratio_; + + // These flags indicate if the image violates one or more optimized image + // policies. When any policy is violated, the image should be rendered with + // inverted color. + bool is_legacy_format_or_compressed_image_; + bool is_downscaled_image_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutImage, IsLayoutImage()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline.cc b/chromium/third_party/blink/renderer/core/layout/layout_inline.cc index b79ce1d326d..278a1099131 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline.cc @@ -36,14 +36,16 @@ #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/box_painter.h" #include "third_party/blink/renderer/core/paint/inline_painter.h" +#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" #include "third_party/blink/renderer/platform/geometry/region.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { @@ -242,6 +244,9 @@ void LayoutInline::StyleDidChange(StyleDifference diff, } PropagateStyleToAnonymousChildren(); + + // Only filtered inlines can contain fixed position elements. + SetCanContainFixedPositionObjects(new_style.HasFilter()); } void LayoutInline::UpdateAlwaysCreateLineBoxes(bool full_layout) { @@ -902,6 +907,26 @@ bool LayoutInline::NodeAtPoint(HitTestResult& result, const HitTestLocation& location_in_container, const LayoutPoint& accumulated_offset, HitTestAction hit_test_action) { + if (EnclosingNGBlockFlow()) { + // In LayoutNG, we reach here only when called from + // PaintLayer::HitTestContents() without going through any ancestor, in + // which case the element must have self painting layer. + DCHECK(HasSelfPaintingLayer()); + for (const NGPaintFragment* fragment : + NGPaintFragment::InlineFragmentsFor(this)) { + // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is accumulated + // up to its parent fragment. Compute this offset. + LayoutPoint adjusted_location = + accumulated_offset + + fragment->Parent()->InlineOffsetToContainerBox().ToLayoutPoint(); + if (NGBoxFragmentPainter(*fragment).NodeAtPoint( + result, location_in_container, adjusted_location, + accumulated_offset, hit_test_action)) + return true; + } + return false; + } + return line_boxes_.HitTest(LineLayoutBoxModel(this), result, location_in_container, accumulated_offset, hit_test_action); @@ -1318,8 +1343,9 @@ bool LayoutInline::MapToVisualRectInAncestorSpaceInternal( ancestor, transform_state, visual_rect_flags); } -LayoutSize LayoutInline::OffsetFromContainer( - const LayoutObject* container) const { +LayoutSize LayoutInline::OffsetFromContainerInternal( + const LayoutObject* container, + bool ignore_scroll_offset) const { DCHECK_EQ(container, Container()); LayoutSize offset; @@ -1327,7 +1353,7 @@ LayoutSize LayoutInline::OffsetFromContainer( offset += OffsetForInFlowPosition(); if (container->HasOverflowClip()) - offset -= ToLayoutBox(container)->ScrolledContentOffset(); + offset += OffsetFromScrollableContainer(container, ignore_scroll_offset); return offset; } @@ -1467,8 +1493,8 @@ LayoutSize LayoutInline::OffsetForInFlowPositionedInline( const LayoutBox& child) const { // FIXME: This function isn't right with mixed writing modes. - DCHECK(IsInFlowPositioned()); - if (!IsInFlowPositioned()) + DCHECK(IsInFlowPositioned() || StyleRef().HasFilter()); + if (!IsInFlowPositioned() && !StyleRef().HasFilter()) return LayoutSize(); // When we have an enclosing relpositioned inline, we need to add in the @@ -1618,9 +1644,6 @@ void LayoutInline::AddAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) { void LayoutInline::InvalidateDisplayItemClients( PaintInvalidationReason invalidation_reason) const { - // TODO(yoichio): Cover other PaintInvalidateionReasons. - DCHECK(invalidation_reason != PaintInvalidationReason::kSelection || - !EnclosingNGBlockFlow()); ObjectPaintInvalidator paint_invalidator(*this); if (RuntimeEnabledFeatures::LayoutNGEnabled()) { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline.h b/chromium/third_party/blink/renderer/core/layout/layout_inline.h index 269b95e832f..00c7f33a17f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline.h @@ -146,8 +146,6 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject { const LayoutPoint& accumulated_offset) const final; FloatRect LocalBoundingBoxRectForAccessibility() const final; - LayoutSize OffsetFromContainer(const LayoutObject*) const final; - LayoutRect LinesBoundingBox() const; LayoutRect VisualOverflowRect() const final; @@ -230,6 +228,9 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject { void AbsoluteQuadsForSelf(Vector<FloatQuad>& quads, MapCoordinatesFlags mode = 0) const override; + LayoutSize OffsetFromContainerInternal(const LayoutObject*, + bool ignore_scroll_offset) const final; + private: LayoutObjectChildList* VirtualChildren() final { return Children(); } const LayoutObjectChildList* VirtualChildren() const final { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc index 6fa072e7ae6..e71516b8509 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc @@ -119,4 +119,126 @@ TEST_F(LayoutInlineTest, RegionHitTest) { EXPECT_TRUE(hit_outcome); } +// crbug.com/844746 +TEST_P(ParameterizedLayoutInlineTest, RelativePositionedHitTest) { + LoadAhem(); + SetBodyInnerHTML( + "<div style='font: 10px/10px Ahem'>" + " <span style='position: relative'>XXX</span>" + "</div>"); + + HitTestRequest hit_request(HitTestRequest::kReadOnly | + HitTestRequest::kActive); + const LayoutPoint container_offset(8, 8); + const LayoutPoint hit_location(18, 15); + + Element* div = GetDocument().QuerySelector("div"); + Element* span = GetDocument().QuerySelector("span"); + Node* text = span->firstChild(); + + // Shouldn't hit anything in SPAN as it's in another paint layer + { + LayoutObject* layout_div = div->GetLayoutObject(); + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = layout_div->HitTestAllPhases(hit_result, hit_location, + container_offset); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(div, hit_result.InnerNode()); + } + + // SPAN and its descendants can be hit only with a hit test that starts from + // the SPAN itself. + { + LayoutObject* layout_span = span->GetLayoutObject(); + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location, + container_offset); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(text, hit_result.InnerNode()); + } + + // Hit test from LayoutView to verify that everything works together. + { + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = GetLayoutView().HitTest(hit_result); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(text, hit_result.InnerNode()); + } +} + +TEST_P(ParameterizedLayoutInlineTest, MultilineRelativePositionedHitTest) { + LoadAhem(); + SetBodyInnerHTML( + "<div style='font: 10px/10px Ahem; width: 30px'>" + " <span id=span style='position: relative'>" + " XXX" + " <span id=line2 style='background-color: red'>YYY</span>" + " <img style='width: 10px; height: 10px; vertical-align: bottom'>" + " </span>" + "</div>"); + + LayoutObject* layout_span = GetLayoutObjectByElementId("span"); + HitTestRequest hit_request(HitTestRequest::kReadOnly | + HitTestRequest::kActive | + HitTestRequest::kIgnorePointerEventsNone); + const LayoutPoint container_offset(8, 8); + + // Hit test first line + { + LayoutPoint hit_location(13, 13); + Node* target = GetElementById("span")->firstChild(); + + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location, + container_offset); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(target, hit_result.InnerNode()); + + // Initiate a hit test from LayoutView to verify the "natural" process. + HitTestResult layout_view_hit_result(hit_request, hit_location); + bool layout_view_hit_outcome = + GetLayoutView().HitTest(layout_view_hit_result); + EXPECT_TRUE(layout_view_hit_outcome); + EXPECT_EQ(target, layout_view_hit_result.InnerNode()); + } + + // Hit test second line + { + LayoutPoint hit_location(13, 23); + Node* target = GetElementById("line2")->firstChild(); + + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location, + container_offset); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(target, hit_result.InnerNode()); + + // Initiate a hit test from LayoutView to verify the "natural" process. + HitTestResult layout_view_hit_result(hit_request, hit_location); + bool layout_view_hit_outcome = + GetLayoutView().HitTest(layout_view_hit_result); + EXPECT_TRUE(layout_view_hit_outcome); + EXPECT_EQ(target, layout_view_hit_result.InnerNode()); + } + + // Hit test image in third line + { + LayoutPoint hit_location(13, 33); + Node* target = GetDocument().QuerySelector("img"); + + HitTestResult hit_result(hit_request, hit_location); + bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location, + container_offset); + EXPECT_TRUE(hit_outcome); + EXPECT_EQ(target, hit_result.InnerNode()); + + // Initiate a hit test from LayoutView to verify the "natural" process. + HitTestResult layout_view_hit_result(hit_request, hit_location); + bool layout_view_hit_outcome = + GetLayoutView().HitTest(layout_view_hit_result); + EXPECT_TRUE(layout_view_hit_outcome); + EXPECT_EQ(target, layout_view_hit_result.InnerNode()); + } +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc b/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc index c0b7c3b5b82..9be563b0552 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc @@ -118,8 +118,10 @@ bool LayoutListItem::IsEmpty() const { return LastChild() == marker_; } -static LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, - LayoutObject* marker) { +namespace { + +LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, + LayoutObject* marker) { LayoutObject* first_child = curr->FirstChild(); if (!first_child) return nullptr; @@ -159,31 +161,42 @@ static LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, return nullptr; } -static LayoutObject* FirstNonMarkerChild(LayoutObject* parent) { +LayoutObject* FirstNonMarkerChild(LayoutObject* parent) { LayoutObject* result = parent->SlowFirstChild(); while (result && result->IsListMarker()) result = result->NextSibling(); return result; } +void ForceLogicalHeight(LayoutObject& layout_object, const Length& height) { + DCHECK(layout_object.IsAnonymous()); + if (layout_object.StyleRef().LogicalHeight() == height) + return; + + scoped_refptr<ComputedStyle> new_style = + ComputedStyle::Clone(layout_object.StyleRef()); + new_style->SetLogicalHeight(height); + layout_object.SetStyleInternal(std::move(new_style)); +} + +} // namespace + // 1. Place marker as a child of <li>. Make sure don't share parent with empty -// inline elements which don't gernerate inlineBox. +// inline elements which don't generate InlineBox. // 2. Manage the logicalHeight of marker_container(marker's anonymous parent): -// If marker is the only child of marker_container, set logicalHeight of -// marker_container to 0px; else restore it to logicalHeight of <li>. +// If marker is the only child of marker_container, set LogicalHeight of +// marker_container to 0px; else restore it to LogicalHeight of <li>. bool LayoutListItem::PrepareForBlockDirectionAlign( const LayoutObject* line_box_parent) { LayoutObject* marker_parent = marker_->Parent(); // Deal with the situation of layout tree changed. if (marker_parent && marker_parent->IsAnonymous()) { // When list-position-style change from outside to inside, we need to - // restore logicalHeight. So add isinside(). + // restore LogicalHeight. So add IsInside(). if (marker_->IsInside() || marker_->NextSibling()) { - // Restore old marker_container logicalHeight. - if (marker_parent->MutableStyleRef().LogicalHeight().IsZero()) { - marker_parent->MutableStyleRef().SetLogicalHeight( - Style()->LogicalHeight()); - } + // Restore old marker_container LogicalHeight. + if (marker_parent->StyleRef().LogicalHeight().IsZero()) + ForceLogicalHeight(*marker_parent, StyleRef().LogicalHeight()); // If marker_parent isn't the ancestor of line_box_parent, marker might // generate a new empty line. We need to remove marker here.E.g: @@ -192,9 +205,8 @@ bool LayoutListItem::PrepareForBlockDirectionAlign( marker_->Remove(); marker_parent = nullptr; } - } else { - if (line_box_parent) - marker_parent->MutableStyleRef().SetLogicalHeight(Length(0, kFixed)); + } else if (line_box_parent) { + ForceLogicalHeight(*marker_parent, Length(0, kFixed)); } } @@ -202,10 +214,10 @@ bool LayoutListItem::PrepareForBlockDirectionAlign( if (!marker_parent) { LayoutObject* before_child = FirstNonMarkerChild(this); if (!marker_->IsInside() && before_child && before_child->IsLayoutBlock()) { - // Create marker_container and set its logicalHeight to 0px. + // Create marker_container and set its LogicalHeight to 0px. LayoutBlock* marker_container = CreateAnonymousBlock(); if (line_box_parent) - marker_container->MutableStyleRef().SetLogicalHeight(Length(0, kFixed)); + ForceLogicalHeight(*marker_container, Length(0, kFixed)); marker_container->AddChild(marker_, FirstNonMarkerChild(marker_container)); AddChild(marker_container, before_child); @@ -266,10 +278,10 @@ bool LayoutListItem::UpdateMarkerLocation() { if (marker_parent != line_box_parent) { marker_->Remove(); line_box_parent->AddChild(marker_, FirstNonMarkerChild(line_box_parent)); - // TODO(rhogan): lineBoxParent and markerParent may be deleted by addChild, - // so they are not safe to reference here. - // Once we have a safe way of referencing them delete markerParent if it is - // an empty anonymous block. + // TODO(rhogan): line_box_parent and marker_parent may be deleted by + // AddChild, so they are not safe to reference here. Once we have a safe way + // of referencing them delete marker_parent if it is an empty anonymous + // block. marker_->UpdateMarginsAndContent(); return true; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc index a555d54d258..37451f7e409 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc @@ -114,23 +114,6 @@ bool LayoutListMarker::IsImage() const { return image_ && !image_->ErrorOccurred(); } -LayoutRect LayoutListMarker::LocalSelectionRect() const { - InlineBox* box = InlineBoxWrapper(); - if (!box) - return LayoutRect(LayoutPoint(), Size()); - RootInlineBox& root = InlineBoxWrapper()->Root(); - const ComputedStyle* block_style = root.Block().Style(); - LayoutUnit new_logical_top = - block_style->IsFlippedBlocksWritingMode() - ? InlineBoxWrapper()->LogicalBottom() - root.SelectionBottom() - : root.SelectionTop() - InlineBoxWrapper()->LogicalTop(); - return block_style->IsHorizontalWritingMode() - ? LayoutRect(LayoutUnit(), new_logical_top, Size().Width(), - root.SelectionHeight()) - : LayoutRect(new_logical_top, LayoutUnit(), root.SelectionHeight(), - Size().Height()); -} - void LayoutListMarker::Paint(const PaintInfo& paint_info, const LayoutPoint& paint_offset) const { ListMarkerPainter(*this).Paint(paint_info, paint_offset); @@ -194,16 +177,14 @@ void LayoutListMarker::ImageChanged(WrappedImagePtr o, } void LayoutListMarker::UpdateMarginsAndContent() { - UpdateContent(); - UpdateMargins(); + if (PreferredLogicalWidthsDirty()) + ComputePreferredLogicalWidths(); + else + UpdateMargins(); } void LayoutListMarker::UpdateContent() { - // FIXME: This if-statement is just a performance optimization, but it's messy - // to use the preferredLogicalWidths dirty bit for this. - // It's unclear if this is a premature optimization. - if (!PreferredLogicalWidthsDirty()) - return; + DCHECK(PreferredLogicalWidthsDirty()); text_ = ""; @@ -299,8 +280,16 @@ void LayoutListMarker::UpdateMargins() { std::tie(margin_start, margin_end) = InlineMarginsForOutside(style, IsImage(), MinPreferredLogicalWidth()); } - MutableStyleRef().SetMarginStart(Length(margin_start, kFixed)); - MutableStyleRef().SetMarginEnd(Length(margin_end, kFixed)); + + Length start_length(margin_start, kFixed); + Length end_length(margin_end, kFixed); + + if (start_length != style.MarginStart() || end_length != style.MarginEnd()) { + scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(style); + new_style->SetMarginStart(start_length); + new_style->SetMarginEnd(end_length); + SetStyleInternal(std::move(new_style)); + } } std::pair<LayoutUnit, LayoutUnit> LayoutListMarker::InlineMarginsForInside( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h index b25b7645a0d..0d53ea51e60 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h @@ -67,7 +67,6 @@ class LayoutListMarker final : public LayoutBox { LayoutUnit marker_inline_size); LayoutRect GetRelativeMarkerRect() const; - LayoutRect LocalSelectionRect() const final; bool IsImage() const override; const StyleImage* GetImage() const { return image_.Get(); } const LayoutListItem* ListItem() const { return list_item_; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_media.cc b/chromium/third_party/blink/renderer/core/layout/layout_media.cc index 01a437e0f9a..70c0aee3661 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_media.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_media.cc @@ -92,11 +92,8 @@ void LayoutMedia::UpdateLayout() { LayoutBox* layout_box = ToLayoutBox(child); layout_box->SetLocation(new_rect.Location()); - // TODO(foolip): Remove the mutableStyleRef() and depend on CSS - // width/height: inherit to match the media element size. - layout_box->MutableStyleRef().SetHeight(Length(new_rect.Height(), kFixed)); - layout_box->MutableStyleRef().SetWidth(Length(width, kFixed)); - + layout_box->SetOverrideLogicalWidth(width); + layout_box->SetOverrideLogicalHeight(new_rect.Height()); layout_box->ForceLayout(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_media.h b/chromium/third_party/blink/renderer/core/layout/layout_media.h index 648e3fed223..f2a37a50b43 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_media.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_media.h @@ -26,8 +26,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MEDIA_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MEDIA_H_ +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/layout_image.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc index ffa3a05e368..22186050829 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc @@ -32,6 +32,7 @@ #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/html/forms/html_option_element.h" #include "third_party/blink/renderer/core/html/forms/html_select_element.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/platform/text/platform_locale.h" @@ -59,6 +60,25 @@ bool LayoutMenuList::IsChildAllowed(LayoutObject* object, return object->IsAnonymous() && !object->IsLayoutFullScreen(); } +scoped_refptr<ComputedStyle> LayoutMenuList::CreateInnerStyle() { + scoped_refptr<ComputedStyle> inner_style = + ComputedStyle::CreateAnonymousStyleWithDisplay(StyleRef(), + EDisplay::kBlock); + + AdjustInnerStyle(*inner_style); + return inner_style; +} + +void LayoutMenuList::UpdateInnerStyle() { + DCHECK(inner_block_); + scoped_refptr<ComputedStyle> inner_style = + ComputedStyle::Clone(inner_block_->StyleRef()); + AdjustInnerStyle(*inner_style); + inner_block_->SetStyleInternal(std::move(inner_style)); + // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). + SetNeedsPaintPropertyUpdate(); +} + void LayoutMenuList::CreateInnerBlock() { if (inner_block_) { DCHECK_EQ(FirstChild(), inner_block_); @@ -68,7 +88,8 @@ void LayoutMenuList::CreateInnerBlock() { // Create an anonymous block. DCHECK(!FirstChild()); - inner_block_ = CreateAnonymousBlock(); + inner_block_ = LayoutBlockFlow::CreateAnonymous(&GetDocument()); + inner_block_->SetStyle(CreateInnerStyle()); button_text_ = LayoutText::CreateEmptyAnonymous(GetDocument()); // We need to set the text explicitly though it was specified in the @@ -76,17 +97,25 @@ void LayoutMenuList::CreateInnerBlock() { // specified in the constructor in a case of re-transforming. button_text_->SetStyle(MutableStyle()); inner_block_->AddChild(button_text_); - - AdjustInnerStyle(); LayoutFlexibleBox::AddChild(inner_block_); + + // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). + SetNeedsPaintPropertyUpdate(); +} + +bool LayoutMenuList::HasOptionStyleChanged( + const ComputedStyle& inner_style) const { + return option_style_ && + ((option_style_->Direction() != inner_style.Direction() || + option_style_->GetUnicodeBidi() != inner_style.GetUnicodeBidi())); } -void LayoutMenuList::AdjustInnerStyle() { - ComputedStyle& inner_style = inner_block_->MutableStyleRef(); +void LayoutMenuList::AdjustInnerStyle(ComputedStyle& inner_style) const { inner_style.SetFlexGrow(1); inner_style.SetFlexShrink(1); // min-width: 0; is needed for correct shrinking. inner_style.SetMinWidth(Length(0, kFixed)); + // Use margin:auto instead of align-items:center to get safe centering, i.e. // when the content overflows, treat it the same as align-items: flex-start. // But we only do that for the cases where html.css would otherwise use @@ -113,21 +142,16 @@ void LayoutMenuList::AdjustInnerStyle() { LayoutTheme::GetTheme().PopupInternalPaddingTop(StyleRef()), kFixed)); inner_style.SetPaddingBottom(Length( LayoutTheme::GetTheme().PopupInternalPaddingBottom(StyleRef()), kFixed)); + inner_style.SetTextAlign(StyleRef().IsLeftToRightDirection() + ? ETextAlign::kLeft + : ETextAlign::kRight); - if (option_style_) { - if ((option_style_->Direction() != inner_style.Direction() || - option_style_->GetUnicodeBidi() != inner_style.GetUnicodeBidi())) - inner_block_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - LayoutInvalidationReason::kStyleChange); - inner_style.SetTextAlign(Style()->IsLeftToRightDirection() - ? ETextAlign::kLeft - : ETextAlign::kRight); + if (HasOptionStyleChanged(inner_style)) { + inner_block_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + LayoutInvalidationReason::kStyleChange); inner_style.SetDirection(option_style_->Direction()); inner_style.SetUnicodeBidi(option_style_->GetUnicodeBidi()); } - - // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). - SetNeedsPaintPropertyUpdate(); } HTMLSelectElement* LayoutMenuList::SelectElement() const { @@ -163,7 +187,7 @@ void LayoutMenuList::StyleDidChange(StyleDifference diff, CreateInnerBlock(); button_text_->SetStyle(MutableStyle()); - AdjustInnerStyle(); + UpdateInnerStyle(); UpdateInnerBlockHeight(); } @@ -181,7 +205,7 @@ void LayoutMenuList::UpdateOptionsWidth() const { String text = option->TextIndentedToRespectGroupLabel(); const ComputedStyle* item_style = option->GetComputedStyle() ? option->GetComputedStyle() : Style(); - ApplyTextTransform(item_style, text, ' '); + item_style->ApplyTextTransform(&text); // We apply SELECT's style, not OPTION's style because m_optionsWidth is // used to determine intrinsic width of the menulist box. TextRun text_run = ConstructTextRun(Style()->GetFont(), text, *Style()); @@ -228,6 +252,10 @@ void LayoutMenuList::UpdateFromElement() { SetText(text.StripWhiteSpace()); DidUpdateActiveOption(option); + + DCHECK(inner_block_); + if (HasOptionStyleChanged(inner_block_->StyleRef())) + UpdateInnerStyle(); } void LayoutMenuList::SetText(const String& s) { @@ -245,7 +273,8 @@ void LayoutMenuList::SetText(const String& s) { is_empty_ = false; button_text_->SetText(s.Impl(), true); } - AdjustInnerStyle(); + // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). + SetNeedsPaintPropertyUpdate(); } String LayoutMenuList::GetText() const { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h index ed2c9ae2ca9..af1433eaaa3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h @@ -94,7 +94,10 @@ class CORE_EXPORT LayoutMenuList final : public LayoutFlexibleBox { } void CreateInnerBlock(); - void AdjustInnerStyle(); + scoped_refptr<ComputedStyle> CreateInnerStyle(); + void UpdateInnerStyle(); + void AdjustInnerStyle(ComputedStyle&) const; + bool HasOptionStyleChanged(const ComputedStyle& inner_style) const; void SetText(const String&); void UpdateInnerBlockHeight(); void UpdateOptionsWidth() const; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc index dff71416047..4f96b5b4d2d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc @@ -106,7 +106,7 @@ static inline bool CanContainSpannerInParentFragmentationContext( return false; const LayoutBlockFlow& block_flow = ToLayoutBlockFlow(object); return !block_flow.CreatesNewFormattingContext() && - !block_flow.HasTransformRelatedProperty() && + !block_flow.StyleRef().CanContainFixedPositionObjects(false) && block_flow.GetPaginationBreakability() != LayoutBox::kForbidBreaks && !IsMultiColumnContainer(block_flow); } @@ -1169,8 +1169,8 @@ static inline bool NeedsToReinsertIntoFlowThread( // re-evaluate the need for column sets. There may be out-of-flow descendants // further down that become part of the flow thread, or cease to be part of // the flow thread, because of this change. - if (old_style.HasTransformRelatedProperty() != - new_style.HasTransformRelatedProperty()) + if (old_style.CanContainFixedPositionObjects(false) != + new_style.CanContainFixedPositionObjects(false)) return true; return (old_style.HasInFlowPosition() && new_style.GetPosition() == EPosition::kStatic) || @@ -1461,4 +1461,45 @@ void LayoutMultiColumnFlowThread::RestoreMultiColumnLayoutState( last_set_worked_on_ = state.ColumnSet(); } +unsigned LayoutMultiColumnFlowThread::CalculateActualColumnCountAllowance() + const { + // To avoid performance problems, limit the maximum number of columns. Try to + // identify legitimate reasons for creating many columns, and allow many + // columns in such cases. The amount of "content" will determine the + // allowance. + unsigned allowance = 0; + + // This isn't a particularly clever algorithm. For example, we don't account + // for parallel flows (absolute positioning, floats, visible overflow, table + // cells, flex items). We just generously add everything together. + for (const LayoutObject* descendant = this; descendant;) { + bool examine_children = false; + if (descendant->IsBox() && !descendant->IsInline() && + !ToLayoutBox(descendant)->IsWritingModeRoot()) { + // Give one point to any kind of block level content. + allowance++; + if (descendant->IsLayoutBlockFlow() && descendant->ChildrenInline()) { + // It's a block-level block container in the same writing mode, and it + // has inline children. Count the lines and add it to the allowance. + allowance += ToLayoutBlockFlow(descendant)->LineCount(); + } else { + // We could examine other types of layout modes (tables, flexbox, etc.) + // as well, but then again, that might be overkill. Just enter and see + // what we find. + examine_children = true; + } + } + + if (allowance >= ColumnCountClampMax()) + return ColumnCountClampMax(); + + descendant = examine_children + ? descendant->NextInPreOrder(this) + : descendant->NextInPreOrderAfterChildren(this); + } + + DCHECK_LE(allowance, ColumnCountClampMax()); + return std::max(allowance, ColumnCountClampMin()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h index 697e426394f..6b5e6548d48 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h @@ -190,6 +190,18 @@ class CORE_EXPORT LayoutMultiColumnFlowThread : public LayoutFlowThread, unsigned ColumnCount() const { return column_count_; } + // Calculate and return the actual column count allowance. This method should + // only be called when we ended up with an actual column count larger than + // ColumnCountClampMin() in some fragmentainer group. + unsigned CalculateActualColumnCountAllowance() const; + + // Any actual column count value lower than this will be allowed + // unconditionally. + static unsigned ColumnCountClampMin() { return 10; } + + // The maximum actual column count we're going to allow. + static unsigned ColumnCountClampMax() { return 2000; } + // Total height available to columns and spanners. This is the multicol // container's content box logical height, or 0 if auto. LayoutUnit ColumnHeightAvailable() const { return column_height_available_; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object.cc b/chromium/third_party/blink/renderer/core/layout/layout_object.cc index a35feb73599..21754a49bda 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object.cc @@ -51,6 +51,7 @@ #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_html_element.h" #include "third_party/blink/renderer/core/html/html_table_cell_element.h" @@ -78,6 +79,8 @@ #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h" +#include "third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" @@ -92,7 +95,6 @@ #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/style/content_data.h" #include "third_party/blink/renderer/core/style/cursor_data.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" @@ -100,6 +102,7 @@ #include "third_party/blink/renderer/platform/instance_counters.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -140,7 +143,8 @@ LayoutBlock* FindContainingBlock(LayoutObject* container, // LayoutObject::Container() method can actually be used to obtain the inline // directly. if (container && container->IsInline() && !container->IsAtomicInlineLevel()) { - DCHECK(container->Style()->HasInFlowPosition()); + DCHECK(container->Style()->HasInFlowPosition() || + container->Style()->HasFilter()); container = container->ContainingBlock(skip_info); } @@ -265,6 +269,8 @@ LayoutObject* LayoutObject::CreateObject(Element* element, return new LayoutNGTableCell(element); return new LayoutTableCell(element); case EDisplay::kTableCaption: + if (ShouldUseNewLayout(style)) + return new LayoutNGTableCaption(element); return new LayoutTableCaption(element); case EDisplay::kWebkitBox: case EDisplay::kWebkitInlineBox: @@ -273,8 +279,7 @@ LayoutObject* LayoutObject::CreateObject(Element* element, case EDisplay::kInlineFlex: if (RuntimeEnabledFeatures::LayoutNGFlexBoxEnabled() && ShouldUseNewLayout(style)) { - // TODO(dgrogan): Change this to new class LayoutNGFlex. - return new LayoutNGBlockFlow(element); + return new LayoutNGFlexibleBox(element); } return new LayoutFlexibleBox(element); case EDisplay::kGrid: @@ -1004,6 +1009,11 @@ inline void LayoutObject::InvalidateContainerPreferredLogicalWidths() { LayoutObject* LayoutObject::ContainerForAbsolutePosition( AncestorSkipInfo* skip_info) const { return FindAncestorByPredicate(this, skip_info, [](LayoutObject* candidate) { + if (!candidate->CanContainAbsolutePositionObjects() && + candidate->StyleRef().ContainsLayout()) { + UseCounter::Count(candidate->GetDocument(), + WebFeature::kCSSContainLayoutPositionedDescendants); + } return candidate->CanContainAbsolutePositionObjects(); }); } @@ -1012,6 +1022,11 @@ LayoutObject* LayoutObject::ContainerForFixedPosition( AncestorSkipInfo* skip_info) const { DCHECK(!IsText()); return FindAncestorByPredicate(this, skip_info, [](LayoutObject* candidate) { + if (!candidate->CanContainFixedPositionObjects() && + candidate->StyleRef().ContainsLayout()) { + UseCounter::Count(candidate->GetDocument(), + WebFeature::kCSSContainLayoutPositionedDescendants); + } return candidate->CanContainFixedPositionObjects(); }); } @@ -1211,7 +1226,7 @@ bool LayoutObject::GetUpperLeftCorner(ExpandScrollMargin expand, } if (runner->IsText() && !runner->IsBR()) { - const Optional<FloatPoint> maybe_point = + const base::Optional<FloatPoint> maybe_point = ToLayoutText(runner)->GetUpperLeftCorner(); if (maybe_point.has_value()) { point = runner->LocalToAbsolute(maybe_point.value(), kUseTransforms); @@ -1560,23 +1575,30 @@ LayoutRect LayoutObject::LocalVisualRectIgnoringVisibility() const { bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath( const LayoutBoxModelObject* ancestor, LayoutRect& rect, - VisualRectFlags visual_rect_flags) const { + VisualRectFlags visual_rect_flags, + bool& intersects) const { if (!(visual_rect_flags & kUseGeometryMapper) || !RuntimeEnabledFeatures::SlimmingPaintV175Enabled() || - (visual_rect_flags & kEdgeInclusive) || !FirstFragment().HasLocalBorderBoxProperties() || !ancestor || !ancestor->FirstFragment().HasLocalBorderBoxProperties()) { + intersects = true; return false; } - if (ancestor == this) + if (ancestor == this) { + intersects = true; return true; + } rect.MoveBy(FirstFragment().PaintOffset()); FloatClipRect clip_rect((FloatRect(rect))); - GeometryMapper::LocalToAncestorVisualRect( + intersects = GeometryMapper::LocalToAncestorVisualRect( FirstFragment().LocalBorderBoxProperties(), - ancestor->FirstFragment().ContentsProperties(), clip_rect); + ancestor->FirstFragment().ContentsProperties(), clip_rect, + kIgnorePlatformOverlayScrollbarSize, + (visual_rect_flags & kEdgeInclusive) ? kInclusiveIntersect + : kNonInclusiveIntersect); + rect = LayoutRect(clip_rect.Rect()); rect.MoveBy(-ancestor->FirstFragment().PaintOffset()); @@ -1587,17 +1609,18 @@ bool LayoutObject::MapToVisualRectInAncestorSpace( const LayoutBoxModelObject* ancestor, LayoutRect& rect, VisualRectFlags visual_rect_flags) const { - if (MapToVisualRectInAncestorSpaceInternalFastPath(ancestor, rect, - visual_rect_flags)) - return !rect.IsEmpty(); + bool intersects = true; + if (MapToVisualRectInAncestorSpaceInternalFastPath( + ancestor, rect, visual_rect_flags, intersects)) + return intersects; TransformState transform_state(TransformState::kApplyTransformDirection, FloatQuad(FloatRect(rect))); - bool retval = MapToVisualRectInAncestorSpaceInternal( - ancestor, transform_state, visual_rect_flags); + intersects = MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state, + visual_rect_flags); transform_state.Flatten(); rect = LayoutRect(transform_state.LastPlanarQuad().BoundingBox()); - return retval; + return intersects; } bool LayoutObject::MapToVisualRectInAncestorSpaceInternal( @@ -2115,14 +2138,25 @@ void LayoutObject::StyleWillChange(StyleDifference diff, // // Since a CSS property cannot be applied directly to a text node, a // handler will have already been added for its parent so ignore it. - TouchAction old_touch_action = - style_ ? style_->GetTouchAction() : TouchAction::kTouchActionAuto; + // + // Elements may inherit touch action from parent frame, so we need to report + // touchstart handler if the root layout object has non-auto effective touch + // action. + TouchAction old_touch_action = TouchAction::kTouchActionAuto; + bool is_document_element = GetNode() && IsDocumentElement(); + if (style_) { + old_touch_action = is_document_element ? style_->GetEffectiveTouchAction() + : style_->GetTouchAction(); + } + TouchAction new_touch_action = is_document_element + ? new_style.GetEffectiveTouchAction() + : new_style.GetTouchAction(); if (GetNode() && !GetNode()->IsTextNode() && (old_touch_action == TouchAction::kTouchActionAuto) != - (new_style.GetTouchAction() == TouchAction::kTouchActionAuto)) { + (new_touch_action == TouchAction::kTouchActionAuto)) { EventHandlerRegistry& registry = - GetDocument().GetPage()->GetEventHandlerRegistry(); - if (new_style.GetTouchAction() != TouchAction::kTouchActionAuto) { + GetDocument().GetFrame()->GetEventHandlerRegistry(); + if (new_touch_action != TouchAction::kTouchActionAuto) { registry.DidAddEventHandler(*GetNode(), EventHandlerRegistry::kTouchAction); } else { @@ -2307,18 +2341,6 @@ void LayoutObject::SetStyleWithWritingModeOfParent( SetStyleWithWritingModeOf(std::move(style), Parent()); } -void LayoutObject::AddChildWithWritingModeOfParent(LayoutObject* new_child, - LayoutObject* before_child) { - const WritingMode old_writing_mode = - new_child->MutableStyleRef().GetWritingMode(); - const WritingMode new_writing_mode = StyleRef().GetWritingMode(); - if (old_writing_mode != new_writing_mode && new_child->IsBoxModelObject()) { - new_child->MutableStyleRef().SetWritingMode(new_writing_mode); - new_child->SetHorizontalWritingMode(IsHorizontalWritingMode()); - } - AddChild(new_child, before_child); -} - void LayoutObject::UpdateFillImages(const FillLayer* old_layers, const FillLayer& new_layers) { // Optimize the common case @@ -2446,8 +2468,8 @@ void LayoutObject::MapLocalToAncestor(const LayoutBoxModelObject* ancestor, } } - LayoutSize container_offset = OffsetFromContainer(container); - + LayoutSize container_offset = + OffsetFromContainer(container, mode & kIgnoreScrollOffset); // TODO(smcgruer): This is inefficient. Instead we should avoid including // offsetForInFlowPosition in offsetFromContainer when ignoring sticky. if (mode & kIgnoreStickyOffset && IsStickyPositioned()) { @@ -2695,13 +2717,34 @@ TransformationMatrix LayoutObject::LocalToAncestorTransform( return transform_state.AccumulatedTransform(); } -LayoutSize LayoutObject::OffsetFromContainer(const LayoutObject* o) const { +LayoutSize LayoutObject::OffsetFromContainer(const LayoutObject* o, + bool ignore_scroll_offset) const { + return OffsetFromContainerInternal(o, ignore_scroll_offset); +} + +LayoutSize LayoutObject::OffsetFromContainerInternal( + const LayoutObject* o, + bool ignore_scroll_offset) const { DCHECK_EQ(o, Container()); return o->HasOverflowClip() - ? LayoutSize(-ToLayoutBox(o)->ScrolledContentOffset()) + ? OffsetFromScrollableContainer(o, ignore_scroll_offset) : LayoutSize(); } +LayoutSize LayoutObject::OffsetFromScrollableContainer( + const LayoutObject* container, + bool ignore_scroll_offset) const { + DCHECK(container->HasOverflowClip()); + const LayoutBox* box = ToLayoutBox(container); + if (!ignore_scroll_offset) + return -LayoutSize(box->ScrolledContentOffset()); + + // ScrollOrigin accounts for other writing modes whose content's origin is not + // at the top-left. + return LayoutSize(ToIntSize(box->GetScrollableArea()->ScrollOrigin()) - + box->OriginAdjustmentForScrollbars()); +} + LayoutSize LayoutObject::OffsetFromAncestorContainer( const LayoutObject* ancestor_container) const { if (ancestor_container == this) @@ -2792,7 +2835,7 @@ void LayoutObject::AddLayerHitTestRects( // tracking those rects outweighs the benefit of doing compositor thread hit // testing. // FIXME: This limit needs to be low due to the O(n^2) algorithm in - // WebLayer::setTouchEventHandlerRegion - crbug.com/300282. + // ScrollingCoordinator::SetTouchEventTargetRects() - crbug.com/300282. const size_t kMaxRectsPerLayer = 100; LayerHitTestRects::iterator iter = layer_rects.find(current_layer); @@ -2981,7 +3024,7 @@ void LayoutObject::WillBeDestroyed() { if (GetNode() && !GetNode()->IsTextNode() && style_ && style_->GetTouchAction() != TouchAction::kTouchActionAuto) { EventHandlerRegistry& registry = - GetDocument().GetPage()->GetEventHandlerRegistry(); + GetDocument().GetFrame()->GetEventHandlerRegistry(); if (registry.EventHandlerTargets(EventHandlerRegistry::kTouchAction) ->Contains(GetNode())) { registry.DidRemoveEventHandler(*GetNode(), @@ -3127,8 +3170,7 @@ void LayoutObject::WillBeRemovedFromTree() { if (Parent()->IsSVG()) Parent()->SetNeedsBoundariesUpdate(); - if (RuntimeEnabledFeatures::ScrollAnchoringEnabled() && - bitfields_.IsScrollAnchorObject()) { + if (bitfields_.IsScrollAnchorObject()) { // Clear the bit first so that anchor.clear() doesn't recurse into // findReferencingScrollAnchors. bitfields_.SetIsScrollAnchorObject(false); @@ -3392,8 +3434,6 @@ static scoped_refptr<ComputedStyle> FirstLineStyleForCachedUncachedType( if (type == kCached) { // A first-line style is in effect. Cache a first-line style for // ourselves. - layout_object_for_first_line_style->MutableStyleRef().SetHasPseudoStyle( - kPseudoIdFirstLineInherited); return layout_object_for_first_line_style->GetCachedPseudoStyle( kPseudoIdFirstLineInherited, parent_style); } @@ -3813,7 +3853,8 @@ void LayoutObject::ClearPaintInvalidationFlags() { #if DCHECK_IS_ON() DCHECK(!ShouldCheckForPaintInvalidation() || PaintInvalidationStateIsDirty()); #endif - if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) + if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled() || + !RuntimeEnabledFeatures::PartialRasterInvalidationEnabled()) fragment_.SetPartialInvalidationRect(LayoutRect()); ClearShouldDoFullPaintInvalidation(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object.h b/chromium/third_party/blink/renderer/core/layout/layout_object.h index d31d5a8515e..25a0dc1757e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_object.h @@ -27,6 +27,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_OBJECT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_OBJECT_H_ +#include "base/auto_reset.h" #include "base/macros.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -43,18 +44,17 @@ #include "third_party/blink/renderer/core/loader/resource/image_resource_observer.h" #include "third_party/blink/renderer/core/paint/compositing/compositing_state.h" #include "third_party/blink/renderer/core/paint/fragment_data.h" -#include "third_party/blink/renderer/core/paint/layer_hit_test_rects.h" #include "third_party/blink/renderer/core/paint/paint_phase.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/style_difference.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h" #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h" +#include "third_party/blink/renderer/platform/graphics/touch_action_rect.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" namespace blink { @@ -522,6 +522,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bool IsLayoutNGBlockFlow() const { return IsOfType(kLayoutObjectNGBlockFlow); } + bool IsLayoutNGFlexibleBox() const { + return IsOfType(kLayoutObjectNGFlexibleBox); + } bool IsLayoutNGMixin() const { return IsOfType(kLayoutObjectNGMixin); } bool IsLayoutNGListItem() const { return IsOfType(kLayoutObjectNGListItem); } bool IsLayoutNGListMarker() const { @@ -530,6 +533,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bool IsLayoutNGListMarkerImage() const { return IsOfType(kLayoutObjectNGListMarkerImage); } + bool IsLayoutNGText() const { return IsOfType(kLayoutObjectNGText); } bool IsLayoutTableCol() const { return IsOfType(kLayoutObjectLayoutTableCol); } @@ -1211,8 +1215,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, void SetStyleWithWritingModeOf(scoped_refptr<ComputedStyle>, LayoutObject* parent); void SetStyleWithWritingModeOfParent(scoped_refptr<ComputedStyle>); - void AddChildWithWritingModeOfParent(LayoutObject* new_child, - LayoutObject* before_child); void FirstLineStyleDidChange(const ComputedStyle& old_style, const ComputedStyle& new_style); @@ -1331,7 +1333,8 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // Return the offset from the container() layoutObject (excluding transforms // and multicol). - virtual LayoutSize OffsetFromContainer(const LayoutObject*) const; + LayoutSize OffsetFromContainer(const LayoutObject*, + bool ignore_scroll_offset = false) const; // Return the offset from an object up the container() chain. Asserts that // none of the intermediate objects have transforms. LayoutSize OffsetFromAncestorContainer(const LayoutObject*) const; @@ -1394,7 +1397,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // style_ can only be nullptr before the first style is set, thus most // callers will never see a nullptr style and should use StyleRef(). - // FIXME: It would be better if style() returned a const reference. const ComputedStyle& StyleRef() const { return MutableStyleRef(); } ComputedStyle& MutableStyleRef() const { DCHECK(style_); @@ -1467,11 +1469,11 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // and clip. This is even true if the main frame is remote. // // If visualRectFlags has the EdgeInclusive bit set, clipping operations will - // use/ LayoutRect::inclusiveIntersect, and the return value of - // inclusiveIntersect will be propagated to the return value of this method. + // use LayoutRect::InclusiveIntersect, and the return value of + // InclusiveIntersect will be propagated to the return value of this method. // Otherwise, clipping operations will use LayoutRect::intersect, and the // return value will be true only if the clipped rect has non-zero area. - // See the documentation for LayoutRect::inclusiveIntersect for more + // See the documentation for LayoutRect::InclusiveIntersect for more // information. bool MapToVisualRectInAncestorSpace( const LayoutBoxModelObject* ancestor, @@ -1959,7 +1961,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, LayoutRect SelectionVisualRect() const { return fragment_.SelectionVisualRect(); } - LayoutRect PartialInvalidationRect() const { + LayoutRect PartialInvalidationRect() const override { return fragment_.PartialInvalidationRect(); } @@ -1969,6 +1971,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, return bitfields_.ContainsInlineWithOutlineAndContinuation(); } + void SetOutlineMayBeAffectedByDescendants(bool b) { + bitfields_.SetOutlineMayBeAffectedByDescendants(b); + } + protected: enum LayoutObjectType { kLayoutObjectBr, @@ -1987,10 +1993,12 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, kLayoutObjectMedia, kLayoutObjectMenuList, kLayoutObjectNGBlockFlow, + kLayoutObjectNGFlexibleBox, kLayoutObjectNGMixin, kLayoutObjectNGListItem, kLayoutObjectNGListMarker, kLayoutObjectNGListMarkerImage, + kLayoutObjectNGText, kLayoutObjectProgress, kLayoutObjectQuote, kLayoutObjectLayoutButton, @@ -2058,11 +2066,15 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, virtual bool AnonymousHasStylePropagationOverride() { return false; } // A fast path for MapToVisualRectInAncestorSpace for when GeometryMapper - // can be used. + // can be used. |intersects| is set to whether the input rect intersected + // (see documentation of return value of MapToVisualRectInAncestorSpace). + // + // The return value of this method is whether the fast path could be used. bool MapToVisualRectInAncestorSpaceInternalFastPath( const LayoutBoxModelObject* ancestor, LayoutRect&, - VisualRectFlags) const; + VisualRectFlags, + bool& intersects) const; // This function is called before calling the destructor so that some clean-up // can happen regardless of whether they call a virtual function or not. As a @@ -2142,9 +2154,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bitfields_.SetContainsInlineWithOutlineAndContinuation(b); } - void SetOutlineMayBeAffectedByDescendants(bool b) { - bitfields_.SetOutlineMayBeAffectedByDescendants(b); - } void SetPreviousOutlineMayBeAffectedByDescendants(bool b) { bitfields_.SetPreviousOutlineMayBeAffectedByDescendants(b); } @@ -2154,6 +2163,12 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, virtual bool CanBeSelectionLeafInternal() const { return false; } + virtual LayoutSize OffsetFromContainerInternal( + const LayoutObject*, + bool ignore_scroll_offset) const; + LayoutSize OffsetFromScrollableContainer(const LayoutObject*, + bool ignore_scroll_offset) const; + private: // Used only by applyFirstLineChanges to get a first line style based off of a // given new style, without accessing the cache. @@ -2327,7 +2342,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, needs_paint_property_update_(true), subtree_needs_paint_property_update_(true), descendant_needs_paint_property_update_(true), - background_changed_since_last_paint_invalidation_(false), + background_changed_since_last_paint_invalidation_(true), outline_may_be_affected_by_descendants_(false), previous_outline_may_be_affected_by_descendants_(false), is_truncated_(false), @@ -2665,7 +2680,7 @@ class DeprecatedDisableModifyLayoutTreeStructureAsserts { static bool CanModifyLayoutTreeStateInAnyState(); private: - AutoReset<bool> disabler_; + base::AutoReset<bool> disabler_; DISALLOW_COPY_AND_ASSIGN(DeprecatedDisableModifyLayoutTreeStructureAsserts); }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc index cb9f7ffe5ac..9d85f86f006 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc @@ -28,12 +28,35 @@ #include "third_party/blink/renderer/core/dom/ax_object_cache.h" #include "third_party/blink/renderer/core/layout/layout_counter.h" +#include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h" #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h" namespace blink { +namespace { + +// Invalidate InineItems() in LayoutNGText. +// +// They need to be invalidated when moving across inline formatting context +// (i.e., to a different LayoutBlockFlow.) +void InvalidateInlineItems(LayoutObject* object) { + if (object->IsLayoutNGText()) { + ToLayoutNGText(object)->InvalidateInlineItems(); + } else if (object->IsLayoutInline()) { + // When moving without |notify_layout_object|, only top-level objects are + // moved. Ensure to invalidate all LayoutNGText in this inline formatting + // context. + for (LayoutObject* curr = object->SlowFirstChild(); curr; + curr = curr->NextSibling()) + InvalidateInlineItems(curr); + } +} + +} // namespace + void LayoutObjectChildList::DestroyLeftoverChildren() { while (FirstChild()) { // List markers are owned by their enclosing list and so don't get destroyed @@ -167,9 +190,16 @@ void LayoutObjectChildList::InsertChildNode(LayoutObject* owner, last_child_ = new_child; } - if (!owner->DocumentBeingDestroyed() && notify_layout_object) { - new_child->InsertedIntoTree(); - LayoutCounter::LayoutObjectSubtreeAttached(new_child); + if (!owner->DocumentBeingDestroyed()) { + if (notify_layout_object) { + new_child->InsertedIntoTree(); + LayoutCounter::LayoutObjectSubtreeAttached(new_child); + } else if (RuntimeEnabledFeatures::LayoutNGEnabled()) { + // |notify_layout_object| is an optimization to skip notifications when + // moving within the same tree. Inline items need to be invalidated even + // when moving. + InvalidateInlineItems(new_child); + } } // Propagate the need to notify ancestors down into any diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc index ccaefc6f812..5d240d4b73a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc @@ -7,11 +7,15 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" +#include "third_party/blink/renderer/core/frame/event_handler_registry.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/layout/layout_text_fragment.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/svg/svg_g_element.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" +#include "third_party/blink/renderer/core/testing/sim/sim_request.h" +#include "third_party/blink/renderer/core/testing/sim/sim_test.h" #include "third_party/blink/renderer/platform/json/json_values.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" @@ -487,7 +491,7 @@ TEST_F(LayoutObjectTest, VisualRect) { MOCK_CONST_METHOD0(VisualRectRespectsVisibility, bool()); private: - LayoutRect LocalVisualRectIgnoringVisibility() const { + LayoutRect LocalVisualRectIgnoringVisibility() const override { return LayoutRect(10, 10, 20, 20); } const char* GetName() const final { return "MockLayoutObject"; } @@ -724,4 +728,67 @@ TEST_F(LayoutObjectTest, DisplayContentsSVGGElementInHTML) { ASSERT_FALSE(text->GetLayoutObject()); } +class LayoutObjectSimTest : public SimTest { + public: + bool DocumentHasTouchActionRegion(const EventHandlerRegistry& registry) { + GetDocument().View()->UpdateAllLifecyclePhases(); + return registry.HasEventHandlers( + EventHandlerRegistry::EventHandlerClass::kTouchAction); + } +}; + +TEST_F(LayoutObjectSimTest, TouchActionUpdatesSubframeEventHandler) { + SimRequest main_resource("https://example.com/test.html", "text/html"); + SimRequest frame_resource("https://example.com/frame.html", "text/html"); + + LoadURL("https://example.com/test.html"); + main_resource.Complete( + "<!DOCTYPE html>" + "<div id='container'>" + "<iframe src=frame.html></iframe>" + "</div>"); + frame_resource.Complete( + "<!DOCTYPE html>" + "<html><body>" + "<div id='inner'></div>" + "</body></html>"); + + Element* iframe_element = GetDocument().QuerySelector("iframe"); + HTMLFrameOwnerElement* frame_owner_element = + ToHTMLFrameOwnerElement(iframe_element); + Document* iframe_doc = frame_owner_element->contentDocument(); + Element* inner = iframe_doc->getElementById("inner"); + Element* iframe_doc_element = iframe_doc->documentElement(); + Element* container = GetDocument().getElementById("container"); + + EventHandlerRegistry& registry = + iframe_doc->GetFrame()->GetEventHandlerRegistry(); + + // We should add event handler if touch action is set on subframe. + inner->setAttribute("style", "touch-action: none"); + EXPECT_TRUE(DocumentHasTouchActionRegion(registry)); + + // We should remove event handler if touch action is removed on subframe. + inner->setAttribute("style", "touch-action: auto"); + EXPECT_FALSE(DocumentHasTouchActionRegion(registry)); + + // We should add event handler if touch action is set on main frame. + container->setAttribute("style", "touch-action: none"); + EXPECT_TRUE(DocumentHasTouchActionRegion(registry)); + + // We should keep event handler if touch action is set on subframe document + // element. + iframe_doc_element->setAttribute("style", "touch-action: none"); + EXPECT_TRUE(DocumentHasTouchActionRegion(registry)); + + // We should keep the event handler if touch action is removed on subframe + // document element. + iframe_doc_element->setAttribute("style", "touch-action: auto"); + EXPECT_TRUE(DocumentHasTouchActionRegion(registry)); + + // We should remove the handler if touch action is removed on main frame. + container->setAttribute("style", "touch-action: auto"); + EXPECT_FALSE(DocumentHasTouchActionRegion(registry)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_progress.cc b/chromium/third_party/blink/renderer/core/layout/layout_progress.cc index 8d6e0a6b8aa..d818339bc51 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_progress.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_progress.cc @@ -36,7 +36,7 @@ LayoutProgress::LayoutProgress(HTMLProgressElement* element) animation_duration_(0), animating_(false), animation_timer_( - element->GetDocument().GetTaskRunner(TaskType::kInternalAnimation), + element->GetDocument().GetTaskRunner(TaskType::kInternalDefault), this, &LayoutProgress::AnimationTimerFired) {} diff --git a/chromium/third_party/blink/renderer/core/layout/layout_quote.cc b/chromium/third_party/blink/renderer/core/layout/layout_quote.cc index a84dea51d31..335af89a053 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_quote.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_quote.cc @@ -233,7 +233,7 @@ const QuotesData* QuotesDataForLanguage(const AtomicString& lang) { return nullptr; // This could be just a hash table, but doing that adds 200k to LayoutQuote.o - Language* languages_end = g_languages + WTF_ARRAY_LENGTH(g_languages); + Language* languages_end = g_languages + arraysize(g_languages); CString lowercase_lang = lang.DeprecatedLower().Utf8(); Language key = {lowercase_lang.data(), 0, 0, 0, 0, nullptr}; Language* match = std::lower_bound(g_languages, languages_end, key); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc b/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc index 8ffe6c326a2..0b8fb3761b8 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc @@ -152,31 +152,17 @@ static inline bool LayoutObjectHasAspectRatio( void LayoutReplaced::ComputeIntrinsicSizingInfoForReplacedContent( IntrinsicSizingInfo& intrinsic_sizing_info) const { - if (GetNestedIntrinsicSizingInfo(intrinsic_sizing_info)) { - // Handle zoom & vertical writing modes here, as the embedded document - // doesn't know about them. - intrinsic_sizing_info.size.Scale(Style()->EffectiveZoom()); - if (IsLayoutImage() && Style()->GetObjectFit() != EObjectFit::kScaleDown) - intrinsic_sizing_info.size.Scale( - ToLayoutImage(this)->ImageDevicePixelRatio()); - - // Update our intrinsic size to match what the content layoutObject has - // computed, so that when we constrain the size below, the correct intrinsic - // size will be obtained for comparison against min and max widths. - if (!intrinsic_sizing_info.aspect_ratio.IsEmpty() && - !intrinsic_sizing_info.size.IsEmpty()) - intrinsic_size_ = LayoutSize(intrinsic_sizing_info.size); - - if (!IsHorizontalWritingMode()) - intrinsic_sizing_info.Transpose(); - } else { - ComputeIntrinsicSizingInfo(intrinsic_sizing_info); - if (!intrinsic_sizing_info.aspect_ratio.IsEmpty() && - !intrinsic_sizing_info.size.IsEmpty()) - intrinsic_size_ = - LayoutSize(IsHorizontalWritingMode() - ? intrinsic_sizing_info.size - : intrinsic_sizing_info.size.TransposedSize()); + ComputeIntrinsicSizingInfo(intrinsic_sizing_info); + + // Update our intrinsic size to match what was computed, so that + // when we constrain the size, the correct intrinsic size will be + // obtained for comparison against min and max widths. + if (!intrinsic_sizing_info.aspect_ratio.IsEmpty() && + !intrinsic_sizing_info.size.IsEmpty()) { + intrinsic_size_ = + LayoutSize(IsHorizontalWritingMode() + ? intrinsic_sizing_info.size + : intrinsic_sizing_info.size.TransposedSize()); } } @@ -961,9 +947,12 @@ PositionWithAffinity LayoutReplaced::PositionForPoint( CaretMaxOffset()); // coordinates are below if (GetNode()) { - if (line_direction_position <= LogicalLeft() + (LogicalWidth() / 2)) - return CreatePositionWithAffinity(0); - return CreatePositionWithAffinity(1); + const bool is_at_left_side = + line_direction_position <= LogicalLeft() + (LogicalWidth() / 2); + const bool is_at_start = is_at_left_side == IsLtr(ResolvedDirection()); + // TODO(crbug.com/827923): Stop creating positions using int offsets on + // non-text nodes. + return CreatePositionWithAffinity(is_at_start ? 0 : 1); } return LayoutBox::PositionForPoint(point); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_replaced.h b/chromium/third_party/blink/renderer/core/layout/layout_replaced.h index d81e62ac4c4..4ae22643a55 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_replaced.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_replaced.h @@ -104,13 +104,6 @@ class CORE_EXPORT LayoutReplaced : public LayoutBox { void ComputeIntrinsicLogicalWidths(LayoutUnit& min_logical_width, LayoutUnit& max_logical_width) const final; - // Extract intrinsic sizing info from a potential nested layout - // context. Returns true if successful, and populates the IntrinsicSizingInfo - // structure if so. - virtual bool GetNestedIntrinsicSizingInfo(IntrinsicSizingInfo&) const { - return false; - } - // This function calculates the placement of the replaced contents. It takes // intrinsic size of the replaced contents, stretch to fit CSS content box // according to object-fit. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar.h b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar.h index bcb617306c9..8b070820d45 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar.h @@ -99,7 +99,7 @@ class LayoutScrollbar final : public Scrollbar { }; DEFINE_TYPE_CASTS(LayoutScrollbar, - ScrollbarThemeClient, + Scrollbar, scrollbar, scrollbar->IsCustomScrollbar(), scrollbar.IsCustomScrollbar()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.cc b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.cc index 39a27295a70..c358de3b4a6 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.cc @@ -39,10 +39,9 @@ LayoutScrollbarTheme* LayoutScrollbarTheme::GetLayoutScrollbarTheme() { return &theme; } -void LayoutScrollbarTheme::ButtonSizesAlongTrackAxis( - const ScrollbarThemeClient& scrollbar, - int& before_size, - int& after_size) { +void LayoutScrollbarTheme::ButtonSizesAlongTrackAxis(const Scrollbar& scrollbar, + int& before_size, + int& after_size) { IntRect first_button = BackButtonRect(scrollbar, kBackButtonStartPart); IntRect second_button = ForwardButtonRect(scrollbar, kForwardButtonStartPart); IntRect third_button = BackButtonRect(scrollbar, kBackButtonEndPart); @@ -56,7 +55,7 @@ void LayoutScrollbarTheme::ButtonSizesAlongTrackAxis( } } -bool LayoutScrollbarTheme::HasButtons(const ScrollbarThemeClient& scrollbar) { +bool LayoutScrollbarTheme::HasButtons(const Scrollbar& scrollbar) { int start_size; int end_size; ButtonSizesAlongTrackAxis(scrollbar, start_size, end_size); @@ -65,31 +64,27 @@ bool LayoutScrollbarTheme::HasButtons(const ScrollbarThemeClient& scrollbar) { : scrollbar.Height()); } -bool LayoutScrollbarTheme::HasThumb(const ScrollbarThemeClient& scrollbar) { +bool LayoutScrollbarTheme::HasThumb(const Scrollbar& scrollbar) { return TrackLength(scrollbar) - ThumbLength(scrollbar) >= 0; } -int LayoutScrollbarTheme::MinimumThumbLength( - const ScrollbarThemeClient& scrollbar) { +int LayoutScrollbarTheme::MinimumThumbLength(const Scrollbar& scrollbar) { return ToLayoutScrollbar(scrollbar).MinimumThumbLength(); } -IntRect LayoutScrollbarTheme::BackButtonRect( - const ScrollbarThemeClient& scrollbar, - ScrollbarPart part_type, - bool) { +IntRect LayoutScrollbarTheme::BackButtonRect(const Scrollbar& scrollbar, + ScrollbarPart part_type, + bool) { return ToLayoutScrollbar(scrollbar).ButtonRect(part_type); } -IntRect LayoutScrollbarTheme::ForwardButtonRect( - const ScrollbarThemeClient& scrollbar, - ScrollbarPart part_type, - bool) { +IntRect LayoutScrollbarTheme::ForwardButtonRect(const Scrollbar& scrollbar, + ScrollbarPart part_type, + bool) { return ToLayoutScrollbar(scrollbar).ButtonRect(part_type); } -IntRect LayoutScrollbarTheme::TrackRect(const ScrollbarThemeClient& scrollbar, - bool) { +IntRect LayoutScrollbarTheme::TrackRect(const Scrollbar& scrollbar, bool) { if (!HasButtons(scrollbar)) return scrollbar.FrameRect(); @@ -101,7 +96,7 @@ IntRect LayoutScrollbarTheme::TrackRect(const ScrollbarThemeClient& scrollbar, } IntRect LayoutScrollbarTheme::ConstrainTrackRectToTrackPieces( - const ScrollbarThemeClient& scrollbar, + const Scrollbar& scrollbar, const IntRect& rect) { IntRect back_rect = ToLayoutScrollbar(scrollbar).TrackPieceRectWithMargins( kBackTrackPart, rect); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.h b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.h index bc9516e8f61..053df443971 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_scrollbar_theme.h @@ -49,12 +49,12 @@ class LayoutScrollbarTheme final : public ScrollbarTheme { const DisplayItemClient&, const IntRect& corner_rect) override; - bool ShouldCenterOnThumb(const ScrollbarThemeClient& scrollbar, + bool ShouldCenterOnThumb(const Scrollbar& scrollbar, const WebMouseEvent& event) override { return ScrollbarTheme::DeprecatedStaticGetTheme().ShouldCenterOnThumb( scrollbar, event); } - bool ShouldSnapBackToDragOrigin(const ScrollbarThemeClient& scrollbar, + bool ShouldSnapBackToDragOrigin(const Scrollbar& scrollbar, const WebMouseEvent& event) override { return ScrollbarTheme::DeprecatedStaticGetTheme() .ShouldSnapBackToDragOrigin(scrollbar, event); @@ -68,35 +68,34 @@ class LayoutScrollbarTheme final : public ScrollbarTheme { return ScrollbarTheme::DeprecatedStaticGetTheme().AutoscrollTimerDelay(); } - void RegisterScrollbar(ScrollbarThemeClient& scrollbar) override { + void RegisterScrollbar(Scrollbar& scrollbar) override { return ScrollbarTheme::DeprecatedStaticGetTheme().RegisterScrollbar( scrollbar); } - void UnregisterScrollbar(ScrollbarThemeClient& scrollbar) override { + void UnregisterScrollbar(Scrollbar& scrollbar) override { return ScrollbarTheme::DeprecatedStaticGetTheme().UnregisterScrollbar( scrollbar); } - int MinimumThumbLength(const ScrollbarThemeClient&) override; + int MinimumThumbLength(const Scrollbar&) override; - void ButtonSizesAlongTrackAxis(const ScrollbarThemeClient&, + void ButtonSizesAlongTrackAxis(const Scrollbar&, int& before_size, int& after_size); static LayoutScrollbarTheme* GetLayoutScrollbarTheme(); protected: - bool HasButtons(const ScrollbarThemeClient&) override; - bool HasThumb(const ScrollbarThemeClient&) override; + bool HasButtons(const Scrollbar&) override; + bool HasThumb(const Scrollbar&) override; - IntRect BackButtonRect(const ScrollbarThemeClient&, + IntRect BackButtonRect(const Scrollbar&, ScrollbarPart, bool painting = false) override; - IntRect ForwardButtonRect(const ScrollbarThemeClient&, + IntRect ForwardButtonRect(const Scrollbar&, ScrollbarPart, bool painting = false) override; - IntRect TrackRect(const ScrollbarThemeClient&, - bool painting = false) override; + IntRect TrackRect(const Scrollbar&, bool painting = false) override; void PaintScrollbarBackground(GraphicsContext&, const Scrollbar&) override; void PaintTrackBackground(GraphicsContext&, @@ -115,7 +114,7 @@ class LayoutScrollbarTheme final : public ScrollbarTheme { const Scrollbar&, const IntRect&) override; - IntRect ConstrainTrackRectToTrackPieces(const ScrollbarThemeClient&, + IntRect ConstrainTrackRectToTrackPieces(const Scrollbar&, const IntRect&) override; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider.cc b/chromium/third_party/blink/renderer/core/layout/layout_slider.cc index 3cdee4139f4..a442b80ea2f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider.cc @@ -24,7 +24,6 @@ #include "third_party/blink/renderer/core/html/forms/slider_thumb_element.h" #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" #include "third_party/blink/renderer/core/input_type_names.h" -#include "third_party/blink/renderer/core/layout/layout_slider_thumb.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { @@ -64,16 +63,6 @@ inline SliderThumbElement* LayoutSlider::GetSliderThumbElement() const { ShadowElementNames::SliderThumb())); } -void LayoutSlider::UpdateLayout() { - // FIXME: Find a way to cascade appearance. - // http://webkit.org/b/62535 - LayoutBox* thumb_box = GetSliderThumbElement()->GetLayoutBox(); - if (thumb_box && thumb_box->IsSliderThumb()) - ToLayoutSliderThumb(thumb_box)->UpdateAppearance(StyleRef()); - - LayoutFlexibleBox::UpdateLayout(); -} - bool LayoutSlider::InDragMode() const { return GetSliderThumbElement()->IsActive(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider.h b/chromium/third_party/blink/renderer/core/layout/layout_slider.h index 10236f42b52..6bfcca82c9a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider.h @@ -53,7 +53,6 @@ class LayoutSlider final : public LayoutFlexibleBox { void ComputeIntrinsicLogicalWidths( LayoutUnit& min_logical_width, LayoutUnit& max_logical_width) const override; - void UpdateLayout() override; SliderThumbElement* GetSliderThumbElement() const; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc index 596286b1eec..111cbabae2a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc @@ -100,15 +100,6 @@ void LayoutSliderContainer::ComputeLogicalHeight( void LayoutSliderContainer::UpdateLayout() { HTMLInputElement* input = ToHTMLInputElement(GetNode()->OwnerShadowHost()); bool is_vertical = HasVerticalAppearance(input); - MutableStyleRef().SetFlexDirection(is_vertical ? EFlexDirection::kColumn - : EFlexDirection::kRow); - TextDirection old_text_direction = Style()->Direction(); - if (is_vertical) { - // FIXME: Work around rounding issues in RTL vertical sliders. We want them - // to render identically to LTR vertical sliders. We can remove this work - // around when subpixel rendering is enabled on all ports. - MutableStyleRef().SetDirection(TextDirection::kLtr); - } Element* thumb_element = input->UserAgentShadowRoot()->getElementById( ShadowElementNames::SliderThumb()); @@ -127,7 +118,6 @@ void LayoutSliderContainer::UpdateLayout() { LayoutFlexibleBox::UpdateLayout(); - MutableStyleRef().SetDirection(old_text_direction); // These should always exist, unless someone mutates the shadow DOM (e.g., in // the inspector). if (!thumb || !track) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.cc b/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.cc deleted file mode 100644 index 4c7b354765a..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/core/layout/layout_slider_thumb.h" - -#include "third_party/blink/renderer/core/html/forms/slider_thumb_element.h" -#include "third_party/blink/renderer/core/layout/layout_theme.h" -#include "third_party/blink/renderer/core/style/computed_style.h" - -namespace blink { - -LayoutSliderThumb::LayoutSliderThumb(SliderThumbElement* element) - : LayoutBlockFlow(element) {} - -void LayoutSliderThumb::UpdateAppearance(const ComputedStyle& parent_style) { - if (parent_style.Appearance() == kSliderVerticalPart) - MutableStyleRef().SetAppearance(kSliderThumbVerticalPart); - else if (parent_style.Appearance() == kSliderHorizontalPart) - MutableStyleRef().SetAppearance(kSliderThumbHorizontalPart); - else if (parent_style.Appearance() == kMediaSliderPart) - MutableStyleRef().SetAppearance(kMediaSliderThumbPart); - else if (parent_style.Appearance() == kMediaVolumeSliderPart) - MutableStyleRef().SetAppearance(kMediaVolumeSliderThumbPart); - if (StyleRef().HasAppearance()) - LayoutTheme::GetTheme().AdjustSliderThumbSize(MutableStyleRef()); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.h b/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.h deleted file mode 100644 index fa0dc424980..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider_thumb.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SLIDER_THUMB_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SLIDER_THUMB_H_ - -#include "third_party/blink/renderer/core/layout/layout_block_flow.h" - -namespace blink { - -class SliderThumbElement; - -class LayoutSliderThumb final : public LayoutBlockFlow { - public: - LayoutSliderThumb(SliderThumbElement*); - void UpdateAppearance(const ComputedStyle& parent_style); - - private: - bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectSliderThumb || LayoutBlockFlow::IsOfType(type); - } -}; - -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSliderThumb, IsSliderThumb()); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SLIDER_THUMB_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_state.cc b/chromium/third_party/blink/renderer/core/layout/layout_state.cc index c1eb06c2338..1dc29c6de9f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_state.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_state.cc @@ -80,11 +80,6 @@ LayoutState::LayoutState(LayoutBox& layout_object, // Now adjust the pagination offset, so that we can easily figure out how far // away we are from the start of the pagination context. - pagination_offset_ = next_->pagination_offset_; - bool fixed = layout_object.IsOutOfFlowPositioned() && - layout_object.Style()->GetPosition() == EPosition::kFixed; - if (fixed) - return; pagination_offset_ = next_->pagination_offset_ + layout_object.LocationOffset(); if (!layout_object.IsOutOfFlowPositioned()) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table.cc b/chromium/third_party/blink/renderer/core/layout/layout_table.cc index 04f1858093d..0e1dfe389a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table.cc @@ -324,8 +324,8 @@ void LayoutTable::UpdateLogicalWidth() { LayoutUnit available_content_logical_width = (container_width_in_inline_direction - margin_total) .ClampNegativeToZero(); - if (!cb->IsLayoutNGMixin() && ShrinkToAvoidFloats() && - cb->IsLayoutBlockFlow() && ToLayoutBlockFlow(cb)->ContainsFloats() && + if (ShrinkToAvoidFloats() && cb->IsLayoutBlockFlow() && + ToLayoutBlockFlow(cb)->ContainsFloats() && !has_perpendicular_containing_block) available_content_logical_width = ShrinkLogicalWidthToAvoidFloats( margin_start, margin_end, ToLayoutBlockFlow(cb)); @@ -1518,8 +1518,10 @@ bool LayoutTable::NodeAtPoint(HitTestResult& result, LayoutPoint adjusted_location = accumulated_offset + Location(); // Check kids first. - if (!HasOverflowClip() || - location_in_container.Intersects(OverflowClipRect(adjusted_location))) { + bool skip_children = (result.GetHitTestRequest().GetStopNode() == this); + if (!skip_children && + (!HasOverflowClip() || + location_in_container.Intersects(OverflowClipRect(adjusted_location)))) { for (LayoutObject* child = LastChild(); child; child = child->PreviousSibling()) { if (child->IsBox() && !ToLayoutBox(child)->HasSelfPaintingLayer() && diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_box_component.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_box_component.cc index 5187525c94f..49e4040afb9 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_box_component.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_box_component.cc @@ -60,7 +60,8 @@ void LayoutTableBoxComponent::MutableForPainting::UpdatePaintResult( void LayoutTableBoxComponent::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { LayoutBox::StyleDidChange(diff, old_style); - SetCanContainFixedPositionObjects(Style()->CanContainFixedPositionObjects()); + SetCanContainFixedPositionObjects( + Style()->CanContainFixedPositionObjects(false)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h b/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h index ec25d93b264..044f88bf100 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h @@ -35,24 +35,25 @@ class LayoutTable; // // This class is very simple as the logic for handling the caption is done in // LayoutTable. In particular, the placement and sizing is done in -// LayoutTable::layoutCaption. The function is called at different timing +// LayoutTable::LayoutCaption. The function is called at different timing // depending on the 'caption-side' property: "top" is laid out before table row // groups when "bottom" ones are laid out after. This ensures that "top" // captions are visually before the row groups and "bottom" ones are after. // // See http://www.w3.org/TR/CSS21/tables.html#caption-position for the // positioning. -class LayoutTableCaption final : public LayoutBlockFlow { +class LayoutTableCaption : public LayoutBlockFlow { public: explicit LayoutTableCaption(Element*); ~LayoutTableCaption() override; LayoutUnit ContainingBlockLogicalWidthForContent() const override; - private: + protected: bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectTableCaption || LayoutBlockFlow::IsOfType(type); } + private: void InsertedIntoTree() override; void WillBeRemovedFromTree() override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc index ff5e08d5655..6663af368fd 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc @@ -38,7 +38,7 @@ #include "third_party/blink/renderer/core/paint/table_cell_paint_invalidator.h" #include "third_party/blink/renderer/core/paint/table_cell_painter.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { @@ -189,14 +189,13 @@ void LayoutTableCell::ComputePreferredLogicalWidths() { // notional height on the cell, such as can happen when a percent sized image // scales up its width to match the available height. Setting a zero override // height prevents this from happening. - LayoutUnit content_height = HasOverrideLogicalContentHeight() - ? OverrideLogicalContentHeight() - : LayoutUnit(-1); - if (content_height > -1) - SetOverrideLogicalContentHeight(LayoutUnit()); + LayoutUnit logical_height = + HasOverrideLogicalHeight() ? OverrideLogicalHeight() : LayoutUnit(-1); + if (logical_height > -1) + SetOverrideLogicalHeight(LayoutUnit()); LayoutBlockFlow::ComputePreferredLogicalWidths(); - if (content_height > -1) - SetOverrideLogicalContentHeight(content_height); + if (logical_height > -1) + SetOverrideLogicalHeight(logical_height); if (GetNode() && Style()->AutoWrap()) { // See if nowrap was set. @@ -347,18 +346,19 @@ LayoutUnit LayoutTableCell::PaddingRight() const { : LayoutUnit(result.ToInt()); } -void LayoutTableCell::SetOverrideLogicalContentHeightFromRowHeight( +void LayoutTableCell::SetOverrideLogicalHeightFromRowHeight( LayoutUnit row_height) { ClearIntrinsicPadding(); - SetOverrideLogicalContentHeight( - (row_height - CollapsedBorderAndCSSPaddingLogicalHeight()) - .ClampNegativeToZero()); + SetOverrideLogicalHeight(row_height); } -LayoutSize LayoutTableCell::OffsetFromContainer(const LayoutObject* o) const { +LayoutSize LayoutTableCell::OffsetFromContainerInternal( + const LayoutObject* o, + bool ignore_scroll_offset) const { DCHECK_EQ(o, Container()); - LayoutSize offset = LayoutBlockFlow::OffsetFromContainer(o); + LayoutSize offset = + LayoutBlockFlow::OffsetFromContainerInternal(o, ignore_scroll_offset); if (Parent()) offset -= ParentBox()->PhysicalLocationOffset(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h index 99c3beac656..ef20ef0213d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h @@ -226,7 +226,7 @@ class CORE_EXPORT LayoutTableCell : public LayoutBlockFlow { LayoutUnit PaddingLeft() const override; LayoutUnit PaddingRight() const override; - void SetOverrideLogicalContentHeightFromRowHeight(LayoutUnit); + void SetOverrideLogicalHeightFromRowHeight(LayoutUnit); void ScrollbarsChanged(bool horizontal_scrollbar_changed, bool vertical_scrollbar_changed, @@ -364,6 +364,10 @@ class CORE_EXPORT LayoutTableCell : public LayoutBlockFlow { PaintInvalidationReason InvalidatePaint( const PaintInvalidatorContext&) const override; + LayoutSize OffsetFromContainerInternal( + const LayoutObject*, + bool ignore_scroll_offset) const override; + protected: bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectTableCell || LayoutBlockFlow::IsOfType(type); @@ -380,8 +384,6 @@ class CORE_EXPORT LayoutTableCell : public LayoutBlockFlow { const LayoutPoint&) const override; void PaintMask(const PaintInfo&, const LayoutPoint&) const override; - LayoutSize OffsetFromContainer(const LayoutObject*) const override; - bool ShouldClipOverflow() const override; using CollapsedBorderValuesMethod = diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc index d06210946e5..7cd66cfba55 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc @@ -904,7 +904,7 @@ int LayoutTableSection::CalcRowLogicalHeight() { row_span_cells.push_back(cell); } - if (cell->HasOverrideLogicalContentHeight()) { + if (cell->HasOverrideLogicalHeight()) { cell->ClearIntrinsicPadding(); cell->ClearOverrideSize(); cell->ForceChildLayout(); @@ -1926,7 +1926,7 @@ void LayoutTableSection::RelayoutCellIfFlexed(LayoutTableCell& cell, // Alignment within a cell is based off the calculated height, which becomes // irrelevant once the cell has been resized based off its percentage. - cell.SetOverrideLogicalContentHeightFromRowHeight(LayoutUnit(row_height)); + cell.SetOverrideLogicalHeightFromRowHeight(LayoutUnit(row_height)); cell.ForceChildLayout(); // If the baseline moved, we may have to update the data for our row. Find @@ -2046,10 +2046,6 @@ bool LayoutTableSection::GroupShouldRepeat() const { if (GetPaginationBreakability() == kAllowAnyBreaks) return false; - // TODO(rhogan): Sections can be self-painting. - if (HasSelfPaintingLayer()) - return false; - // If we don't know the page height yet, just assume we fit. if (!IsPageLogicalHeightKnown()) return true; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text.cc b/chromium/third_party/blink/renderer/core/layout/layout_text.cc index a27c592b2d4..f5f6ed18829 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text.cc @@ -32,6 +32,8 @@ #include "third_party/blink/renderer/core/dom/text.h" #include "third_party/blink/renderer/core/editing/ephemeral_range.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" +#include "third_party/blink/renderer/core/editing/inline_box_position.h" +#include "third_party/blink/renderer/core/editing/inline_box_traversal.h" #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h" #include "third_party/blink/renderer/core/editing/text_affinity.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -53,11 +55,13 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/text_autosizer.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/platform/fonts/character_range.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" -#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/text/bidi_resolver.h" #include "third_party/blink/renderer/platform/text/character.h" #include "third_party/blink/renderer/platform/text/hyphenation.h" @@ -69,7 +73,7 @@ namespace blink { struct SameSizeAsLayoutText : public LayoutObject { - uint32_t bitfields : 11; + uint32_t bitfields : 12; float widths[4]; String text; void* pointers[2]; @@ -111,55 +115,11 @@ class SecureTextTimer final : public TimerBase { int last_typed_character_offset_; }; -static void MakeCapitalized(String* string, UChar previous) { - if (string->IsNull()) - return; - - unsigned length = string->length(); - const StringImpl& input = *string->Impl(); - - CHECK_LT(length, std::numeric_limits<unsigned>::max()); - StringBuffer<UChar> string_with_previous(length + 1); - string_with_previous[0] = - previous == kNoBreakSpaceCharacter ? kSpaceCharacter : previous; - for (unsigned i = 1; i < length + 1; i++) { - // Replace   with a real space since ICU no longer treats   as a - // word separator. - if (input[i - 1] == kNoBreakSpaceCharacter) - string_with_previous[i] = kSpaceCharacter; - else - string_with_previous[i] = input[i - 1]; - } - - TextBreakIterator* boundary = - WordBreakIterator(string_with_previous.Characters(), length + 1); - if (!boundary) - return; - - StringBuilder result; - result.ReserveCapacity(length); - - int32_t end_of_word; - int32_t start_of_word = boundary->first(); - for (end_of_word = boundary->next(); end_of_word != kTextBreakDone; - start_of_word = end_of_word, end_of_word = boundary->next()) { - if (start_of_word) { // Ignore first char of previous string - result.Append( - input[start_of_word - 1] == kNoBreakSpaceCharacter - ? kNoBreakSpaceCharacter - : WTF::Unicode::ToTitleCase(string_with_previous[start_of_word])); - } - for (int i = start_of_word + 1; i < end_of_word; i++) - result.Append(input[i - 1]); - } - - *string = result.ToString(); -} - LayoutText::LayoutText(Node* node, scoped_refptr<StringImpl> str) : LayoutObject(node), has_tab_(false), lines_dirty_(false), + valid_ng_items_(false), contains_reversed_text_(false), known_to_have_no_overflow_and_no_fallback_fonts_(false), contains_only_whitespace_or_nbsp_( @@ -219,6 +179,10 @@ void LayoutText::StyleDidChange(StyleDifference diff, TextAutosizer* text_autosizer = GetDocument().GetTextAutosizer(); if (!old_style && text_autosizer) text_autosizer->Record(this); + + // TODO(layout-dev): This is only really needed for style changes that affect + // how text is rendered. Font, text-decoration, etc. + valid_ng_items_ = false; } void LayoutText::RemoveAndDestroyTextBoxes() { @@ -245,6 +209,7 @@ void LayoutText::WillBeDestroyed() { RemoveAndDestroyTextBoxes(); LayoutObject::WillBeDestroyed(); + valid_ng_items_ = false; } void LayoutText::ExtractTextBox(InlineTextBox* box) { @@ -263,7 +228,7 @@ void LayoutText::DeleteTextBoxes() { text_boxes_.DeleteLineBoxes(); } -Optional<FloatPoint> LayoutText::GetUpperLeftCorner() const { +base::Optional<FloatPoint> LayoutText::GetUpperLeftCorner() const { DCHECK(!IsBR()); if (HasLegacyTextBoxes()) { if (StyleRef().IsHorizontalWritingMode()) { @@ -284,7 +249,7 @@ Optional<FloatPoint> LayoutText::GetUpperLeftCorner() const { return FloatPoint(line_box->InlineOffsetToContainerBox().left.ToFloat(), LinesBoundingBox().Y()); } - return WTF::nullopt; + return base::nullopt; } bool LayoutText::HasTextBoxes() const { @@ -580,13 +545,15 @@ FloatRect LayoutText::LocalBoundingBoxRectForAccessibility() const { return result; } +namespace { + enum ShouldAffinityBeDownstream { kAlwaysDownstream, kAlwaysUpstream, kUpstreamIfPositionIsNotAtStart }; -static bool LineDirectionPointFitsInBox( +bool LineDirectionPointFitsInBox( int point_line_direction, InlineTextBox* box, ShouldAffinityBeDownstream& should_affinity_be_downstream) { @@ -626,7 +593,7 @@ static bool LineDirectionPointFitsInBox( return false; } -static PositionWithAffinity CreatePositionWithAffinityForBox( +PositionWithAffinity CreatePositionWithAffinityForBox( const InlineBox* box, int offset, ShouldAffinityBeDownstream should_affinity_be_downstream) { @@ -652,7 +619,7 @@ static PositionWithAffinity CreatePositionWithAffinityForBox( offset + text_start_offset, affinity); } -static PositionWithAffinity +PositionWithAffinity CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( const InlineTextBox* box, int offset, @@ -660,94 +627,24 @@ CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( DCHECK(box); DCHECK_GE(offset, 0); + // TODO(layout-dev): Stop passing out-of-range |offset|. + if (static_cast<unsigned>(offset) > box->Len()) + offset = box->Len(); + if (offset && static_cast<unsigned>(offset) < box->Len()) { return CreatePositionWithAffinityForBox(box, box->Start() + offset, should_affinity_be_downstream); } - bool position_is_at_start_of_box = !offset; - if (position_is_at_start_of_box == box->IsLeftToRightDirection()) { - // offset is on the left edge - - const InlineBox* prev_box = box->PrevLeafChildIgnoringLineBreak(); - if ((prev_box && prev_box->BidiLevel() == box->BidiLevel()) || - box->GetLineLayoutItem().ContainingBlock().Style()->Direction() == - box->Direction()) { // FIXME: left on 12CBA - return CreatePositionWithAffinityForBox(box, box->CaretLeftmostOffset(), - should_affinity_be_downstream); - } - - if (prev_box && prev_box->BidiLevel() > box->BidiLevel()) { - // e.g. left of B in aDC12BAb - const InlineBox* leftmost_box; - do { - leftmost_box = prev_box; - prev_box = leftmost_box->PrevLeafChildIgnoringLineBreak(); - } while (prev_box && prev_box->BidiLevel() > box->BidiLevel()); - return CreatePositionWithAffinityForBox( - leftmost_box, leftmost_box->CaretRightmostOffset(), - should_affinity_be_downstream); - } - - if (!prev_box || prev_box->BidiLevel() < box->BidiLevel()) { - // e.g. left of D in aDC12BAb - const InlineBox* rightmost_box; - const InlineBox* next_box = box; - do { - rightmost_box = next_box; - next_box = rightmost_box->NextLeafChildIgnoringLineBreak(); - } while (next_box && next_box->BidiLevel() >= box->BidiLevel()); - return CreatePositionWithAffinityForBox( - rightmost_box, - box->IsLeftToRightDirection() ? rightmost_box->CaretMaxOffset() - : rightmost_box->CaretMinOffset(), - should_affinity_be_downstream); - } - - return CreatePositionWithAffinityForBox(box, box->CaretRightmostOffset(), - should_affinity_be_downstream); - } - - const InlineBox* next_box = box->NextLeafChildIgnoringLineBreak(); - if ((next_box && next_box->BidiLevel() == box->BidiLevel()) || - box->GetLineLayoutItem().ContainingBlock().Style()->Direction() == - box->Direction()) { - return CreatePositionWithAffinityForBox(box, box->CaretRightmostOffset(), - should_affinity_be_downstream); - } - - // offset is on the right edge - if (next_box && next_box->BidiLevel() > box->BidiLevel()) { - // e.g. right of C in aDC12BAb - const InlineBox* rightmost_box; - do { - rightmost_box = next_box; - next_box = rightmost_box->NextLeafChildIgnoringLineBreak(); - } while (next_box && next_box->BidiLevel() > box->BidiLevel()); - return CreatePositionWithAffinityForBox( - rightmost_box, rightmost_box->CaretLeftmostOffset(), - should_affinity_be_downstream); - } - - if (!next_box || next_box->BidiLevel() < box->BidiLevel()) { - // e.g. right of A in aDC12BAb - const InlineBox* leftmost_box; - const InlineBox* prev_box = box; - do { - leftmost_box = prev_box; - prev_box = leftmost_box->PrevLeafChildIgnoringLineBreak(); - } while (prev_box && prev_box->BidiLevel() >= box->BidiLevel()); - return CreatePositionWithAffinityForBox( - leftmost_box, - box->IsLeftToRightDirection() ? leftmost_box->CaretMinOffset() - : leftmost_box->CaretMaxOffset(), - should_affinity_be_downstream); - } - - return CreatePositionWithAffinityForBox(box, box->CaretLeftmostOffset(), + const InlineBoxPosition adjusted = BidiAdjustment::AdjustForHitTest( + InlineBoxPosition(box, box->Start() + offset)); + return CreatePositionWithAffinityForBox(adjusted.inline_box, + adjusted.offset_in_box, should_affinity_be_downstream); } +} // namespace + PositionWithAffinity LayoutText::PositionForPoint( const LayoutPoint& point) const { if (const LayoutBlockFlow* ng_block_flow = EnclosingNGBlockFlow()) @@ -1646,6 +1543,12 @@ void LayoutText::SetTextWithOffset(scoped_refptr<StringImpl> text, lines_dirty_ = dirtied_lines; SetText(std::move(text), force || dirtied_lines); + + // TODO(layout-dev): Invalidation is currently all or nothing in LayoutNG, + // this is probably fine for NGInlineItem reuse as recreating the individual + // items is relatively cheap. If partial relayout performance improvement are + // needed partial re-shapes are likely to be sufficient. Revisit as needed. + valid_ng_items_ = false; } void LayoutText::TransformText() { @@ -1693,37 +1596,16 @@ void LayoutText::AddLayerHitTestRects( // Text nodes aren't event targets, so don't descend any further. } -void ApplyTextTransform(const ComputedStyle* style, - String& text, - UChar previous_character) { - if (!style) - return; - - switch (style->TextTransform()) { - case ETextTransform::kNone: - break; - case ETextTransform::kCapitalize: - MakeCapitalized(&text, previous_character); - break; - case ETextTransform::kUppercase: - text = text.UpperUnicode(style->Locale()); - break; - case ETextTransform::kLowercase: - text = text.LowerUnicode(style->Locale()); - break; - } -} - void LayoutText::SetTextInternal(scoped_refptr<StringImpl> text) { DCHECK(text); text_ = String(std::move(text)); - if (Style()) { - ApplyTextTransform(Style(), text_, PreviousCharacter()); + if (const ComputedStyle* style = Style()) { + style->ApplyTextTransform(&text_, PreviousCharacter()); // We use the same characters here as for list markers. // See the listMarkerText function in LayoutListMarker.cpp. - switch (Style()->TextSecurity()) { + switch (style->TextSecurity()) { case ETextSecurity::kNone: break; case ETextSecurity::kCircle: @@ -1789,6 +1671,8 @@ void LayoutText::SetText(scoped_refptr<StringImpl> text, bool force) { TextAutosizer* text_autosizer = GetDocument().GetTextAutosizer(); if (text_autosizer) text_autosizer->Record(this); + + valid_ng_items_ = false; } void LayoutText::DirtyOrDeleteLineBoxesIfNeeded(bool full_layout) { @@ -1797,12 +1681,14 @@ void LayoutText::DirtyOrDeleteLineBoxesIfNeeded(bool full_layout) { else if (!lines_dirty_) DirtyLineBoxes(); lines_dirty_ = false; + valid_ng_items_ = false; } void LayoutText::DirtyLineBoxes() { for (InlineTextBox* box : TextBoxes()) box->DirtyLineBoxes(); lines_dirty_ = false; + valid_ng_items_ = false; } InlineTextBox* LayoutText::CreateTextBox(int start, unsigned short length) { @@ -2006,6 +1892,23 @@ LayoutRect LayoutText::LocalSelectionRect() const { if (!cb) return LayoutRect(); + const FrameSelection& frame_selection = GetFrame()->Selection(); + const auto fragments = NGPaintFragment::InlineFragmentsFor(this); + if (fragments.IsInLayoutNGInlineFormattingContext()) { + LayoutRect rect; + for (const NGPaintFragment* fragment : fragments) { + const LayoutSelectionStatus status = + frame_selection.ComputeLayoutSelectionStatus(*fragment); + if (status.start == status.end) + continue; + NGPhysicalOffsetRect fragment_rect = + fragment->ComputeLocalSelectionRect(status); + fragment_rect.offset += fragment->InlineOffsetToContainerBox(); + rect.Unite(fragment_rect.ToLayoutRect()); + } + return rect; + } + // Now calculate startPos and endPos for painting selection. // We include a selection while endPos > 0 unsigned start_pos, end_pos; @@ -2014,7 +1917,6 @@ LayoutRect LayoutText::LocalSelectionRect() const { start_pos = 0; end_pos = TextLength(); } else { - const FrameSelection& frame_selection = GetFrame()->Selection(); if (GetSelectionState() == SelectionState::kStart) { // TODO(yoichio): value_or is used to prevent use uininitialized value // on release. It should be value() after LayoutSelection brushup. @@ -2030,12 +1932,8 @@ LayoutRect LayoutText::LocalSelectionRect() const { } } - // TODO(yoichio): The following DCHECK should pass, but fails 14 tests. - // DCHECK_LE(start_pos, end_pos); + DCHECK_LE(start_pos, end_pos); LayoutRect rect; - if (start_pos >= end_pos) - return rect; - for (InlineTextBox* box : TextBoxes()) { rect.Unite(box->LocalSelectionRect(start_pos, end_pos)); rect.Unite(LayoutRect(EllipsisRectForBox(box, start_pos, end_pos))); @@ -2053,52 +1951,39 @@ const NGOffsetMapping* LayoutText::GetNGOffsetMapping() const { Position LayoutText::PositionForCaretOffset(unsigned offset) const { // ::first-letter handling should be done by LayoutTextFragment override. DCHECK(!IsTextFragment()); + // BR handling should be done by LayoutBR override. + DCHECK(!IsBR()); // WBR handling should be done by LayoutWordBreak override. DCHECK(!IsWordBreak()); DCHECK_LE(offset, TextLength()); const Node* node = GetNode(); if (!node) return Position(); - if (node->IsTextNode()) { - // TODO(layout-dev): Support offset change due to text-transform. - return Position(node, offset); - } - // TODO(xiaochengh): This should be done in LayoutBR override. - if (IsBR()) { - DCHECK(IsHTMLBRElement(node)); - DCHECK_LE(offset, 1u); - return offset ? Position::AfterNode(*node) : Position::BeforeNode(*node); - } - NOTREACHED(); - return Position(); + DCHECK(node->IsTextNode()); + // TODO(layout-dev): Support offset change due to text-transform. + return Position(node, offset); } -Optional<unsigned> LayoutText::CaretOffsetForPosition( +base::Optional<unsigned> LayoutText::CaretOffsetForPosition( const Position& position) const { // ::first-letter handling should be done by LayoutTextFragment override. DCHECK(!IsTextFragment()); + // BR handling should be done by LayoutBR override. + DCHECK(!IsBR()); // WBR handling should be done by LayoutWordBreak override. DCHECK(!IsWordBreak()); if (position.IsNull() || position.AnchorNode() != GetNode()) - return WTF::nullopt; - if (GetNode()->IsTextNode()) { - if (position.IsBeforeAnchor()) - return 0; - // TODO(layout-dev): Support offset change due to text-transform. - if (position.IsAfterAnchor()) - return TextLength(); - DCHECK(position.IsOffsetInAnchor()) << position; - DCHECK_LE(position.OffsetInContainerNode(), static_cast<int>(TextLength())) - << position; - return position.OffsetInContainerNode(); - } - // TODO(xiaochengh): This should be done by LayoutBR override. - if (IsBR()) { - DCHECK(position.IsBeforeAnchor() || position.IsAfterAnchor()) << position; - return position.IsBeforeAnchor() ? 0 : 1; - } - NOTREACHED(); - return WTF::nullopt; + return base::nullopt; + DCHECK(GetNode()->IsTextNode()); + if (position.IsBeforeAnchor()) + return 0; + // TODO(layout-dev): Support offset change due to text-transform. + if (position.IsAfterAnchor()) + return TextLength(); + DCHECK(position.IsOffsetInAnchor()) << position; + DCHECK_LE(position.OffsetInContainerNode(), static_cast<int>(TextLength())) + << position; + return position.OffsetInContainerNode(); } int LayoutText::CaretMinOffset() const { @@ -2108,7 +1993,7 @@ int LayoutText::CaretMinOffset() const { const Position first_position = PositionForCaretOffset(0); if (first_position.IsNull()) return 0; - Optional<unsigned> candidate = CaretOffsetForPosition( + base::Optional<unsigned> candidate = CaretOffsetForPosition( mapping->StartOfNextNonCollapsedContent(first_position)); // Align with the legacy behavior that 0 is returned if the entire node // contains only collapsed whitespaces. @@ -2132,7 +2017,7 @@ int LayoutText::CaretMaxOffset() const { const Position last_position = PositionForCaretOffset(TextLength()); if (last_position.IsNull()) return TextLength(); - Optional<unsigned> candidate = CaretOffsetForPosition( + base::Optional<unsigned> candidate = CaretOffsetForPosition( mapping->EndOfLastNonCollapsedContent(last_position)); // Align with the legacy behavior that |TextLenght()| is returned if the // entire node contains only collapsed whitespaces. @@ -2159,8 +2044,9 @@ unsigned LayoutText::ResolvedTextLength() const { return 0; } DCHECK(end_position.IsNotNull()) << start_position; - Optional<unsigned> start = mapping->GetTextContentOffset(start_position); - Optional<unsigned> end = mapping->GetTextContentOffset(end_position); + base::Optional<unsigned> start = + mapping->GetTextContentOffset(start_position); + base::Optional<unsigned> end = mapping->GetTextContentOffset(end_position); if (!start.has_value() || !end.has_value()) { DCHECK(!start.has_value()) << this; DCHECK(!end.has_value()) << this; @@ -2334,9 +2220,6 @@ scoped_refptr<AbstractInlineTextBox> LayoutText::FirstAbstractInlineTextBox() { void LayoutText::InvalidateDisplayItemClients( PaintInvalidationReason invalidation_reason) const { - // TODO(yoichio): Cover other PaintInvalidateionReasons. - DCHECK(invalidation_reason != PaintInvalidationReason::kSelection || - !EnclosingNGBlockFlow()); ObjectPaintInvalidator paint_invalidator(*this); if (RuntimeEnabledFeatures::LayoutNGEnabled()) { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text.h b/chromium/third_party/blink/renderer/core/layout/layout_text.h index b90bafab86e..6fac55a2ad1 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text.h @@ -59,10 +59,9 @@ enum class OnlyWhitespaceOrNbsp : unsigned { kUnknown = 0, kNo = 1, kYes = 2 }; // // // ***** LINE BOXES OWNERSHIP ***** -// InlineTextBox in text_boxes_ are not owned by LayoutText -// but are pointers into the enclosing inline / block (see LayoutInline's -// and LayoutBlockFlow's m_lineBoxes). -// +// InlineTextBox in text_boxes_ are not owned by LayoutText but are pointers +// into the enclosing inline / block (see LayoutInline's and LayoutBlockFlow's +// line_boxes_). // // This class implements the preferred logical widths computation // for its underlying text. The widths are stored into m_minWidth @@ -207,7 +206,7 @@ class CORE_EXPORT LayoutText : public LayoutObject { // Returns upper left corner point in local physical coordinates with flipped // block-flow direction if this object has rendered text. - Optional<FloatPoint> GetUpperLeftCorner() const; + base::Optional<FloatPoint> GetUpperLeftCorner() const; // True if we have inline text box children which implies rendered text (or // whitespace) output. @@ -225,7 +224,8 @@ class CORE_EXPORT LayoutText : public LayoutObject { // Returns the offset in the |text_| string that corresponds to the given // position in DOM; Returns nullopt is the position is not in this LayoutText. // TODO(layout-dev): Fix it when text-transform changes text length. - virtual Optional<unsigned> CaretOffsetForPosition(const Position&) const; + virtual base::Optional<unsigned> CaretOffsetForPosition( + const Position&) const; // Returns true if the offset (0-based in the |text_| string) is next to a // non-collapsed non-linebreak character, or before a forced linebreak (<br>, @@ -354,7 +354,7 @@ class CORE_EXPORT LayoutText : public LayoutObject { LayoutRect LocalVisualRectIgnoringVisibility() const final; // We put the bitfield first to minimize padding on 64-bit. - + protected: // Whether or not we can be broken into multiple lines. unsigned has_breakable_char_ : 1; // Whether or not we have a hard break (e.g., <pre> with '\n'). @@ -370,10 +370,18 @@ class CORE_EXPORT LayoutText : public LayoutObject { // dirtying everything when character data is modified (e.g., appended/ // inserted or removed). unsigned lines_dirty_ : 1; + + // Used by LayoutNGText. Whether the NGInlineItems associated with this + // object are valid. Set after layout and cleared whenever the LayoutText is + // modified. + // Functionally the inverse equivalent of lines_dirty_ for LayoutNG. + unsigned valid_ng_items_ : 1; + unsigned contains_reversed_text_ : 1; mutable unsigned known_to_have_no_overflow_and_no_fallback_fonts_ : 1; unsigned contains_only_whitespace_or_nbsp_ : 2; + private: float min_width_; float max_width_; float first_line_min_width_; @@ -421,8 +429,6 @@ inline LayoutText* Text::GetLayoutObject() const { return ToLayoutText(CharacterData::GetLayoutObject()); } -void ApplyTextTransform(const ComputedStyle*, String&, UChar); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_TEXT_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc index 9fced5e8f96..dde546fa0ee 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc @@ -31,21 +31,44 @@ LayoutTextCombine::LayoutTextCombine(Node* node, : LayoutText(node, std::move(string)), combined_text_width_(0), scale_x_(1.0f), - is_combined_(false), - needs_font_update_(false) {} + is_combined_(false) {} void LayoutTextCombine::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { - SetStyleInternal(ComputedStyle::Clone(StyleRef())); LayoutText::StyleDidChange(diff, old_style); - UpdateIsCombined(); + if (!IsCombined()) + return; + + // We need to call LayoutText::StyleDidChange before updating combined text + // font because StyleDidChange may change the text through text-transform. + UpdateFontStyleForCombinedText(); } void LayoutTextCombine::SetTextInternal(scoped_refptr<StringImpl> text) { LayoutText::SetTextInternal(std::move(text)); + bool was_combined = IsCombined(); UpdateIsCombined(); + + // SetTextInternal may be called on construction for applying text-transform + // in which case Parent() is nullptr. However, was_combined should be false + // since it initially is. + DCHECK(!was_combined || Parent()); + + if (was_combined) { + // Re-set the ComputedStyle from the parent to base the measurements in + // UpdateFontStyleForCombinedText on the original font and not what was + // previously set for combined text. If IsCombined() is now false, we are + // simply resetting the style to the parent style. + SetStyle(Parent()->MutableStyle()); + } else if (IsCombined()) { + // If the text was previously not combined, SetStyle would have been a no-op + // since the before and after style would be the same ComputedStyle + // instance and StyleDidChange would not be called. Instead, call + // UpdateFontStyleForCombinedText directly. + UpdateFontStyleForCombinedText(); + } } float LayoutTextCombine::Width(unsigned from, @@ -82,7 +105,6 @@ void ScaleHorizontallyAndTranslate(GraphicsContext& context, void LayoutTextCombine::TransformToInlineCoordinates(GraphicsContext& context, const LayoutRect& box_rect, bool clip) const { - DCHECK_EQ(needs_font_update_, false); DCHECK(is_combined_); // No transform needed if we don't have a font. @@ -130,37 +152,31 @@ void LayoutTextCombine::UpdateIsCombined() { is_combined_ = !Style()->IsHorizontalWritingMode() // Nothing to combine. && !HasEmptyText(); - - if (is_combined_) - needs_font_update_ = true; } -void LayoutTextCombine::UpdateFont() { - if (!needs_font_update_) - return; - - needs_font_update_ = false; +void LayoutTextCombine::UpdateFontStyleForCombinedText() { + DCHECK(is_combined_); - if (!is_combined_) - return; + scoped_refptr<ComputedStyle> style = ComputedStyle::Clone(StyleRef()); + SetStyleInternal(style); unsigned offset = 0; - TextRun run = ConstructTextRun(OriginalFont(), this, offset, TextLength(), - StyleRef(), Style()->Direction()); - FontDescription description = OriginalFont().GetFontDescription(); + TextRun run = ConstructTextRun(style->GetFont(), this, offset, TextLength(), + *style, style->Direction()); + FontDescription description = style->GetFont().GetFontDescription(); float em_width = description.ComputedSize(); - if (!EnumHasFlags(Style()->TextDecorationsInEffect(), + if (!EnumHasFlags(style->TextDecorationsInEffect(), TextDecoration::kUnderline | TextDecoration::kOverline)) em_width *= kTextCombineMargin; // We are going to draw combined text horizontally. description.SetOrientation(FontOrientation::kHorizontal); - combined_text_width_ = OriginalFont().Width(run); + combined_text_width_ = style->GetFont().Width(run); - FontSelector* font_selector = Style()->GetFont().GetFontSelector(); + FontSelector* font_selector = style->GetFont().GetFontSelector(); - bool should_update_font = MutableStyleRef().SetFontDescription( - description); // Need to change font orientation to horizontal. + // Need to change font orientation to horizontal. + bool should_update_font = style->SetFontDescription(description); if (combined_text_width_ <= em_width) { scale_x_ = 1.0f; @@ -168,7 +184,7 @@ void LayoutTextCombine::UpdateFont() { // Need to try compressed glyphs. static const FontWidthVariant kWidthVariants[] = {kHalfWidth, kThirdWidth, kQuarterWidth}; - for (size_t i = 0; i < WTF_ARRAY_LENGTH(kWidthVariants); ++i) { + for (size_t i = 0; i < arraysize(kWidthVariants); ++i) { description.SetWidthVariant(kWidthVariants[i]); Font compressed_font = Font(description); compressed_font.Update(font_selector); @@ -177,7 +193,7 @@ void LayoutTextCombine::UpdateFont() { combined_text_width_ = run_width; // Replace my font with the new one. - should_update_font = MutableStyleRef().SetFontDescription(description); + should_update_font = style->SetFontDescription(description); break; } } @@ -194,7 +210,7 @@ void LayoutTextCombine::UpdateFont() { } if (should_update_font) - Style()->GetFont().Update(font_selector); + style->GetFont().Update(font_selector); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.h b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.h index 02649ce5d6e..2afca4e195c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.h @@ -33,7 +33,6 @@ class LayoutTextCombine final : public LayoutText { public: LayoutTextCombine(Node*, scoped_refptr<StringImpl>); - void UpdateFont(); bool IsCombined() const { return is_combined_; } float CombinedTextWidth(const Font& font) const { return font.GetFontDescription().ComputedSize(); @@ -59,17 +58,16 @@ class LayoutTextCombine final : public LayoutText { void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void SetTextInternal(scoped_refptr<StringImpl>) override; void UpdateIsCombined(); + void UpdateFontStyleForCombinedText(); float combined_text_width_; float scale_x_; bool is_combined_ : 1; - bool needs_font_update_ : 1; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTextCombine, IsCombineText()); inline LayoutUnit LayoutTextCombine::InlineWidthForLayout() const { - DCHECK(!needs_font_update_); return LayoutUnit::FromFloatCeil(combined_text_width_); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc index e975a51f045..986814a2177 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc @@ -41,24 +41,19 @@ TextControlElement* LayoutTextControl::GetTextControlElement() const { return ToTextControl(GetNode()); } -HTMLElement* LayoutTextControl::InnerEditorElement() const { +TextControlInnerEditorElement* LayoutTextControl::InnerEditorElement() const { return GetTextControlElement()->InnerEditorElement(); } void LayoutTextControl::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { LayoutBlockFlow::StyleDidChange(diff, old_style); - Element* inner_editor = InnerEditorElement(); + TextControlInnerEditorElement* inner_editor = InnerEditorElement(); if (!inner_editor) return; LayoutBlock* inner_editor_layout_object = ToLayoutBlock(inner_editor->GetLayoutObject()); if (inner_editor_layout_object) { - // We may have set the width and the height in the old style in layout(). - // Reset them now to avoid getting a spurious layout hint. - inner_editor_layout_object->MutableStyleRef().SetHeight(Length()); - inner_editor_layout_object->MutableStyleRef().SetWidth(Length()); - inner_editor_layout_object->SetStyle(CreateInnerEditorStyle(StyleRef())); inner_editor->SetNeedsStyleRecalc( kSubtreeStyleChange, StyleChangeReasonForTracing::Create(StyleChangeReason::kControl)); @@ -75,25 +70,6 @@ void LayoutTextControl::StyleDidChange(StyleDifference diff, GetTextControlElement()->UpdatePlaceholderVisibility(); } -static inline void UpdateUserModifyProperty(TextControlElement& node, - ComputedStyle& style) { - style.SetUserModify(node.IsDisabledOrReadOnly() - ? EUserModify::kReadOnly - : EUserModify::kReadWritePlaintextOnly); -} - -void LayoutTextControl::AdjustInnerEditorStyle( - ComputedStyle& text_block_style) const { - // The inner block, if present, always has its direction set to LTR, - // so we need to inherit the direction and unicode-bidi style from the - // element. - text_block_style.SetDirection(Style()->Direction()); - text_block_style.SetUnicodeBidi(Style()->GetUnicodeBidi()); - text_block_style.SetUserSelect(EUserSelect::kText); - - UpdateUserModifyProperty(*GetTextControlElement(), text_block_style); -} - int LayoutTextControl::TextBlockLogicalHeight() const { return (LogicalHeight() - BorderAndPaddingLogicalHeight()).ToInt(); } @@ -110,14 +86,6 @@ int LayoutTextControl::TextBlockLogicalWidth() const { return unit_width.ToInt(); } -void LayoutTextControl::UpdateFromElement() { - Element* inner_editor = InnerEditorElement(); - if (inner_editor && inner_editor->GetLayoutObject()) - UpdateUserModifyProperty( - *GetTextControlElement(), - inner_editor->GetLayoutObject()->MutableStyleRef()); -} - int LayoutTextControl::ScrollbarThickness() const { // FIXME: We should get the size of the scrollbar from the LayoutTheme // instead. @@ -237,8 +205,7 @@ bool LayoutTextControl::HasValidAvgCharWidth(const SimpleFontData* font_data, if (!font_families_with_invalid_char_width_map) { font_families_with_invalid_char_width_map = new HashSet<AtomicString>; - for (size_t i = 0; i < WTF_ARRAY_LENGTH(kFontFamiliesWithInvalidCharWidth); - ++i) + for (size_t i = 0; i < arraysize(kFontFamiliesWithInvalidCharWidth); ++i) font_families_with_invalid_char_width_map->insert( AtomicString(kFontFamiliesWithInvalidCharWidth[i])); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control.h index 89c608180e9..8f42483d431 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control.h @@ -30,15 +30,13 @@ namespace blink { class TextControlElement; +class TextControlInnerEditorElement; class CORE_EXPORT LayoutTextControl : public LayoutBlockFlow { public: ~LayoutTextControl() override; TextControlElement* GetTextControlElement() const; - virtual scoped_refptr<ComputedStyle> CreateInnerEditorStyle( - const ComputedStyle& start_style) const = 0; - const char* GetName() const override { return "LayoutTextControl"; } protected: @@ -46,10 +44,9 @@ class CORE_EXPORT LayoutTextControl : public LayoutBlockFlow { // This convenience function should not be made public because // innerEditorElement may outlive the layout tree. - HTMLElement* InnerEditorElement() const; + TextControlInnerEditorElement* InnerEditorElement() const; int ScrollbarThickness() const; - void AdjustInnerEditorStyle(ComputedStyle& text_block_style) const; void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; @@ -70,7 +67,6 @@ class CORE_EXPORT LayoutTextControl : public LayoutBlockFlow { LayoutUnit line_height, LayoutUnit non_content_height) const = 0; - void UpdateFromElement() override; void ComputeLogicalHeight(LayoutUnit logical_height, LayoutUnit logical_top, LogicalExtentComputedValues&) const override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc index c068c735a98..1a71eb52b03 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.cc @@ -88,17 +88,6 @@ LayoutUnit LayoutTextControlMultiLine::BaselinePosition( line_position_mode); } -scoped_refptr<ComputedStyle> LayoutTextControlMultiLine::CreateInnerEditorStyle( - const ComputedStyle& start_style) const { - scoped_refptr<ComputedStyle> text_block_style = ComputedStyle::Create(); - text_block_style->InheritFrom(start_style); - AdjustInnerEditorStyle(*text_block_style); - text_block_style->SetDisplay(EDisplay::kBlock); - text_block_style->SetUnique(); - - return text_block_style; -} - LayoutObject* LayoutTextControlMultiLine::LayoutSpecialExcludedChild( bool relayout_children, SubtreeLayoutScope& layout_scope) { @@ -110,9 +99,6 @@ LayoutObject* LayoutTextControlMultiLine::LayoutSpecialExcludedChild( if (!placeholder_layout_object->IsBox()) return placeholder_layout_object; LayoutBox* placeholder_box = ToLayoutBox(placeholder_layout_object); - placeholder_box->MutableStyleRef().SetLogicalWidth(Length( - ContentLogicalWidth() - placeholder_box->BorderAndPaddingLogicalWidth(), - kFixed)); placeholder_box->LayoutIfNeeded(); placeholder_box->SetX(BorderLeft() + PaddingLeft()); placeholder_box->SetY(BorderTop() + PaddingTop()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h index 91f36806736..77a522cc75d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h @@ -60,8 +60,6 @@ class LayoutTextControlMultiLine final : public LayoutTextControl { return LayoutUnit(-1); } - scoped_refptr<ComputedStyle> CreateInnerEditorStyle( - const ComputedStyle& start_style) const override; LayoutObject* LayoutSpecialExcludedChild(bool relayout_children, SubtreeLayoutScope&) override; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc index e1616a9e9f1..6263ec7939b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc @@ -131,13 +131,11 @@ void LayoutTextControlSingleLine::UpdateLayout() { HTMLElement* placeholder_element = InputElement()->PlaceholderElement(); if (LayoutBox* placeholder_box = placeholder_element ? placeholder_element->GetLayoutBox() : nullptr) { - LayoutSize inner_editor_size; + LayoutUnit inner_editor_logical_width; if (inner_editor_layout_object) - inner_editor_size = inner_editor_layout_object->Size(); - placeholder_box->MutableStyleRef().SetWidth(Length( - inner_editor_size.Width() - placeholder_box->BorderAndPaddingWidth(), - kFixed)); + inner_editor_logical_width = inner_editor_layout_object->LogicalWidth(); + placeholder_box->SetOverrideLogicalWidth(inner_editor_logical_width); bool needed_layout = placeholder_box->NeedsLayout(); placeholder_box->LayoutIfNeeded(); LayoutPoint text_offset; @@ -200,16 +198,6 @@ bool LayoutTextControlSingleLine::NodeAtPoint( return true; } -void LayoutTextControlSingleLine::StyleDidChange( - StyleDifference diff, - const ComputedStyle* old_style) { - LayoutTextControl::StyleDidChange(diff, old_style); - if (HTMLElement* placeholder = InputElement()->PlaceholderElement()) - placeholder->SetInlineStyleProperty( - CSSPropertyTextOverflow, - TextShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip); -} - void LayoutTextControlSingleLine::CapsLockStateMayHaveChanged() { if (!GetNode()) return; @@ -304,66 +292,6 @@ LayoutUnit LayoutTextControlSingleLine::ComputeControlLogicalHeight( return line_height + non_content_height; } -scoped_refptr<ComputedStyle> -LayoutTextControlSingleLine::CreateInnerEditorStyle( - const ComputedStyle& start_style) const { - scoped_refptr<ComputedStyle> text_block_style = ComputedStyle::Create(); - text_block_style->InheritFrom(start_style); - AdjustInnerEditorStyle(*text_block_style); - - text_block_style->SetWhiteSpace(EWhiteSpace::kPre); - text_block_style->SetOverflowWrap(EOverflowWrap::kNormal); - text_block_style->SetTextOverflow(TextShouldBeTruncated() - ? ETextOverflow::kEllipsis - : ETextOverflow::kClip); - - int computed_line_height = - LineHeight(true, kHorizontalLine, kPositionOfInteriorLineBoxes).ToInt(); - // Do not allow line-height to be smaller than our default. - if (text_block_style->FontSize() >= computed_line_height) { - text_block_style->SetLineHeight( - ComputedStyleInitialValues::InitialLineHeight()); - } - - // We'd like to remove line-height if it's unnecessary because - // overflow:scroll clips editing text by line-height. - Length logical_height = start_style.LogicalHeight(); - // Here, we remove line-height if the INPUT fixed height is taller than the - // line-height. It's not the precise condition because logicalHeight - // includes border and padding if box-sizing:border-box, and there are cases - // in which we don't want to remove line-height with percent or calculated - // length. - // TODO(tkent): This should be done during layout. - if (logical_height.IsPercentOrCalc() || - (logical_height.IsFixed() && - logical_height.GetFloatValue() > computed_line_height)) { - text_block_style->SetLineHeight( - ComputedStyleInitialValues::InitialLineHeight()); - } - - text_block_style->SetDisplay(EDisplay::kBlock); - text_block_style->SetUnique(); - - if (InputElement()->ShouldRevealPassword()) - text_block_style->SetTextSecurity(ETextSecurity::kNone); - - text_block_style->SetOverflowX(EOverflow::kScroll); - // overflow-y:visible doesn't work because overflow-x:scroll makes a layer. - text_block_style->SetOverflowY(EOverflow::kScroll); - scoped_refptr<ComputedStyle> no_scrollbar_style = ComputedStyle::Create(); - no_scrollbar_style->SetStyleType(kPseudoIdScrollbar); - no_scrollbar_style->SetDisplay(EDisplay::kNone); - text_block_style->AddCachedPseudoStyle(no_scrollbar_style); - text_block_style->SetHasPseudoStyle(kPseudoIdScrollbar); - - return text_block_style; -} - -bool LayoutTextControlSingleLine::TextShouldBeTruncated() const { - return GetDocument().FocusedElement() != GetNode() && - StyleRef().TextOverflow() == ETextOverflow::kEllipsis; -} - void LayoutTextControlSingleLine::Autoscroll(const IntPoint& position) { LayoutBox* layout_object = InnerEditorElement()->GetLayoutBox(); if (!layout_object) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h index f30518fcffd..c849f746f54 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h @@ -35,9 +35,6 @@ class LayoutTextControlSingleLine : public LayoutTextControl { public: LayoutTextControlSingleLine(HTMLInputElement*); ~LayoutTextControlSingleLine() override; - // FIXME: Move createInnerEditorStyle() to TextControlInnerEditorElement. - scoped_refptr<ComputedStyle> CreateInnerEditorStyle( - const ComputedStyle& start_style) const final; void CapsLockStateMayHaveChanged(); @@ -77,12 +74,10 @@ class LayoutTextControlSingleLine : public LayoutTextControl { LayoutUnit ComputeControlLogicalHeight( LayoutUnit line_height, LayoutUnit non_content_height) const override; - void StyleDidChange(StyleDifference, const ComputedStyle* old_style) final; void AddOverflowFromChildren() final; bool AllowsOverflowClip() const override { return false; } - bool TextShouldBeTruncated() const; HTMLElement* InnerSpinButtonElement() const; bool should_draw_caps_lock_indicator_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.cc index 26293a503fc..41153d33ddc 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.cc @@ -183,10 +183,10 @@ Position LayoutTextFragment::PositionForCaretOffset(unsigned offset) const { return Position(node, Start() + offset); } -Optional<unsigned> LayoutTextFragment::CaretOffsetForPosition( +base::Optional<unsigned> LayoutTextFragment::CaretOffsetForPosition( const Position& position) const { if (position.IsNull() || position.AnchorNode() != AssociatedTextNode()) - return WTF::nullopt; + return base::nullopt; unsigned dom_offset; if (position.IsBeforeAnchor()) { dom_offset = 0; @@ -199,7 +199,7 @@ Optional<unsigned> LayoutTextFragment::CaretOffsetForPosition( dom_offset = position.OffsetInContainerNode(); } if (dom_offset < Start() || dom_offset > Start() + FragmentLength()) - return WTF::nullopt; + return base::nullopt; return dom_offset - Start(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.h b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.h index b744e13da9c..3782d3d41f6 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment.h @@ -52,7 +52,8 @@ class CORE_EXPORT LayoutTextFragment final : public LayoutText { bool IsTextFragment() const override { return true; } Position PositionForCaretOffset(unsigned) const override; - Optional<unsigned> CaretOffsetForPosition(const Position&) const override; + base::Optional<unsigned> CaretOffsetForPosition( + const Position&) const override; unsigned Start() const { return start_; } unsigned FragmentLength() const { return fragment_length_; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc index 3cd39c3da89..255966a250f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc @@ -6,6 +6,9 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/editing/frame_selection.h" +#include "third_party/blink/renderer/core/editing/selection_template.h" +#include "third_party/blink/renderer/core/editing/testing/selection_sample.h" #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -33,6 +36,35 @@ class LayoutTextTest : public RenderingTest { } LayoutText* GetBasicText() { return GetLayoutTextById("target"); } + + void SetSelectionAndUpdateLayoutSelection(const std::string& selection_text) { + const SelectionInDOMTree selection = + SelectionSample::SetSelectionText(GetDocument().body(), selection_text); + UpdateAllLifecyclePhases(); + Selection().SetSelectionAndEndTyping(selection); + Selection().CommitAppearanceIfNeeded(); + } + + const LayoutText* FindFirstLayoutText() { + for (const Node& node : + NodeTraversal::DescendantsOf(*GetDocument().body())) { + if (node.GetLayoutObject() && node.GetLayoutObject()->IsText()) + return ToLayoutTextOrDie(node.GetLayoutObject()); + } + NOTREACHED(); + return nullptr; + } + + LayoutRect GetSelectionRectFor(const std::string& selection_text) { + std::stringstream stream; + stream << "<div style='font: 10px/10px Ahem;'>" << selection_text + << "</div>"; + SetSelectionAndUpdateLayoutSelection(stream.str()); + const Node* target = GetDocument().getElementById("target"); + const LayoutObject* layout_object = + target ? target->GetLayoutObject() : FindFirstLayoutText(); + return layout_object->LocalSelectionRect(); + } }; const char kTacoText[] = "Los Compadres Taco Truck"; @@ -447,7 +479,7 @@ TEST_P(ParameterizedLayoutTextTest, GetUpperLeftCorner) { <div>12345 123<span id="target">45</span></div> )HTML"); LayoutText* layout_text = GetLayoutTextById("target"); - Optional<FloatPoint> upper_left = layout_text->GetUpperLeftCorner(); + base::Optional<FloatPoint> upper_left = layout_text->GetUpperLeftCorner(); EXPECT_TRUE(upper_left.has_value()); EXPECT_EQ(FloatPoint(30, 10), upper_left.value()); } @@ -465,7 +497,7 @@ TEST_P(ParameterizedLayoutTextTest, GetUpperLeftCornerVLR) { <div>12345 123<span id="target">45</span></div> )HTML"); LayoutText* layout_text = GetLayoutTextById("target"); - Optional<FloatPoint> upper_left = layout_text->GetUpperLeftCorner(); + base::Optional<FloatPoint> upper_left = layout_text->GetUpperLeftCorner(); EXPECT_TRUE(upper_left.has_value()); EXPECT_EQ(FloatPoint(10, 30), upper_left.value()); } @@ -579,4 +611,32 @@ TEST_P(ParameterizedLayoutTextTest, WordBreakElement) { EXPECT_EQ(0, layout_wbr->CaretMaxOffset()); } +TEST_P(ParameterizedLayoutTextTest, LocalSelectionRect) { + LoadAhem(); + // TODO(yoichio): Fix LayoutNG incompatibility. + EXPECT_EQ(LayoutRect(10, 0, 50, 10), GetSelectionRectFor("f^oo ba|r")); + EXPECT_EQ(LayoutRect(0, 0, 40, 20), + GetSelectionRectFor("<div style='width: 2em'>f^oo ba|r</div>")); + EXPECT_EQ( + LayoutNGEnabled() ? LayoutRect(0, 0, 0, 0) : LayoutRect(30, 0, 10, 10), + GetSelectionRectFor("foo^<br id='target'>|bar")); + EXPECT_EQ( + LayoutNGEnabled() ? LayoutRect(10, 0, 30, 10) : LayoutRect(10, 0, 20, 10), + GetSelectionRectFor("f^oo<br>b|ar")); + EXPECT_EQ(LayoutRect(10, 0, 30, 10), + GetSelectionRectFor("<div>f^oo</div><div>b|ar</div>")); + EXPECT_EQ(LayoutRect(30, 0, 10, 10), GetSelectionRectFor("foo^ |bar")); + EXPECT_EQ(LayoutRect(0, 0, 0, 0), GetSelectionRectFor("^ |foo")); + EXPECT_EQ(LayoutRect(0, 0, 0, 0), + GetSelectionRectFor("fo^o<wbr id='target'>ba|r")); + EXPECT_EQ( + LayoutRect(0, 0, 10, 10), + GetSelectionRectFor("<style>:first-letter { float: right}</style>^fo|o")); + // Since we don't paint trimed white spaces on LayoutNG, we don't need fix + // this case. + EXPECT_EQ( + LayoutNGEnabled() ? LayoutRect(0, 0, 0, 0) : LayoutRect(30, 0, 10, 10), + GetSelectionRectFor("foo^ |")); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme.cc b/chromium/third_party/blink/renderer/core/layout/layout_theme.cc index a5a2a3b0a8b..fe2ee59d0ec 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme.cc @@ -22,7 +22,6 @@ #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/public/platform/web_fallback_theme_engine.h" #include "third_party/blink/public/platform/web_rect.h" #include "third_party/blink/public/web/blink.h" #include "third_party/blink/renderer/core/css_value_keywords.h" @@ -47,16 +46,18 @@ #include "third_party/blink/renderer/core/layout/layout_theme_mobile.h" #include "third_party/blink/renderer/core/page/focus_controller.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/paint/fallback_theme.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/file_metadata.h" #include "third_party/blink/renderer/platform/fonts/font_selector.h" +#include "third_party/blink/renderer/platform/fonts/string_truncator.h" #include "third_party/blink/renderer/platform/graphics/touch_action.h" #include "third_party/blink/renderer/platform/layout_test_support.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/text/platform_locale.h" -#include "third_party/blink/renderer/platform/text/string_truncator.h" #include "third_party/blink/renderer/platform/theme.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "ui/native_theme/native_theme.h" // The methods in this file are shared by all themes on every platform. @@ -883,8 +884,9 @@ void LayoutTheme::AdjustCheckboxStyleUsingFallbackTheme( if (!style.Width().IsIntrinsicOrAuto() && !style.Height().IsAuto()) return; - IntSize size = Platform::Current()->FallbackThemeEngine()->GetSize( - WebFallbackThemeEngine::kPartCheckbox); + IntSize size(GetFallbackTheme().GetPartSize(ui::NativeTheme::kCheckbox, + ui::NativeTheme::kNormal, + ui::NativeTheme::ExtraParams())); float zoom_level = style.EffectiveZoom(); size.SetWidth(size.Width() * zoom_level); size.SetHeight(size.Height() * zoom_level); @@ -906,8 +908,9 @@ void LayoutTheme::AdjustRadioStyleUsingFallbackTheme( if (!style.Width().IsIntrinsicOrAuto() && !style.Height().IsAuto()) return; - IntSize size = Platform::Current()->FallbackThemeEngine()->GetSize( - WebFallbackThemeEngine::kPartRadio); + IntSize size(GetFallbackTheme().GetPartSize(ui::NativeTheme::kRadio, + ui::NativeTheme::kNormal, + ui::NativeTheme::ExtraParams())); float zoom_level = style.EffectiveZoom(); size.SetWidth(size.Width() * zoom_level); size.SetHeight(size.Height() * zoom_level); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme.h b/chromium/third_party/blink/renderer/core/layout/layout_theme.h index 59baded35ab..0a7a6a6035c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme.h @@ -36,6 +36,7 @@ namespace blink { +class ChromeClient; class ComputedStyle; class Element; class FileList; @@ -45,7 +46,6 @@ class HTMLInputElement; class LengthSize; class Locale; class Node; -class PlatformChromeClient; class Theme; class ThemePainter; @@ -190,7 +190,7 @@ class CORE_EXPORT LayoutTheme : public RefCounted<LayoutTheme> { virtual int PopupInternalPaddingStart(const ComputedStyle&) const { return 0; } - virtual int PopupInternalPaddingEnd(const PlatformChromeClient*, + virtual int PopupInternalPaddingEnd(const ChromeClient*, const ComputedStyle&) const { return 0; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc index 4b9acafd7f7..e00e520626e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc @@ -28,11 +28,11 @@ #include "third_party/blink/public/platform/web_theme_engine.h" #include "third_party/blink/renderer/core/css_value_keywords.h" #include "third_party/blink/renderer/core/layout/layout_theme_font_provider.h" +#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/data_resource_helper.h" #include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/layout_test_support.h" -#include "third_party/blink/renderer/platform/platform_chrome_client.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" namespace blink { @@ -317,7 +317,7 @@ int LayoutThemeDefault::PopupInternalPaddingStart( } int LayoutThemeDefault::PopupInternalPaddingEnd( - const PlatformChromeClient* client, + const ChromeClient* client, const ComputedStyle& style) const { if (style.Appearance() == kNoControlPart) return 0; @@ -344,7 +344,7 @@ int LayoutThemeDefault::MenuListArrowWidthInDIP() const { } float LayoutThemeDefault::ClampedMenuListArrowPaddingSize( - const PlatformChromeClient* client, + const ChromeClient* client, const ComputedStyle& style) const { if (cached_menu_list_arrow_padding_size_ > 0 && style.EffectiveZoom() == cached_menu_list_arrow_zoom_level_) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h index 1d9b1fb38b2..ba106da1757 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h @@ -112,7 +112,7 @@ class CORE_EXPORT LayoutThemeDefault : public LayoutTheme { // These methods define the padding for the MenuList's inner block. int PopupInternalPaddingStart(const ComputedStyle&) const override; - int PopupInternalPaddingEnd(const PlatformChromeClient*, + int PopupInternalPaddingEnd(const ChromeClient*, const ComputedStyle&) const override; int PopupInternalPaddingTop(const ComputedStyle&) const override; int PopupInternalPaddingBottom(const ComputedStyle&) const override; @@ -121,7 +121,7 @@ class CORE_EXPORT LayoutThemeDefault : public LayoutTheme { // thickness, which is 3px or 4px, and we use the value from the default Aura // theme. int MenuListArrowWidthInDIP() const; - float ClampedMenuListArrowPaddingSize(const PlatformChromeClient*, + float ClampedMenuListArrowPaddingSize(const ChromeClient*, const ComputedStyle&) const; static void SetSelectionColors(unsigned active_background_color, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h index 986809fa106..f0ffd0fb0da 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h @@ -69,9 +69,6 @@ class LayoutThemeMac final : public LayoutTheme { float& font_size, AtomicString& font_family) const override; - bool NeedsHackForTextControlWithFontFamily( - const AtomicString& family) const override; - int MinimumMenuListSize(const ComputedStyle&) const override; void AdjustSliderThumbSize(ComputedStyle&) const override; @@ -80,7 +77,7 @@ class LayoutThemeMac final : public LayoutTheme { int SliderTickOffsetFromTrackCenter() const override; int PopupInternalPaddingStart(const ComputedStyle&) const override; - int PopupInternalPaddingEnd(const PlatformChromeClient*, + int PopupInternalPaddingEnd(const ChromeClient*, const ComputedStyle&) const override; int PopupInternalPaddingTop(const ComputedStyle&) const override; int PopupInternalPaddingBottom(const ComputedStyle&) const override; @@ -97,7 +94,7 @@ class LayoutThemeMac final : public LayoutTheme { bool SupportsSelectionForegroundColors() const override { return false; } - virtual bool IsModalColorChooser() const { return false; } + bool IsModalColorChooser() const override { return false; } protected: LayoutThemeMac(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm index 79f3c20dbe6..d2d26f07ab9 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm @@ -31,6 +31,7 @@ #import "third_party/blink/renderer/core/layout/layout_view.h" #import "third_party/blink/renderer/core/style/shadow_list.h" #import "third_party/blink/renderer/platform/data_resource_helper.h" +#import "third_party/blink/renderer/platform/fonts/string_truncator.h" #import "third_party/blink/renderer/platform/graphics/bitmap_image.h" #import "third_party/blink/renderer/platform/layout_test_support.h" #import "third_party/blink/renderer/platform/mac/color_mac.h" @@ -39,7 +40,6 @@ #import "third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h" #import "third_party/blink/renderer/platform/runtime_enabled_features.h" #import "third_party/blink/renderer/platform/text/platform_locale.h" -#import "third_party/blink/renderer/platform/text/string_truncator.h" #import "third_party/blink/renderer/platform/theme.h" // The methods in this file are specific to the Mac OS X platform. @@ -265,13 +265,6 @@ void LayoutThemeMac::SystemFont(CSSValueID system_font_id, font_family = FontFamilyNames::system_ui; } -bool LayoutThemeMac::NeedsHackForTextControlWithFontFamily( - const AtomicString& family) const { - // This hack is only applied on OSX 10.9. - // https://code.google.com/p/chromium/issues/detail?id=515989#c8 - return IsOS10_9() && family == FontFamilyNames::system_ui; -} - static RGBA32 ConvertNSColorToColor(NSColor* color) { NSColor* color_in_color_space = ColorInColorSpace(color); if (color_in_color_space) { @@ -494,10 +487,6 @@ bool LayoutThemeMac::IsControlStyled(const ComputedStyle& style) const { return true; if (!style.Height().IsIntrinsicOrAuto()) return true; - // NSPopUpButtonCell on macOS 10.9 doesn't support - // NSUserInterfaceLayoutDirectionRightToLeft. - if (IsOS10_9() && style.Direction() == TextDirection::kRtl) - return true; } // Some other cells don't work well when scaled. if (style.EffectiveZoom() != 1) { @@ -770,7 +759,7 @@ int LayoutThemeMac::PopupInternalPaddingStart( return 0; } -int LayoutThemeMac::PopupInternalPaddingEnd(const PlatformChromeClient*, +int LayoutThemeMac::PopupInternalPaddingEnd(const ChromeClient*, const ComputedStyle& style) const { if (style.Appearance() == kMenulistPart) return PopupButtonPadding( @@ -981,7 +970,7 @@ NSSearchFieldCell* LayoutThemeMac::Search() const { // this is achieved by calling |setCenteredLook| with NO. In OS10.11 and // later, instead call |setPlaceholderString| with an empty string. // See https://crbug.com/752362. - if (IsOS10_9() || IsOS10_10()) { + if (IsOS10_10()) { SEL sel = @selector(setCenteredLook:); if ([search_.Get() respondsToSelector:sel]) { BOOL bool_value = NO; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc index 893292f5a68..f7d8c402de2 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc @@ -48,6 +48,7 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h" @@ -67,7 +68,8 @@ namespace blink { using namespace HTMLNames; -static void PrintBorderStyle(TextStream& ts, const EBorderStyle border_style) { +static void PrintBorderStyle(WTF::TextStream& ts, + const EBorderStyle border_style) { switch (border_style) { case EBorderStyle::kNone: ts << "none"; @@ -141,11 +143,11 @@ String QuoteAndEscapeNonPrintables(const String& s) { return result.ToString(); } -TextStream& operator<<(TextStream& ts, const Color& c) { +WTF::TextStream& operator<<(WTF::TextStream& ts, const Color& c) { return ts << c.NameForLayoutTreeAsText(); } -void LayoutTreeAsText::WriteLayoutObject(TextStream& ts, +void LayoutTreeAsText::WriteLayoutObject(WTF::TextStream& ts, const LayoutObject& o, LayoutAsTextBehavior behavior) { ts << o.DecoratedName(); @@ -363,7 +365,9 @@ void LayoutTreeAsText::WriteLayoutObject(TextStream& ts, } } -static void WriteInlineBox(TextStream& ts, const InlineBox& box, int indent) { +static void WriteInlineBox(WTF::TextStream& ts, + const InlineBox& box, + int indent) { WriteIndent(ts, indent); ts << "+ "; ts << box.BoxName() << " {" << box.GetLineLayoutItem().DebugName() << "}" @@ -373,7 +377,7 @@ static void WriteInlineBox(TextStream& ts, const InlineBox& box, int indent) { << box.BaselinePosition(kIdeographicBaseline); } -static void WriteInlineTextBox(TextStream& ts, +static void WriteInlineTextBox(WTF::TextStream& ts, const InlineTextBox& text_box, int indent) { WriteInlineBox(ts, text_box, indent); @@ -386,7 +390,7 @@ static void WriteInlineTextBox(TextStream& ts, << " \"" << value << "\""; } -static void WriteInlineFlowBox(TextStream& ts, +static void WriteInlineFlowBox(WTF::TextStream& ts, const InlineFlowBox& root_box, int indent) { WriteInlineBox(ts, root_box, indent); @@ -407,7 +411,7 @@ static void WriteInlineFlowBox(TextStream& ts, } } -void LayoutTreeAsText::WriteLineBoxTree(TextStream& ts, +void LayoutTreeAsText::WriteLineBoxTree(WTF::TextStream& ts, const LayoutBlockFlow& o, int indent) { for (const InlineFlowBox* root_box : o.LineBoxes()) { @@ -415,7 +419,7 @@ void LayoutTreeAsText::WriteLineBoxTree(TextStream& ts, } } -static void WriteTextRun(TextStream& ts, +static void WriteTextRun(WTF::TextStream& ts, const LayoutText& o, const InlineTextBox& run) { // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, @@ -444,7 +448,7 @@ static void WriteTextRun(TextStream& ts, ts << "\n"; } -static void WriteTextFragment(TextStream& ts, +static void WriteTextFragment(WTF::TextStream& ts, const NGPhysicalFragment& physical_fragment, NGPhysicalOffset offset_to_container_box) { if (!physical_fragment.IsText()) @@ -464,7 +468,7 @@ static void WriteTextFragment(TextStream& ts, ts << "\n"; } -static void WritePaintProperties(TextStream& ts, +static void WritePaintProperties(WTF::TextStream& ts, const LayoutObject& o, int indent) { bool has_fragments = o.FirstFragment().NextFragment(); @@ -490,7 +494,7 @@ static void WritePaintProperties(TextStream& ts, } } -void Write(TextStream& ts, +void Write(WTF::TextStream& ts, const LayoutObject& o, int indent, LayoutAsTextBehavior behavior) { @@ -585,7 +589,7 @@ enum LayerPaintPhase { kLayerPaintPhaseForeground = 1 }; -static void Write(TextStream& ts, +static void Write(WTF::TextStream& ts, PaintLayer& layer, const LayoutRect& layer_bounds, const LayoutRect& background_clip_rect, @@ -595,24 +599,10 @@ static void Write(TextStream& ts, LayoutAsTextBehavior behavior = kLayoutAsTextBehaviorNormal, const PaintLayer* marked_layer = nullptr) { IntRect adjusted_layout_bounds = PixelSnappedIntRect(layer_bounds); - IntRect adjusted_layout_bounds_with_scrollbars = adjusted_layout_bounds; IntRect adjusted_background_clip_rect = PixelSnappedIntRect(background_clip_rect); IntRect adjusted_clip_rect = PixelSnappedIntRect(clip_rect); - bool report_frame_scroll_info = - layer.GetLayoutObject().IsLayoutView() && - !RuntimeEnabledFeatures::RootLayerScrollingEnabled(); - - if (report_frame_scroll_info) { - LayoutView& layout_view = ToLayoutView(layer.GetLayoutObject()); - - adjusted_layout_bounds_with_scrollbars.SetWidth( - layout_view.ViewWidth(kIncludeScrollbars)); - adjusted_layout_bounds_with_scrollbars.SetHeight( - layout_view.ViewHeight(kIncludeScrollbars)); - } - if (marked_layer) ts << (marked_layer == &layer ? "*" : " "); @@ -626,24 +616,19 @@ static void Write(TextStream& ts, if (behavior & kLayoutAsTextShowAddresses) ts << static_cast<const void*>(&layer) << " "; - ts << adjusted_layout_bounds_with_scrollbars; + ts << adjusted_layout_bounds; if (!adjusted_layout_bounds.IsEmpty()) { if (!adjusted_background_clip_rect.Contains(adjusted_layout_bounds)) ts << " backgroundClip " << adjusted_background_clip_rect; - if (!adjusted_clip_rect.Contains(adjusted_layout_bounds_with_scrollbars)) + if (!adjusted_clip_rect.Contains(adjusted_layout_bounds)) ts << " clip " << adjusted_clip_rect; } if (layer.IsTransparent()) ts << " transparent"; - if (layer.GetLayoutObject().HasOverflowClip() || report_frame_scroll_info) { - ScrollableArea* scrollable_area; - if (report_frame_scroll_info) - scrollable_area = ToLayoutView(layer.GetLayoutObject()).GetFrameView(); - else - scrollable_area = layer.GetScrollableArea(); - + if (layer.GetLayoutObject().HasOverflowClip()) { + ScrollableArea* scrollable_area = layer.GetScrollableArea(); ScrollOffset adjusted_scroll_offset = scrollable_area->GetScrollOffset() + ToFloatSize(scrollable_area->ScrollOrigin()); @@ -669,8 +654,9 @@ static void Write(TextStream& ts, if (layer.GetLayoutObject().Style()->HasBlendMode()) { ts << " blendMode: " - << CompositeOperatorName(kCompositeSourceOver, - layer.GetLayoutObject().Style()->BlendMode()); + << CompositeOperatorName( + kCompositeSourceOver, + layer.GetLayoutObject().Style()->GetBlendMode()); } if (behavior & kLayoutAsTextShowCompositedLayers) { @@ -703,7 +689,7 @@ static Vector<PaintLayerStackingNode*> NormalFlowListFor( return vector; } -void LayoutTreeAsText::WriteLayers(TextStream& ts, +void LayoutTreeAsText::WriteLayers(WTF::TextStream& ts, const PaintLayer* root_layer, PaintLayer* layer, const LayoutRect& paint_rect, @@ -829,7 +815,7 @@ static String NodePosition(Node* node) { return result.ToString(); } -static void WriteSelection(TextStream& ts, const LayoutObject* o) { +static void WriteSelection(WTF::TextStream& ts, const LayoutObject* o) { Node* n = o->GetNode(); if (!n || !n->IsDocumentNode()) return; @@ -859,7 +845,7 @@ static void WriteSelection(TextStream& ts, const LayoutObject* o) { static String ExternalRepresentation(LayoutBox* layout_object, LayoutAsTextBehavior behavior, const PaintLayer* marked_layer = nullptr) { - TextStream ts; + WTF::TextStream ts; if (!layout_object->HasLayer()) return ts.Release(); @@ -917,7 +903,7 @@ String ExternalRepresentation(Element* element, LayoutAsTextBehavior behavior) { behavior | kLayoutAsTextShowAllLayers); } -static void WriteCounterValuesFromChildren(TextStream& stream, +static void WriteCounterValuesFromChildren(WTF::TextStream& stream, LayoutObject* parent, bool& is_first_counter) { for (LayoutObject* child = parent->SlowFirstChild(); child; @@ -934,7 +920,7 @@ static void WriteCounterValuesFromChildren(TextStream& stream, String CounterValueForElement(Element* element) { element->GetDocument().UpdateStyleAndLayout(); - TextStream stream; + WTF::TextStream stream; bool is_first_counter = true; // The counter layoutObjects should be children of :before or :after // pseudo-elements. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.h b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.h index 601a9dd2e21..2c6191a187d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.h @@ -27,9 +27,9 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_TREE_AS_TEXT_H_ #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/text/text_stream.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/text/text_stream.h" namespace blink { @@ -40,7 +40,6 @@ class LayoutRect; class LocalFrame; class LayoutBlockFlow; class LayoutObject; -class TextStream; enum LayoutAsTextBehaviorFlags { kLayoutAsTextBehaviorNormal = 0, @@ -77,7 +76,7 @@ ExternalRepresentation(LocalFrame*, CORE_EXPORT String ExternalRepresentation(Element*, LayoutAsTextBehavior = kLayoutAsTextBehaviorNormal); -void Write(TextStream&, +void Write(WTF::TextStream&, const LayoutObject&, int indent = 0, LayoutAsTextBehavior = kLayoutAsTextBehaviorNormal); @@ -89,17 +88,17 @@ class LayoutTreeAsText { // (This just involves rebaselining many results though, so for now it's // not being done). public: - static void WriteLayoutObject(TextStream&, + static void WriteLayoutObject(WTF::TextStream&, const LayoutObject&, LayoutAsTextBehavior); - static void WriteLayers(TextStream&, + static void WriteLayers(WTF::TextStream&, const PaintLayer* root_layer, PaintLayer*, const LayoutRect& paint_dirty_rect, int indent = 0, LayoutAsTextBehavior = kLayoutAsTextBehaviorNormal, const PaintLayer* marked_layer = nullptr); - static void WriteLineBoxTree(TextStream&, + static void WriteLineBoxTree(WTF::TextStream&, const LayoutBlockFlow&, int indent = 0); }; @@ -111,7 +110,7 @@ CORE_EXPORT String CounterValueForElement(Element*); CORE_EXPORT String MarkerTextForListItem(Element*); -TextStream& operator<<(TextStream&, const Color&); +WTF::TextStream& operator<<(WTF::TextStream&, const Color&); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_video.cc b/chromium/third_party/blink/renderer/core/layout/layout_video.cc index db9b951b1d6..b66e83313a3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_video.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_video.cc @@ -25,7 +25,6 @@ #include "third_party/blink/renderer/core/layout/layout_video.h" -#include "third_party/blink/public/platform/web_layer.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html_names.h" @@ -185,7 +184,7 @@ LayoutRect LayoutVideo::ReplacedContentRect() const { } bool LayoutVideo::SupportsAcceleratedRendering() const { - return !!MediaElement()->PlatformLayer(); + return !!MediaElement()->CcLayer(); } static const LayoutBlock* LayoutObjectPlaceholder( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_view.cc b/chromium/third_party/blink/renderer/core/layout/layout_view.cc index 1ce91caf717..bc3bf00be63 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_view.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_view.cc @@ -45,12 +45,12 @@ #include "third_party/blink/renderer/core/paint/view_painter.h" #include "third_party/blink/renderer/core/svg/svg_document_extensions.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { @@ -244,8 +244,7 @@ void LayoutView::SetShouldDoFullPaintInvalidationOnResizeIfNeeded( // should fully invalidate on viewport resize if the background image is not // composited and needs full paint invalidation on background positioning area // resize. - if (Style()->HasFixedBackgroundImage() && - (!compositor_ || !compositor_->NeedsFixedRootBackgroundLayer())) { + if (Style()->HasFixedBackgroundImage()) { if ((width_changed && MustInvalidateFillLayersPaintOnWidthChange( Style()->BackgroundLayers())) || (height_changed && MustInvalidateFillLayersPaintOnHeightChange( @@ -294,14 +293,10 @@ void LayoutView::UpdateLayout() { if (!GetDocument().Paginated()) SetPageLogicalHeight(LayoutUnit()); - // TODO(wangxianzhu): Move this into ViewPaintInvalidator when - // rootLayerScrolling is permanently enabled. - IncludeScrollbarsInRect include_scrollbars = - RuntimeEnabledFeatures::RootLayerScrollingEnabled() ? kIncludeScrollbars - : kExcludeScrollbars; + // TODO(wangxianzhu): Move this into ViewPaintInvalidator. SetShouldDoFullPaintInvalidationOnResizeIfNeeded( - OffsetWidth() != GetLayoutSize(include_scrollbars).Width(), - OffsetHeight() != GetLayoutSize(include_scrollbars).Height()); + OffsetWidth() != GetLayoutSize(kIncludeScrollbars).Width(), + OffsetHeight() != GetLayoutSize(kIncludeScrollbars).Height()); if (PageLogicalHeight() && ShouldUsePrintingLayout()) { min_preferred_logical_width_ = max_preferred_logical_width_ = @@ -327,22 +322,7 @@ void LayoutView::UpdateLayout() { ClearNeedsLayout(); } -LayoutRect LayoutView::VisualOverflowRect() const { - // In root layer scrolling mode, the LayoutView performs overflow clipping - // like a regular scrollable div. - if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) - return LayoutBlockFlow::VisualOverflowRect(); - - // In normal compositing mode, LayoutView doesn't actually apply clipping - // on its descendants. Instead their visual overflow is propagated to - // compositor()->m_rootContentLayer for accelerated scrolling. - return LayoutOverflowRect(); -} - LayoutRect LayoutView::LocalVisualRectIgnoringVisibility() const { - // TODO(wangxianzhu): This is only required without rootLayerScrolls (though - // it is also correct but unnecessary with rootLayerScrolls) because of the - // special LayoutView overflow model. LayoutRect rect = VisualOverflowRect(); rect.Unite(LayoutRect(rect.Location(), ViewRect().Size())); return rect; @@ -492,17 +472,18 @@ bool LayoutView::MapToVisualRectInAncestorSpace( LayoutRect& rect, MapCoordinatesFlags mode, VisualRectFlags visual_rect_flags) const { - if (MapToVisualRectInAncestorSpaceInternalFastPath(ancestor, rect, - visual_rect_flags)) - return !rect.IsEmpty(); + bool intersects = true; + if (MapToVisualRectInAncestorSpaceInternalFastPath( + ancestor, rect, visual_rect_flags, intersects)) + return intersects; TransformState transform_state(TransformState::kApplyTransformDirection, FloatQuad(FloatRect(rect))); - bool retval = MapToVisualRectInAncestorSpaceInternal( - ancestor, transform_state, mode, visual_rect_flags); + intersects = MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state, + mode, visual_rect_flags); transform_state.Flatten(); rect = LayoutRect(transform_state.LastPlanarQuad().BoundingBox()); - return retval; + return intersects; } bool LayoutView::MapToVisualRectInAncestorSpaceInternal( @@ -749,10 +730,6 @@ IntRect LayoutView::DocumentRect() const { return PixelSnappedIntRect(overflow_rect); } -bool LayoutView::RootBackgroundIsEntirelyFixed() const { - return Style()->HasEntirelyFixedBackground(); -} - IntSize LayoutView::GetLayoutSize( IncludeScrollbarsInRect scrollbar_inclusion) const { if (ShouldUsePrintingLayout()) @@ -780,18 +757,6 @@ int LayoutView::ViewLogicalHeight( : ViewWidth(scrollbar_inclusion); } -int LayoutView::ViewLogicalWidthForBoxSizing() const { - return ViewLogicalWidth(RuntimeEnabledFeatures::RootLayerScrollingEnabled() - ? kIncludeScrollbars - : kExcludeScrollbars); -} - -int LayoutView::ViewLogicalHeightForBoxSizing() const { - return ViewLogicalHeight(RuntimeEnabledFeatures::RootLayerScrollingEnabled() - ? kIncludeScrollbars - : kExcludeScrollbars); -} - LayoutUnit LayoutView::ViewLogicalHeightForPercentages() const { if (ShouldUsePrintingLayout()) return PageLogicalHeight(); @@ -892,24 +857,6 @@ void LayoutView::UpdateFromStyle() { SetHasBoxDecorationBackground(true); } -bool LayoutView::AllowsOverflowClip() const { - return RuntimeEnabledFeatures::RootLayerScrollingEnabled(); -} - -ScrollResult LayoutView::Scroll(ScrollGranularity granularity, - const FloatSize& delta) { - // TODO(bokan): We shouldn't need this specialization but we currently do - // because of the Windows pan scrolling path. That should go through a more - // normalized ScrollManager-like scrolling path and we should get rid of - // of this override. All frame scrolling should be handled by - // ViewportScrollCallback. - - if (!GetFrameView()) - return ScrollResult(false, false, delta.Width(), delta.Height()); - - return GetFrameView()->GetScrollableArea()->UserScroll(granularity, delta); -} - LayoutRect LayoutView::DebugRect() const { LayoutRect rect; LayoutBlock* block = ContainingBlock(); @@ -935,16 +882,6 @@ bool LayoutView::UpdateLogicalWidthAndColumnWidth() { return relayout_children || ShouldUsePrintingLayout(); } -bool LayoutView::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - // Frame scroll corner is painted using LayoutView as the display item client. - if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled() && - (GetFrameView()->HorizontalScrollbar() || - GetFrameView()->VerticalScrollbar())) - return false; - - return LayoutBlockFlow::PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); -} - void LayoutView::UpdateCounters() { if (!needs_counter_update_) return; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_view.h b/chromium/third_party/blink/renderer/core/layout/layout_view.h index e6bbcdc0460..867d64060c1 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_view.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_view.h @@ -184,10 +184,6 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { IntRect DocumentRect() const; - // LayoutObject that paints the root background has background-images which - // all have background-attachment: fixed. - bool RootBackgroundIsEntirelyFixed() const; - IntervalArena* GetIntervalArena(); void SetLayoutQuoteHead(LayoutQuote* head) { layout_quote_head_ = head; } @@ -224,7 +220,6 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { layout_state_ = layout_state_->Next(); } - LayoutRect VisualOverflowRect() const override; LayoutRect LocalVisualRectIgnoringVisibility() const override; // Invalidates paint for the entire view, including composited descendants, @@ -243,13 +238,9 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { return false; } - // The rootLayerScrolls setting will ultimately determine whether - // LocalFrameView or PaintLayerScrollableArea handle the scroll. - ScrollResult Scroll(ScrollGranularity, const FloatSize&) override; - LayoutRect DebugRect() const override; - virtual IntSize ScrolledContentOffset() const; + IntSize ScrolledContentOffset() const override; private: void MapLocalToAncestor( @@ -275,17 +266,18 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { #endif void UpdateFromStyle() override; - bool AllowsOverflowClip() const override; bool ShouldUsePrintingLayout() const; - int ViewLogicalWidthForBoxSizing() const; - int ViewLogicalHeightForBoxSizing() const; + int ViewLogicalWidthForBoxSizing() const { + return ViewLogicalWidth(kIncludeScrollbars); + } + int ViewLogicalHeightForBoxSizing() const { + return ViewLogicalHeight(kIncludeScrollbars); + } bool UpdateLogicalWidthAndColumnWidth() override; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; - UntracedMember<LocalFrameView> frame_view_; // The page logical height. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_word_break.cc b/chromium/third_party/blink/renderer/core/layout/layout_word_break.cc index a52a3672888..71af9f4e0fb 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_word_break.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_word_break.cc @@ -47,10 +47,10 @@ Position LayoutWordBreak::PositionForCaretOffset(unsigned offset) const { return Position::BeforeNode(*GetNode()); } -Optional<unsigned> LayoutWordBreak::CaretOffsetForPosition( +base::Optional<unsigned> LayoutWordBreak::CaretOffsetForPosition( const Position& position) const { if (position.IsNull() || position.AnchorNode() != GetNode()) - return WTF::nullopt; + return base::nullopt; DCHECK(position.IsBeforeAnchor() || position.IsAfterAnchor()); // The only allowed caret offset is 0, since LayoutWordBreak always has // |TextLength() == 0|. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_word_break.h b/chromium/third_party/blink/renderer/core/layout/layout_word_break.h index a065ef0fd45..35d79ff71ca 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_word_break.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_word_break.h @@ -38,7 +38,7 @@ class LayoutWordBreak final : public LayoutText { explicit LayoutWordBreak(HTMLElement*); Position PositionForCaretOffset(unsigned offset) const final; - Optional<unsigned> CaretOffsetForPosition(const Position&) const final; + base::Optional<unsigned> CaretOffsetForPosition(const Position&) const final; const char* GetName() const override { return "LayoutWordBreak"; } bool IsWordBreak() const override; diff --git a/chromium/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h b/chromium/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h index 0b50205bac7..b948bc287b7 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h +++ b/chromium/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h @@ -1491,7 +1491,11 @@ inline void BreakingContext::CommitAndUpdateLineBreakIfNeeded() { bool check_for_break = auto_wrap_; if (width_.CommittedWidth() && !width_.FitsOnLine() && line_break_.GetLineLayoutItem() && curr_ws_ == EWhiteSpace::kNowrap) { - if (width_.FitsOnLine(0, kExcludeWhitespace)) { + // If this nowrap item fits but its trailing spaces does not, and if the + // next item is auto-wrap, break before the next item. + // TODO(kojii): This case should be handled when we read next item. + if (width_.FitsOnLine(0, kExcludeWhitespace) && + (!next_object_ || next_object_.StyleRef().AutoWrap())) { width_.Commit(); line_break_.MoveToStartOf(next_object_); } diff --git a/chromium/third_party/blink/renderer/core/layout/line/inline_box.h b/chromium/third_party/blink/renderer/core/layout/line/inline_box.h index ddc8753cf76..ae29ffb06f4 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/inline_box.h +++ b/chromium/third_party/blink/renderer/core/layout/line/inline_box.h @@ -202,9 +202,6 @@ class CORE_EXPORT InlineBox : public DisplayItemClient { InlineBox* PrevLeafChild() const; // Helper functions for editing and hit-testing code. - // FIXME: These two functions should be moved to RenderedPosition once the - // code to convert between Position and inline box, offset pair is moved to - // RenderedPosition. InlineBox* NextLeafChildIgnoringLineBreak() const; InlineBox* PrevLeafChildIgnoringLineBreak() const; diff --git a/chromium/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/chromium/third_party/blink/renderer/core/layout/line/inline_flow_box.cc index 337921a55a7..3f9a3fa0370 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/inline_flow_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/line/inline_flow_box.cc @@ -34,6 +34,7 @@ #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/line/glyph_overflow.h" #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" +#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" #include "third_party/blink/renderer/core/layout/line/root_inline_box.h" #include "third_party/blink/renderer/core/paint/box_painter.h" #include "third_party/blink/renderer/core/paint/inline_flow_box_painter.h" @@ -988,8 +989,8 @@ inline void InlineFlowBox::AddBoxShadowVisualOverflow( // Similar to how glyph overflow works, if our lines are flipped, then it's // actually the opposite shadow that applies, since the line is "upside down" // in terms of block coordinates. - LayoutRectOutsets logical_outsets( - outsets.LineOrientationOutsetsWithFlippedLines(writing_mode)); + LayoutRectOutsets logical_outsets = + LineOrientationLayoutRectOutsetsWithFlippedLines(outsets, writing_mode); LayoutRect shadow_bounds(LogicalFrameRect()); shadow_bounds.Expand(logical_outsets); @@ -1013,8 +1014,8 @@ inline void InlineFlowBox::AddBorderOutsetVisualOverflow( // actually the opposite border that applies, since the line is "upside down" // in terms of block coordinates. vertical-rl is the flipped line mode. LayoutRectOutsets logical_outsets = - style.BorderImageOutsets().LineOrientationOutsetsWithFlippedLines( - style.GetWritingMode()); + LineOrientationLayoutRectOutsetsWithFlippedLines( + style.BorderImageOutsets(), style.GetWritingMode()); if (!IncludeLogicalLeftEdge()) logical_outsets.SetLeft(LayoutUnit()); @@ -1082,8 +1083,9 @@ inline void InlineFlowBox::AddTextBoxVisualOverflow( if (ShadowList* text_shadow = style.TextShadow()) { LayoutRectOutsets text_shadow_logical_outsets = - LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()) - .LineOrientationOutsets(style.GetWritingMode()); + LineOrientationLayoutRectOutsets( + LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()), + style.GetWritingMode()); text_shadow_logical_outsets.ClampNegativeToZero(); visual_rect_outsets += text_shadow_logical_outsets; } diff --git a/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc new file mode 100644 index 00000000000..555a4a95e78 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc @@ -0,0 +1,31 @@ +// 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/line/line_orientation_utils.h" + +namespace blink { + +LayoutRectOutsets LineOrientationLayoutRectOutsets( + const LayoutRectOutsets& outsets, + WritingMode writing_mode) { + if (!IsHorizontalWritingMode(writing_mode)) { + return LayoutRectOutsets(outsets.Left(), outsets.Bottom(), outsets.Right(), + outsets.Top()); + } + return outsets; +} + +LayoutRectOutsets LineOrientationLayoutRectOutsetsWithFlippedLines( + const LayoutRectOutsets& original, + WritingMode writing_mode) { + LayoutRectOutsets outsets = + LineOrientationLayoutRectOutsets(original, writing_mode); + if (IsFlippedLinesWritingMode(writing_mode)) { + return LayoutRectOutsets(outsets.Bottom(), outsets.Right(), outsets.Top(), + outsets.Left()); + } + return outsets; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.h b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.h new file mode 100644 index 00000000000..0cc963d3e96 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h" +#include "third_party/blink/renderer/platform/text/writing_mode.h" + +namespace blink { + +// Produces a new LayoutRectOutsets in line orientation +// (https://www.w3.org/TR/css-writing-modes-3/#line-orientation), whose +// - |top| is the logical 'over', +// - |right| is the logical 'line right', +// - |bottom| is the logical 'under', +// - |left| is the logical 'line left'. +CORE_EXPORT LayoutRectOutsets +LineOrientationLayoutRectOutsets(const LayoutRectOutsets&, WritingMode); + +// The same as |logicalOutsets|, but also adjusting for flipped lines. +CORE_EXPORT LayoutRectOutsets +LineOrientationLayoutRectOutsetsWithFlippedLines(const LayoutRectOutsets&, + WritingMode); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc new file mode 100644 index 00000000000..35b3376c366 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc @@ -0,0 +1,44 @@ +// Copyright 2015 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/line/line_orientation_utils.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { +namespace { + +TEST(LineOrientationUtilsTest, LineOrientationLayoutRectOutsets_Horizontal) { + LayoutRectOutsets outsets(1, 2, 3, 4); + EXPECT_EQ( + LayoutRectOutsets(1, 2, 3, 4), + LineOrientationLayoutRectOutsets(outsets, WritingMode::kHorizontalTb)); +} + +TEST(LineOrientationUtilsTest, LineOrientationLayoutRectOutsets_Vertical) { + LayoutRectOutsets outsets(1, 2, 3, 4); + EXPECT_EQ( + LayoutRectOutsets(4, 3, 2, 1), + LineOrientationLayoutRectOutsets(outsets, WritingMode::kVerticalLr)); + EXPECT_EQ( + LayoutRectOutsets(4, 3, 2, 1), + LineOrientationLayoutRectOutsets(outsets, WritingMode::kVerticalRl)); +} + +TEST(LineOrientationUtilsTest, + LineOrientationLayoutRectOutsetsWithFlippedLines) { + LayoutRectOutsets outsets(1, 2, 3, 4); + EXPECT_EQ(LayoutRectOutsets(1, 2, 3, 4), + LineOrientationLayoutRectOutsetsWithFlippedLines( + outsets, WritingMode::kHorizontalTb)); + EXPECT_EQ(LayoutRectOutsets(2, 3, 4, 1), + LineOrientationLayoutRectOutsetsWithFlippedLines( + outsets, WritingMode::kVerticalLr)); + EXPECT_EQ(LayoutRectOutsets(4, 3, 2, 1), + LineOrientationLayoutRectOutsetsWithFlippedLines( + outsets, WritingMode::kVerticalRl)); +} + +} // namespace +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.cc b/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.cc index e2b2b8eae7d..8cb5e52f5be 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.cc @@ -768,31 +768,26 @@ bool RootInlineBox::IncludeLeadingForBox(InlineBox* box) const { (box->GetLineLayoutItem().IsText() && !box->IsText())); } -Node* RootInlineBox::GetLogicalStartBoxWithNode(InlineBox*& start_box) const { +const InlineBox* RootInlineBox::GetLogicalStartNonPseudoBox() const { Vector<InlineBox*> leaf_boxes_in_logical_order; CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order); for (size_t i = 0; i < leaf_boxes_in_logical_order.size(); ++i) { - if (leaf_boxes_in_logical_order[i]->GetLineLayoutItem().NonPseudoNode()) { - start_box = leaf_boxes_in_logical_order[i]; - return start_box->GetLineLayoutItem().NonPseudoNode(); - } + if (leaf_boxes_in_logical_order[i]->GetLineLayoutItem().NonPseudoNode()) + return leaf_boxes_in_logical_order[i]; } - start_box = nullptr; return nullptr; } -Node* RootInlineBox::GetLogicalEndBoxWithNode(InlineBox*& end_box) const { +const InlineBox* RootInlineBox::GetLogicalEndNonPseudoBox() const { Vector<InlineBox*> leaf_boxes_in_logical_order; CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order); for (size_t i = leaf_boxes_in_logical_order.size(); i > 0; --i) { if (leaf_boxes_in_logical_order[i - 1] ->GetLineLayoutItem() .NonPseudoNode()) { - end_box = leaf_boxes_in_logical_order[i - 1]; - return end_box->GetLineLayoutItem().NonPseudoNode(); + return leaf_boxes_in_logical_order[i - 1]; } } - end_box = nullptr; return nullptr; } diff --git a/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.h b/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.h index 5390deb2792..522cb2f5523 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.h +++ b/chromium/third_party/blink/renderer/core/layout/line/root_inline_box.h @@ -205,8 +205,8 @@ class RootInlineBox : public InlineFlowBox { return InlineFlowBox::LogicalBottomLayoutOverflow(LineBottom()); } - Node* GetLogicalStartBoxWithNode(InlineBox*&) const; - Node* GetLogicalEndBoxWithNode(InlineBox*&) const; + const InlineBox* GetLogicalStartNonPseudoBox() const; + const InlineBox* GetLogicalEndNonPseudoBox() const; const char* BoxName() const override; diff --git a/chromium/third_party/blink/renderer/core/layout/map_coordinates_flags.h b/chromium/third_party/blink/renderer/core/layout/map_coordinates_flags.h index 311acb42a6b..5a3af39fece 100644 --- a/chromium/third_party/blink/renderer/core/layout/map_coordinates_flags.h +++ b/chromium/third_party/blink/renderer/core/layout/map_coordinates_flags.h @@ -29,6 +29,10 @@ enum MapCoordinatesMode { // Ignore offset adjustments caused by position:sticky calculations when // walking the chain. kIgnoreStickyOffset = 1 << 5, + + // Ignore scroll offset from container, i.e. scrolling has no effect on mapped + // position. + kIgnoreScrollOffset = 1 << 6, }; typedef unsigned MapCoordinatesFlags; diff --git a/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc b/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc index e8889112534..dc608308fa3 100644 --- a/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc @@ -6,20 +6,13 @@ #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { -typedef bool TestParamRootLayerScrolling; -class MapCoordinatesTest - : public testing::WithParamInterface<TestParamRootLayerScrolling>, - private ScopedRootLayerScrollingForTest, - public RenderingTest { +class MapCoordinatesTest : public RenderingTest { public: - MapCoordinatesTest() - : ScopedRootLayerScrollingForTest(GetParam()), - RenderingTest(SingleChildLocalFrameClient::Create()) {} + MapCoordinatesTest() : RenderingTest(SingleChildLocalFrameClient::Create()) {} FloatPoint MapLocalToAncestor(const LayoutObject*, const LayoutBoxModelObject* ancestor, FloatPoint, @@ -113,9 +106,7 @@ FloatQuad MapCoordinatesTest::MapAncestorToLocal( return transform_state.LastPlanarQuad(); } -INSTANTIATE_TEST_CASE_P(All, MapCoordinatesTest, testing::Bool()); - -TEST_P(MapCoordinatesTest, SimpleText) { +TEST_F(MapCoordinatesTest, SimpleText) { SetBodyInnerHTML("<div id='container'><br>text</div>"); LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); @@ -128,7 +119,7 @@ TEST_P(MapCoordinatesTest, SimpleText) { EXPECT_EQ(FloatPoint(10, 30), mapped_point); } -TEST_P(MapCoordinatesTest, SimpleInline) { +TEST_F(MapCoordinatesTest, SimpleInline) { SetBodyInnerHTML("<div><span id='target'>text</span></div>"); LayoutObject* target = GetLayoutObjectByElementId("target"); @@ -140,7 +131,7 @@ TEST_P(MapCoordinatesTest, SimpleInline) { EXPECT_EQ(FloatPoint(10, 10), mapped_point); } -TEST_P(MapCoordinatesTest, SimpleBlock) { +TEST_F(MapCoordinatesTest, SimpleBlock) { SetBodyInnerHTML(R"HTML( <div style='margin:666px; border:8px solid; padding:7px;'> <div id='target' style='margin:10px; border:666px; @@ -157,7 +148,7 @@ TEST_P(MapCoordinatesTest, SimpleBlock) { EXPECT_EQ(FloatPoint(100, 100), mapped_point); } -TEST_P(MapCoordinatesTest, OverflowClip) { +TEST_F(MapCoordinatesTest, OverflowClip) { SetBodyInnerHTML(R"HTML( <div id='overflow' style='height: 100px; width: 100px; border:8px solid; padding:7px; overflow:scroll'> @@ -179,7 +170,7 @@ TEST_P(MapCoordinatesTest, OverflowClip) { EXPECT_EQ(FloatPoint(100, 100), mapped_point); } -TEST_P(MapCoordinatesTest, TextInRelPosInline) { +TEST_F(MapCoordinatesTest, TextInRelPosInline) { SetBodyInnerHTML( "<div><span style='position:relative; left:7px; top:4px;'><br " "id='sibling'>text</span></div>"); @@ -195,7 +186,7 @@ TEST_P(MapCoordinatesTest, TextInRelPosInline) { EXPECT_EQ(FloatPoint(10, 30), mapped_point); } -TEST_P(MapCoordinatesTest, RelposInline) { +TEST_F(MapCoordinatesTest, RelposInline) { SetBodyInnerHTML( "<span id='target' style='position:relative; left:50px; " "top:100px;'>text</span>"); @@ -209,7 +200,7 @@ TEST_P(MapCoordinatesTest, RelposInline) { EXPECT_EQ(FloatPoint(10, 10), mapped_point); } -TEST_P(MapCoordinatesTest, RelposInlineInRelposInline) { +TEST_F(MapCoordinatesTest, RelposInlineInRelposInline) { SetBodyInnerHTML(R"HTML( <div style='padding-left:10px;'> <span style='position:relative; left:5px; top:6px;'> @@ -243,7 +234,7 @@ TEST_P(MapCoordinatesTest, RelposInlineInRelposInline) { EXPECT_EQ(FloatPoint(20, 10), mapped_point); } -TEST_P(MapCoordinatesTest, RelPosBlock) { +TEST_F(MapCoordinatesTest, RelPosBlock) { SetBodyInnerHTML(R"HTML( <div id='container' style='margin:666px; border:8px solid; padding:7px;'> @@ -278,7 +269,7 @@ TEST_P(MapCoordinatesTest, RelPosBlock) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, AbsPos) { +TEST_F(MapCoordinatesTest, AbsPos) { SetBodyInnerHTML(R"HTML( <div id='container' style='position:relative; margin:666px; border:8px solid; padding:7px;'> @@ -315,7 +306,7 @@ TEST_P(MapCoordinatesTest, AbsPos) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, AbsPosAuto) { +TEST_F(MapCoordinatesTest, AbsPosAuto) { SetBodyInnerHTML(R"HTML( <div id='container' style='position:absolute; margin:666px; border:8px solid; padding:7px;'> @@ -352,7 +343,7 @@ TEST_P(MapCoordinatesTest, AbsPosAuto) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPos) { +TEST_F(MapCoordinatesTest, FixedPos) { // Assuming BODY margin of 8px. SetBodyInnerHTML(R"HTML( <div id='container' style='position:absolute; margin:4px; border:5px @@ -411,7 +402,7 @@ TEST_P(MapCoordinatesTest, FixedPos) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosAuto) { +TEST_F(MapCoordinatesTest, FixedPosAuto) { // Assuming BODY margin of 8px. SetBodyInnerHTML(R"HTML( <div id='container' style='position:absolute; margin:3px; border:8px @@ -472,7 +463,7 @@ TEST_P(MapCoordinatesTest, FixedPosAuto) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInFixedPos) { +TEST_F(MapCoordinatesTest, FixedPosInFixedPos) { // Assuming BODY margin of 8px. SetBodyInnerHTML(R"HTML( <div id='container' style='position:absolute; margin:4px; border:5px @@ -542,7 +533,7 @@ TEST_P(MapCoordinatesTest, FixedPosInFixedPos) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInFixedPosScrollView) { +TEST_F(MapCoordinatesTest, FixedPosInFixedPosScrollView) { SetBodyInnerHTML(R"HTML( <div style='height: 4000px'></div> <div id='container' style='position:fixed; top: 100px; left: 100px'> @@ -578,7 +569,7 @@ TEST_P(MapCoordinatesTest, FixedPosInFixedPosScrollView) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInAbsolutePosScrollView) { +TEST_F(MapCoordinatesTest, FixedPosInAbsolutePosScrollView) { SetBodyInnerHTML(R"HTML( <div style='height: 4000px'></div> <div id='container' style='position:absolute; top: 100px; left: 100px'> @@ -614,7 +605,7 @@ TEST_P(MapCoordinatesTest, FixedPosInAbsolutePosScrollView) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInTransform) { +TEST_F(MapCoordinatesTest, FixedPosInTransform) { SetBodyInnerHTML(R"HTML( <style>#container { transform: translateY(100px); position: absolute; left: 0; top: 100px; } @@ -656,7 +647,7 @@ TEST_P(MapCoordinatesTest, FixedPosInTransform) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInContainPaint) { +TEST_F(MapCoordinatesTest, FixedPosInContainPaint) { SetBodyInnerHTML(R"HTML( <style>#container { contain: paint; position: absolute; left: 0; top: 100px; } @@ -699,7 +690,7 @@ TEST_P(MapCoordinatesTest, FixedPosInContainPaint) { } // TODO(chrishtr): add more multi-frame tests. -TEST_P(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) { +TEST_F(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); SetBodyInnerHTML(R"HTML( <style>body { margin: 0; }</style> @@ -729,7 +720,7 @@ TEST_P(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) { EXPECT_EQ(FloatPoint(10, -7930), AdjustForFrameScroll(mapped_point)); } -TEST_P(MapCoordinatesTest, IFrameTransformed) { +TEST_F(MapCoordinatesTest, IFrameTransformed) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); SetBodyInnerHTML(R"HTML( <style>body { margin: 0; }</style> @@ -762,7 +753,7 @@ TEST_P(MapCoordinatesTest, IFrameTransformed) { EXPECT_EQ(FloatPoint(225, 1225), mapped_point); } -TEST_P(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) { +TEST_F(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); SetBodyInnerHTML(R"HTML( <style>* { margin: 0; }</style> @@ -792,7 +783,7 @@ TEST_P(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) { EXPECT_EQ(FloatPoint(0, 0), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithText) { +TEST_F(MapCoordinatesTest, MulticolWithText) { SetBodyInnerHTML(R"HTML( <div id='multicol' style='columns:2; column-gap:20px; width:400px; line-height:50px; padding:5px; orphans:1; widows:1;'> @@ -819,7 +810,7 @@ TEST_P(MapCoordinatesTest, MulticolWithText) { EXPECT_EQ(FloatPoint(10, 70), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithInline) { +TEST_F(MapCoordinatesTest, MulticolWithInline) { SetBodyInnerHTML(R"HTML( <div id='multicol' style='columns:2; column-gap:20px; width:400px; line-height:50px; padding:5px; orphans:1; widows:1;'> @@ -844,7 +835,7 @@ TEST_P(MapCoordinatesTest, MulticolWithInline) { EXPECT_EQ(FloatPoint(10, 70), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithBlock) { +TEST_F(MapCoordinatesTest, MulticolWithBlock) { SetBodyInnerHTML(R"HTML( <div id='container' style='-webkit-columns:3; -webkit-column-gap:0; column-fill:auto; width:300px; height:100px; border:8px solid; @@ -879,7 +870,7 @@ TEST_P(MapCoordinatesTest, MulticolWithBlock) { EXPECT_EQ(FloatPoint(10, 120), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithBlockAbove) { +TEST_F(MapCoordinatesTest, MulticolWithBlockAbove) { SetBodyInnerHTML(R"HTML( <div id='container' style='columns:3; column-gap:0; column-fill:auto; width:300px; height:200px;'> @@ -910,7 +901,7 @@ TEST_P(MapCoordinatesTest, MulticolWithBlockAbove) { EXPECT_EQ(FloatPoint(0, -50), mapped_point); } -TEST_P(MapCoordinatesTest, NestedMulticolWithBlock) { +TEST_F(MapCoordinatesTest, NestedMulticolWithBlock) { SetBodyInnerHTML(R"HTML( <div id='outerMulticol' style='columns:2; column-gap:0; column-fill:auto; width:560px; height:215px; border:8px solid; @@ -968,7 +959,7 @@ TEST_P(MapCoordinatesTest, NestedMulticolWithBlock) { EXPECT_EQ(FloatPoint(140, 315), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithAbsPosInRelPos) { +TEST_F(MapCoordinatesTest, MulticolWithAbsPosInRelPos) { SetBodyInnerHTML(R"HTML( <div id='multicol' style='-webkit-columns:3; -webkit-column-gap:0; column-fill:auto; width:300px; height:100px; border:8px solid; @@ -1010,7 +1001,7 @@ TEST_P(MapCoordinatesTest, MulticolWithAbsPosInRelPos) { EXPECT_EQ(FloatPoint(29, 139), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithAbsPosNotContained) { +TEST_F(MapCoordinatesTest, MulticolWithAbsPosNotContained) { SetBodyInnerHTML(R"HTML( <div id='container' style='position:relative; margin:666px; border:7px solid; padding:3px;'> @@ -1058,7 +1049,7 @@ TEST_P(MapCoordinatesTest, MulticolWithAbsPosNotContained) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolRtl) { +TEST_F(MapCoordinatesTest, MulticolRtl) { SetBodyInnerHTML(R"HTML( <div id='container' style='columns:3; column-gap:0; column-fill:auto; width:300px; height:200px; direction:rtl;'> @@ -1090,7 +1081,7 @@ TEST_P(MapCoordinatesTest, MulticolRtl) { EXPECT_EQ(FloatPoint(0, 200), mapped_point); } -TEST_P(MapCoordinatesTest, MulticolWithLargeBorder) { +TEST_F(MapCoordinatesTest, MulticolWithLargeBorder) { SetBodyInnerHTML(R"HTML( <div id='container' style='columns:3; column-gap:0; column-fill:auto; width:300px; height:200px; border:200px solid;'> @@ -1123,7 +1114,7 @@ TEST_P(MapCoordinatesTest, MulticolWithLargeBorder) { EXPECT_EQ(FloatPoint(0, 200), mapped_point); } -TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithText) { +TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithText) { SetBodyInnerHTML(R"HTML( <div style='-webkit-writing-mode:vertical-rl;'> <div style='width:13px;'></div> @@ -1169,7 +1160,7 @@ TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithText) { EXPECT_EQ(FloatPoint(75, 10), mapped_point); } -TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithInline) { +TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithInline) { SetBodyInnerHTML(R"HTML( <div style='-webkit-writing-mode:vertical-rl;'> <div style='width:13px;'></div> @@ -1235,7 +1226,7 @@ TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithInline) { EXPECT_EQ(FloatPoint(75, 10), mapped_point); } -TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithBlock) { +TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithBlock) { SetBodyInnerHTML(R"HTML( <div id='container' style='-webkit-writing-mode:vertical-rl; border:8px solid; padding:7px; width:200px; height:200px;'> @@ -1268,7 +1259,7 @@ TEST_P(MapCoordinatesTest, FlippedBlocksWritingModeWithBlock) { EXPECT_EQ(FloatPoint(7, 7), mapped_point); } -TEST_P(MapCoordinatesTest, Table) { +TEST_F(MapCoordinatesTest, Table) { SetBodyInnerHTML(R"HTML( <style>td { padding: 2px; }</style> <div id='container' style='border:3px solid;'> @@ -1368,7 +1359,7 @@ static bool FloatQuadsAlmostEqual(const FloatQuad& expected, } \ } while (false) -TEST_P(MapCoordinatesTest, Transforms) { +TEST_F(MapCoordinatesTest, Transforms) { SetBodyInnerHTML(R"HTML( <div id='container'> <div id='outerTransform' style='transform:rotate(45deg); @@ -1436,7 +1427,7 @@ TEST_P(MapCoordinatesTest, Transforms) { EXPECT_FLOAT_QUAD_EQ(initial_quad, mapped_quad); } -TEST_P(MapCoordinatesTest, SVGShape) { +TEST_F(MapCoordinatesTest, SVGShape) { SetBodyInnerHTML(R"HTML( <svg id='container'> <g transform='translate(100 200)'> @@ -1454,7 +1445,7 @@ TEST_P(MapCoordinatesTest, SVGShape) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGShapeScale) { +TEST_F(MapCoordinatesTest, SVGShapeScale) { SetBodyInnerHTML(R"HTML( <svg id='container'> <g transform='scale(2) translate(50 40)'> @@ -1473,7 +1464,7 @@ TEST_P(MapCoordinatesTest, SVGShapeScale) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithoutScale) { +TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithoutScale) { SetBodyInnerHTML(R"HTML( <svg id='container' viewBox='0 0 200 200' width='400' height='200'> <g transform='translate(100 50)'> @@ -1491,7 +1482,7 @@ TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithoutScale) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithScale) { +TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithScale) { SetBodyInnerHTML(R"HTML( <svg id='container' viewBox='0 0 100 100' width='400' height='200'> <g transform='translate(50 50)'> @@ -1509,7 +1500,7 @@ TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithScale) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffset) { +TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffset) { SetBodyInnerHTML(R"HTML( <svg id='container' viewBox='100 100 200 200' width='400' height='200'> <g transform='translate(100 50)'> @@ -1528,7 +1519,7 @@ TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffset) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffsetAndScale) { +TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffsetAndScale) { SetBodyInnerHTML(R"HTML( <svg id='container' viewBox='100 100 100 100' width='400' height='200'> <g transform='translate(50 50)'> @@ -1547,7 +1538,7 @@ TEST_P(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffsetAndScale) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, SVGForeignObject) { +TEST_F(MapCoordinatesTest, SVGForeignObject) { SetBodyInnerHTML(R"HTML( <svg id='container' viewBox='0 0 100 100' width='400' height='200'> <g transform='translate(50 50)'> @@ -1583,7 +1574,7 @@ TEST_P(MapCoordinatesTest, SVGForeignObject) { EXPECT_EQ(FloatPoint(), mapped_point); } -TEST_P(MapCoordinatesTest, LocalToAbsoluteTransform) { +TEST_F(MapCoordinatesTest, LocalToAbsoluteTransform) { SetBodyInnerHTML(R"HTML( <div id='container' style='position: absolute; left: 0; top: 0;'> <div id='scale' style='transform: scale(2.0); transform-origin: left @@ -1607,7 +1598,7 @@ TEST_P(MapCoordinatesTest, LocalToAbsoluteTransform) { EXPECT_EQ(40.0, child_matrix.ProjectPoint(FloatPoint(10.0, 20.0)).Y()); } -TEST_P(MapCoordinatesTest, LocalToAncestorTransform) { +TEST_F(MapCoordinatesTest, LocalToAncestorTransform) { SetBodyInnerHTML(R"HTML( <div id='container'> <div id='rotate1' style='transform: rotate(45deg); transform-origin: @@ -1652,7 +1643,7 @@ TEST_P(MapCoordinatesTest, LocalToAncestorTransform) { LayoutUnit::Epsilon()); } -TEST_P(MapCoordinatesTest, LocalToAbsoluteTransformFlattens) { +TEST_F(MapCoordinatesTest, LocalToAbsoluteTransformFlattens) { GetDocument().GetFrame()->GetSettings()->SetAcceleratedCompositingEnabled( true); SetBodyInnerHTML(R"HTML( @@ -1701,4 +1692,150 @@ TEST_P(MapCoordinatesTest, LocalToAbsoluteTransformFlattens) { LayoutUnit::Epsilon()); } +// This test verifies that the mapped location of a div within a scroller +// remains the same after scroll when ignoring scroll offset. +TEST_F(MapCoordinatesTest, IgnoreScrollOffset) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { overflow: scroll; height: 100px; width: 100px; + top: 100px; position: absolute; } + .box { width: 10px; height: 10px; top: 10px; position: absolute; } + .spacer { height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='box' id='box'></div> + <div class='spacer'></div> + </div> + )HTML"); + + LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); + + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint(), + kIgnoreScrollOffset)); + + scroller->ScrollToPosition(FloatPoint(0, 50)); + + EXPECT_EQ(FloatPoint(0, -40), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint(), + kIgnoreScrollOffset)); +} + +// This test verifies that the mapped location of an inline div within a +// scroller remains the same after scroll when ignoring scroll offset. +TEST_F(MapCoordinatesTest, IgnoreScrollOffsetForInline) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { overflow: scroll; width: 100px; height: 100px; top: 100px; + position: absolute; } + .box { width: 10px; height: 10px; top: 10px; position: sticky; } + .inline { display: inline; } + .spacer { height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='inline box' id='box'></div> + <div class='spacer'></div> + </div> + )HTML"); + + LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + LayoutInline* box = ToLayoutInline(GetLayoutObjectByElementId("box")); + + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint(), + kIgnoreScrollOffset)); + + scroller->ScrollToPosition(FloatPoint(0, 50)); + + EXPECT_EQ(FloatPoint(0, 10), MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ(FloatPoint(0, 60), MapLocalToAncestor(box, scroller, FloatPoint(), + kIgnoreScrollOffset)); +} + +// This test verifies that ignoring scroll offset works with writing modes. +TEST_F(MapCoordinatesTest, IgnoreScrollOffsetWithWritingModes) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { writing-mode: vertical-rl; overflow: scroll; height: 100px; + width: 100px; top: 100px; position: absolute; } + .box { width: 10px; height: 10px; top: 10px; position: absolute; } + .spacer { width: 2000px; height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='box' id='box'></div> + <div class='spacer'></div> + </div> + )HTML"); + + LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); + + EXPECT_EQ(FloatPoint(90, 10), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ( + FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint(), kIgnoreScrollOffset)); + + scroller->ScrollToPosition(FloatPoint(0, 50)); + + EXPECT_EQ(FloatPoint(1990, -40), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ( + FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint(), kIgnoreScrollOffset)); + + scroller->ScrollToPosition(FloatPoint(1900, 50)); + + EXPECT_EQ(FloatPoint(90, -40), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ( + FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint(), kIgnoreScrollOffset)); +} + +// This test verifies that ignoring scroll offset works with writing modes and +// non-overlay scrollbar. +TEST_F(MapCoordinatesTest, + IgnoreScrollOffsetWithWritingModesAndNonOverlayScrollbar) { + ScopedOverlayScrollbarsForTest overlay_scrollbars(false); + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + .scroller { writing-mode: vertical-rl; overflow: scroll; height: 100px; + width: 100px; top: 100px; position: absolute; } + .box { width: 10px; height: 10px; top: 10px; position: absolute; } + .spacer { width: 2000px; height: 2000px; } + </style> + <div class='scroller' id='scroller'> + <div class='box' id='box'></div> + <div class='spacer'></div> + </div> + )HTML"); + + LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); + + // The box is on the left of the scrollbar so the width of the scrollbar + // affects the location of the box. + EXPECT_EQ(FloatPoint(75, 10), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ( + FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint(), kIgnoreScrollOffset)); + + scroller->ScrollToPosition(FloatPoint(0, 0)); + // The box is now on the right of the scrollbar therefore there is nothing + // between the box and the right border of the content. + EXPECT_EQ(FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint())); + EXPECT_EQ( + FloatPoint(1990, 10), + MapLocalToAncestor(box, scroller, FloatPoint(), kIgnoreScrollOffset)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc index 50da8b67790..869820da343 100644 --- a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc +++ b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.cc @@ -62,6 +62,7 @@ LayoutUnit MultiColumnFragmentainerGroup::LogicalHeightInFlowThreadAt( } void MultiColumnFragmentainerGroup::ResetColumnHeight() { + actual_column_count_allowance_ = 0; max_logical_height_ = CalculateMaxColumnHeight(); LayoutMultiColumnFlowThread* flow_thread = @@ -137,6 +138,24 @@ bool MultiColumnFragmentainerGroup::RecalculateColumnHeight( // height. is_logical_height_known_ = true; + unsigned column_count = UnclampedActualColumnCount(); + if (column_count > LayoutMultiColumnFlowThread::ColumnCountClampMax() || + (column_count > LayoutMultiColumnFlowThread::ColumnCountClampMin() && + column_count > column_set_.UsedColumnCount())) { + // That's a lot of columns! We have either exceeded the maximum value, or we + // have overflowing columns, and the proposed count is within clamping + // range. Calculate allowance to make sure we have a legitimate reason for + // it, or else clamp it. We have quadratic performance complexity for + // painting columns. + if (!actual_column_count_allowance_) { + const auto* flow_thread = column_set_.MultiColumnFlowThread(); + unsigned allowance = flow_thread->CalculateActualColumnCountAllowance(); + DCHECK_GE(allowance, LayoutMultiColumnFlowThread::ColumnCountClampMin()); + DCHECK_LE(allowance, LayoutMultiColumnFlowThread::ColumnCountClampMax()); + actual_column_count_allowance_ = allowance; + } + } + if (logical_height_ == old_column_height) return false; // No change. We're done. @@ -309,36 +328,9 @@ LayoutRect MultiColumnFragmentainerGroup::CalculateOverflow() const { } unsigned MultiColumnFragmentainerGroup::ActualColumnCount() const { - // We must always return a value of 1 or greater. Column count = 0 is a - // meaningless situation, and will confuse and cause problems in other parts - // of the code. - if (!IsLogicalHeightKnown()) - return 1; - // Our flow thread portion determines our column count. We have as many - // columns as needed to fit all the content. - LayoutUnit flow_thread_portion_height = LogicalHeightInFlowThread(); - if (!flow_thread_portion_height) - return 1; - - LayoutUnit column_height = ColumnLogicalHeight(); - unsigned count = (flow_thread_portion_height / column_height).Floor(); - // flowThreadPortionHeight may be saturated, so detect the remainder manually. - if (count * column_height < flow_thread_portion_height) - count++; - - static const unsigned kColumnCountClampMin = 10; - static const unsigned kColumnCountClampMax = 500; - if (count > kColumnCountClampMin) { - // To avoid performance problems, limit the maximum number of columns. Try - // to identify legitimate reasons for creating many columns, and allow many - // columns in such cases. We currently use a function of column height to - // determine this (limit the column count to the column height in pixels, - // but never clamp to less than 10 columns). - unsigned max_count = column_height.ToInt(); - count = std::min(count, std::max(std::min(max_count, kColumnCountClampMax), - kColumnCountClampMin)); - } - + unsigned count = UnclampedActualColumnCount(); + if (actual_column_count_allowance_) + count = std::min(count, actual_column_count_allowance_); DCHECK_GE(count, 1u); return count; } @@ -648,6 +640,28 @@ void MultiColumnFragmentainerGroup::ColumnIntervalForVisualRect( DCHECK_LE(first_column, last_column); } +unsigned MultiColumnFragmentainerGroup::UnclampedActualColumnCount() const { + // We must always return a value of 1 or greater. Column count = 0 is a + // meaningless situation, and will confuse and cause problems in other parts + // of the code. + if (!IsLogicalHeightKnown()) + return 1; + // Our flow thread portion determines our column count. We have as many + // columns as needed to fit all the content. + LayoutUnit flow_thread_portion_height = LogicalHeightInFlowThread(); + if (!flow_thread_portion_height) + return 1; + + LayoutUnit column_height = ColumnLogicalHeight(); + unsigned count = (flow_thread_portion_height / column_height).Floor(); + // flowThreadPortionHeight may be saturated, so detect the remainder manually. + if (count * column_height < flow_thread_portion_height) + count++; + + DCHECK_GE(count, 1u); + return count; +} + MultiColumnFragmentainerGroupList::MultiColumnFragmentainerGroupList( LayoutMultiColumnSet& column_set) : column_set_(column_set) { diff --git a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h index 3387bc15308..13aedea607f 100644 --- a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h +++ b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h @@ -193,6 +193,8 @@ class CORE_EXPORT MultiColumnFragmentainerGroup { // to a column, even if said point is not inside any of the columns. unsigned ColumnIndexAtVisualPoint(const LayoutPoint& visual_point) const; + unsigned UnclampedActualColumnCount() const; + const LayoutMultiColumnSet& column_set_; LayoutUnit logical_top_; @@ -207,6 +209,8 @@ class CORE_EXPORT MultiColumnFragmentainerGroup { // Maximum logical height allowed. LayoutUnit max_logical_height_; + unsigned actual_column_count_allowance_ = 0; + bool is_logical_height_known_ = false; }; diff --git a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc index fd0e4967fc8..1d9420faff2 100644 --- a/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/multi_column_fragmentainer_group_test.cc @@ -100,9 +100,9 @@ TEST_F(MultiColumnFragmentainerGroupTest, // The following test tests that we restrict actual column count, to not run // into unnecessary performance problems. The code that applies this limitation // is in MultiColumnFragmentainerGroup::ActualColumnCount(). -TEST_F(MultiColumnFragmentainerGroupTest, ShortColumnTallContent) { +TEST_F(MultiColumnFragmentainerGroupTest, TallEmptyContent) { SetBodyInnerHTML(R"HTML( - <div id="multicol" style="columns:3; column-gap:1px; width:101px; height:1px;"> + <div id="multicol" style="columns:3; column-gap:1px; width:101px; line-height:50px; height:200px;"> <div style="height:1000000px;"></div> </div> )HTML"); @@ -115,23 +115,28 @@ TEST_F(MultiColumnFragmentainerGroupTest, ShortColumnTallContent) { const auto& fragmentainer_group = ToLayoutMultiColumnSet(column_set)->FirstFragmentainerGroup(); EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 10U); - EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(1)); + EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(200)); auto overflow = ToLayoutBox(multicol)->LayoutOverflowRect(); EXPECT_EQ(ToLayoutBox(multicol)->LogicalWidth(), LayoutUnit(101)); - EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(1)); + EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(200)); EXPECT_EQ(overflow.Width(), LayoutUnit(339)); - EXPECT_EQ(overflow.Height(), LayoutUnit(999991)); + EXPECT_EQ(overflow.Height(), LayoutUnit(998200)); } -// The following test tests that we restrict actual column count, to not run -// into unnecessary performance problems. The code that applies this limitation -// is in MultiColumnFragmentainerGroup::ActualColumnCount(). -TEST_F(MultiColumnFragmentainerGroupTest, MediumTallColumnTallContent) { - SetBodyInnerHTML(R"HTML( - <div id="multicol" style="columns:3; column-gap:1px; width:101px; height:100px;"> - <div style="height:1000000px;"></div> - </div> - )HTML"); +// The following test tests that we DON'T restrict actual column count, when +// there's a legitimate reason to use many columns. The code that checks the +// allowance and potentially applies this limitation is in +// MultiColumnFragmentainerGroup::ActualColumnCount(). +TEST_F(MultiColumnFragmentainerGroupTest, LotsOfContent) { + StringBuilder builder; + builder.Append( + "<div id='multicol' style='columns:3; column-gap:1px; width:101px; " + "line-height:50px; orphans:1; widows:1; height:60px;'>"); + for (int i = 0; i < 100; i++) + builder.Append("line<br>"); + builder.Append("</div>"); + String html; + SetBodyInnerHTML(builder.ToString()); const auto* multicol = GetLayoutObjectByElementId("multicol"); ASSERT_TRUE(multicol); ASSERT_TRUE(multicol->IsLayoutBlockFlow()); @@ -141,23 +146,28 @@ TEST_F(MultiColumnFragmentainerGroupTest, MediumTallColumnTallContent) { const auto& fragmentainer_group = ToLayoutMultiColumnSet(column_set)->FirstFragmentainerGroup(); EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 100U); - EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(100)); + EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(60)); auto overflow = ToLayoutBox(multicol)->LayoutOverflowRect(); EXPECT_EQ(ToLayoutBox(multicol)->LogicalWidth(), LayoutUnit(101)); - EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(100)); + EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(60)); EXPECT_EQ(overflow.Width(), LayoutUnit(3399)); - EXPECT_EQ(overflow.Height(), LayoutUnit(990100)); + EXPECT_EQ(overflow.Height(), LayoutUnit(60)); } -// The following test tests that we restrict actual column count, to not run -// into unnecessary performance problems. The code that applies this limitation -// is in MultiColumnFragmentainerGroup::ActualColumnCount(). -TEST_F(MultiColumnFragmentainerGroupTest, TallColumnTallContent) { - SetBodyInnerHTML(R"HTML( - <div id="multicol" style="columns:3; column-gap:1px; width:101px; height:1000px;"> - <div style="height:1000000px;"></div> - </div> - )HTML"); +// The following test tests that we DON'T restrict actual column count, when +// there's a legitimate reason to use many columns. The code that checks the +// allowance and potentially applies this limitation is in +// MultiColumnFragmentainerGroup::ActualColumnCount(). +TEST_F(MultiColumnFragmentainerGroupTest, LotsOfNestedBlocksWithText) { + StringBuilder builder; + builder.Append( + "<div id='multicol' style='columns:3; column-gap:1px; width:101px; " + "line-height:50px; height:200px;'>"); + for (int i = 0; i < 1000; i++) + builder.Append("<div><div><div>line</div></div></div>"); + builder.Append("</div>"); + String html; + SetBodyInnerHTML(builder.ToString()); const auto* multicol = GetLayoutObjectByElementId("multicol"); ASSERT_TRUE(multicol); ASSERT_TRUE(multicol->IsLayoutBlockFlow()); @@ -166,13 +176,44 @@ TEST_F(MultiColumnFragmentainerGroupTest, TallColumnTallContent) { ASSERT_TRUE(column_set->IsLayoutMultiColumnSet()); const auto& fragmentainer_group = ToLayoutMultiColumnSet(column_set)->FirstFragmentainerGroup(); - EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 500U); - EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(1000)); + EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 250U); + EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(200)); auto overflow = ToLayoutBox(multicol)->LayoutOverflowRect(); EXPECT_EQ(ToLayoutBox(multicol)->LogicalWidth(), LayoutUnit(101)); - EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(1000)); - EXPECT_EQ(overflow.Width(), LayoutUnit(16999)); - EXPECT_EQ(overflow.Height(), LayoutUnit(501000)); + EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(200)); + EXPECT_EQ(overflow.Width(), LayoutUnit(8499)); + EXPECT_EQ(overflow.Height(), LayoutUnit(200)); +} + +// The following test tests that we DON'T restrict actual column count, when +// there's a legitimate reason to use many columns. The code that checks the +// allowance and potentially applies this limitation is in +// MultiColumnFragmentainerGroup::ActualColumnCount(). +TEST_F(MultiColumnFragmentainerGroupTest, NestedBlocksWithLotsOfContent) { + StringBuilder builder; + builder.Append( + "<div id='multicol' style='columns:3; column-gap:1px; width:101px; " + "line-height:50px; orphans:1; widows:1; height:60px;'><div><div><div>"); + for (int i = 0; i < 100; i++) + builder.Append("line<br>"); + builder.Append("</div></div></div></div>"); + String html; + SetBodyInnerHTML(builder.ToString()); + const auto* multicol = GetLayoutObjectByElementId("multicol"); + ASSERT_TRUE(multicol); + ASSERT_TRUE(multicol->IsLayoutBlockFlow()); + const auto* column_set = multicol->SlowLastChild(); + ASSERT_TRUE(column_set); + ASSERT_TRUE(column_set->IsLayoutMultiColumnSet()); + const auto& fragmentainer_group = + ToLayoutMultiColumnSet(column_set)->FirstFragmentainerGroup(); + EXPECT_EQ(fragmentainer_group.ActualColumnCount(), 100U); + EXPECT_EQ(fragmentainer_group.GroupLogicalHeight(), LayoutUnit(60)); + auto overflow = ToLayoutBox(multicol)->LayoutOverflowRect(); + EXPECT_EQ(ToLayoutBox(multicol)->LogicalWidth(), LayoutUnit(101)); + EXPECT_EQ(ToLayoutBox(multicol)->LogicalHeight(), LayoutUnit(60)); + EXPECT_EQ(overflow.Width(), LayoutUnit(3399)); + EXPECT_EQ(overflow.Height(), LayoutUnit(60)); } } // anonymous namespace diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc index cff6ef8c505..3c572fb11eb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc @@ -101,10 +101,18 @@ bool Intersects(const NGLayoutOpportunity& opportunity, bool Intersects(const NGExclusionSpace::NGShelf& shelf, const NGBfcOffset& offset, const LayoutUnit inline_size) { - return (shelf.line_right == LayoutUnit::Max() || - shelf.line_right >= offset.line_offset) && - (shelf.line_left == LayoutUnit::Min() || - shelf.line_left <= offset.line_offset + inline_size); + if (shelf.line_right >= offset.line_offset && + shelf.line_left <= offset.line_offset + inline_size) + return true; + // Negative available space creates a zero-width opportunity at the inline-end + // of the shelf. Consider such shelf intersects. + // TODO(kojii): This is correct to find layout opportunities for zero-width + // in-flow inline or block objects (e.g., <br>,) but not correct for + // zero-width floats. + if (UNLIKELY(shelf.line_left > offset.line_offset || + shelf.line_right < offset.line_offset + inline_size)) + return true; + return false; } // Creates a new layout opportunity. The given layout opportunity *must* @@ -122,7 +130,8 @@ NGLayoutOpportunity CreateLayoutOpportunity(const NGLayoutOpportunity& other, std::min(other.rect.LineEndOffset(), offset.line_offset + inline_size), other.rect.BlockEndOffset()); - return NGLayoutOpportunity(NGBfcRect(start_offset, end_offset)); + return NGLayoutOpportunity(NGBfcRect(start_offset, end_offset), + other.shape_exclusions); } // Creates a new layout opportunity. The given shelf *must* intersect with the @@ -136,11 +145,18 @@ NGLayoutOpportunity CreateLayoutOpportunity( NGBfcOffset start_offset(std::max(shelf.line_left, offset.line_offset), std::max(shelf.block_offset, offset.block_offset)); + // Max with |start_offset.line_offset| in case the shelf has a negative + // inline-size. NGBfcOffset end_offset( - std::min(shelf.line_right, offset.line_offset + inline_size), + std::max(std::min(shelf.line_right, offset.line_offset + inline_size), + start_offset.line_offset), LayoutUnit::Max()); - return NGLayoutOpportunity(NGBfcRect(start_offset, end_offset)); + return NGLayoutOpportunity( + NGBfcRect(start_offset, end_offset), + shelf.has_shape_exclusions + ? base::AdoptRef(new NGShapeExclusions(*shelf.shape_exclusions)) + : nullptr); } } // namespace @@ -148,9 +164,7 @@ NGLayoutOpportunity CreateLayoutOpportunity( NGExclusionSpace::NGExclusionSpace() : last_float_block_start_(LayoutUnit::Min()), left_float_clear_offset_(LayoutUnit::Min()), - right_float_clear_offset_(LayoutUnit::Min()), - has_left_float_(false), - has_right_float_(false) { + right_float_clear_offset_(LayoutUnit::Min()) { // The exclusion space must always have at least one shelf, at -Infinity. shelves_.push_back(NGShelf(/* block_offset */ LayoutUnit::Min())); } @@ -163,11 +177,9 @@ void NGExclusionSpace::Add(scoped_refptr<const NGExclusion> exclusion) { // Update the members used for clearance calculations. if (exclusion->type == EFloat::kLeft) { - has_left_float_ = true; left_float_clear_offset_ = std::max(left_float_clear_offset_, exclusion->rect.BlockEndOffset()); } else if (exclusion->type == EFloat::kRight) { - has_right_float_ = true; right_float_clear_offset_ = std::max(right_float_clear_offset_, exclusion->rect.BlockEndOffset()); } @@ -266,10 +278,14 @@ void NGExclusionSpace::Add(scoped_refptr<const NGExclusion> exclusion) { // Insert a closed-off layout opportunity if needed. if (has_solid_edges && is_overlapping) { - NGLayoutOpportunity opportunity(NGBfcRect( - /* start_offset */ {shelf.line_left, shelf.block_offset}, - /* end_offset */ {shelf.line_right, - exclusion->rect.BlockStartOffset()})); + NGLayoutOpportunity opportunity( + NGBfcRect( + /* start_offset */ {shelf.line_left, shelf.block_offset}, + /* end_offset */ {shelf.line_right, + exclusion->rect.BlockStartOffset()}), + shelf.has_shape_exclusions ? base::AdoptRef(new NGShapeExclusions( + *shelf.shape_exclusions)) + : nullptr); InsertOpportunity(opportunity, &opportunities_); } @@ -322,6 +338,7 @@ void NGExclusionSpace::Add(scoped_refptr<const NGExclusion> exclusion) { shelf.line_left = exclusion->rect.LineEndOffset(); shelf.line_left_edges.push_back(exclusion); } + shelf.shape_exclusions->line_left_shapes.push_back(exclusion); } else { DCHECK_EQ(exclusion->type, EFloat::kRight); if (exclusion->rect.LineStartOffset() <= shelf.line_right) { @@ -331,8 +348,20 @@ void NGExclusionSpace::Add(scoped_refptr<const NGExclusion> exclusion) { shelf.line_right = exclusion->rect.LineStartOffset(); shelf.line_right_edges.push_back(exclusion); } + shelf.shape_exclusions->line_right_shapes.push_back(exclusion); } + // We collect all exclusions in shape_exclusions (even if they don't + // have any shape data associated with them - normal floats need to be + // included in the shape line algorithm). We use this bool to track + // if the shape exclusions should be copied to the resulting layout + // opportunity. + if (exclusion->shape_data) + shelf.has_shape_exclusions = true; + + // Just in case the shelf has a negative inline-size. + shelf.line_right = std::max(shelf.line_left, shelf.line_right); + // We can end up in a situation where a shelf is the same as the // previous one. For example: // @@ -351,12 +380,7 @@ void NGExclusionSpace::Add(scoped_refptr<const NGExclusion> exclusion) { bool is_same_as_previous = (i > 0) && shelf.line_left == shelves_[i - 1].line_left && shelf.line_right == shelves_[i - 1].line_right; - - // The shelf also may now be non-existent. Note that zero inline size is - // allowed, since subsequent zero-size content may still fit there. - bool shelf_disappearing = shelf.line_right < shelf.line_left; - - if (is_same_as_previous || shelf_disappearing) { + if (is_same_as_previous) { shelves_.EraseAt(i); removed_shelf = true; } @@ -440,11 +464,11 @@ Vector<NGLayoutOpportunity> NGExclusionSpace::AllLayoutOpportunities( const LayoutUnit available_inline_size) const { Vector<NGLayoutOpportunity> opportunities; - auto shelves_it = shelves_.begin(); - auto opps_it = opportunities_.begin(); + auto* shelves_it = shelves_.begin(); + auto* opps_it = opportunities_.begin(); - const auto shelves_end = shelves_.end(); - const auto opps_end = opportunities_.end(); + auto* const shelves_end = shelves_.end(); + auto* const opps_end = opportunities_.end(); while (shelves_it != shelves_end || opps_it != opps_end) { // We should never exhaust the opportunities list before the shelves list, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h index 5af897497e8..190a934ae6e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h @@ -47,9 +47,6 @@ class CORE_EXPORT NGExclusionSpace { // Returns the block start offset of the last float added. LayoutUnit LastFloatBlockStart() const { return last_float_block_start_; } - bool HasLeftFloat() const { return has_left_float_; } - bool HasRightFloat() const { return has_right_float_; } - bool operator==(const NGExclusionSpace& other) const; bool operator!=(const NGExclusionSpace& other) const { return !(*this == other); @@ -90,7 +87,21 @@ class CORE_EXPORT NGExclusionSpace { explicit NGShelf(LayoutUnit block_offset) : block_offset(block_offset), line_left(LayoutUnit::Min()), - line_right(LayoutUnit::Max()) {} + line_right(LayoutUnit::Max()), + shape_exclusions(base::AdoptRef(new NGShapeExclusions)), + has_shape_exclusions(false) {} + + // The copy constructor explicitly copies the shape_exclusions member, + // instead of just incrementing the ref. + NGShelf(const NGShelf& other) + : block_offset(other.block_offset), + line_left(other.line_left), + line_right(other.line_right), + line_left_edges(other.line_left_edges), + line_right_edges(other.line_right_edges), + shape_exclusions( + base::AdoptRef(new NGShapeExclusions(*other.shape_exclusions))), + has_shape_exclusions(other.has_shape_exclusions) {} LayoutUnit block_offset; LayoutUnit line_left; @@ -98,6 +109,13 @@ class CORE_EXPORT NGExclusionSpace { Vector<scoped_refptr<const NGExclusion>, 1> line_left_edges; Vector<scoped_refptr<const NGExclusion>, 1> line_right_edges; + + // shape_exclusions contains all the floats which sit below this shelf. The + // has_shape_exclusions member will be true if shape_exclusions contains an + // exclusion with shape-outside specified (and therefore should be copied + // to any layout opportunity). + scoped_refptr<NGShapeExclusions> shape_exclusions; + bool has_shape_exclusions; }; private: @@ -147,9 +165,6 @@ class CORE_EXPORT NGExclusionSpace { // type of float. This is used for implementing float clearance. LayoutUnit left_float_clear_offset_; LayoutUnit right_float_clear_offset_; - - unsigned has_left_float_ : 1; - unsigned has_right_float_ : 1; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc index 40c283b3c8a..68c61fe092d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc @@ -97,16 +97,18 @@ TEST(NGExclusionSpaceTest, SingleExclusion) { TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(10), LayoutUnit(90)), NGBfcOffset(LayoutUnit(60), LayoutUnit::Max())); - // This will only produce two opportunities, as the RHS opportunity will be - // outside the search area. + // This will also produce three opportunities, as the RHS opportunity outside + // the search area creates a zero-width opportunity. opportunites = exclusion_space.AllLayoutOpportunities( /* offset */ {LayoutUnit(10), LayoutUnit(10)}, /* available_size */ LayoutUnit(49)); - EXPECT_EQ(2u, opportunites.size()); + EXPECT_EQ(3u, opportunites.size()); TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(10), LayoutUnit(10)), NGBfcOffset(LayoutUnit(59), LayoutUnit(20))); - TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(10), LayoutUnit(90)), + TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(60), LayoutUnit(10)), + NGBfcOffset(LayoutUnit(60), LayoutUnit::Max())); + TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(10), LayoutUnit(90)), NGBfcOffset(LayoutUnit(59), LayoutUnit::Max())); } @@ -127,12 +129,14 @@ TEST(NGExclusionSpaceTest, TwoExclusions) { /* offset */ {LayoutUnit(), LayoutUnit()}, /* available_size */ LayoutUnit(400)); - EXPECT_EQ(3u, opportunites.size()); + EXPECT_EQ(4u, opportunites.size()); TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(150), LayoutUnit()), NGBfcOffset(LayoutUnit(400), LayoutUnit(75))); - TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(75)), + TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(150), LayoutUnit()), + NGBfcOffset(LayoutUnit(150), LayoutUnit::Max())); + TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(75)), NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); - TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(150)), + TEST_OPPORTUNITY(opportunites[3], NGBfcOffset(LayoutUnit(), LayoutUnit(150)), NGBfcOffset(LayoutUnit(400), LayoutUnit::Max())); } @@ -286,5 +290,65 @@ TEST(NGExclusionSpaceTest, InsertBetweenShelves) { NGBfcOffset(LayoutUnit(60), LayoutUnit::Max())); } +TEST(NGExclusionSpaceTest, ZeroInlineSizeOpportunity) { + NGExclusionSpace exclusion_space; + + exclusion_space.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(100), LayoutUnit(10))), + EFloat::kLeft)); + + Vector<NGLayoutOpportunity> opportunites = + exclusion_space.AllLayoutOpportunities( + /* offset */ {LayoutUnit(), LayoutUnit()}, + /* available_size */ LayoutUnit(100)); + + EXPECT_EQ(2u, opportunites.size()); + TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(100), LayoutUnit()), + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); + TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)), + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); +} + +TEST(NGExclusionSpaceTest, NegativeInlineSizeOpportunityLeft) { + NGExclusionSpace exclusion_space; + + exclusion_space.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(120), LayoutUnit(10))), + EFloat::kLeft)); + + Vector<NGLayoutOpportunity> opportunites = + exclusion_space.AllLayoutOpportunities( + /* offset */ {LayoutUnit(), LayoutUnit()}, + /* available_size */ LayoutUnit(100)); + + EXPECT_EQ(2u, opportunites.size()); + TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(120), LayoutUnit()), + NGBfcOffset(LayoutUnit(120), LayoutUnit::Max())); + TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)), + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); +} + +TEST(NGExclusionSpaceTest, NegativeInlineSizeOpportunityRight) { + NGExclusionSpace exclusion_space; + + exclusion_space.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(-20), LayoutUnit()), + NGBfcOffset(LayoutUnit(100), LayoutUnit(10))), + EFloat::kRight)); + + Vector<NGLayoutOpportunity> opportunites = + exclusion_space.AllLayoutOpportunities( + /* offset */ {LayoutUnit(), LayoutUnit()}, + /* available_size */ LayoutUnit(100)); + + EXPECT_EQ(2u, opportunites.size()); + TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(), LayoutUnit::Max())); + TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)), + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); +} + } // namespace } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc new file mode 100644 index 00000000000..34d8691aac6 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc @@ -0,0 +1,172 @@ +// 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/ng/exclusions/ng_layout_opportunity.h" + +#include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" + +namespace blink { + +namespace { + +// Returns how far a line can "fit" into a given exclusion based on its shape +// area. If the exclusion does not obstruct the line, then the returned +// LineSegment will be "invalid". +LineSegment ExcludedSegment(const NGExclusion& exclusion, + LayoutUnit bfc_block_offset, + LayoutUnit line_block_size) { + DCHECK(exclusion.shape_data); + const NGExclusionShapeData& shape_data = *exclusion.shape_data; + const Shape& shape = + shape_data.layout_box->GetShapeOutsideInfo()->ComputedShape(); + + // Determine the block offset (relative to the shape) at which we need to + // test for. + LayoutUnit shape_relative_block_offset = + bfc_block_offset - + (exclusion.rect.BlockStartOffset() + shape_data.margins.block_start + + shape_data.shape_insets.block_start); + + // At the block-start/end of shapes it is possible for a line to just touch, + // and GetExcludedInterval will return a valid segment. + // This check skips the shape when this happens. + if (!shape.LineOverlapsShapeMarginBounds(shape_relative_block_offset, + line_block_size)) + return LineSegment(); + + // Clamp the line size to the size of the shape. + LayoutUnit clamped_line_block_size = + std::min(line_block_size, exclusion.rect.BlockSize() - + shape_data.shape_insets.BlockSum() - + shape_data.margins.BlockSum()); + + LineSegment segment = shape.GetExcludedInterval(shape_relative_block_offset, + clamped_line_block_size); + + // Adjust the segment offsets to be relative to the line-left margin edge. + LayoutUnit margin_delta = + shape_data.margins.LineLeft(TextDirection::kLtr) + + shape_data.shape_insets.LineLeft(TextDirection::kLtr); + segment.logical_left += margin_delta; + segment.logical_right += margin_delta; + + // Clamp the segment offsets to the size of the exclusion. + segment.logical_left = clampTo<LayoutUnit>(segment.logical_left, LayoutUnit(), + exclusion.rect.InlineSize()); + segment.logical_right = clampTo<LayoutUnit>( + segment.logical_right, LayoutUnit(), exclusion.rect.InlineSize()); + + // Make the segment offsets relative to the BFC coordinate space. + segment.logical_left += exclusion.rect.LineStartOffset(); + segment.logical_right += exclusion.rect.LineStartOffset(); + + return segment; +} + +// Returns if the given line block-size and offset intersects with the given +// exclusion. +bool IntersectsExclusion(const NGExclusion& exclusion, + LayoutUnit bfc_block_offset, + LayoutUnit line_block_size) { + return bfc_block_offset < exclusion.rect.BlockEndOffset() && + bfc_block_offset + line_block_size > exclusion.rect.BlockStartOffset(); +} + +} // namespace + +bool NGLayoutOpportunity::IsBlockDeltaBelowShapes( + LayoutUnit block_delta) const { + DCHECK(shape_exclusions); + + for (const auto& exclusion : shape_exclusions->line_left_shapes) { + if (rect.BlockStartOffset() + block_delta < + exclusion->rect.BlockEndOffset()) + return false; + } + + for (const auto& exclusion : shape_exclusions->line_right_shapes) { + if (rect.BlockStartOffset() + block_delta < + exclusion->rect.BlockEndOffset()) + return false; + } + + return true; +} + +NGLineLayoutOpportunity NGLayoutOpportunity::ComputeLineLayoutOpportunity( + const NGConstraintSpace& space, + LayoutUnit line_block_size, + LayoutUnit block_delta) const { + return NGLineLayoutOpportunity( + ComputeLineLeftOffset(space, line_block_size, block_delta), + ComputeLineRightOffset(space, line_block_size, block_delta), + rect.LineStartOffset(), rect.LineEndOffset(), + rect.BlockStartOffset() + block_delta, line_block_size); +} + +LayoutUnit NGLayoutOpportunity::ComputeLineLeftOffset( + const NGConstraintSpace& space, + LayoutUnit line_block_size, + LayoutUnit block_delta) const { + if (!shape_exclusions || shape_exclusions->line_left_shapes.IsEmpty()) + return rect.LineStartOffset(); + + LayoutUnit bfc_block_offset = rect.BlockStartOffset() + block_delta; + + // Step through each exclusion and re-build the line_left_offset. Without + // shapes this would be the same as the opportunity offset. + // + // We rebuild this offset from the line-left end, checking each exclusion and + // increasing the line_left when an exclusion intersects. + LayoutUnit line_left = space.BfcOffset().line_offset; + for (auto& exclusion : shape_exclusions->line_left_shapes) { + if (exclusion->shape_data) { + LineSegment segment = + ExcludedSegment(*exclusion, bfc_block_offset, line_block_size); + if (segment.is_valid) + line_left = std::max(line_left, segment.logical_right); + } else { + if (IntersectsExclusion(*exclusion, bfc_block_offset, line_block_size)) + line_left = std::max(line_left, exclusion->rect.LineEndOffset()); + } + } + + return std::min(line_left, rect.LineEndOffset()); +} + +LayoutUnit NGLayoutOpportunity::ComputeLineRightOffset( + const NGConstraintSpace& space, + LayoutUnit line_block_size, + LayoutUnit block_delta) const { + if (!shape_exclusions || shape_exclusions->line_right_shapes.IsEmpty()) + return rect.LineEndOffset(); + + LayoutUnit bfc_block_offset = rect.BlockStartOffset() + block_delta; + + LayoutUnit line_right = + space.BfcOffset().line_offset + space.AvailableSize().inline_size; + + // Step through each exclusion and re-build the line_right_offset. Without + // shapes this would be the same as the opportunity offset. + // + // We rebuild this offset from the line-right end, checking each exclusion and + // reducing the line_right when an exclusion intersects. + for (auto& exclusion : shape_exclusions->line_right_shapes) { + if (exclusion->shape_data) { + LineSegment segment = + ExcludedSegment(*exclusion, bfc_block_offset, line_block_size); + if (segment.is_valid) + line_right = std::min(line_right, segment.logical_left); + } else { + if (IntersectsExclusion(*exclusion, bfc_block_offset, line_block_size)) + line_right = std::min(line_right, exclusion->rect.LineStartOffset()); + } + } + + return std::max(line_right, rect.LineStartOffset()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h index 87e23b39c98..226f01d89df 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h @@ -6,23 +6,58 @@ #define NGLayoutOpportunity_h #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h" +#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_shape_exclusions.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h" namespace blink { +class NGConstraintSpace; + +// This struct represents an 2D-area where a NGFragment can fit within the +// exclusion space. A layout opportunity is produced by the exclusion space by +// calling FindLayoutOpportunity, or AllLayoutOpportunities. +// +// Its coordinates are relative to the BFC. struct CORE_EXPORT NGLayoutOpportunity { NGLayoutOpportunity() : rect(NGBfcOffset(LayoutUnit::Min(), LayoutUnit::Min()), NGBfcOffset(LayoutUnit::Max(), LayoutUnit::Max())) {} - explicit NGLayoutOpportunity(const NGBfcRect& rect) : rect(rect) {} + NGLayoutOpportunity( + const NGBfcRect& rect, + scoped_refptr<const NGShapeExclusions> shape_exclusions = nullptr) + : rect(rect), shape_exclusions(std::move(shape_exclusions)) {} // Rectangle in BFC coordinates that represents this opportunity. NGBfcRect rect; - // TODO(ikilpatrick): This will also need to hold all the adjacent exclusions - // on each edge for implementing css-shapes correctly. It'll need to have - // methods which use these adjacent exclusions to determine the available - // inline-size for a line-box. + // The shape exclusions hold all of the adjacent exclusions which may affect + // the line layout opportunity when queried. May be null if no shapes are + // present. + scoped_refptr<const NGShapeExclusions> shape_exclusions; + + // Returns if the opportunity has any shapes which may affect a line layout + // opportunity. + bool HasShapeExclusions() const { return shape_exclusions.get(); } + + // Returns if the given delta (relative to the start of the opportunity) will + // be below any shapes. + bool IsBlockDeltaBelowShapes(LayoutUnit block_delta) const; + + // Calculates a line layout opportunity which takes into account any shapes + // which may affect the available inline size for the line breaker. + NGLineLayoutOpportunity ComputeLineLayoutOpportunity( + const NGConstraintSpace&, + LayoutUnit line_block_size, + LayoutUnit block_delta) const; + + private: + LayoutUnit ComputeLineLeftOffset(const NGConstraintSpace&, + LayoutUnit line_block_size, + LayoutUnit block_delta) const; + LayoutUnit ComputeLineRightOffset(const NGConstraintSpace&, + LayoutUnit line_block_size, + LayoutUnit block_delta) const; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h new file mode 100644 index 00000000000..ee9f8b10141 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_LINE_LAYOUT_OPPORTUNITY_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_LINE_LAYOUT_OPPORTUNITY_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/layout_unit.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" + +namespace blink { + +// This struct represents a 1D-area where a line can fit. It is only used for +// representing the inline size. +struct CORE_EXPORT NGLineLayoutOpportunity { + STACK_ALLOCATED(); + + NGLineLayoutOpportunity() {} + NGLineLayoutOpportunity(LayoutUnit inline_size) + : line_right_offset(inline_size), float_line_right_offset(inline_size) {} + NGLineLayoutOpportunity(LayoutUnit line_left_offset, + LayoutUnit line_right_offset, + LayoutUnit float_line_left_offset, + LayoutUnit float_line_right_offset, + LayoutUnit bfc_block_offset, + LayoutUnit line_block_size) + : line_left_offset(line_left_offset), + line_right_offset(line_right_offset), + float_line_left_offset(float_line_left_offset), + float_line_right_offset(float_line_right_offset), + bfc_block_offset(bfc_block_offset), + line_block_size(line_block_size) {} + + // The available inline-size of the line, taking shapes into account. Both + // offsets are relative to the BFC coordinate system. + LayoutUnit line_left_offset; + LayoutUnit line_right_offset; + + // The available inline-size of the line *for floats*. This is the same size + // as the layout opportunity which generated this object. Both offsets are + // relative to the BFC coordinate system. + LayoutUnit float_line_left_offset; + LayoutUnit float_line_right_offset; + + LayoutUnit bfc_block_offset; + + // The block-size this line layout opportunity was created with. Should + // *only* be used for re-querying for a new line layout opportunity with the + // same block-size. + LayoutUnit line_block_size; + + LayoutUnit AvailableInlineSize() const { + DCHECK_GE(line_right_offset, line_left_offset); + return line_right_offset - line_left_offset; + } + + LayoutUnit AvailableFloatInlineSize() const { + DCHECK_GE(float_line_right_offset, float_line_left_offset); + return float_line_right_offset - float_line_left_offset; + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_LINE_LAYOUT_OPPORTUNITY_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_shape_exclusions.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_shape_exclusions.h new file mode 100644 index 00000000000..8130c956c7f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_shape_exclusions.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_SHAPE_EXCLUSIONS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_SHAPE_EXCLUSIONS_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/ng_exclusion.h" +#include "third_party/blink/renderer/platform/layout_unit.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// This struct represents exclusions which have shape data associated with them. +// As shapes are relatively uncommon we store these as a separate struct, and +// allocate only when necessary. +// +// This struct can belong to either a NGShelf within the exclusion space, or on +// NGLayoutOpportunity. Outside these classes normal code shouldn't interact +// with this class. +class CORE_EXPORT NGShapeExclusions : public RefCounted<NGShapeExclusions> { + public: + NGShapeExclusions() {} + NGShapeExclusions(const NGShapeExclusions& other) + : line_left_shapes(other.line_left_shapes), + line_right_shapes(other.line_right_shapes) {} + Vector<scoped_refptr<const NGExclusion>> line_left_shapes; + Vector<scoped_refptr<const NGExclusion>> line_right_shapes; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_SHAPE_EXCLUSIONS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h index 2c1f86493be..c6566b7652c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h @@ -60,6 +60,20 @@ struct CORE_EXPORT NGBoxStrut { return result; } + NGBoxStrut& operator-=(const NGBoxStrut& other) { + inline_start -= other.inline_start; + inline_end -= other.inline_end; + block_start -= other.block_start; + block_end -= other.block_end; + return *this; + } + + NGBoxStrut operator-(const NGBoxStrut& other) { + NGBoxStrut result(*this); + result -= other; + return result; + } + bool operator==(const NGBoxStrut& other) const; String ToString() const; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_edge.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_edge.h deleted file mode 100644 index e68fec28352..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_edge.h +++ /dev/null @@ -1,21 +0,0 @@ -// 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. - -#ifndef NGEdge_h -#define NGEdge_h - -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/layout_unit.h" - -namespace blink { - -// Struct to represent a simple edge that has start and end. -struct CORE_EXPORT NGEdge { - LayoutUnit start; - LayoutUnit end; -}; - -} // namespace blink - -#endif // NGEdge_h diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc index dab6bf141fa..37dc6f0a2da 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc @@ -4,7 +4,8 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/platform/geometry/float_rect.h" +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" +#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -35,6 +36,23 @@ void NGPhysicalOffsetRect::Unite(const NGPhysicalOffsetRect& other) { size = {right - left, bottom - top}; } +void NGPhysicalOffsetRect::Expand(const NGPhysicalBoxStrut& strut) { + if (strut.top) { + offset.top -= strut.top; + size.height += strut.top; + } + if (strut.bottom) { + size.height += strut.bottom; + } + if (strut.left) { + offset.left -= strut.left; + size.width += strut.left; + } + if (strut.right) { + size.width += strut.right; + } +} + NGPhysicalOffsetRect::NGPhysicalOffsetRect(const LayoutRect& source) : NGPhysicalOffsetRect({source.X(), source.Y()}, {source.Width(), source.Height()}) {} @@ -43,6 +61,15 @@ LayoutRect NGPhysicalOffsetRect::ToLayoutRect() const { return {offset.left, offset.top, size.width, size.height}; } +LayoutRect NGPhysicalOffsetRect::ToLayoutFlippedRect( + const ComputedStyle& style, + const NGPhysicalSize& container_size) const { + if (!style.IsFlippedBlocksWritingMode()) + return {offset.left, offset.top, size.width, size.height}; + return {container_size.width - offset.left - size.width, offset.top, + size.width, size.height}; +} + FloatRect NGPhysicalOffsetRect::ToFloatRect() const { return {offset.left, offset.top, size.width, size.height}; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h index c41a79ddaf4..723c8961491 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h @@ -12,8 +12,10 @@ namespace blink { +class ComputedStyle; class FloatRect; class LayoutRect; +struct NGPhysicalBoxStrut; // NGPhysicalOffsetRect is the position and size of a rect (typically a // fragment) relative to its parent rect in the physical coordinate system. @@ -36,10 +38,15 @@ struct CORE_EXPORT NGPhysicalOffsetRect { void Unite(const NGPhysicalOffsetRect&); + void Expand(const NGPhysicalBoxStrut&); + // Conversions from/to existing code. New code prefers type safety for // logical/physical distinctions. explicit NGPhysicalOffsetRect(const LayoutRect&); LayoutRect ToLayoutRect() const; + LayoutRect ToLayoutFlippedRect(const ComputedStyle&, + const NGPhysicalSize&) const; + FloatRect ToFloatRect() const; String ToString() const; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h new file mode 100644 index 00000000000..8ea80821037 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_LAYOUT_NG_TEXT_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_LAYOUT_NG_TEXT_H_ + +#include "third_party/blink/renderer/core/layout/layout_text.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" + +namespace blink { + +class NGInlineItem; + +// This overrides the default LayoutText to reference LayoutNGInlineItems +// instead of InlineTextBoxes. +// +// ***** INLINE ITEMS OWNERSHIP ***** +// NGInlineItems in items_ are not owned by LayoutText but are pointers into the +// LayoutNGBlockFlow's items_. Should not be accessed outside of layout. +class CORE_EXPORT LayoutNGText : public LayoutText { + public: + LayoutNGText(Node* node, scoped_refptr<StringImpl> text) + : LayoutText(node, text) {} + + bool IsOfType(LayoutObjectType type) const override { + return type == kLayoutObjectNGText || LayoutText::IsOfType(type); + } + + bool HasValidLayout() const { return valid_ng_items_; } + const Vector<NGInlineItem*>& InlineItems() const { + DCHECK(valid_ng_items_); + return inline_items_; + } + + // Inline items depends on context. It needs to be invalidated not only when + // it was inserted/changed but also it was moved. + void InvalidateInlineItems() { valid_ng_items_ = false; } + + void ClearInlineItems() { + inline_items_.clear(); + valid_ng_items_ = false; + } + + void AddInlineItem(NGInlineItem* item) { + inline_items_.push_back(item); + valid_ng_items_ = true; + } + + protected: + void InsertedIntoTree() override { + valid_ng_items_ = false; + LayoutText::InsertedIntoTree(); + } + + private: + Vector<NGInlineItem*> inline_items_; +}; + +DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGText, IsLayoutNGText()); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_LAYOUT_NG_TEXT_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc new file mode 100644 index 00000000000..cadd9c38992 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc @@ -0,0 +1,313 @@ +// 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/ng/inline/ng_caret_position.h" + +#include "third_party/blink/renderer/core/editing/inline_box_traversal.h" +#include "third_party/blink/renderer/core/editing/position_with_affinity.h" +#include "third_party/blink/renderer/core/editing/text_affinity.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h" + +namespace blink { + +namespace { + +void AssertValidPositionForCaretPositionComputation( + const PositionWithAffinity& position) { +#if DCHECK_IS_ON() + DCHECK(NGOffsetMapping::AcceptsPosition(position.GetPosition())); + const LayoutObject* layout_object = position.AnchorNode()->GetLayoutObject(); + DCHECK(layout_object); + DCHECK(layout_object->IsText() || layout_object->IsAtomicInlineLevel()); +#endif +} + +// The calculation takes the following input: +// - An inline formatting context as a |LayoutBlockFlow| +// - An offset in the |text_content_| string of the above context +// - A TextAffinity +// +// The calculation iterates all inline fragments in the context, and tries to +// compute an NGCaretPosition using the "caret resolution process" below: +// +// The (offset, affinity) pair is compared against each inline fragment to see +// if the corresponding caret should be placed in the fragment, using the +// |TryResolveCaretPositionInXXX()| functions. These functions may return: +// - Failed, indicating that the caret must not be placed in the fragment; +// - Resolved, indicating that the care should be placed in the fragment, and +// no further search is required. The result NGCaretPosition is returned +// together. +// - FoundCandidate, indicating that the caret may be placed in the fragment; +// however, further search may find a better position. The candidate +// NGCaretPosition is also returned together. + +enum class ResolutionType { kFailed, kFoundCandidate, kResolved }; +struct CaretPositionResolution { + ResolutionType type = ResolutionType::kFailed; + NGCaretPosition caret_position; +}; + +bool CanResolveCaretPositionBeforeFragment(const NGPaintFragment& fragment, + TextAffinity affinity) { + if (affinity == TextAffinity::kDownstream) + return true; + const NGPaintFragment* current_line_paint = fragment.ContainerLineBox(); + const NGPhysicalLineBoxFragment& current_line = + ToNGPhysicalLineBoxFragment(current_line_paint->PhysicalFragment()); + // A fragment after line wrap must be the first logical leaf in its line. + if (&fragment.PhysicalFragment() != current_line.FirstLogicalLeaf()) + return true; + const NGPaintFragment* last_line_paint = + NGPaintFragmentTraversal::PreviousLineOf(*current_line_paint); + return !last_line_paint || + !ToNGPhysicalLineBoxFragment(last_line_paint->PhysicalFragment()) + .HasSoftWrapToNextLine(); +} + +bool CanResolveCaretPositionAfterFragment(const NGPaintFragment& fragment, + TextAffinity affinity) { + if (affinity == TextAffinity::kUpstream) + return true; + const NGPaintFragment* current_line_paint = fragment.ContainerLineBox(); + const NGPhysicalLineBoxFragment& current_line = + ToNGPhysicalLineBoxFragment(current_line_paint->PhysicalFragment()); + // A fragment before line wrap must be the last logical leaf in its line. + if (&fragment.PhysicalFragment() != current_line.LastLogicalLeaf()) + return true; + return !current_line.HasSoftWrapToNextLine(); +} + +// Returns a |kFailed| resolution if |offset| doesn't belong to the text +// fragment. Otherwise, return either |kFoundCandidate| or |kResolved| depending +// on |affinity|. +CaretPositionResolution TryResolveCaretPositionInTextFragment( + const NGPaintFragment& paint_fragment, + unsigned offset, + TextAffinity affinity) { + DCHECK(paint_fragment.PhysicalFragment().IsText()); + const NGPhysicalTextFragment& fragment = + ToNGPhysicalTextFragment(paint_fragment.PhysicalFragment()); + if (fragment.IsAnonymousText()) + return CaretPositionResolution(); + + const NGOffsetMapping& mapping = + *NGOffsetMapping::GetFor(paint_fragment.GetLayoutObject()); + + // A text fragment natually allows caret placement in offset range + // [StartOffset(), EndOffset()], i.e., from before the first character to + // after the last character. + // Besides, leading/trailing bidi control characters are ignored since their + // two sides are considered the same caret position. Hence, if there are n and + // m leading and trailing bidi control characters, then the allowed offset + // range is [StartOffset() - n, EndOffset() + m]. + // Note that we don't ignore other characters that are not in fragments. For + // example, a trailing space of a line is not in any fragment, but its two + // sides are still different caret positions, so we don't ignore it. + if (offset < fragment.StartOffset() && + !mapping.HasBidiControlCharactersOnly(offset, fragment.StartOffset())) + return CaretPositionResolution(); + if (offset > fragment.EndOffset() && + !mapping.HasBidiControlCharactersOnly(fragment.EndOffset(), offset)) + return CaretPositionResolution(); + + offset = std::max(offset, fragment.StartOffset()); + offset = std::min(offset, fragment.EndOffset()); + NGCaretPosition candidate = {&paint_fragment, + NGCaretPositionType::kAtTextOffset, offset}; + + // Offsets in the interior of a fragment can be resolved directly. + if (offset > fragment.StartOffset() && offset < fragment.EndOffset()) + return {ResolutionType::kResolved, candidate}; + + if (offset == fragment.StartOffset() && + CanResolveCaretPositionBeforeFragment(paint_fragment, affinity)) { + return {ResolutionType::kResolved, candidate}; + } + + if (offset == fragment.EndOffset() && !fragment.IsLineBreak() && + CanResolveCaretPositionAfterFragment(paint_fragment, affinity)) { + return {ResolutionType::kResolved, candidate}; + } + + // We may have a better candidate + return {ResolutionType::kFoundCandidate, candidate}; +} + +unsigned GetTextOffsetBefore(const NGPhysicalFragment& fragment) { + // TODO(xiaochengh): Design more straightforward way to get text offset of + // atomic inline box. + DCHECK(fragment.IsAtomicInline()); + const Node* node = fragment.GetNode(); + DCHECK(node); + const Position before_node = Position::BeforeNode(*node); + base::Optional<unsigned> maybe_offset_before = + NGOffsetMapping::GetFor(before_node)->GetTextContentOffset(before_node); + // We should have offset mapping for atomic inline boxes. + DCHECK(maybe_offset_before.has_value()); + return maybe_offset_before.value(); +} + +// Returns a |kFailed| resolution if |offset| doesn't belong to the atomic +// inline box fragment. Otherwise, return either |kFoundCandidate| or +// |kResolved| depending on |affinity|. +CaretPositionResolution TryResolveCaretPositionByBoxFragmentSide( + const NGPaintFragment& fragment, + unsigned offset, + TextAffinity affinity) { + if (!fragment.GetNode()) { + // TODO(xiaochengh): This leads to false negatives for, e.g., RUBY, where an + // anonymous wrapping inline block is created. + return CaretPositionResolution(); + } + + const unsigned offset_before = + GetTextOffsetBefore(fragment.PhysicalFragment()); + const unsigned offset_after = offset_before + 1; + // TODO(xiaochengh): Ignore bidi control characters before & after the box. + if (offset != offset_before && offset != offset_after) + return CaretPositionResolution(); + const NGCaretPositionType position_type = + offset == offset_before ? NGCaretPositionType::kBeforeBox + : NGCaretPositionType::kAfterBox; + NGCaretPosition candidate{&fragment, position_type, base::nullopt}; + + if (offset == offset_before && + CanResolveCaretPositionBeforeFragment(fragment, affinity)) { + return {ResolutionType::kResolved, candidate}; + } + + if (offset == offset_after && + CanResolveCaretPositionAfterFragment(fragment, affinity)) { + return {ResolutionType::kResolved, candidate}; + } + + return {ResolutionType::kFoundCandidate, candidate}; +} + +CaretPositionResolution TryResolveCaretPositionWithFragment( + const NGPaintFragment& paint_fragment, + unsigned offset, + TextAffinity affinity) { + const NGPhysicalFragment& fragment = paint_fragment.PhysicalFragment(); + if (fragment.IsText()) { + return TryResolveCaretPositionInTextFragment(paint_fragment, offset, + affinity); + } + if (fragment.IsBox() && fragment.IsAtomicInline()) { + return TryResolveCaretPositionByBoxFragmentSide(paint_fragment, offset, + affinity); + } + return CaretPositionResolution(); +} + +bool NeedsBidiAdjustment(const NGCaretPosition& caret_position) { + if (caret_position.IsNull()) + return false; + if (caret_position.position_type != NGCaretPositionType::kAtTextOffset) + return true; + DCHECK(caret_position.text_offset.has_value()); + DCHECK(caret_position.fragment->PhysicalFragment().IsText()); + const NGPhysicalTextFragment& text_fragment = + ToNGPhysicalTextFragment(caret_position.fragment->PhysicalFragment()); + DCHECK_GE(*caret_position.text_offset, text_fragment.StartOffset()); + DCHECK_LE(*caret_position.text_offset, text_fragment.EndOffset()); + // Bidi adjustment is needed only for caret positions at bidi boundaries. + // Caret positions in the middle of a text fragment can't be at bidi + // boundaries, and hence, don't need any adjustment. + return *caret_position.text_offset == text_fragment.StartOffset() || + *caret_position.text_offset == text_fragment.EndOffset(); +} + +NGCaretPosition AdjustCaretPositionForBidiText( + const NGCaretPosition& caret_position) { + if (!NeedsBidiAdjustment(caret_position)) + return caret_position; + return BidiAdjustment::AdjustForCaretPositionResolution(caret_position); +} + +} // namespace + +// The main function for compute an NGCaretPosition. See the comments at the top +// of this file for details. +NGCaretPosition ComputeNGCaretPosition(const LayoutBlockFlow& context, + unsigned offset, + TextAffinity affinity) { + const NGPaintFragment* root_fragment = context.PaintFragment(); + DCHECK(root_fragment) << "no paint fragment on layout object " << &context; + + NGCaretPosition candidate; + for (const auto& child : + NGPaintFragmentTraversal::InlineDescendantsOf(*root_fragment)) { + const CaretPositionResolution resolution = + TryResolveCaretPositionWithFragment(*child.fragment, offset, affinity); + + if (resolution.type == ResolutionType::kFailed) + continue; + + // TODO(xiaochengh): Handle caret poisition in empty container (e.g. empty + // line box). + + if (resolution.type == ResolutionType::kResolved) + return AdjustCaretPositionForBidiText(resolution.caret_position); + + DCHECK_EQ(ResolutionType::kFoundCandidate, resolution.type); + // TODO(xiaochengh): We are not sure if we can ever find multiple + // candidates. Handle it once reached. + DCHECK(candidate.IsNull()); + candidate = resolution.caret_position; + } + + return AdjustCaretPositionForBidiText(candidate); +} + +NGCaretPosition ComputeNGCaretPosition(const PositionWithAffinity& position) { + AssertValidPositionForCaretPositionComputation(position); + const LayoutBlockFlow* context = + NGInlineFormattingContextOf(position.GetPosition()); + if (!context) + return NGCaretPosition(); + + const NGOffsetMapping* mapping = NGOffsetMapping::GetFor(context); + DCHECK(mapping); + const base::Optional<unsigned> maybe_offset = + mapping->GetTextContentOffset(position.GetPosition()); + if (!maybe_offset.has_value()) { + // TODO(xiaochengh): Investigate if we reach here. + NOTREACHED(); + return NGCaretPosition(); + } + + const unsigned offset = maybe_offset.value(); + const TextAffinity affinity = position.Affinity(); + return ComputeNGCaretPosition(*context, offset, affinity); +} + +Position NGCaretPosition::ToPositionInDOMTree() const { + if (!fragment) + return Position(); + switch (position_type) { + case NGCaretPositionType::kBeforeBox: + if (!fragment->GetNode()) + return Position(); + return Position::BeforeNode(*fragment->GetNode()); + case NGCaretPositionType::kAfterBox: + if (!fragment->GetNode()) + return Position(); + return Position::AfterNode(*fragment->GetNode()); + case NGCaretPositionType::kAtTextOffset: + DCHECK(text_offset.has_value()); + const NGOffsetMapping* mapping = + NGOffsetMapping::GetFor(fragment->GetLayoutObject()); + return mapping->GetFirstPosition(*text_offset); + } + NOTREACHED(); + return Position(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h new file mode 100644 index 00000000000..e6a963099c6 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_POSITION_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_POSITION_H_ + +#include "base/optional.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/editing/forward.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" + +namespace blink { + +class NGPaintFragment; +class LayoutBlockFlow; + +// An NGCaretPosition indicates a caret position relative to an inline +// NGPaintFragment: +// - When |fragment| is box, |position_type| is either |kBeforeBox| or +// |kAfterBox|, indicating either of the two caret positions by the box sides; +// |text_offset| is |nullopt| in this case. +// - When |fragment| is text, |position_type| is |kAtTextOffset|, and +// |text_offset| is in the text offset range of the fragment. +// +// TODO(xiaochengh): Support "in empty container" caret type + +enum class NGCaretPositionType { kBeforeBox, kAfterBox, kAtTextOffset }; +struct NGCaretPosition { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + + bool IsNull() const { return !fragment; } + + Position ToPositionInDOMTree() const; + + const NGPaintFragment* fragment = nullptr; // owned by root LayoutNGMixin + NGCaretPositionType position_type; + base::Optional<unsigned> text_offset; +}; + +// Given an inline formatting context, a text offset in the context and a text +// affinity, returns the corresponding NGCaretPosition, or null if not found. +// Note that in many cases, null result indicates that we have reached an +// unexpected case that is not properly handled. +CORE_EXPORT NGCaretPosition ComputeNGCaretPosition(const LayoutBlockFlow&, + unsigned, + TextAffinity); + +// Shorthand of the above when the input is a position instead of a +// (context, offset) pair. +NGCaretPosition ComputeNGCaretPosition(const PositionWithAffinity&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_POSITION_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position_test.cc index 1ed9a35639e..da232c2bf83 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position_test.cc @@ -2,8 +2,9 @@ // 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/inline/ng_caret_rect.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h" +#include "third_party/blink/renderer/core/editing/text_affinity.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" @@ -14,9 +15,9 @@ namespace blink { -class NGCaretRectTest : public NGLayoutTest { +class NGCaretPositionTest : public NGLayoutTest { public: - NGCaretRectTest() : NGLayoutTest() {} + NGCaretPositionTest() : NGLayoutTest() {} void SetUp() override { NGLayoutTest::SetUp(); @@ -69,31 +70,31 @@ class NGCaretRectTest : public NGLayoutTest { EXPECT_EQ(caret.text_offset, offset_) << caret.text_offset.value_or(-1); \ } -TEST_F(NGCaretRectTest, CaretPositionInOneLineOfText) { +TEST_F(NGCaretPositionTest, CaretPositionInOneLineOfText) { SetInlineFormattingContext("t", "foo", 3); const Node* text = container_->firstChild(); const NGPhysicalFragment* text_fragment = FragmentOf(text); // Beginning of line TEST_CARET(ComputeNGCaretPosition(0, TextAffinity::kDownstream), - text_fragment, kAtTextOffset, Optional<unsigned>(0)); + text_fragment, kAtTextOffset, base::Optional<unsigned>(0)); TEST_CARET(ComputeNGCaretPosition(0, TextAffinity::kUpstream), text_fragment, - kAtTextOffset, Optional<unsigned>(0)); + kAtTextOffset, base::Optional<unsigned>(0)); // Middle in the line TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kDownstream), - text_fragment, kAtTextOffset, Optional<unsigned>(1)); + text_fragment, kAtTextOffset, base::Optional<unsigned>(1)); TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kUpstream), text_fragment, - kAtTextOffset, Optional<unsigned>(1)); + kAtTextOffset, base::Optional<unsigned>(1)); // End of line TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kDownstream), - text_fragment, kAtTextOffset, Optional<unsigned>(3)); + text_fragment, kAtTextOffset, base::Optional<unsigned>(3)); TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kUpstream), text_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); } -TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrap) { +TEST_F(NGCaretPositionTest, CaretPositionAtSoftLineWrap) { SetInlineFormattingContext("t", "foobar", 3); const Node* text = container_->firstChild(); const auto text_fragments = NGInlineFragmentTraversal::SelfFragmentsOf( @@ -102,12 +103,12 @@ TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrap) { const NGPhysicalFragment* bar_fragment = text_fragments[1].fragment.get(); TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kDownstream), bar_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kUpstream), foo_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); } -TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapWithSpace) { +TEST_F(NGCaretPositionTest, CaretPositionAtSoftLineWrapWithSpace) { SetInlineFormattingContext("t", "foo bar", 3); const Node* text = container_->firstChild(); const auto text_fragments = NGInlineFragmentTraversal::SelfFragmentsOf( @@ -117,18 +118,18 @@ TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapWithSpace) { // Before the space TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kDownstream), foo_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kUpstream), foo_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); // After the space TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kDownstream), bar_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kUpstream), bar_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); } -TEST_F(NGCaretRectTest, CaretPositionAtForcedLineBreak) { +TEST_F(NGCaretPositionTest, CaretPositionAtForcedLineBreak) { SetInlineFormattingContext("t", "foo<br>bar", 3); const Node* foo = container_->firstChild(); const Node* br = foo->nextSibling(); @@ -138,18 +139,18 @@ TEST_F(NGCaretRectTest, CaretPositionAtForcedLineBreak) { // Before the BR TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kDownstream), foo_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); TEST_CARET(ComputeNGCaretPosition(3, TextAffinity::kUpstream), foo_fragment, - kAtTextOffset, Optional<unsigned>(3)); + kAtTextOffset, base::Optional<unsigned>(3)); // After the BR TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kDownstream), bar_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kUpstream), bar_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); } -TEST_F(NGCaretRectTest, CaretPositionAtEmptyLine) { +TEST_F(NGCaretPositionTest, CaretPositionAtEmptyLine) { SetInlineFormattingContext("f", "foo<br><br>bar", 3); const Node* foo = container_->firstChild(); const Node* br1 = foo->nextSibling(); @@ -157,30 +158,30 @@ TEST_F(NGCaretRectTest, CaretPositionAtEmptyLine) { const NGPhysicalFragment* br2_fragment = FragmentOf(br2); TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kDownstream), br2_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); TEST_CARET(ComputeNGCaretPosition(4, TextAffinity::kUpstream), br2_fragment, - kAtTextOffset, Optional<unsigned>(4)); + kAtTextOffset, base::Optional<unsigned>(4)); } -TEST_F(NGCaretRectTest, CaretPositionInOneLineOfImage) { +TEST_F(NGCaretPositionTest, CaretPositionInOneLineOfImage) { SetInlineFormattingContext("t", "<img>", 3); const Node* img = container_->firstChild(); const NGPhysicalFragment* img_fragment = FragmentOf(img); // Before the image TEST_CARET(ComputeNGCaretPosition(0, TextAffinity::kDownstream), img_fragment, - kBeforeBox, WTF::nullopt); + kBeforeBox, base::nullopt); TEST_CARET(ComputeNGCaretPosition(0, TextAffinity::kUpstream), img_fragment, - kBeforeBox, WTF::nullopt); + kBeforeBox, base::nullopt); // After the image TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kDownstream), img_fragment, - kAfterBox, WTF::nullopt); + kAfterBox, base::nullopt); TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kUpstream), img_fragment, - kAfterBox, WTF::nullopt); + kAfterBox, base::nullopt); } -TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenImages) { +TEST_F(NGCaretPositionTest, CaretPositionAtSoftLineWrapBetweenImages) { SetInlineFormattingContext("t", "<img id=img1><img id=img2>" "<style>img{width: 1em; height: 1em}</style>", @@ -191,12 +192,13 @@ TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenImages) { const NGPhysicalFragment* img2_fragment = FragmentOf(img2); TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kDownstream), - img2_fragment, kBeforeBox, WTF::nullopt); + img2_fragment, kBeforeBox, base::nullopt); TEST_CARET(ComputeNGCaretPosition(1, TextAffinity::kUpstream), img1_fragment, - kAfterBox, WTF::nullopt); + kAfterBox, base::nullopt); } -TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenMultipleTextNodes) { +TEST_F(NGCaretPositionTest, + CaretPositionAtSoftLineWrapBetweenMultipleTextNodes) { SetInlineFormattingContext("t", "<span>A</span>" "<span>B</span>" @@ -216,12 +218,12 @@ TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenMultipleTextNodes) { mapping.GetTextContentOffset(wrap_position).value(); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kUpstream), - fragment_c, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_c, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kDownstream), - fragment_d, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_d, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); } -TEST_F(NGCaretRectTest, +TEST_F(NGCaretPositionTest, CaretPositionAtSoftLineWrapBetweenMultipleTextNodesRtl) { SetInlineFormattingContext("t", "<span>A</span>" @@ -242,12 +244,12 @@ TEST_F(NGCaretRectTest, mapping.GetTextContentOffset(wrap_position).value(); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kUpstream), - fragment_c, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_c, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kDownstream), - fragment_d, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_d, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); } -TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenDeepTextNodes) { +TEST_F(NGCaretPositionTest, CaretPositionAtSoftLineWrapBetweenDeepTextNodes) { SetInlineFormattingContext( "t", "<style>span {border: 1px solid black}</style>" @@ -269,9 +271,9 @@ TEST_F(NGCaretRectTest, CaretPositionAtSoftLineWrapBetweenDeepTextNodes) { mapping.GetTextContentOffset(wrap_position).value(); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kUpstream), - fragment_c, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_c, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); TEST_CARET(ComputeNGCaretPosition(wrap_offset, TextAffinity::kDownstream), - fragment_d, kAtTextOffset, Optional<unsigned>(wrap_offset)); + fragment_d, kAtTextOffset, base::Optional<unsigned>(wrap_offset)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc index 7b009be5ee1..b0e7e25ba52 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc @@ -5,187 +5,18 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h" #include "third_party/blink/renderer/core/editing/local_caret_rect.h" -#include "third_party/blink/renderer/core/editing/position_with_affinity.h" +#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" -#include "third_party/blink/renderer/core/layout/layout_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" -#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h" -#include "third_party/blink/renderer/platform/fonts/character_range.h" namespace blink { namespace { -// ------------------------------------- - -// Caret position calculation and its helpers. - -// The calculation takes the following input: -// - An inline formatting context as a |LayoutBlockFlow| -// - An offset in the |text_content_| string of the above context -// - A TextAffinity -// -// The calculation iterates all inline fragments in the context, and tries to -// compute an NGCaretPosition using the "caret resolution process" below: -// -// The (offset, affinity) pair is compared against each inline fragment to see -// if the corresponding caret should be placed in the fragment, using the -// |TryResolveCaretPositionInXXX()| functions. These functions may return: -// - Failed, indicating that the caret must not be placed in the fragment; -// - Resolved, indicating that the care should be placed in the fragment, and -// no further search is required. The result NGCaretPosition is returned -// together. -// - FoundCandidate, indicating that the caret may be placed in the fragment; -// however, further search may find a better position. The candidate -// NGCaretPosition is also returned together. - -enum class ResolutionType { kFailed, kFoundCandidate, kResolved }; -struct CaretPositionResolution { - ResolutionType type = ResolutionType::kFailed; - NGCaretPosition caret_position; -}; - -bool CanResolveCaretPositionBeforeFragment(const NGPaintFragment& fragment, - TextAffinity affinity) { - if (affinity == TextAffinity::kDownstream) - return true; - const NGPaintFragment* current_line_paint = fragment.ContainerLineBox(); - const NGPhysicalLineBoxFragment& current_line = - ToNGPhysicalLineBoxFragment(current_line_paint->PhysicalFragment()); - // A fragment after line wrap must be the first logical leaf in its line. - if (&fragment.PhysicalFragment() != current_line.FirstLogicalLeaf()) - return true; - const NGPaintFragment* last_line_paint = - NGPaintFragmentTraversal::PreviousLineOf(*current_line_paint); - return !last_line_paint || - !ToNGPhysicalLineBoxFragment(last_line_paint->PhysicalFragment()) - .HasSoftWrapToNextLine(); -} - -bool CanResolveCaretPositionAfterFragment(const NGPaintFragment& fragment, - TextAffinity affinity) { - if (affinity == TextAffinity::kUpstream) - return true; - const NGPaintFragment* current_line_paint = fragment.ContainerLineBox(); - const NGPhysicalLineBoxFragment& current_line = - ToNGPhysicalLineBoxFragment(current_line_paint->PhysicalFragment()); - // A fragment before line wrap must be the last logical leaf in its line. - if (&fragment.PhysicalFragment() != current_line.LastLogicalLeaf()) - return true; - return !current_line.HasSoftWrapToNextLine(); -} - -CaretPositionResolution TryResolveCaretPositionInTextFragment( - const NGPaintFragment& paint_fragment, - unsigned offset, - TextAffinity affinity) { - DCHECK(paint_fragment.PhysicalFragment().IsText()); - const NGPhysicalTextFragment& fragment = - ToNGPhysicalTextFragment(paint_fragment.PhysicalFragment()); - if (fragment.IsAnonymousText()) - return CaretPositionResolution(); - - // [StartOffset(), EndOffset()] is the range allowing caret placement. - // For example, "foo" has 4 offsets allowing caret placement. - if (offset < fragment.StartOffset() || offset > fragment.EndOffset()) { - // TODO(xiaochengh): This may introduce false negatives. Investigate. - return CaretPositionResolution(); - } - NGCaretPosition candidate = {&paint_fragment, - NGCaretPositionType::kAtTextOffset, offset}; - - // Offsets in the interior of a fragment can be resolved directly. - if (offset > fragment.StartOffset() && offset < fragment.EndOffset()) - return {ResolutionType::kResolved, candidate}; - - if (offset == fragment.StartOffset() && - CanResolveCaretPositionBeforeFragment(paint_fragment, affinity)) { - return {ResolutionType::kResolved, candidate}; - } - - if (offset == fragment.EndOffset() && !fragment.IsLineBreak() && - CanResolveCaretPositionAfterFragment(paint_fragment, affinity)) { - return {ResolutionType::kResolved, candidate}; - } - - // We may have a better candidate - return {ResolutionType::kFoundCandidate, candidate}; -} - -unsigned GetTextOffsetBefore(const NGPhysicalFragment& fragment) { - // TODO(xiaochengh): Design more straightforward way to get text offset of - // atomic inline box. - DCHECK(fragment.IsAtomicInline()); - const Node* node = fragment.GetNode(); - DCHECK(node); - const Position before_node = Position::BeforeNode(*node); - Optional<unsigned> maybe_offset_before = - NGOffsetMapping::GetFor(before_node)->GetTextContentOffset(before_node); - // We should have offset mapping for atomic inline boxes. - DCHECK(maybe_offset_before.has_value()); - return maybe_offset_before.value(); -} - -CaretPositionResolution TryResolveCaretPositionByBoxFragmentSide( - const NGPaintFragment& fragment, - unsigned offset, - TextAffinity affinity) { - if (!fragment.GetNode()) { - // TODO(xiaochengh): This leads to false negatives for, e.g., RUBY, where an - // anonymous wrapping inline block is created. - return CaretPositionResolution(); - } - - const unsigned offset_before = - GetTextOffsetBefore(fragment.PhysicalFragment()); - const unsigned offset_after = offset_before + 1; - if (offset != offset_before && offset != offset_after) - return CaretPositionResolution(); - const NGCaretPositionType position_type = - offset == offset_before ? NGCaretPositionType::kBeforeBox - : NGCaretPositionType::kAfterBox; - NGCaretPosition candidate{&fragment, position_type, WTF::nullopt}; - - if (offset == offset_before && - CanResolveCaretPositionBeforeFragment(fragment, affinity)) { - return {ResolutionType::kResolved, candidate}; - } - - if (offset == offset_after && - CanResolveCaretPositionAfterFragment(fragment, affinity)) { - return {ResolutionType::kResolved, candidate}; - } - - return {ResolutionType::kFoundCandidate, candidate}; -} - -CaretPositionResolution TryResolveCaretPositionWithFragment( - const NGPaintFragment& paint_fragment, - unsigned offset, - TextAffinity affinity) { - const NGPhysicalFragment& fragment = paint_fragment.PhysicalFragment(); - if (fragment.IsText()) { - return TryResolveCaretPositionInTextFragment(paint_fragment, offset, - affinity); - } - if (fragment.IsBox() && fragment.IsAtomicInline()) { - return TryResolveCaretPositionByBoxFragmentSide(paint_fragment, offset, - affinity); - } - return CaretPositionResolution(); -} - -// ------------------------------------- - -// Helpers for converting NGCaretPositions to caret rects. - NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide( - const LayoutBlockFlow& context, const NGPaintFragment& fragment, NGCaretPositionType position_type) { const bool is_horizontal = fragment.Style().IsHorizontalWritingMode(); @@ -203,7 +34,7 @@ NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide( fragment.GetLayoutObject()->GetDocument().View(); LayoutUnit caret_width = frame_view->CaretWidth(); - const bool is_ltr = fragment.Style().Direction() == TextDirection::kLtr; + const bool is_ltr = IsLtr(fragment.PhysicalFragment().ResolvedDirection()); LayoutUnit caret_left; if (is_ltr != (position_type == NGCaretPositionType::kBeforeBox)) { if (is_horizontal) @@ -223,7 +54,6 @@ NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide( } NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset( - const LayoutBlockFlow& context, const NGPaintFragment& paint_fragment, unsigned offset) { const NGPhysicalTextFragment& fragment = @@ -255,7 +85,8 @@ NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset( paint_fragment.InlineOffsetToContainerBox(); NGPhysicalSize caret_size(caret_width, caret_height); - const NGPhysicalBoxFragment& context_fragment = *context.CurrentFragment(); + const NGPaintFragment& context_fragment = + *NGPaintFragment::GetForInlineContainer(fragment.GetLayoutObject()); const NGPaintFragment* line_box = paint_fragment.ContainerLineBox(); const NGPhysicalOffset line_box_offset = line_box->InlineOffsetToContainerBox(); @@ -284,101 +115,48 @@ NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset( return NGPhysicalOffsetRect(caret_location, caret_size); } -LocalCaretRect ComputeLocalCaretRect(const LayoutBlockFlow& context, - const NGCaretPosition& caret_position) { +LocalCaretRect ComputeLocalCaretRect(const NGCaretPosition& caret_position) { if (caret_position.IsNull()) return LocalCaretRect(); + const NGPaintFragment& fragment = *caret_position.fragment; + const LayoutObject* layout_object = fragment.GetLayoutObject(); switch (caret_position.position_type) { case NGCaretPositionType::kBeforeBox: case NGCaretPositionType::kAfterBox: { - DCHECK(caret_position.fragment->PhysicalFragment().IsBox()); + DCHECK(fragment.PhysicalFragment().IsBox()); const NGPhysicalOffsetRect fragment_local_rect = - ComputeLocalCaretRectByBoxSide(context, *caret_position.fragment, + ComputeLocalCaretRectByBoxSide(fragment, caret_position.position_type); - return {caret_position.fragment->GetLayoutObject(), - fragment_local_rect.ToLayoutRect()}; + return {layout_object, fragment_local_rect.ToLayoutRect()}; } case NGCaretPositionType::kAtTextOffset: { - DCHECK(caret_position.fragment->PhysicalFragment().IsText()); + DCHECK(fragment.PhysicalFragment().IsText()); DCHECK(caret_position.text_offset.has_value()); const NGPhysicalOffsetRect caret_rect = ComputeLocalCaretRectAtTextOffset( - context, *caret_position.fragment, *caret_position.text_offset); - - return {caret_position.fragment->GetLayoutObject(), - caret_rect.ToLayoutRect()}; + fragment, *caret_position.text_offset); + LayoutRect layout_rect = caret_rect.ToLayoutRect(); + + // For vertical-rl, convert to "flipped block-flow" coordinates space. + // See core/layout/README.md#coordinate-spaces for details. + if (fragment.Style().IsFlippedBlocksWritingMode()) { + const LayoutBlockFlow* container = + layout_object->EnclosingNGBlockFlow(); + container->FlipForWritingMode(layout_rect); + } + + return {layout_object, layout_rect}; } } NOTREACHED(); - return {caret_position.fragment->GetLayoutObject(), LayoutRect()}; -} - -// ------------------------------------- - -void AssertValidPositionForCaretRectComputation( - const PositionWithAffinity& position) { -#if DCHECK_IS_ON() - DCHECK(NGOffsetMapping::AcceptsPosition(position.GetPosition())); - const LayoutObject* layout_object = position.AnchorNode()->GetLayoutObject(); - DCHECK(layout_object); - DCHECK(layout_object->IsText() || layout_object->IsAtomicInlineLevel()); -#endif + return {layout_object, LayoutRect()}; } } // namespace -// The main function for compute an NGCaretPosition. See the comments at the top -// of this file for details. -NGCaretPosition ComputeNGCaretPosition(const LayoutBlockFlow& context, - unsigned offset, - TextAffinity affinity) { - const NGPaintFragment* root_fragment = context.PaintFragment(); - DCHECK(root_fragment); - - NGCaretPosition candidate; - for (const auto& child : - NGPaintFragmentTraversal::InlineDescendantsOf(*root_fragment)) { - const CaretPositionResolution resolution = - TryResolveCaretPositionWithFragment(*child.fragment, offset, affinity); - - if (resolution.type == ResolutionType::kFailed) - continue; - - // TODO(xiaochengh): Handle caret poisition in empty container (e.g. empty - // line box). - - if (resolution.type == ResolutionType::kResolved) - return resolution.caret_position; - - DCHECK_EQ(ResolutionType::kFoundCandidate, resolution.type); - // TODO(xiaochengh): We are not sure if we can ever find multiple - // candidates. Handle it once reached. - DCHECK(candidate.IsNull()); - candidate = resolution.caret_position; - } - - return candidate; -} - -LocalCaretRect ComputeNGLocalCaretRect(const LayoutBlockFlow& context, - const PositionWithAffinity& position) { - AssertValidPositionForCaretRectComputation(position); - DCHECK_EQ(&context, NGInlineFormattingContextOf(position.GetPosition())); - const NGOffsetMapping* mapping = NGOffsetMapping::GetFor(&context); - DCHECK(mapping); - const Optional<unsigned> maybe_offset = - mapping->GetTextContentOffset(position.GetPosition()); - if (!maybe_offset.has_value()) { - // TODO(xiaochengh): Investigate if we reach here. - NOTREACHED(); - return LocalCaretRect(); - } - - const unsigned offset = maybe_offset.value(); - const TextAffinity affinity = position.Affinity(); - return ComputeLocalCaretRect( - context, ComputeNGCaretPosition(context, offset, affinity)); +LocalCaretRect ComputeNGLocalCaretRect(const PositionWithAffinity& position) { + return ComputeLocalCaretRect(ComputeNGCaretPosition(position)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h index 2268a8220c8..4215ef0d6f5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h @@ -5,53 +5,20 @@ #ifndef NGCaretRect_h #define NGCaretRect_h +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/editing/forward.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/platform/wtf/forward.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { // This file provides utility functions for computing caret rect in LayoutNG. -class NGPaintFragment; -class LayoutBlockFlow; struct LocalCaretRect; -// Given an inline formatting context and a position in the context, returns the -// caret rect if a caret should be placed at the position, with the given -// affinity. The caret rect location is local to the given formatting context. -CORE_EXPORT LocalCaretRect ComputeNGLocalCaretRect(const LayoutBlockFlow&, - const PositionWithAffinity&); - -// An NGCaretPosition indicates a caret position relative to an inline -// NGPaintFragment: -// - When |fragment| is box, |position_type| is either |kBeforeBox| or -// |kAfterBox|, indicating either of the two caret positions by the box sides; -// |text_offset| is |nullopt| in this case. -// - When |fragment| is text, |position_type| is |kAtTextOffset|, and -// |text_offset| is in the text offset range of the fragment. -// -// TODO(xiaochengh): Support "in empty container" caret type - -enum class NGCaretPositionType { kBeforeBox, kAfterBox, kAtTextOffset }; -struct NGCaretPosition { - DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); - - bool IsNull() const { return !fragment; } - - const NGPaintFragment* fragment = nullptr; // owned by root LayoutNGMixin - NGCaretPositionType position_type; - Optional<unsigned> text_offset; -}; - -// Given an inline formatting context, a text offset in the context and a text -// affinity, returns the corresponding NGCaretPosition, or null if not found. -// Note that in many cases, null result indicates that we have reached an -// unexpected case that is not properly handled. -CORE_EXPORT NGCaretPosition ComputeNGCaretPosition(const LayoutBlockFlow&, - unsigned, - TextAffinity); +// Given a position with affinity, returns the caret rect if the position is +// laid out with LayoutNG, and a caret can be placed at the position with the +// given affinity. The caret rect location is local to the containing inline +// formatting context. +CORE_EXPORT LocalCaretRect ComputeNGLocalCaretRect(const PositionWithAffinity&); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc index 3cffb0aab2c..fca16e8c347 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc @@ -274,10 +274,13 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( // Do not defer creating a box fragment if this is an empty inline box. // An empty box fragment is still flat that we do not have to defer. // Also, placeholders cannot be reordred if empty. - scoped_refptr<NGLayoutResult> layout_result = - box_data.CreateBoxFragment(line_box); offset.inline_offset += box_data.margin_line_left; - line_box->AddChild(layout_result, offset, box_data.size.inline_size, 0); + LayoutUnit advance = box_data.margin_border_padding_line_left + + box_data.margin_border_padding_line_right; + box_data.size.inline_size = + advance - box_data.margin_line_left - box_data.margin_line_right; + line_box->AddChild(box_data.CreateBoxFragment(line_box), offset, advance, + 0); box_data_list_.pop_back(); } } @@ -365,55 +368,67 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions( if (box_data_list_.IsEmpty()) return position; - // Create box fragments. + // Compute inline positions of inline boxes. for (auto& box_data : box_data_list_) { unsigned start = box_data.fragment_start; unsigned end = box_data.fragment_end; DCHECK_GT(end, start); NGLineBoxFragmentBuilder::Child& start_child = (*line_box)[start]; + // Clamping left offset is not defined, match to the existing behavior. LayoutUnit line_left_offset = start_child.offset.inline_offset.ClampNegativeToZero(); LayoutUnit line_right_offset = end < line_box->size() ? (*line_box)[end].offset.inline_offset : position; - box_data.offset.inline_offset = line_left_offset; - box_data.size.inline_size = line_right_offset - line_left_offset; + box_data.offset.inline_offset = + line_left_offset + box_data.margin_line_left; + box_data.size.inline_size = + line_right_offset - line_left_offset + + box_data.margin_border_padding_line_left - box_data.margin_line_left + + box_data.margin_border_padding_line_right - box_data.margin_line_right; + + // Adjust child offsets for margin/border/padding. + if (box_data.margin_border_padding_line_left) { + line_box->MoveInInlineDirection(box_data.margin_border_padding_line_left, + start, line_box->size()); + position += box_data.margin_border_padding_line_left; + } + + if (box_data.margin_border_padding_line_right) { + line_box->MoveInInlineDirection(box_data.margin_border_padding_line_right, + end, line_box->size()); + position += box_data.margin_border_padding_line_right; + } + } + + return position; +} + +void NGInlineLayoutStateStack::CreateBoxFragments( + NGLineBoxFragmentBuilder::ChildList* line_box) { + DCHECK(!box_data_list_.IsEmpty()); + + for (auto& box_data : box_data_list_) { + unsigned start = box_data.fragment_start; + unsigned end = box_data.fragment_end; + DCHECK_GT(end, start); + NGLineBoxFragmentBuilder::Child& start_child = (*line_box)[start]; scoped_refptr<NGLayoutResult> box_fragment = box_data.CreateBoxFragment(line_box); - NGLogicalOffset offset(line_left_offset + box_data.margin_line_left, - box_data.offset.block_offset); if (!start_child.HasFragment()) { start_child.layout_result = std::move(box_fragment); - start_child.offset = offset; + start_child.offset = box_data.offset; } else { // In most cases, |start_child| is moved to the children of the box, and // is empty. It's not empty when it's out-of-flow. Insert in such case. - line_box->InsertChild(start, std::move(box_fragment), offset, + line_box->InsertChild(start, std::move(box_fragment), box_data.offset, LayoutUnit(), 0); } - - // Out-of-flow fragments are left in (start + 1, end). Move them by the left - // margin/border/padding. - if (box_data.margin_border_padding_line_left) { - line_box->MoveInInlineDirection(box_data.margin_border_padding_line_left, - start + 1, end); - } - // Move the rest of children by the inline size the box consumes. - LayoutUnit margin_border_padding = - box_data.margin_border_padding_line_left + - box_data.margin_border_padding_line_right; - if (margin_border_padding) { - line_box->MoveInInlineDirection(margin_border_padding, end, - line_box->size()); - position += margin_border_padding; - } } box_data_list_.clear(); - - return position; } scoped_refptr<NGLayoutResult> @@ -427,17 +442,12 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment( NGFragmentBuilder box(item->GetLayoutObject(), &style, style.GetWritingMode(), TextDirection::kLtr); box.SetBoxType(NGPhysicalFragment::kInlineBox); + box.SetStyleVariant(item->StyleVariant()); // Inline boxes have block start/end borders, even when its containing block // was fragmented. Fragmenting a line box in block direction is not // supported today. box.SetBorderEdges({true, has_line_right_edge, true, has_line_left_edge}); - LayoutUnit border_padding_line_left = - margin_border_padding_line_left - margin_line_left; - LayoutUnit border_padding_line_right = - margin_border_padding_line_right - margin_line_right; - offset.inline_offset -= border_padding_line_left; - size.inline_size += border_padding_line_left + border_padding_line_right; box.SetInlineSize(size.inline_size.ClampNegativeToZero()); box.SetBlockSize(size.block_size); box.SetPadding(padding); @@ -462,7 +472,9 @@ NGInlineLayoutStateStack::ApplyBaselineShift( NGInlineBoxState* box, NGLineBoxFragmentBuilder::ChildList* line_box, FontBaseline baseline_type) { - // Compute descendants that depend on the layout size of this box if any. + // Some 'vertical-align' values require the size of their parents. Align all + // such descendant boxes that require the size of this box; they are queued in + // |pending_descendants|. LayoutUnit baseline_shift; if (!box->pending_descendants.IsEmpty()) { for (auto& child : box->pending_descendants) { @@ -514,9 +526,15 @@ NGInlineLayoutStateStack::ApplyBaselineShift( if (vertical_align == EVerticalAlign::kBaseline) return kPositionNotPending; - // 'vertical-align' aplies only to inline-level elements. + // 'vertical-align' aligns boxes relative to themselves, to their parent + // boxes, or to the line box, depends on the value. + // Because |box| is an item in |stack_|, |box[-1]| is its parent box. + // If this box doesn't have a parent; i.e., this box is a line box, + // 'vertical-align' has no effect. + DCHECK(box >= stack_.begin() && box < stack_.end()); if (box == stack_.begin()) return kPositionNotPending; + NGInlineBoxState& parent_box = box[-1]; // Check if there are any fragments to move. unsigned fragment_end = line_box->size(); @@ -525,10 +543,10 @@ NGInlineLayoutStateStack::ApplyBaselineShift( switch (vertical_align) { case EVerticalAlign::kSub: - baseline_shift = style.ComputedFontSizeAsFixed() / 5 + 1; + baseline_shift = parent_box.style->ComputedFontSizeAsFixed() / 5 + 1; break; case EVerticalAlign::kSuper: - baseline_shift = -(style.ComputedFontSizeAsFixed() / 3 + 1); + baseline_shift = -(parent_box.style->ComputedFontSizeAsFixed() / 3 + 1); break; case EVerticalAlign::kLength: { // 'Percentages: refer to the 'line-height' of the element itself'. @@ -542,9 +560,10 @@ NGInlineLayoutStateStack::ApplyBaselineShift( } case EVerticalAlign::kMiddle: baseline_shift = (box->metrics.ascent - box->metrics.descent) / 2; - if (const SimpleFontData* font_data = style.GetFont().PrimaryFont()) { + if (const SimpleFontData* parent_font_data = + parent_box.style->GetFont().PrimaryFont()) { baseline_shift -= LayoutUnit::FromFloatRound( - font_data->GetFontMetrics().XHeight() / 2); + parent_font_data->GetFontMetrics().XHeight() / 2); } break; case EVerticalAlign::kBaselineMiddle: @@ -558,8 +577,7 @@ NGInlineLayoutStateStack::ApplyBaselineShift( return kPositionPending; default: // Other values require the layout size of the parent box. - SECURITY_CHECK(box != stack_.begin()); - box[-1].pending_descendants.push_back(NGPendingPositions{ + parent_box.pending_descendants.push_back(NGPendingPositions{ box->fragment_start, fragment_end, box->metrics, vertical_align}); return kPositionPending; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h index 68fd6c23673..626ad129d87 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h @@ -5,7 +5,6 @@ #ifndef NGInlineBoxState_h #define NGInlineBoxState_h -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" @@ -140,10 +139,13 @@ class CORE_EXPORT NGInlineLayoutStateStack { // reordering. void UpdateAfterReorder(NGLineBoxFragmentBuilder::ChildList*); - // Compute inline positions of fragments. Also creates box fragments if - // needed. + // Compute inline positions of fragments and boxes. LayoutUnit ComputeInlinePositions(NGLineBoxFragmentBuilder::ChildList*); + // Create box fragments. This function turns a flat list of children into + // a box tree. + void CreateBoxFragments(NGLineBoxFragmentBuilder::ChildList*); + private: // End of a box state, either explicitly by close tag, or implicitly at the // end of a line. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc index 870af1c1802..353acbabc68 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc @@ -24,6 +24,9 @@ void CollectInlineFragments(const NGPhysicalContainerFragment& container, NGPhysicalOffset offset_to_container_box, Filter& filter, Vector<Result, inline_capacity>* results) { + DCHECK(container.IsInline() || container.IsLineBox() || + (container.IsBlockFlow() && + ToNGPhysicalBoxFragment(container).ChildrenInline())); for (const auto& child : container.Children()) { NGPhysicalOffset child_offset = child->Offset() + offset_to_container_box; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc index fe68677cdc2..40bf8c8431a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc @@ -64,7 +64,7 @@ TEST_F(NGInlineFragmentTraversalTest, DescendantsOf) { "<div id=t>foo<b id=b>bar</b><br>baz</div>"); const auto descendants = NGInlineFragmentTraversal::DescendantsOf(GetRootFragmentById("t")); - auto iter = descendants.begin(); + auto* iter = descendants.begin(); EXPECT_NEXT_LINE_BOX(iter); EXPECT_NEXT_TEXT(iter, "foo"); @@ -82,7 +82,7 @@ TEST_F(NGInlineFragmentTraversalTest, InclusiveDescendantsOf) { "<div id=t>foo<b id=b>bar</b><br>baz</div>"); auto descendants = NGInlineFragmentTraversal::InclusiveDescendantsOf( GetRootFragmentById("t")); - auto iter = descendants.begin(); + auto* iter = descendants.begin(); EXPECT_NEXT_BOX(iter, "t"); EXPECT_NEXT_LINE_BOX(iter); @@ -102,7 +102,7 @@ TEST_F(NGInlineFragmentTraversalTest, SelfFragmentsOf) { const auto descendants = NGInlineFragmentTraversal::SelfFragmentsOf( GetRootFragmentById("t"), GetLayoutObjectByElementId("filter")); - auto iter = descendants.begin(); + auto* iter = descendants.begin(); // <b> generates two box fragments since its content is in two lines. EXPECT_NEXT_BOX(iter, "filter"); @@ -123,7 +123,7 @@ TEST_F(NGInlineFragmentTraversalTest, AncestorsOf) { const NGPhysicalFragment& target = GetFragmentOfNode(root, GetElementById("target")->firstChild()); auto ancestors = NGInlineFragmentTraversal::AncestorsOf(root, target); - auto iter = ancestors.begin(); + auto* iter = ancestors.begin(); EXPECT_NEXT_BOX(iter, "target"); EXPECT_NEXT_BOX(iter, "i"); @@ -143,7 +143,7 @@ TEST_F(NGInlineFragmentTraversalTest, InclusiveAncestorsOf) { GetFragmentOfNode(root, GetElementById("target")->firstChild()); auto ancestors = NGInlineFragmentTraversal::InclusiveAncestorsOf(root, target); - auto iter = ancestors.begin(); + auto* iter = ancestors.begin(); EXPECT_NEXT_TEXT(iter, "foo"); EXPECT_NEXT_BOX(iter, "target"); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc index 42fd483de3d..8e5a6288ddf 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc @@ -7,7 +7,6 @@ #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/style/computed_style.h" -#include "third_party/blink/renderer/platform/fonts/character_range.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h" namespace blink { @@ -44,7 +43,8 @@ NGInlineItem::NGInlineItem(NGInlineItemType type, unsigned start, unsigned end, const ComputedStyle* style, - LayoutObject* layout_object) + LayoutObject* layout_object, + bool end_may_collapse) : start_offset_(start), end_offset_(end), script_(USCRIPT_INVALID_CODE), @@ -56,11 +56,33 @@ NGInlineItem::NGInlineItem(NGInlineItemType type, is_empty_item_(false), should_create_box_fragment_(false), style_variant_(static_cast<unsigned>(NGStyleVariant::kStandard)), - end_collapse_type_(kNotCollapsible) { + end_collapse_type_(kNotCollapsible), + end_may_collapse_(end_may_collapse) { DCHECK_GE(end, start); ComputeBoxProperties(); } +NGInlineItem::NGInlineItem(const NGInlineItem& other, + unsigned start, + unsigned end, + scoped_refptr<const ShapeResult> shape_result) + : start_offset_(start), + end_offset_(end), + script_(other.script_), + shape_result_(shape_result), + style_(other.style_), + layout_object_(other.layout_object_), + type_(other.type_), + bidi_level_(other.bidi_level_), + shape_options_(other.shape_options_), + is_empty_item_(other.is_empty_item_), + should_create_box_fragment_(other.should_create_box_fragment_), + style_variant_(other.style_variant_), + end_collapse_type_(other.end_collapse_type_), + end_may_collapse_(other.end_may_collapse_) { + DCHECK_GE(end, start); +} + NGInlineItem::~NGInlineItem() = default; void NGInlineItem::ComputeBoxProperties() { @@ -82,7 +104,7 @@ void NGInlineItem::ComputeBoxProperties() { should_create_box_fragment_ = ToLayoutBoxModelObject(layout_object_)->HasSelfPaintingLayer() || style_->HasOutline() || style_->CanContainAbsolutePositionObjects() || - style_->CanContainFixedPositionObjects(); + style_->CanContainFixedPositionObjects(false); } return; } @@ -99,6 +121,13 @@ const char* NGInlineItem::NGInlineItemTypeToString(int val) const { return kNGInlineItemTypeStrings[val]; } +void NGInlineItem::SetBidiLevel(UBiDiLevel level) { + // Invalidate ShapeResult because it depends on the resolved direction. + if (DirectionFromLevel(level) != DirectionFromLevel(bidi_level_)) + shape_result_ = nullptr; + bidi_level_ = level; +} + // Set bidi level to a list of NGInlineItem from |index| to the item that ends // with |end_offset|. // If |end_offset| is mid of an item, the item is split to ensure each item has @@ -113,14 +142,14 @@ unsigned NGInlineItem::SetBidiLevel(Vector<NGInlineItem>& items, unsigned end_offset, UBiDiLevel level) { for (; items[index].end_offset_ < end_offset; index++) - items[index].bidi_level_ = level; - items[index].bidi_level_ = level; + items[index].SetBidiLevel(level); + items[index].SetBidiLevel(level); if (items[index].end_offset_ == end_offset) { // Let close items have the same bidi-level as the previous item. while (index + 1 < items.size() && items[index + 1].Type() == NGInlineItem::kCloseTag) { - items[++index].bidi_level_ = level; + items[++index].SetBidiLevel(level); } } else { Split(items, index, end_offset); @@ -152,6 +181,7 @@ void NGInlineItem::Split(Vector<NGInlineItem>& items, unsigned offset) { DCHECK_GT(offset, items[index].start_offset_); DCHECK_LT(offset, items[index].end_offset_); + items[index].shape_result_ = nullptr; items.insert(index + 1, items[index]); items[index].end_offset_ = offset; items[index + 1].start_offset_ = offset; @@ -161,11 +191,15 @@ void NGInlineItem::SetOffset(unsigned start, unsigned end) { DCHECK_GE(end, start); start_offset_ = start; end_offset_ = end; + // Any modification to the offset will invalidate the shape result. + shape_result_ = nullptr; } void NGInlineItem::SetEndOffset(unsigned end_offset) { DCHECK_GE(end_offset, start_offset_); end_offset_ = end_offset; + // Any modification to the offset will invalidate the shape result. + shape_result_ = nullptr; } bool NGInlineItem::HasStartEdge() const { @@ -181,14 +215,4 @@ bool NGInlineItem::HasEndEdge() const { !ToLayoutInline(GetLayoutObject())->Continuation(); } -NGInlineItemRange::NGInlineItemRange(Vector<NGInlineItem>* items, - unsigned start_index, - unsigned end_index) - : start_item_(&(*items)[start_index]), - size_(end_index - start_index), - start_index_(start_index) { - CHECK_LE(start_index, end_index); - CHECK_LE(end_index, items->size()); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h index d9b183b3e0b..1b40a3a53d7 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h @@ -6,19 +6,17 @@ #define NGInlineItem_h #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h" -#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h" +#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" -#include "third_party/blink/renderer/platform/fonts/simple_font_data.h" -#include "third_party/blink/renderer/platform/layout_unit.h" #include "third_party/blink/renderer/platform/text/text_direction.h" -#include "third_party/blink/renderer/platform/wtf/hash_set.h" #include <unicode/ubidi.h> +#include <unicode/uscript.h> namespace blink { -class ComputedStyle; class LayoutObject; // Class representing a single text node or styled inline element with text @@ -66,9 +64,16 @@ class CORE_EXPORT NGInlineItem { unsigned start, unsigned end, const ComputedStyle* style = nullptr, - LayoutObject* layout_object = nullptr); + LayoutObject* layout_object = nullptr, + bool end_may_collapse = false); ~NGInlineItem(); + // Copy constructor adjusting start/end and shape results. + NGInlineItem(const NGInlineItem&, + unsigned adjusted_start, + unsigned adjusted_end, + scoped_refptr<const ShapeResult>); + NGInlineItemType Type() const { return static_cast<NGInlineItemType>(type_); } const char* NGInlineItemTypeToString(int val) const; @@ -118,7 +123,13 @@ class CORE_EXPORT NGInlineItem { } void SetEndCollapseType(NGCollapseType type) { end_collapse_type_ = type; } + // Whether the item may be affected by whitespace collapsing. Unlike the + // EndCollapseType() method this returns true even if a trailing space has + // been removed. + bool EndMayCollapse() const { return end_may_collapse_; } + static void Split(Vector<NGInlineItem>&, unsigned index, unsigned offset); + void SetBidiLevel(UBiDiLevel); static unsigned SetBidiLevel(Vector<NGInlineItem>&, unsigned index, unsigned end_offset, @@ -146,6 +157,7 @@ class CORE_EXPORT NGInlineItem { unsigned should_create_box_fragment_ : 1; unsigned style_variant_ : 2; unsigned end_collapse_type_ : 2; // NGCollapseType + unsigned end_may_collapse_ : 1; friend class NGInlineNode; }; @@ -159,41 +171,23 @@ inline void NGInlineItem::AssertEndOffset(unsigned offset) const { DCHECK_LE(offset, end_offset_); } -// A vector-like object that points to a subset of an array of |NGInlineItem|. -// The source vector must keep alive and must not resize while this object -// is alive. -class NGInlineItemRange { - STACK_ALLOCATED(); - - public: - NGInlineItemRange(Vector<NGInlineItem>*, - unsigned start_index, - unsigned end_index); +// Represents a text content with a list of NGInlineItem. A node may have an +// additional NGInlineItemsData for ::first-line pseudo element. +struct CORE_EXPORT NGInlineItemsData { + // Text content for all inline items represented by a single NGInlineNode. + // Encoded either as UTF-16 or latin-1 depending on the content. + String text_content; + Vector<NGInlineItem> items; - unsigned StartIndex() const { return start_index_; } - unsigned EndIndex() const { return start_index_ + size_; } - unsigned Size() const { return size_; } + // The DOM to text content offset mapping of this inline node. + std::unique_ptr<NGOffsetMapping> offset_mapping; - NGInlineItem& operator[](unsigned index) { - CHECK_LT(index, size_); - return start_item_[index]; + void AssertOffset(unsigned index, unsigned offset) const { + items[index].AssertOffset(offset); } - const NGInlineItem& operator[](unsigned index) const { - CHECK_LT(index, size_); - return start_item_[index]; + void AssertEndOffset(unsigned index, unsigned offset) const { + items[index].AssertEndOffset(offset); } - - using iterator = NGInlineItem*; - using const_iterator = const NGInlineItem*; - iterator begin() { return start_item_; } - iterator end() { return start_item_ + size_; } - const_iterator begin() const { return start_item_; } - const_iterator end() const { return start_item_ + size_; } - - private: - NGInlineItem* start_item_; - unsigned size_; - unsigned start_index_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc index 74e3e3ca598..43cdd7b803a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc @@ -19,14 +19,14 @@ NGInlineItemResult::NGInlineItemResult(const NGInlineItem* item, : item(item), item_index(index), start_offset(start), end_offset(end) {} void NGLineInfo::SetLineStyle(const NGInlineNode& node, + const NGInlineItemsData& items_data, const NGConstraintSpace& constraint_space, bool is_first_line, + bool use_first_line_style, bool is_after_forced_break) { - LayoutObject* layout_object = node.GetLayoutObject(); - use_first_line_style_ = - is_first_line && - layout_object->GetDocument().GetStyleEngine().UsesFirstLineRules(); - line_style_ = layout_object->Style(use_first_line_style_); + use_first_line_style_ = use_first_line_style; + items_data_ = &items_data; + line_style_ = node.GetLayoutObject()->Style(use_first_line_style_); if (line_style_->ShouldUseTextIndent(is_first_line, is_after_forced_break)) { // 'text-indent' applies to block container, and percentage is of its diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h index 401e52a0a54..514ae11066d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h @@ -19,6 +19,8 @@ class NGConstraintSpace; class NGInlineItem; class NGInlineNode; +struct NGInlineItemsData; + // The result of measuring NGInlineItem. // // This is a transient context object only while building line boxes. @@ -106,14 +108,21 @@ class CORE_EXPORT NGLineInfo { NGLineInfo() = default; explicit NGLineInfo(size_t capacity) : results_(capacity) {} + const NGInlineItemsData& ItemsData() const { + DCHECK(items_data_); + return *items_data_; + } + // The style to use for the line. const ComputedStyle& LineStyle() const { DCHECK(line_style_); return *line_style_; } void SetLineStyle(const NGInlineNode&, + const NGInlineItemsData&, const NGConstraintSpace&, - bool is_first_line, + bool is_first_formatted_line, + bool use_first_line_style, bool is_after_forced_break); // Use ::first-line style if true. @@ -158,6 +167,7 @@ class CORE_EXPORT NGLineInfo { void SetLineEndFragment(scoped_refptr<NGPhysicalTextFragment>); private: + const NGInlineItemsData* items_data_ = nullptr; const ComputedStyle* line_style_ = nullptr; NGInlineItemResults results_; scoped_refptr<NGPhysicalTextFragment> line_end_fragment_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc index be19e467b59..7fad403e72e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc @@ -20,27 +20,46 @@ NGInlineItemsBuilderTemplate< template <typename OffsetMappingBuilder> String NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::ToString() { - // Segment Break Transformation Rules[1] defines to keep trailing new lines, - // but it will be removed in Phase II[2]. We prefer not to add trailing new - // lines and collapsible spaces in Phase I. + // Segment Break Transformation Rules[1] defines to keep trailing new lines in + // Phase I, but to remove after line break, in Phase II[2]. Although the spec + // defines so, trailing collapsible spaces at the end of an inline formatting + // context will be removed in Phase II and that removing here makes no + // differences. + // + // However, doing so reduces the opportunities to re-use NGInlineItem a lot in + // appending scenario, which is quite common. In order to re-use NGInlineItem + // as much as posssible, trailing spaces are removed in Phase II, exactly as + // defined in the spec. + // // [1] https://drafts.csswg.org/css-text-3/#line-break-transform // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 + return text_.ToString(); +} + +template <> +String NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::ToString() { + // While trailing collapsible space is kept as above, NGOffsetMappingBuilder + // assumes NGLineBreaker does not remove it. For now, remove only for + // NGOffsetMappingBuilder. + // TODO(kojii): Consider NGOffsetMappingBuilder to support NGLineBreaker to + // remove trailing spaces. RemoveTrailingCollapsibleSpaceIfExists(); return text_.ToString(); } +namespace { // Determine "Ambiguous" East Asian Width is Wide or Narrow. // Unicode East Asian Width // http://unicode.org/reports/tr11/ -static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { +bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { UScriptCode script = style->GetFontDescription().GetScript(); return script == USCRIPT_KATAKANA_OR_HIRAGANA || script == USCRIPT_SIMPLIFIED_HAN || script == USCRIPT_TRADITIONAL_HAN; } // Determine if a character has "Wide" East Asian Width. -static bool IsEastAsianWidthWide(UChar32 c, const ComputedStyle* style) { +bool IsEastAsianWidthWide(UChar32 c, const ComputedStyle* style) { UEastAsianWidth eaw = static_cast<UEastAsianWidth>( u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH)); return eaw == U_EA_WIDE || eaw == U_EA_FULLWIDTH || eaw == U_EA_HALFWIDTH || @@ -51,11 +70,11 @@ static bool IsEastAsianWidthWide(UChar32 c, const ComputedStyle* style) { // Determine whether a newline should be removed or not. // CSS Text, Segment Break Transformation Rules // https://drafts.csswg.org/css-text-3/#line-break-transform -static bool ShouldRemoveNewlineSlow(const StringBuilder& before, - unsigned space_index, - const ComputedStyle* before_style, - const StringView& after, - const ComputedStyle* after_style) { +bool ShouldRemoveNewlineSlow(const StringBuilder& before, + unsigned space_index, + const ComputedStyle* before_style, + const StringView& after, + const ComputedStyle* after_style) { // Remove if either before/after the newline is zeroWidthSpaceCharacter. UChar32 last = 0; DCHECK(space_index == before.length() || @@ -95,27 +114,29 @@ static bool ShouldRemoveNewlineSlow(const StringBuilder& before, return false; } -static bool ShouldRemoveNewline(const StringBuilder& before, - unsigned space_index, - const ComputedStyle* before_style, - const StringView& after, - const ComputedStyle* after_style) { +bool ShouldRemoveNewline(const StringBuilder& before, + unsigned space_index, + const ComputedStyle* before_style, + const StringView& after, + const ComputedStyle* after_style) { // All characters before/after removable newline are 16 bits. return (!before.Is8Bit() || !after.Is8Bit()) && ShouldRemoveNewlineSlow(before, space_index, before_style, after, after_style); } -static void AppendItem(Vector<NGInlineItem>* items, - NGInlineItem::NGInlineItemType type, - unsigned start, - unsigned end, - const ComputedStyle* style = nullptr, - LayoutObject* layout_object = nullptr) { - items->push_back(NGInlineItem(type, start, end, style, layout_object)); +void AppendItem(Vector<NGInlineItem>* items, + NGInlineItem::NGInlineItemType type, + unsigned start, + unsigned end, + const ComputedStyle* style = nullptr, + LayoutObject* layout_object = nullptr, + bool end_may_collapse = false) { + items->push_back( + NGInlineItem(type, start, end, style, layout_object, end_may_collapse)); } -static inline bool ShouldIgnore(UChar c) { +inline bool ShouldIgnore(UChar c) { // Ignore carriage return and form feed. // https://drafts.csswg.org/css-text-3/#white-space-processing // https://github.com/w3c/csswg-drafts/issues/855 @@ -126,14 +147,14 @@ static inline bool ShouldIgnore(UChar c) { return c == kCarriageReturnCharacter || c == kFormFeedCharacter; } -static inline bool IsCollapsibleSpace(UChar c) { +inline bool IsCollapsibleSpace(UChar c) { return c == kSpaceCharacter || c == kNewlineCharacter || c == kTabulationCharacter || c == kCarriageReturnCharacter; } // Characters needing a separate control item than other text items. // It makes the line breaker easier to handle. -static inline bool IsControlItemCharacter(UChar c) { +inline bool IsControlItemCharacter(UChar c) { return c == kNewlineCharacter || c == kTabulationCharacter || // Include ignorable character here to avoids shaping/rendering // these glyphs, and to help the line breaker to ignore them. @@ -143,9 +164,9 @@ static inline bool IsControlItemCharacter(UChar c) { // Find the end of the collapsible spaces. // Returns whether this space run contains a newline or not, because it changes // the collapsing behavior. -static inline bool MoveToEndOfCollapsibleSpaces(const StringView& string, - unsigned* offset, - UChar* c) { +inline bool MoveToEndOfCollapsibleSpaces(const StringView& string, + unsigned* offset, + UChar* c) { DCHECK_EQ(*c, string[*offset]); DCHECK(IsCollapsibleSpace(*c)); bool space_run_has_newline = *c == kNewlineCharacter; @@ -161,7 +182,7 @@ static inline bool MoveToEndOfCollapsibleSpaces(const StringView& string, // Find the last item to compute collapsing with. Opaque items such as // open/close or bidi controls are ignored. // Returns nullptr if there were no previous items. -static NGInlineItem* LastItemToCollapseWith(Vector<NGInlineItem>* items) { +NGInlineItem* LastItemToCollapseWith(Vector<NGInlineItem>* items) { for (auto it = items->rbegin(); it != items->rend(); it++) { NGInlineItem& item = *it; if (item.EndCollapseType() != NGInlineItem::kOpaqueToCollapsing) @@ -170,6 +191,66 @@ static NGInlineItem* LastItemToCollapseWith(Vector<NGInlineItem>* items) { return nullptr; } +inline bool MayCollapseWithLast(const NGInlineItem* item) { + return item && item->EndMayCollapse(); +} + +} // anonymous namespace + +template <typename OffsetMappingBuilder> +bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append( + const String& original_string, + LayoutObject* layout_object, + const Vector<NGInlineItem*>& items) { + // Don't reuse existing items if they might be affected by whitespace + // collapsing. + // TODO(layout-dev): This could likely be optimized further. + // TODO(layout-dev): Handle cases where the old items are not consecutive. + if (MayCollapseWithLast(LastItemToCollapseWith(items_)) || + IsCollapsibleSpace(original_string[items[0]->StartOffset()])) + return false; + + for (const NGInlineItem* item : items) { + unsigned start = text_.length(); + text_.Append(original_string, item->StartOffset(), item->Length()); + + // If the item's position within the container remains unchanged the item + // itself may be reused. + if (item->StartOffset() == start) { + items_->push_back(*item); + is_empty_inline_ &= item->IsEmptyItem(); + continue; + } + + // If the position has shifted the item and the shape result needs to be + // adjusted to reflect the new start and end offsets. + unsigned end = start + item->Length(); + DCHECK(item->TextShapeResult()); + NGInlineItem adjusted_item( + *item, start, end, item->TextShapeResult()->CopyAdjustedOffset(start)); + + DCHECK(adjusted_item.TextShapeResult()); + DCHECK_EQ(start, adjusted_item.StartOffset()); + DCHECK_EQ(start, adjusted_item.TextShapeResult()->StartIndexForResult()); + DCHECK_EQ(end, adjusted_item.EndOffset()); + DCHECK_EQ(end, adjusted_item.TextShapeResult()->EndIndexForResult()); + DCHECK_EQ(item->IsEmptyItem(), adjusted_item.IsEmptyItem()); + + items_->push_back(adjusted_item); + is_empty_inline_ &= adjusted_item.IsEmptyItem(); + } + return true; +} + +template <> +bool NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::Append( + const String&, + LayoutObject*, + const Vector<NGInlineItem*>&) { + NOTREACHED(); + return false; +} + template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append( const String& string, @@ -371,7 +452,7 @@ void NGInlineItemsBuilderTemplate< if (text_.length() > start_offset) { AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, - layout_object); + layout_object, end_collapse != NGInlineItem::kNotCollapsible); NGInlineItem& item = items_->back(); item.SetEndCollapseType(end_collapse); is_empty_inline_ &= item.IsEmptyItem(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h index f041c6b9f63..53e34b6cef3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h @@ -19,7 +19,6 @@ namespace blink { class ComputedStyle; class LayoutObject; class LayoutText; -class NGInlineItem; // NGInlineItemsBuilder builds a string and a list of NGInlineItem from inlines. // @@ -54,6 +53,12 @@ class CORE_TEMPLATE_CLASS_EXPORT NGInlineItemsBuilderTemplate { // <span></span> or <span><float></float></span>. bool IsEmptyInline() const { return is_empty_inline_; } + // Append existing items from an unchanged LayoutObject. + // Returns whether the existing items could be reused. + // NOTE: The state of the builder remains unchanged if the append operation + // fails (i.e. if it returns false). + bool Append(const String&, LayoutObject*, const Vector<NGInlineItem*>&); + // Append a string. // When appending, spaces are collapsed according to CSS Text, The white space // processing rules @@ -149,6 +154,15 @@ class CORE_TEMPLATE_CLASS_EXPORT NGInlineItemsBuilderTemplate { void Exit(LayoutObject*); }; +template <> +String NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::ToString(); + +template <> +bool NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::Append( + const String&, + LayoutObject*, + const Vector<NGInlineItem*>&); + extern template class CORE_EXTERN_TEMPLATE_EXPORT NGInlineItemsBuilderTemplate<EmptyOffsetMappingBuilder>; extern template class CORE_EXTERN_TEMPLATE_EXPORT diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc index d3b4b9eff09..ae5a273962c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h" #include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/core/testing/page_test_base.h" namespace blink { @@ -18,13 +19,6 @@ namespace { EXPECT_EQ(start, (item).StartOffset()); \ EXPECT_EQ(end, (item).EndOffset()); -static scoped_refptr<ComputedStyle> CreateWhitespaceStyle( - EWhiteSpace whitespace) { - scoped_refptr<ComputedStyle> style(ComputedStyle::Create()); - style->SetWhiteSpace(whitespace); - return style; -} - static String GetCollapsed(const NGOffsetMappingBuilder& builder) { Vector<unsigned> mapping = builder.DumpOffsetMappingForTesting(); @@ -47,40 +41,60 @@ static String GetCollapsed(const NGOffsetMappingBuilder& builder) { return result.ToString(); } -class NGInlineItemsBuilderTest : public testing::Test { +class NGInlineItemsBuilderTest : public PageTestBase { protected: - void SetUp() override { style_ = ComputedStyle::Create(); } + void SetUp() override { + PageTestBase::SetUp(); + style_ = ComputedStyle::Create(); + } void SetWhiteSpace(EWhiteSpace whitespace) { style_->SetWhiteSpace(whitespace); } - const String& TestAppend(const String inputs[], int size) { + scoped_refptr<ComputedStyle> GetStyle(EWhiteSpace whitespace) { + if (whitespace == EWhiteSpace::kNormal) + return style_; + scoped_refptr<ComputedStyle> style(ComputedStyle::Create()); + style->SetWhiteSpace(whitespace); + return style; + } + + struct Input { + const String text; + EWhiteSpace whitespace = EWhiteSpace::kNormal; + LayoutText* layout_text = nullptr; + }; + + const String& TestAppend(Vector<Input> inputs) { items_.clear(); NGInlineItemsBuilderForOffsetMapping builder(&items_); - for (int i = 0; i < size; i++) - builder.Append(inputs[i], style_.get()); + for (Input& input : inputs) { + if (!input.layout_text) + input.layout_text = LayoutText::CreateEmptyAnonymous(GetDocument()); + builder.Append(input.text, GetStyle(input.whitespace).get(), + input.layout_text); + } text_ = builder.ToString(); collapsed_ = GetCollapsed(builder.GetOffsetMappingBuilder()); ValidateItems(); + CheckReuseItemsProducesSameResult(inputs); return text_; } const String& TestAppend(const String& input) { - String inputs[] = {input}; - return TestAppend(inputs, 1); + return TestAppend({Input{input}}); + } + const String& TestAppend(const Input& input1, const Input& input2) { + return TestAppend({input1, input2}); } - const String& TestAppend(const String& input1, const String& input2) { - String inputs[] = {input1, input2}; - return TestAppend(inputs, 2); + return TestAppend(Input{input1}, Input{input2}); } - const String& TestAppend(const String& input1, const String& input2, const String& input3) { - String inputs[] = {input1, input2, input3}; - return TestAppend(inputs, 3); + return TestAppend({{input1}, {input2}, {input3}}); } void ValidateItems() { @@ -94,6 +108,35 @@ class NGInlineItemsBuilderTest : public testing::Test { EXPECT_EQ(current_offset, text_.length()); } + void CheckReuseItemsProducesSameResult(Vector<Input> inputs) { + Vector<NGInlineItem> reuse_items; + NGInlineItemsBuilder reuse_builder(&reuse_items); + for (Input& input : inputs) { + // Collect items for this LayoutObject. + DCHECK(input.layout_text); + Vector<NGInlineItem*> previous_items; + for (auto& item : items_) { + if (item.GetLayoutObject() == input.layout_text) + previous_items.push_back(&item); + } + + // Try to re-use previous items, or Append if it was not re-usable. + bool reused = !previous_items.IsEmpty() && + reuse_builder.Append(text_, nullptr, previous_items); + if (!reused) + reuse_builder.Append(input.text, style_.get()); + } + + // Currently, NGInlineItemsBuilder does not strip trailing spaces while + // NGInlineItemsBuilderForOffsetMapping does. See + // NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::ToString(). + String reuse_text = reuse_builder.ToString(); + if (!reuse_text.IsEmpty() && reuse_text != text_ && + reuse_text[reuse_text.length() - 1] == kSpaceCharacter) + reuse_text = reuse_text.Substring(0, reuse_text.length() - 1); + EXPECT_EQ(text_, reuse_text); + } + Vector<NGInlineItem> items_; String text_; String collapsed_; @@ -254,15 +297,11 @@ TEST_F(NGInlineItemsBuilderTest, CollapseBeforeAndAfterNewline) { TEST_F(NGInlineItemsBuilderTest, CollapsibleSpaceAfterNonCollapsibleSpaceAcrossElements) { - NGInlineItemsBuilderForOffsetMapping builder(&items_); - scoped_refptr<ComputedStyle> pre_wrap( - CreateWhitespaceStyle(EWhiteSpace::kPreWrap)); - builder.Append("text ", pre_wrap.get()); - builder.Append(" text", style_.get()); - EXPECT_EQ("text text", builder.ToString()) + EXPECT_EQ("text text", + TestAppend({"text ", EWhiteSpace::kPreWrap}, {" text"})) << "The whitespace in constructions like '<span style=\"white-space: " "pre-wrap\">text <span><span> text</span>' does not collapse."; - EXPECT_EQ("{}", GetCollapsed(builder.GetOffsetMappingBuilder())); + EXPECT_EQ("{}", collapsed_); } TEST_F(NGInlineItemsBuilderTest, CollapseZeroWidthSpaces) { @@ -419,16 +458,11 @@ INSTANTIATE_TEST_CASE_P(NGInlineItemsBuilderTest, TEST_P(CollapsibleSpaceTest, CollapsedSpaceAfterNoWrap) { UChar space = GetParam(); - Vector<NGInlineItem> items; - NGInlineItemsBuilderForOffsetMapping builder(&items); - scoped_refptr<ComputedStyle> nowrap_style(ComputedStyle::Create()); - nowrap_style->SetWhiteSpace(EWhiteSpace::kNowrap); - builder.Append(String("nowrap") + space, nowrap_style.get()); - builder.Append(" wrap", style_.get()); - EXPECT_EQ(String("nowrap " - u"\u200B" - "wrap"), - builder.ToString()); + EXPECT_EQ( + String("nowrap " + u"\u200B" + "wrap"), + TestAppend({String("nowrap") + space, EWhiteSpace::kNowrap}, {" wrap"})); } TEST_F(NGInlineItemsBuilderTest, BidiBlockOverride) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc index 5b4a1d99f34..0d04530a405 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc @@ -4,30 +4,27 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h" -#include <algorithm> -#include <limits> #include <memory> -#include <utility> #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -42,8 +39,12 @@ struct NGLineAlign { NGLineAlign(const NGLineInfo&); NGLineAlign() = delete; + // The space to align or justify. This includes trailing spaces if exists. LayoutUnit space; + + // The end offset with trailing spaces excluded. unsigned end_offset; + LayoutUnit trailing_spaces_width; }; NGLineAlign::NGLineAlign(const NGLineInfo& line_info) { @@ -55,14 +56,16 @@ NGLineAlign::NGLineAlign(const NGLineInfo& line_info) { const NGInlineItemResult& item_result = *it; if (!item_result.has_only_trailing_spaces) { end_offset = item_result.end_offset; + space += trailing_spaces_width; return; } - space += item_result.inline_size; + trailing_spaces_width += item_result.inline_size; } // An empty line, or only trailing spaces. DCHECK_EQ(space, line_info.AvailableWidth() - line_info.TextIndent()); end_offset = line_info.StartOffset(); + space += trailing_spaces_width; } } // namespace @@ -80,15 +83,16 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( // lays out in visual order. TextDirection::kLtr, break_token), + baseline_type_(container_builder_.Style().GetFontBaseline()), is_horizontal_writing_mode_( blink::IsHorizontalWritingMode(space.GetWritingMode())) { quirks_mode_ = inline_node.InLineHeightQuirksMode(); - unpositioned_floats_ = ConstraintSpace().UnpositionedFloats(); - - if (!is_horizontal_writing_mode_) - baseline_type_ = FontBaseline::kIdeographicBaseline; } +// Define the destructor here, so that we can forward-declare more in the +// header. +NGInlineLayoutAlgorithm::~NGInlineLayoutAlgorithm() = default; + NGInlineBoxState* NGInlineLayoutAlgorithm::HandleOpenTag( const NGInlineItem& item, const NGInlineItemResult& item_result) { @@ -127,7 +131,7 @@ void NGInlineLayoutAlgorithm::PrepareBoxStates( DCHECK(break_token->UseFirstLineStyle()); // Compute which tags are not closed at the beginning of this line. - const Vector<NGInlineItem>& items = Node().Items(); + const Vector<NGInlineItem>& items = line_info.ItemsData().items; Vector<const NGInlineItem*, 16> open_items; for (unsigned i = 0; i < break_token->ItemIndex(); i++) { const NGInlineItem& item = items[i]; @@ -191,12 +195,13 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, baseline_type_); } - text_builder.SetItem(NGPhysicalTextFragment::kNormalText, &item_result, + text_builder.SetItem(NGPhysicalTextFragment::kNormalText, + line_info->ItemsData(), &item_result, box->text_height); line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, item_result.inline_size, item.BidiLevel()); } else if (item.Type() == NGInlineItem::kControl) { - PlaceControlItem(item, &item_result, box); + PlaceControlItem(item, *line_info, &item_result, box); } else if (item.Type() == NGInlineItem::kOpenTag) { box = HandleOpenTag(item, item_result); } else if (item.Type() == NGInlineItem::kCloseTag) { @@ -244,6 +249,19 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, BidiReorder(); box_states_->UpdateAfterReorder(&line_box_); LayoutUnit inline_size = box_states_->ComputeInlinePositions(&line_box_); + + // Truncate the line if 'text-overflow: ellipsis' is set. + if (UNLIKELY(inline_size > line_info->AvailableWidth() && + node_.GetLayoutBlockFlow()->ShouldTruncateOverflowingText())) { + inline_size = NGLineTruncator(node_, *line_info) + .TruncateLine(inline_size, &line_box_); + } + + // Create box fragmetns if needed. After this point forward, |line_box_| is a + // tree structure. + if (box_states_->HasBoxFragments()) + box_states_->CreateBoxFragments(&line_box_); + const NGLineHeightMetrics& line_box_metrics = box_states_->LineBoxState().metrics; @@ -280,6 +298,8 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, if (IsLtr(line_info->BaseDirection())) line_bfc_offset.line_offset += line_info->TextIndent(); + if (line_info->UseFirstLineStyle()) + container_builder_.SetStyleVariant(NGStyleVariant::kFirstLine); container_builder_.AddChildren(line_box_); container_builder_.SetInlineSize(inline_size); container_builder_.SetBaseDirection(line_info->BaseDirection()); @@ -288,12 +308,13 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, } void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item, + const NGLineInfo& line_info, NGInlineItemResult* item_result, NGInlineBoxState* box) { DCHECK_EQ(item.Type(), NGInlineItem::kControl); DCHECK_EQ(item.Length(), 1u); DCHECK(!item.TextShapeResult()); - UChar character = Node().Text()[item.StartOffset()]; + UChar character = line_info.ItemsData().text_content[item.StartOffset()]; NGPhysicalTextFragment::NGTextType type; switch (character) { case kNewlineCharacter: @@ -321,7 +342,8 @@ void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item, NGTextFragmentBuilder text_builder(Node(), ConstraintSpace().GetWritingMode()); - text_builder.SetItem(type, item_result, box->text_height); + text_builder.SetItem(type, line_info.ItemsData(), item_result, + box->text_height); line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, item_result->inline_size, item.BidiLevel()); } @@ -450,7 +472,9 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(NGLineInfo* line_info) { // Construct the line text to compute spacing for. String line_text = - Node().Text(line_info->StartOffset(), align.end_offset).ToString(); + StringView(line_info->ItemsData().text_content, line_info->StartOffset(), + align.end_offset - line_info->StartOffset()) + .ToString(); // Append a hyphen if the last word is hyphenated. The hyphen is in // |ShapeResult|, but not in text. |ShapeResultSpacing| needs the text that @@ -498,8 +522,9 @@ LayoutUnit NGInlineLayoutAlgorithm::OffsetForTextAlign( // Justification is applied in earlier phase, see PlaceItems(). DCHECK_NE(text_align, ETextAlign::kJustify); + NGLineAlign align(line_info); return LineOffsetForTextAlign(text_align, line_info.BaseDirection(), - NGLineAlign(line_info).space); + align.space, align.trailing_spaces_width); } LayoutUnit NGInlineLayoutAlgorithm::ComputeContentSize( @@ -538,10 +563,20 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { bool is_empty_inline = Node().IsEmptyInline(); - if (!is_empty_inline) { - DCHECK(ConstraintSpace().UnpositionedFloats().IsEmpty()); + if (is_empty_inline) { + // We're just going to collapse through this one, so whatever went in on one + // side will go out on the other side. The position of the adjoining floats + // will be affected by any subsequent block, until the BFC offset is + // resolved. + container_builder_.AddAdjoiningFloatTypes( + ConstraintSpace().AdjoiningFloatTypes()); + } else { DCHECK(ConstraintSpace().MarginStrut().IsEmpty()); container_builder_.SetBfcOffset(ConstraintSpace().BfcOffset()); + + // The BFC offset was determined before entering this algorithm. This means + // that there should be no adjoining floats. + DCHECK(!ConstraintSpace().AdjoiningFloatTypes()); } // In order to get the correct list of layout opportunities, we need to @@ -552,10 +587,9 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // If we are an empty inline, we don't have to run the full algorithm, we can // return now as we should have positioned all of our floats. if (is_empty_inline) { - DCHECK_EQ(handled_item_index, Node().Items().size()); + DCHECK_EQ(handled_item_index, Node().ItemsData(false).items.size()); container_builder_.SwapPositionedFloats(&positioned_floats_); - container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut()); container_builder_.SetExclusionSpace(std::move(initial_exclusion_space)); @@ -572,7 +606,7 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // We query all the layout opportunities on the initial exclusion space up // front, as if the line breaker may add floats and change the opportunities. - Vector<NGLayoutOpportunity> opportunities = + const Vector<NGLayoutOpportunity> opportunities = initial_exclusion_space->AllLayoutOpportunities( ConstraintSpace().BfcOffset(), ConstraintSpace().AvailableSize().inline_size); @@ -583,7 +617,30 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { std::unique_ptr<NGExclusionSpace> exclusion_space; NGInlineBreakToken* break_token = BreakToken(); - for (const auto& opportunity : opportunities) { + LayoutUnit line_block_size; + LayoutUnit block_delta; + const auto* opportunities_it = opportunities.begin(); + while (opportunities_it != opportunities.end()) { + const NGLayoutOpportunity& opportunity = *opportunities_it; + +#if DCHECK_IS_ON() + // Make sure the last opportunity has the correct properties. + if (opportunities_it + 1 == opportunities.end()) { + // We shouldn't have any shapes affecting the last opportunity. + DCHECK(!opportunity.HasShapeExclusions()); + DCHECK_EQ(line_block_size, LayoutUnit()); + DCHECK_EQ(block_delta, LayoutUnit()); + + // The opportunity should match the given available size, (however need + // to check if the inline-size got saturated first). + if (opportunity.rect.InlineSize() != LayoutUnit::Max()) { + DCHECK_EQ(opportunity.rect.InlineSize(), + ConstraintSpace().AvailableSize().inline_size); + } + DCHECK_EQ(opportunity.rect.BlockSize(), LayoutUnit::Max()); + } +#endif + // Reset any state that may have been modified in a previous pass. positioned_floats.clear(); unpositioned_floats_.clear(); @@ -591,24 +648,44 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { exclusion_space = std::make_unique<NGExclusionSpace>(*initial_exclusion_space); + NGLineLayoutOpportunity line_opportunity = + opportunity.ComputeLineLayoutOpportunity(ConstraintSpace(), + line_block_size, block_delta); + NGLineInfo line_info; - NGLineBreaker line_breaker(Node(), NGLineBreakerMode::kContent, - constraint_space_, &positioned_floats, - &unpositioned_floats_, exclusion_space.get(), - handled_item_index, break_token); + NGLineBreaker line_breaker( + Node(), NGLineBreakerMode::kContent, constraint_space_, + &positioned_floats, &unpositioned_floats_, &container_builder_, + exclusion_space.get(), handled_item_index, break_token); // TODO(ikilpatrick): Does this always succeed when we aren't an empty // inline? - if (!line_breaker.NextLine(opportunity, &line_info)) + if (!line_breaker.NextLine(line_opportunity, &line_info)) break; // If this fragment will be larger than the inline-size of the opportunity, - // *and* the opportunity is smaller than the available inline-size, - // continue to the next opportunity. - if (line_info.Width() > opportunity.rect.InlineSize() && - opportunity.rect.InlineSize() != - ConstraintSpace().AvailableSize().inline_size) + // *and* the opportunity is smaller than the available inline-size, and the + // container autowraps, continue to the next opportunity. + if (line_info.Width() > line_opportunity.AvailableInlineSize() && + ConstraintSpace().AvailableSize().inline_size != + line_opportunity.AvailableFloatInlineSize() && + Node().Style().AutoWrap()) { + // Shapes are *special*. We need to potentially increment the block-delta + // by 1px each loop to properly test each potential position of the line. + if (UNLIKELY(opportunity.HasShapeExclusions()) && + block_delta < opportunity.rect.BlockSize() && + !opportunity.IsBlockDeltaBelowShapes(block_delta)) { + block_delta += LayoutUnit(1); + line_block_size = LayoutUnit(); + } else { + // We've either don't have any shapes, or run out of block-delta space + // to test, proceed to the next layout opportunity. + block_delta = LayoutUnit(); + line_block_size = LayoutUnit(); + ++opportunities_it; + } continue; + } PrepareBoxStates(line_info, break_token); CreateLine(&line_info, exclusion_space.get()); @@ -616,8 +693,33 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // We now can check the block-size of the fragment, and it fits within the // opportunity. LayoutUnit block_size = container_builder_.ComputeBlockSize(); - if (block_size > opportunity.rect.BlockSize()) + + // Now that we have the block-size of the line, we can re-test the layout + // opportunity to see if we fit into the (potentially) non-rectangular + // shape area. + // If the AvailableInlineSize changes we need to run the line breaker again + // with the calculated line_block_size. This is *safe* as the line breaker + // won't produce a line which has a larger block-size, (as it can only + // decrease or stay the same size). + if (UNLIKELY(opportunity.HasShapeExclusions())) { + NGLineLayoutOpportunity line_opportunity_with_height = + opportunity.ComputeLineLayoutOpportunity(ConstraintSpace(), + block_size, block_delta); + + if (line_opportunity_with_height.AvailableInlineSize() != + line_opportunity.AvailableInlineSize()) { + line_block_size = block_size; + continue; + } + } + + // Check if the line will fit in the current opportunity. + if (block_size + block_delta > opportunity.rect.BlockSize()) { + block_delta = LayoutUnit(); + line_block_size = LayoutUnit(); + ++opportunities_it; continue; + } if (opportunity.rect.BlockStartOffset() > ConstraintSpace().BfcOffset().block_offset) @@ -662,7 +764,7 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // TODO(ikilpatrick): Do we need to always add the OOFs here? unsigned NGInlineLayoutAlgorithm::PositionLeadingItems( NGExclusionSpace* exclusion_space) { - const Vector<NGInlineItem>& items = Node().Items(); + const Vector<NGInlineItem>& items = Node().ItemsData(false).items; bool is_empty_inline = Node().IsEmptyInline(); LayoutUnit bfc_line_offset = ConstraintSpace().BfcOffset().line_offset; @@ -675,10 +777,12 @@ unsigned NGInlineLayoutAlgorithm::PositionLeadingItems( NGBoxStrut margins = ComputeMarginsForContainer(ConstraintSpace(), node.Style()); - unpositioned_floats_.push_back(NGUnpositionedFloat::Create( + auto unpositioned_float = NGUnpositionedFloat::Create( ConstraintSpace().AvailableSize(), ConstraintSpace().PercentageResolutionSize(), bfc_line_offset, - bfc_line_offset, margins, node, /* break_token */ nullptr)); + bfc_line_offset, margins, node, /* break_token */ nullptr); + AddUnpositionedFloat(&unpositioned_floats_, &container_builder_, + std::move(unpositioned_float)); } else if (is_empty_inline && item.Type() == NGInlineItem::kOutOfFlowPositioned) { NGBlockNode node(ToLayoutBox(item.GetLayoutObject())); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h index b0c9a2edb91..017544f453a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h @@ -6,9 +6,6 @@ #define NGInlineLayoutAlgorithm_h #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" @@ -23,7 +20,10 @@ class NGConstraintSpace; class NGInlineBreakToken; class NGInlineNode; class NGInlineItem; -class NGLineBoxFragmentBuilder; +class NGInlineLayoutStateStack; +class NGLineInfo; +struct NGInlineBoxState; +struct NGInlineItemResult; struct NGPositionedFloat; // A class for laying out an inline formatting context, i.e. a block with inline @@ -40,6 +40,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final NGInlineLayoutAlgorithm(NGInlineNode, const NGConstraintSpace&, NGInlineBreakToken* = nullptr); + ~NGInlineLayoutAlgorithm() override; void CreateLine(NGLineInfo*, NGExclusionSpace*); @@ -59,6 +60,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final void BidiReorder(); void PlaceControlItem(const NGInlineItem&, + const NGLineInfo&, NGInlineItemResult*, NGInlineBoxState*); void PlaceGeneratedContent(scoped_refptr<NGPhysicalFragment>, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc index ed5f88b97c1..654b7eb887e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc @@ -5,14 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h" #include "third_party/blink/renderer/core/dom/tag_collection.h" -#include "third_party/blink/renderer/core/layout/line/inline_text_box.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc index 92aabbded97..bf394eba54f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc @@ -5,39 +5,28 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include <algorithm> -#include <limits> #include <memory> -#include <utility> +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_text.h" -#include "third_party/blink/renderer/core/layout/layout_text_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/text/character_names.h" namespace blink { @@ -49,6 +38,11 @@ template <typename OffsetMappingBuilder> void ClearNeedsLayoutIfUpdatingLayout(LayoutObject* node) { node->ClearNeedsLayout(); node->ClearNeedsCollectInlines(); + // Reset previous items if they cannot be reused to prevent stale items + // for subsequent layouts. Items that can be reused have already been + // added to the builder. + if (node->IsLayoutNGText()) + ToLayoutNGText(node)->ClearInlineItems(); } template <> @@ -67,19 +61,33 @@ void ClearNeedsLayoutIfUpdatingLayout<NGOffsetMappingBuilder>(LayoutObject*) {} template <typename OffsetMappingBuilder> void CollectInlinesInternal( LayoutBlockFlow* block, - NGInlineItemsBuilderTemplate<OffsetMappingBuilder>* builder) { + NGInlineItemsBuilderTemplate<OffsetMappingBuilder>* builder, + String* previous_text) { builder->EnterBlock(block->Style()); LayoutObject* node = GetLayoutObjectForFirstChildNode(block); while (node) { if (node->IsText()) { LayoutText* layout_text = ToLayoutText(node); - if (UNLIKELY(layout_text->IsWordBreak())) { - builder->AppendBreakOpportunity(node->Style(), layout_text); - } else { - builder->Append(layout_text->GetText(), node->Style(), layout_text); + + // If the LayoutText element hasn't changed, reuse the existing items. + + // if the last ended with space and this starts with space, do not allow + // reuse. builder->MightCollapseWithPreceding(*previous_text) + bool item_reused = false; + if (node->IsLayoutNGText() && ToLayoutNGText(node)->HasValidLayout() && + previous_text) { + item_reused = builder->Append(*previous_text, ToLayoutNGText(node), + ToLayoutNGText(node)->InlineItems()); } - ClearNeedsLayoutIfUpdatingLayout<OffsetMappingBuilder>(layout_text); + // If not create a new item as needed. + if (!item_reused) { + if (UNLIKELY(layout_text->IsWordBreak())) + builder->AppendBreakOpportunity(node->Style(), layout_text); + else + builder->Append(layout_text->GetText(), node->Style(), layout_text); + } + ClearNeedsLayoutIfUpdatingLayout<OffsetMappingBuilder>(layout_text); } else if (node->IsFloating()) { // Add floats and positioned objects in the same way as atomic inlines. @@ -145,6 +153,33 @@ void CollectInlinesInternal( builder->ExitBlock(); } +static bool NeedsShaping(const NGInlineItem& item) { + return item.Type() == NGInlineItem::kText && !item.TextShapeResult(); +} + +// Determine if reshape is needed for ::first-line style. +bool FirstLineNeedsReshape(const ComputedStyle& first_line_style, + const ComputedStyle& base_style) { + const Font& base_font = base_style.GetFont(); + const Font& first_line_font = first_line_style.GetFont(); + return &base_font != &first_line_font && base_font != first_line_font; +} + +// Make a string to the specified length, either by truncating if longer, or +// appending space characters if shorter. +void TruncateOrPadText(String* text, unsigned length) { + if (text->length() > length) { + *text = text->Substring(0, length); + } else if (text->length() < length) { + StringBuilder builder; + builder.ReserveCapacity(length); + builder.Append(*text); + while (builder.length() < length) + builder.Append(kSpaceCharacter); + *text = builder.ToString(); + } +} + } // namespace NGInlineNode::NGInlineNode(LayoutBlockFlow* block) @@ -159,13 +194,29 @@ bool NGInlineNode::InLineHeightQuirksMode() const { return GetDocument().InLineHeightQuirksMode(); } +bool NGInlineNode::CanContainFirstFormattedLine() const { + // TODO(kojii): In LayoutNG, leading OOF creates an anonymous block box, + // and that |LayoutBlockFlow::CanContainFirstFormattedLine()| does not work. + // crbug.com/734554 + LayoutObject* layout_object = GetLayoutBlockFlow(); + if (!layout_object->IsAnonymousBlock()) + return true; + for (;;) { + layout_object = layout_object->PreviousSibling(); + if (!layout_object) + return true; + if (!layout_object->IsFloatingOrOutOfFlowPositioned()) + return false; + } +} + NGInlineNodeData* NGInlineNode::MutableData() { return ToLayoutBlockFlow(box_)->GetNGInlineNodeData(); } bool NGInlineNode::IsPrepareLayoutFinished() const { const NGInlineNodeData* data = ToLayoutBlockFlow(box_)->GetNGInlineNodeData(); - return data && !data->text_content_.IsNull(); + return data && !data->text_content.IsNull(); } const NGInlineNodeData& NGInlineNode::Data() const { @@ -174,28 +225,19 @@ const NGInlineNodeData& NGInlineNode::Data() const { return *ToLayoutBlockFlow(box_)->GetNGInlineNodeData(); } -const Vector<NGInlineItem>& NGInlineNode::Items(bool is_first_line) const { - const NGInlineNodeData& data = Data(); - if (!is_first_line || !data.first_line_items_) - return data.items_; - return *data.first_line_items_; -} - -NGInlineItemRange NGInlineNode::Items(unsigned start, unsigned end) { - return NGInlineItemRange(&MutableData()->items_, start, end); -} - -void NGInlineNode::InvalidatePrepareLayout() { +void NGInlineNode::InvalidatePrepareLayoutForTest() { GetLayoutBlockFlow()->ResetNGInlineNodeData(); DCHECK(!IsPrepareLayoutFinished()); } void NGInlineNode::PrepareLayoutIfNeeded() { + std::unique_ptr<NGInlineNodeData> previous_data; LayoutBlockFlow* block_flow = GetLayoutBlockFlow(); if (IsPrepareLayoutFinished()) { if (!block_flow->NeedsCollectInlines()) return; + previous_data.reset(block_flow->TakeNGInlineNodeData()); block_flow->ResetNGInlineNodeData(); } @@ -203,9 +245,11 @@ void NGInlineNode::PrepareLayoutIfNeeded() { // NGInlineNode represent a collection of adjacent non-atomic inlines. NGInlineNodeData* data = MutableData(); DCHECK(data); - CollectInlines(data); + CollectInlines(data, previous_data.get()); SegmentText(data); - ShapeText(data); + ShapeText(data, previous_data.get()); + ShapeTextForFirstLineIfNeeded(data); + AssociateItemsWithInlines(data); DCHECK_EQ(data, MutableData()); block_flow->ClearNeedsCollectInlines(); @@ -213,10 +257,10 @@ void NGInlineNode::PrepareLayoutIfNeeded() { #if DCHECK_IS_ON() // ComputeOffsetMappingIfNeeded() runs some integrity checks as part of // creating offset mapping. Run the check, and discard the result. - DCHECK(!data->offset_mapping_); + DCHECK(!data->offset_mapping); ComputeOffsetMappingIfNeeded(); - DCHECK(data->offset_mapping_); - data->offset_mapping_.reset(); + DCHECK(data->offset_mapping); + data->offset_mapping.reset(); #endif } @@ -228,45 +272,56 @@ const NGInlineNodeData& NGInlineNode::EnsureData() { const NGOffsetMapping* NGInlineNode::ComputeOffsetMappingIfNeeded() { DCHECK(!GetLayoutBlockFlow()->GetDocument().NeedsLayoutTreeUpdate()); - if (!Data().offset_mapping_) { + NGInlineNodeData* data = MutableData(); + if (!data->offset_mapping) { // TODO(xiaochengh): ComputeOffsetMappingIfNeeded() discards the // NGInlineItems and text content built by |builder|, because they are // already there in NGInlineNodeData. For efficiency, we should make // |builder| not construct items and text content. Vector<NGInlineItem> items; NGInlineItemsBuilderForOffsetMapping builder(&items); - CollectInlinesInternal(GetLayoutBlockFlow(), &builder); - builder.ToString(); + CollectInlinesInternal(GetLayoutBlockFlow(), &builder, nullptr); + String text = builder.ToString(); + + // The trailing space of the text for offset mapping may be removed. If not, + // share the string instance. + if (text == data->text_content) + text = data->text_content; // TODO(xiaochengh): This doesn't compute offset mapping correctly when // text-transform CSS property changes text length. NGOffsetMappingBuilder& mapping_builder = builder.GetOffsetMappingBuilder(); - mapping_builder.SetDestinationString(Text()); - MutableData()->offset_mapping_ = + mapping_builder.SetDestinationString(text); + data->offset_mapping = std::make_unique<NGOffsetMapping>(mapping_builder.Build()); } - return Data().offset_mapping_.get(); + return data->offset_mapping.get(); } // Depth-first-scan of all LayoutInline and LayoutText nodes that make up this // NGInlineNode object. Collects LayoutText items, merging them up into the // parent LayoutInline where possible, and joining all text content in a single // string to allow bidi resolution and shaping of the entire block. -void NGInlineNode::CollectInlines(NGInlineNodeData* data) { - DCHECK(data->text_content_.IsNull()); - DCHECK(data->items_.IsEmpty()); +void NGInlineNode::CollectInlines(NGInlineNodeData* data, + NGInlineNodeData* previous_data) { + DCHECK(data->text_content.IsNull()); + DCHECK(data->items.IsEmpty()); LayoutBlockFlow* block = GetLayoutBlockFlow(); block->WillCollectInlines(); - NGInlineItemsBuilder builder(&data->items_); - CollectInlinesInternal(block, &builder); - data->text_content_ = builder.ToString(); + + String* previous_text = + previous_data ? &previous_data->text_content : nullptr; + NGInlineItemsBuilder builder(&data->items); + CollectInlinesInternal(block, &builder, previous_text); + data->text_content = builder.ToString(); + // Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this // point the string may or may not contain RTL characters. // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it // doesn't contain any RTL characters. data->is_bidi_enabled_ = - !data->text_content_.Is8Bit() || builder.HasBidiControls(); + !data->text_content.Is8Bit() || builder.HasBidiControls(); data->is_empty_inline_ = builder.IsEmptyInline(); } @@ -277,8 +332,8 @@ void NGInlineNode::SegmentText(NGInlineNodeData* data) { } NGBidiParagraph bidi; - data->text_content_.Ensure16Bit(); - if (!bidi.SetParagraph(data->text_content_, Style())) { + data->text_content.Ensure16Bit(); + if (!bidi.SetParagraph(data->text_content, Style())) { // On failure, give up bidi resolving and reordering. data->is_bidi_enabled_ = false; data->SetBaseDirection(TextDirection::kLtr); @@ -293,9 +348,9 @@ void NGInlineNode::SegmentText(NGInlineNodeData* data) { return; } - Vector<NGInlineItem>& items = data->items_; + Vector<NGInlineItem>& items = data->items; unsigned item_index = 0; - for (unsigned start = 0; start < data->text_content_.length();) { + for (unsigned start = 0; start < data->text_content.length();) { UBiDiLevel level; unsigned end = bidi.GetLogicalRun(start, &level); DCHECK_EQ(items[item_index].start_offset_, start); @@ -313,19 +368,21 @@ void NGInlineNode::SegmentText(NGInlineNodeData* data) { #endif } -void NGInlineNode::ShapeText(NGInlineNodeData* data) { +void NGInlineNode::ShapeText(NGInlineItemsData* data, + NGInlineItemsData* previous_data) { // TODO(eae): Add support for shaping latin-1 text? - data->text_content_.Ensure16Bit(); - ShapeText(data->text_content_, &data->items_); - - ShapeTextForFirstLineIfNeeded(data); + data->text_content.Ensure16Bit(); + ShapeText(data->text_content, &data->items, + previous_data ? &previous_data->text_content : nullptr); } void NGInlineNode::ShapeText(const String& text_content, - Vector<NGInlineItem>* items) { - // Shape each item with the full context of the entire node. + Vector<NGInlineItem>* items, + const String* previous_text) { + // Provide full context of the entire node to the shaper. HarfBuzzShaper shaper(text_content.Characters16(), text_content.length()); ShapeResultSpacing<String> spacing(text_content); + for (unsigned index = 0; index < items->size();) { NGInlineItem& start_item = (*items)[index]; if (start_item.Type() != NGInlineItem::kText) { @@ -339,6 +396,12 @@ void NGInlineNode::ShapeText(const String& text_content, unsigned end_offset = start_item.EndOffset(); for (; end_index < items->size(); end_index++) { const NGInlineItem& item = (*items)[end_index]; + + if (item.Type() == NGInlineItem::kControl) { + // Do not shape across control characters (line breaks, zero width + // spaces, etc). + break; + } if (item.Type() == NGInlineItem::kText) { // Shape adjacent items together if the font and direction matches to // allow ligatures and kerning to apply. @@ -359,6 +422,43 @@ void NGInlineNode::ShapeText(const String& text_content, } } + // Shaping a single item. Skip if the existing results remain valid. + if (previous_text && end_offset == start_item.EndOffset() && + !NeedsShaping(start_item)) { + DCHECK_EQ(start_item.StartOffset(), + start_item.TextShapeResult()->StartIndexForResult()); + DCHECK_EQ(start_item.EndOffset(), + start_item.TextShapeResult()->EndIndexForResult()); + index++; + continue; + } + + // Results may only be reused if all items in the range remain valid. + bool has_valid_shape_results = true; + for (unsigned item_index = index; item_index < end_index; item_index++) { + if (NeedsShaping((*items)[item_index])) { + has_valid_shape_results = false; + break; + } + } + + // When shaping across multiple items checking whether the individual + // items has valid shape results isn't sufficient as items may have been + // re-ordered or removed. + // TODO(layout-dev): It would probably be faster to check for removed or + // moved items but for now comparing the string itself will do. + unsigned text_start = start_item.StartOffset(); + DCHECK_GE(end_offset, text_start); + unsigned text_length = end_offset - text_start; + if (has_valid_shape_results && previous_text && + end_offset <= previous_text->length() && + StringView(text_content, text_start, text_length) == + StringView(*previous_text, text_start, text_length)) { + index = end_index; + continue; + } + + // Shape each item with the full context of the entire node. scoped_refptr<ShapeResult> shape_result = shaper.Shape(&font, direction, start_item.StartOffset(), end_offset); if (UNLIKELY(spacing.SetSpacing(font.GetFontDescription()))) @@ -404,9 +504,28 @@ void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) { if (block_style == first_line_style) return; - auto first_line_items = std::make_unique<Vector<NGInlineItem>>(); - first_line_items->AppendVector(data->items_); - for (auto& item : *first_line_items) { + auto first_line_items = std::make_unique<NGInlineItemsData>(); + first_line_items->text_content = data->text_content; + bool needs_reshape = false; + if (first_line_style->TextTransform() != block_style->TextTransform()) { + // TODO(kojii): This logic assumes that text-transform is applied only to + // ::first-line, and does not work when the base style has text-transform + // and ::first-line has different text-transform. + first_line_style->ApplyTextTransform(&first_line_items->text_content); + if (first_line_items->text_content != data->text_content) { + // TODO(kojii): When text-transform changes the length, we need to adjust + // offset in NGInlineItem, or re-collect inlines. Other classes such as + // line breaker need to support the scenario too. For now, we force the + // string to be the same length to prevent them from crashing. This may + // result in a missing or a duplicate character if the length changes. + TruncateOrPadText(&first_line_items->text_content, + data->text_content.length()); + needs_reshape = true; + } + } + + first_line_items->items.AppendVector(data->items); + for (auto& item : first_line_items->items) { if (item.style_) { DCHECK(item.layout_object_); item.style_ = item.layout_object_->FirstLineStyle(); @@ -415,15 +534,26 @@ void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) { } // Re-shape if the font is different. - const Font& font = block_style->GetFont(); - const Font& first_line_font = first_line_style->GetFont(); - if (&font != &first_line_font && font != first_line_font) { - ShapeText(data->text_content_, first_line_items.get()); - } + if (needs_reshape || FirstLineNeedsReshape(*first_line_style, *block_style)) + ShapeText(first_line_items.get()); data->first_line_items_ = std::move(first_line_items); } +void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) { + LayoutObject* last_object = nullptr; + for (auto& item : data->items) { + LayoutObject* object = item.GetLayoutObject(); + if (object && object->IsLayoutNGText()) { + LayoutNGText* layout_text = ToLayoutNGText(object); + if (object != last_object) + layout_text->ClearInlineItems(); + layout_text->AddInlineItem(&item); + } + last_object = object; + } +} + scoped_refptr<NGLayoutResult> NGInlineNode::Layout( const NGConstraintSpace& constraint_space, NGBreakToken* break_token) { @@ -446,6 +576,7 @@ static LayoutUnit ComputeContentSize(NGInlineNode node, NGConstraintSpaceBuilder(writing_mode, node.InitialContainingBlockSize()) .SetTextDirection(style.Direction()) .SetAvailableSize({available_inline_size, NGSizeIndefinite}) + .SetIsIntermediateLayout(true) .ToConstraintSpace(writing_mode); Vector<NGPositionedFloat> positioned_floats; @@ -454,8 +585,7 @@ static LayoutUnit ComputeContentSize(NGInlineNode node, scoped_refptr<NGInlineBreakToken> break_token; NGLineInfo line_info; NGExclusionSpace empty_exclusion_space; - NGLayoutOpportunity opportunity(NGBfcRect( - NGBfcOffset(), NGBfcOffset(available_inline_size, LayoutUnit::Max()))); + NGLineLayoutOpportunity line_opportunity(available_inline_size); LayoutUnit result; LayoutUnit previous_floats_inline_size = input.float_left_inline_size + input.float_right_inline_size; @@ -463,9 +593,10 @@ static LayoutUnit ComputeContentSize(NGInlineNode node, unpositioned_floats.clear(); NGLineBreaker line_breaker(node, mode, *space, &positioned_floats, - &unpositioned_floats, &empty_exclusion_space, 0u, - break_token.get()); - if (!line_breaker.NextLine(opportunity, &line_info)) + &unpositioned_floats, + nullptr /* container_builder */, + &empty_exclusion_space, 0u, break_token.get()); + if (!line_breaker.NextLine(line_opportunity, &line_info)) break; break_token = line_breaker.CreateBreakToken(line_info, nullptr); @@ -490,14 +621,9 @@ static LayoutUnit ComputeContentSize(NGInlineNode node, NGBlockNode float_node = unpositioned_float->node; const ComputedStyle& float_style = float_node.Style(); - Optional<MinMaxSize> child_minmax; - if (NeedMinMaxSizeForContentContribution(float_style)) { - MinMaxSizeInput zero_input; // Floats don't intrude into floats. - child_minmax = float_node.ComputeMinMaxSize(zero_input); - } - - MinMaxSize child_sizes = - ComputeMinAndMaxContentContribution(float_style, child_minmax); + MinMaxSizeInput zero_input; // Floats don't intrude into floats. + MinMaxSize child_sizes = ComputeMinAndMaxContentContribution( + writing_mode, float_node, zero_input); LayoutUnit child_inline_margins = ComputeMinMaxMargins(style, float_node).InlineSum(); @@ -560,7 +686,7 @@ MinMaxSize NGInlineNode::ComputeMinMaxSize(const MinMaxSizeInput& input) { void NGInlineNode::CheckConsistency() const { #if DCHECK_IS_ON() - const Vector<NGInlineItem>& items = Data().items_; + const Vector<NGInlineItem>& items = Data().items; for (const NGInlineItem& item : items) { DCHECK(!item.GetLayoutObject() || !item.Style() || item.Style() == item.GetLayoutObject()->Style()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h index 333c5b1b092..7685706584d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h @@ -7,28 +7,19 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { -template <typename OffsetMappingBuilder> -class NGInlineItemsBuilderTemplate; - -class EmptyOffsetMappingBuilder; -class LayoutBlockFlow; -struct MinMaxSize; class NGConstraintSpace; class NGInlineItem; -class NGInlineItemRange; -using NGInlineItemsBuilder = - NGInlineItemsBuilderTemplate<EmptyOffsetMappingBuilder>; -struct NGInlineNodeData; class NGLayoutResult; class NGOffsetMapping; class NGInlineNodeLegacy; +struct MinMaxSize; +struct NGInlineItemsData; // Represents an anonymous block box to be laid out, that contains consecutive // inline nodes and their descendants. @@ -55,17 +46,12 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { MinMaxSize ComputeMinMaxSize(const MinMaxSizeInput&); // Instruct to re-compute |PrepareLayout| on the next layout. - void InvalidatePrepareLayout(); + void InvalidatePrepareLayoutForTest(); - const String& Text() const { return Data().text_content_; } - StringView Text(unsigned start_offset, unsigned end_offset) const { - return StringView(Data().text_content_, start_offset, - end_offset - start_offset); + const NGInlineItemsData& ItemsData(bool is_first_line) const { + return Data().ItemsData(is_first_line); } - const Vector<NGInlineItem>& Items(bool is_first_line = false) const; - NGInlineItemRange Items(unsigned start_index, unsigned end_index); - // Returns the DOM to text content offset mapping of this block. If it is not // computed before, compute and store it in NGInlineNodeData. // This funciton must be called with clean layout. @@ -76,8 +62,10 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { bool IsEmptyInline() { return EnsureData().is_empty_inline_; } - void AssertOffset(unsigned index, unsigned offset) const; - void AssertEndOffset(unsigned index, unsigned offset) const; + // @return if this node can contain the "first formatted line". + // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line + bool CanContainFirstFormattedLine() const; + void CheckConsistency() const; String ToString() const; @@ -89,11 +77,16 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { // calling the Layout method. void PrepareLayoutIfNeeded(); - void CollectInlines(NGInlineNodeData*); + void CollectInlines(NGInlineNodeData*, + NGInlineNodeData* previous_data = nullptr); void SegmentText(NGInlineNodeData*); - void ShapeText(NGInlineNodeData*); - void ShapeText(const String&, Vector<NGInlineItem>*); + void ShapeText(NGInlineItemsData*, + NGInlineItemsData* previous_data = nullptr); + void ShapeText(const String& text, + Vector<NGInlineItem>*, + const String* previous_text); void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*); + void AssociateItemsWithInlines(NGInlineNodeData*); NGInlineNodeData* MutableData(); const NGInlineNodeData& Data() const; @@ -103,15 +96,6 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { friend class NGInlineNodeLegacy; }; -inline void NGInlineNode::AssertOffset(unsigned index, unsigned offset) const { - Data().items_[index].AssertOffset(offset); -} - -inline void NGInlineNode::AssertEndOffset(unsigned index, - unsigned offset) const { - Data().items_[index].AssertEndOffset(offset); -} - DEFINE_TYPE_CASTS(NGInlineNode, NGLayoutInputNode, node, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.cc index a45f608f7b5..07eaa48884b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.cc @@ -4,13 +4,6 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" -#include "third_party/blink/renderer/core/dom/node.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" -#include "third_party/blink/renderer/core/style/computed_style.h" - namespace blink { -NGInlineNodeData::NGInlineNodeData() = default; -NGInlineNodeData::~NGInlineNodeData() = default; - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h index 2d8efffd2d5..dbf328bc9ea 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h @@ -11,16 +11,14 @@ namespace blink { -class NGOffsetMapping; - // Data which is required for inline nodes. -struct CORE_EXPORT NGInlineNodeData { - // The constructor and destructor can't be implicit or inlined, because they - // need full definition of NGOffsetMapping. - NGInlineNodeData(); - ~NGInlineNodeData(); - +struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData { private: + const NGInlineItemsData& ItemsData(bool is_first_line) const { + return !is_first_line || !first_line_items_ + ? (const NGInlineItemsData&)*this + : *first_line_items_; + } TextDirection BaseDirection() const { return static_cast<TextDirection>(base_direction_); } @@ -33,18 +31,12 @@ struct CORE_EXPORT NGInlineNodeData { friend class NGInlineNodeForTest; friend class NGOffsetMappingTest; - // Text content for all inline items represented by a single NGInlineNode. - // Encoded either as UTF-16 or latin-1 depending on the content. - String text_content_; - Vector<NGInlineItem> items_; - - // |items_| to use for the first line, when the node has :first-line rules. - // Items have different ComputedStyle, and may also have different ShapeResult - // if fonts are different. - std::unique_ptr<Vector<NGInlineItem>> first_line_items_; - - // The DOM to text content offset mapping of this inline node. - std::unique_ptr<NGOffsetMapping> offset_mapping_; + // Items to use for the first line, when the node has :first-line rules. + // + // Items have different ComputedStyle, and may also have different + // text_content and ShapeResult if 'text-transform' is applied or fonts are + // different. + std::unique_ptr<NGInlineItemsData> first_line_items_; unsigned is_bidi_enabled_ : 1; unsigned base_direction_ : 1; // TextDirection diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc index ca628714647..f04e5e70b56 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc @@ -22,29 +22,29 @@ class NGInlineNodeForTest : public NGInlineNode { public: using NGInlineNode::NGInlineNode; - std::string Text() const { return Data().text_content_.Utf8().data(); } - Vector<NGInlineItem>& Items() { return MutableData()->items_; } + std::string Text() const { return Data().text_content.Utf8().data(); } + Vector<NGInlineItem>& Items() { return MutableData()->items; } static Vector<NGInlineItem>& Items(NGInlineNodeData& data) { - return data.items_; + return data.items; } void Append(const String& text, const ComputedStyle* style = nullptr, LayoutObject* layout_object = nullptr) { NGInlineNodeData* data = MutableData(); - unsigned start = data->text_content_.length(); - data->text_content_.append(text); - data->items_.push_back(NGInlineItem(NGInlineItem::kText, start, - start + text.length(), style, - layout_object)); + unsigned start = data->text_content.length(); + data->text_content.append(text); + data->items.push_back(NGInlineItem(NGInlineItem::kText, start, + start + text.length(), style, + layout_object)); data->is_empty_inline_ = false; } void Append(UChar character) { NGInlineNodeData* data = MutableData(); - data->text_content_.append(character); - unsigned end = data->text_content_.length(); - data->items_.push_back( + data->text_content.append(character); + unsigned end = data->text_content.length(); + data->items.push_back( NGInlineItem(NGInlineItem::kBidiControl, end - 1, end, nullptr)); data->is_bidi_enabled_ = true; data->is_empty_inline_ = false; @@ -52,8 +52,8 @@ class NGInlineNodeForTest : public NGInlineNode { void ClearText() { NGInlineNodeData* data = MutableData(); - data->text_content_ = String(); - data->items_.clear(); + data->text_content = String(); + data->items.clear(); data->is_empty_inline_ = true; } @@ -93,7 +93,7 @@ class NGInlineNodeTest : public NGLayoutTest { if (!layout_block_flow_) SetupHtml("t", "<div id=t style='font:10px'>test</div>"); NGInlineNodeForTest node(layout_block_flow_); - node.InvalidatePrepareLayout(); + node.InvalidatePrepareLayoutForTest(); return node; } @@ -304,7 +304,6 @@ TEST_F(NGInlineNodeTest, SegmentSplit1To2) { NGInlineNodeForTest node = CreateInlineNode(); node.Append(u"Hello \u05E2\u05D1\u05E8\u05D9\u05EA"); node.SegmentText(); - ASSERT_EQ(2u, node.Items().size()); Vector<NGInlineItem>& items = node.Items(); ASSERT_EQ(2u, items.size()); TEST_ITEM_OFFSET_DIR(items[0], 0u, 6u, TextDirection::kLtr); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc index eeef40c579c..2b7a7e3d58e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc @@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" @@ -56,6 +55,25 @@ const NGPhysicalFragment* NGLineBoxFragmentBuilder::Child::PhysicalFragment() : fragment.get(); } +NGLineBoxFragmentBuilder::Child* +NGLineBoxFragmentBuilder::ChildList::FirstInFlowChild() { + for (auto& child : *this) { + if (child.HasInFlowFragment()) + return &child; + } + return nullptr; +} + +NGLineBoxFragmentBuilder::Child* +NGLineBoxFragmentBuilder::ChildList::LastInFlowChild() { + for (auto it = rbegin(); it != rend(); it++) { + auto& child = *it; + if (child.HasInFlowFragment()) + return &child; + } + return nullptr; +} + void NGLineBoxFragmentBuilder::ChildList::InsertChild( unsigned index, scoped_refptr<NGLayoutResult> layout_result, @@ -128,28 +146,32 @@ scoped_refptr<NGLayoutResult> NGLineBoxFragmentBuilder::ToLineBoxFragment() { NGPhysicalSize physical_size = Size().ConvertToPhysical(writing_mode); NGPhysicalOffsetRect contents_visual_rect({}, physical_size); + NGPhysicalOffsetRect scrollable_overflow({}, physical_size); for (size_t i = 0; i < children_.size(); ++i) { NGPhysicalFragment* child = children_[i].get(); child->SetOffset(offsets_[i].ConvertToPhysical( writing_mode, Direction(), physical_size, child->Size())); child->PropagateContentsVisualRect(&contents_visual_rect); + NGPhysicalOffsetRect child_scroll_overflow = child->ScrollableOverflow(); + child_scroll_overflow.offset += child->Offset(); + scrollable_overflow.Unite(child_scroll_overflow); } scoped_refptr<NGPhysicalLineBoxFragment> fragment = base::AdoptRef(new NGPhysicalLineBoxFragment( - Style(), physical_size, children_, contents_visual_rect, metrics_, - base_direction_, + Style(), style_variant_, physical_size, children_, + contents_visual_rect, scrollable_overflow, metrics_, base_direction_, break_token_ ? std::move(break_token_) : NGInlineBreakToken::Create(node_))); return base::AdoptRef(new NGLayoutResult( std::move(fragment), oof_positioned_descendants_, positioned_floats_, - unpositioned_floats_, unpositioned_list_marker_, - std::move(exclusion_space_), bfc_offset_, end_margin_strut_, + unpositioned_list_marker_, std::move(exclusion_space_), bfc_offset_, + end_margin_strut_, /* intrinsic_block_size */ LayoutUnit(), /* minimal_space_shortage */ LayoutUnit::Max(), EBreakBetween::kAuto, EBreakBetween::kAuto, /* has_forced_break */ false, is_pushed_by_floats_, - NGLayoutResult::kSuccess)); + adjoining_floats_, NGLayoutResult::kSuccess)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h index 9f4ed515a70..9ea29183fb1 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h @@ -9,15 +9,15 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" -#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { class ComputedStyle; class NGInlineBreakToken; -class NGInlineNode; class NGPhysicalFragment; +struct NGPositionedFloat; class CORE_EXPORT NGLineBoxFragmentBuilder final : public NGContainerFragmentBuilder { @@ -78,6 +78,14 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final bidi_level(bidi_level) {} // Create an in-flow |NGPhysicalFragment|. Child(scoped_refptr<NGPhysicalFragment> fragment, + NGLogicalOffset offset, + LayoutUnit inline_size, + UBiDiLevel bidi_level) + : fragment(std::move(fragment)), + offset(offset), + inline_size(inline_size), + bidi_level(bidi_level) {} + Child(scoped_refptr<NGPhysicalFragment> fragment, LayoutUnit block_offset, LayoutUnit inline_size, UBiDiLevel bidi_level) @@ -130,6 +138,15 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final using const_iterator = Vector<Child, 16>::const_iterator; const_iterator begin() const { return children_.begin(); } const_iterator end() const { return children_.end(); } + using reverse_iterator = Vector<Child, 16>::reverse_iterator; + reverse_iterator rbegin() { return children_.rbegin(); } + reverse_iterator rend() { return children_.rend(); } + using const_reverse_iterator = Vector<Child, 16>::const_reverse_iterator; + const_reverse_iterator rbegin() const { return children_.rbegin(); } + const_reverse_iterator rend() const { return children_.rend(); } + + Child* FirstInFlowChild(); + Child* LastInFlowChild(); // Add a child. Accepts all constructor arguments for |Child|. template <class... Args> diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc index 343a5fec56c..79294415634 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc @@ -8,12 +8,12 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h" @@ -35,24 +35,40 @@ inline bool CanBreakAfterLast(const NGInlineItemResults& item_results) { } // namespace +NGLineBreaker::LineData::LineData(NGInlineNode node, + const NGInlineBreakToken* break_token) { + is_first_formatted_line = (!break_token || (!break_token->ItemIndex() && + !break_token->TextOffset())) && + node.CanContainFirstFormattedLine(); + use_first_line_style = is_first_formatted_line && node.GetLayoutObject() + ->GetDocument() + .GetStyleEngine() + .UsesFirstLineRules(); +} + NGLineBreaker::NGLineBreaker( NGInlineNode node, NGLineBreakerMode mode, const NGConstraintSpace& space, Vector<NGPositionedFloat>* positioned_floats, Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats, + NGContainerFragmentBuilder* container_builder, NGExclusionSpace* exclusion_space, unsigned handled_float_index, const NGInlineBreakToken* break_token) - : node_(node), + : line_(node, break_token), + node_(node), + items_data_(node.ItemsData(line_.use_first_line_style)), mode_(mode), constraint_space_(space), positioned_floats_(positioned_floats), unpositioned_floats_(unpositioned_floats), + container_builder_(container_builder), exclusion_space_(exclusion_space), - break_iterator_(node.Text()), - shaper_(node.Text().Characters16(), node.Text().length()), - spacing_(node.Text()), + break_iterator_(items_data_.text_content), + shaper_(items_data_.text_content.Characters16(), + items_data_.text_content.length()), + spacing_(items_data_.text_content), handled_floats_end_item_index_(handled_float_index), base_direction_(node_.BaseDirection()), in_line_height_quirks_mode_(node.InLineHeightQuirksMode()) { @@ -63,11 +79,15 @@ NGLineBreaker::NGLineBreaker( item_index_ = break_token->ItemIndex(); offset_ = break_token->TextOffset(); previous_line_had_forced_break_ = break_token->IsForcedBreak(); - node.AssertOffset(item_index_, offset_); + items_data_.AssertOffset(item_index_, offset_); ignore_floats_ = break_token->IgnoreFloats(); } } +// Define the destructor here, so that we can forward-declare more in the +// header. +NGLineBreaker::~NGLineBreaker() = default; + inline NGInlineItemResult* NGLineBreaker::AddItem( const NGInlineItem& item, unsigned end_offset, @@ -106,59 +126,53 @@ inline void NGLineBreaker::ComputeCanBreakAfter( auto_wrap_ && break_iterator_.IsBreakable(item_result->end_offset); } -// @return if this is the "first formatted line". -// https://www.w3.org/TR/CSS22/selector.html#first-formatted-line -bool NGLineBreaker::IsFirstFormattedLine() const { - if (item_index_ || offset_) - return false; - - // TODO(kojii): In LayoutNG, leading OOF creates an anonymous block box, - // and that |CanContainFirstFormattedLine()| does not work. - // crbug.com/734554 - // return node_.GetLayoutBlockFlow()->CanContainFirstFormattedLine(); - LayoutObject* layout_object = node_.GetLayoutBlockFlow(); - if (!layout_object->IsAnonymousBlock()) - return true; - for (;;) { - layout_object = layout_object->PreviousSibling(); - if (!layout_object) - return true; - if (!layout_object->IsFloatingOrOutOfFlowPositioned()) +// True if |item| is trailing; i.e., |item| and all items after it are opaque to +// whitespace collapsing. +bool NGLineBreaker::IsTrailing(const NGInlineItem& item, + const NGLineInfo& line_info) const { + const Vector<NGInlineItem>& items = line_info.ItemsData().items; + for (const NGInlineItem* it = &item; it != items.end(); ++it) { + if (it->EndCollapseType() != NGInlineItem::kOpaqueToCollapsing) return false; } + return true; } // Compute the base direction for bidi algorithm for this line. -void NGLineBreaker::ComputeBaseDirection() { +void NGLineBreaker::ComputeBaseDirection(const NGLineInfo& line_info) { // If 'unicode-bidi' is not 'plaintext', use the base direction of the block. if (!previous_line_had_forced_break_ || node_.Style().GetUnicodeBidi() != UnicodeBidi::kPlaintext) return; // If 'unicode-bidi: plaintext', compute the base direction for each paragraph // (separated by forced break.) - const String& text = node_.Text(); + const String& text = line_info.ItemsData().text_content; if (text.Is8Bit()) return; size_t end_offset = text.find(kNewlineCharacter, offset_); - base_direction_ = NGBidiParagraph::BaseDirectionForString(node_.Text( - offset_, end_offset == kNotFound ? text.length() : end_offset)); + base_direction_ = NGBidiParagraph::BaseDirectionForString( + end_offset == kNotFound + ? StringView(text, offset_) + : StringView(text, offset_, end_offset - offset_)); } // Initialize internal states for the next line. -void NGLineBreaker::PrepareNextLine(const NGLayoutOpportunity& opportunity, - NGLineInfo* line_info) { +void NGLineBreaker::PrepareNextLine( + const NGLineLayoutOpportunity& line_opportunity, + NGLineInfo* line_info) { NGInlineItemResults* item_results = &line_info->Results(); item_results->clear(); line_info->SetStartOffset(offset_); - line_info->SetLineStyle(node_, constraint_space_, IsFirstFormattedLine(), - previous_line_had_forced_break_); + line_info->SetLineStyle( + node_, items_data_, constraint_space_, line_.is_first_formatted_line, + line_.use_first_line_style, previous_line_had_forced_break_); // Set the initial style of this line from the break token. Example: // <p>...<span>....</span></p> // When the line wraps in <span>, the 2nd line needs to start with the style // of the <span>. override_break_anywhere_ = false; SetCurrentStyle(current_style_ ? *current_style_ : line_info->LineStyle()); - ComputeBaseDirection(); + ComputeBaseDirection(*line_info); line_info->SetBaseDirection(base_direction_); line_.is_after_forced_break = false; @@ -168,17 +182,12 @@ void NGLineBreaker::PrepareNextLine(const NGLayoutOpportunity& opportunity, // regardless of 'text-indent'. line_.position = line_info->TextIndent(); - line_.opportunity = opportunity; - line_.line_left_bfc_offset = opportunity.rect.LineStartOffset(); - line_.line_right_bfc_offset = opportunity.rect.LineEndOffset(); - bfc_block_offset_ = opportunity.rect.BlockStartOffset(); + line_.line_opportunity = line_opportunity; } -bool NGLineBreaker::NextLine(const NGLayoutOpportunity& opportunity, +bool NGLineBreaker::NextLine(const NGLineLayoutOpportunity& line_opportunity, NGLineInfo* line_info) { - bfc_block_offset_ = constraint_space_.BfcOffset().block_offset; - - PrepareNextLine(opportunity, line_info); + PrepareNextLine(line_opportunity, line_info); BreakLine(line_info); if (line_info->Results().IsEmpty()) @@ -186,22 +195,15 @@ bool NGLineBreaker::NextLine(const NGLayoutOpportunity& opportunity, // TODO(kojii): There are cases where we need to PlaceItems() without creating // line boxes. These cases need to be reviewed. - if (line_.should_create_line_box) { - if (!line_.CanFit() && - node_.GetLayoutBlockFlow()->ShouldTruncateOverflowingText()) { - TruncateOverflowingText(line_info); - } - + if (line_.should_create_line_box) ComputeLineLocation(line_info); - } return true; } void NGLineBreaker::BreakLine(NGLineInfo* line_info) { NGInlineItemResults* item_results = &line_info->Results(); - const Vector<NGInlineItem>& items = - node_.Items(line_info->UseFirstLineStyle()); + const Vector<NGInlineItem>& items = line_info->ItemsData().items; LineBreakState state = LineBreakState::kContinue; while (state != LineBreakState::kDone) { // Check overflow even if |item_index_| is at the end of the block, because @@ -214,6 +216,7 @@ void NGLineBreaker::BreakLine(NGLineInfo* line_info) { // If we reach at the end of the block, this is the last line. DCHECK_LE(item_index_, items.size()); if (item_index_ == items.size()) { + RemoveTrailingCollapsibleSpace(line_info); line_info->SetIsLastLine(true); return; } @@ -255,7 +258,7 @@ void NGLineBreaker::BreakLine(NGLineInfo* line_info) { } else if (item.Type() == NGInlineItem::kOpenTag) { HandleOpenTag(item, AddItem(item, item_results)); } else if (item.Type() == NGInlineItem::kFloating) { - HandleFloat(item, AddItem(item, item_results)); + HandleFloat(item, line_info, AddItem(item, item_results)); } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { DCHECK_EQ(item.Length(), 0u); AddItem(item, item_results); @@ -291,14 +294,14 @@ void NGLineBreaker::UpdatePosition(const NGInlineItemResults& results) { } void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const { - LayoutUnit bfc_line_offset = line_.line_left_bfc_offset; + LayoutUnit bfc_line_offset = line_.line_opportunity.line_left_offset; LayoutUnit available_width = line_.AvailableWidth(); // Negative margins can make the position negative, but the inline size is // always positive or 0. - line_info->SetLineBfcOffset({bfc_line_offset, bfc_block_offset_}, - available_width, - line_.position.ClampNegativeToZero()); + line_info->SetLineBfcOffset( + {bfc_line_offset, line_.line_opportunity.bfc_block_offset}, + available_width, line_.position.ClampNegativeToZero()); } NGLineBreaker::LineBreakState NGLineBreaker::HandleText( @@ -394,7 +397,7 @@ void NGLineBreaker::BreakText(NGInlineItemResult* item_result, // * If offset == item.EndOffset(): the break opportunity at the end fits, // or the first break opportunity is beyond the end. // There may be room for more characters. - // * If width > available_width: The first break opporunity does not fit. + // * If width > available_width: The first break opportunity does not fit. // offset is the first break opportunity, either inside, at the end, or // beyond the end. if (item_result->end_offset < item.EndOffset()) { @@ -438,6 +441,7 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleTrailingSpaces( NGInlineItemResult* item_result = AddItem(item, end, item_results); item_result->has_only_trailing_spaces = true; + // TODO(kojii): Should reshape if it's not safe to break. item_result->shape_result = item.TextShapeResult()->SubRange(offset_, end); item_result->inline_size = item_result->shape_result->SnappedWidth(); line_.position += item_result->inline_size; @@ -455,6 +459,44 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleTrailingSpaces( return LineBreakState::kTrailing; } +// Remove trailing collapsible spaces in |line_info|. +// https://drafts.csswg.org/css-text-3/#white-space-phase-2 +void NGLineBreaker::RemoveTrailingCollapsibleSpace(NGLineInfo* line_info) { + NGInlineItemResults* item_results = &line_info->Results(); + if (item_results->IsEmpty()) + return; + for (auto it = item_results->rbegin(); it != item_results->rend(); ++it) { + NGInlineItemResult& item_result = *it; + DCHECK(item_result.item); + const NGInlineItem& item = *item_result.item; + if (item.EndCollapseType() == NGInlineItem::kOpaqueToCollapsing) + continue; + if (item.Type() != NGInlineItem::kText) + return; + const String& text = Text(); + if (text[item_result.end_offset - 1] != kSpaceCharacter) + return; + DCHECK(item.Style()); + if (!item.Style()->CollapseWhiteSpace()) + return; + + // We have a trailing collapsible space. Remove it. + line_.position -= item_result.inline_size; + --item_result.end_offset; + if (item_result.end_offset == item_result.start_offset) { + unsigned index = std::distance(item_results->begin(), &item_result); + item_results->EraseAt(index); + } else { + // TODO(kojii): Should reshape if it's not safe to break. + item_result.shape_result = item_result.shape_result->SubRange( + item_result.start_offset, item_result.end_offset); + item_result.inline_size = item_result.shape_result->SnappedWidth(); + line_.position += item_result.inline_size; + } + return; + } +} + void NGLineBreaker::AppendHyphen(const NGInlineItem& item, NGLineInfo* line_info) { DCHECK(item.Style()); @@ -565,16 +607,33 @@ void NGLineBreaker::HandleAtomicInline(const NGInlineItem& item, line_.should_create_line_box = true; NGInlineItemResult* item_result = AddItem(item, &line_info->Results()); - item_result->layout_result = - NGBlockNode(ToLayoutBox(item.GetLayoutObject())) - .LayoutAtomicInline(constraint_space_, - line_info->UseFirstLineStyle()); - DCHECK(item_result->layout_result->PhysicalFragment()); - - item_result->inline_size = - NGFragment(constraint_space_.GetWritingMode(), - *item_result->layout_result->PhysicalFragment()) - .InlineSize(); + // When we're just computing min/max content sizes, we can skip the full + // layout and just compute those sizes. On the other hand, for regular + // layout we need to do the full layout and get the layout result. + // Doing a full layout for min/max content can also have undesirable + // side effects when that falls back to legacy layout. + if (mode_ == NGLineBreakerMode::kContent) { + item_result->layout_result = + NGBlockNode(ToLayoutBox(item.GetLayoutObject())) + .LayoutAtomicInline(constraint_space_, + line_info->LineStyle().GetFontBaseline(), + line_info->UseFirstLineStyle()); + DCHECK(item_result->layout_result->PhysicalFragment()); + + item_result->inline_size = + NGFragment(constraint_space_.GetWritingMode(), + *item_result->layout_result->PhysicalFragment()) + .InlineSize(); + } else { + NGBlockNode block_node(ToLayoutBox(item.GetLayoutObject())); + MinMaxSizeInput input; + MinMaxSize sizes = ComputeMinAndMaxContentContribution( + constraint_space_.GetWritingMode(), block_node, input, + &constraint_space_); + item_result->inline_size = mode_ == NGLineBreakerMode::kMinContent + ? sizes.min_size + : sizes.max_size; + } DCHECK(item.Style()); item_result->margins = @@ -601,9 +660,8 @@ void NGLineBreaker::HandleAtomicInline(const NGInlineItem& item, // We have this check if there are already UnpositionedFloats as we aren't // allowed to position a float "above" another float which has come before us // in the document. -// -// TODO(glebl): Add the support of clearance for inline floats. void NGLineBreaker::HandleFloat(const NGInlineItem& item, + NGLineInfo* line_info, NGInlineItemResult* item_result) { // When rewind occurs, an item may be handled multiple times. // Since floats are put into a separate list, avoid handling same floats @@ -619,6 +677,13 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item, if (item_index_ <= handled_floats_end_item_index_ || ignore_floats_) return; + // Floats need to know the current line width to determine whether to put it + // into the current line or to the next line. Remove trailing spaces if this + // float is trailing, because whitespace should be collapsed across floats, + // and this logic requires the width after trailing spaces are collapsed. + if (IsTrailing(item, *line_info)) + RemoveTrailingCollapsibleSpace(line_info); + NGBlockNode node(ToLayoutBox(item.GetLayoutObject())); const ComputedStyle& float_style = node.Style(); @@ -641,49 +706,47 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item, margins.InlineSum()) .ClampNegativeToZero(); + LayoutUnit bfc_block_offset = line_.line_opportunity.bfc_block_offset; + // The float should be positioned after the current line if: - // - It can't fit. + // - It can't fit within the non-shape area. (Assuming the current position + // also is strictly within the non-shape area). // - It will be moved down due to block-start edge alignment. // - It will be moved down due to clearance. // - We are currently computing our min/max-content size. (We use the // unpositioned_floats to manually adjust the min/max-content size after // the line breaker has run). bool float_after_line = - !line_.CanFit(inline_margin_size) || - exclusion_space_->LastFloatBlockStart() > bfc_block_offset_ || + !line_.CanFloatFit(inline_margin_size) || + exclusion_space_->LastFloatBlockStart() > bfc_block_offset || exclusion_space_->ClearanceOffset(float_style.Clear()) > - bfc_block_offset_ || + bfc_block_offset || mode_ != NGLineBreakerMode::kContent; // Check if we already have a pending float. That's because a float cannot be // higher than any block or floated box generated before. if (!unpositioned_floats_->IsEmpty() || float_after_line) { - unpositioned_floats_->push_back(std::move(unpositioned_float)); + AddUnpositionedFloat(unpositioned_floats_, container_builder_, + std::move(unpositioned_float)); } else { - LayoutUnit origin_block_offset = bfc_block_offset_; - NGPositionedFloat positioned_float = PositionFloat( - origin_block_offset, constraint_space_.BfcOffset().block_offset, + bfc_block_offset, constraint_space_.BfcOffset().block_offset, unpositioned_float.get(), constraint_space_, exclusion_space_); positioned_floats_->push_back(positioned_float); DCHECK_EQ(positioned_float.bfc_offset.block_offset, - bfc_block_offset_ + margins.block_start); + bfc_block_offset + margins.block_start); - if (float_style.Floating() == EFloat::kLeft) { - line_.line_left_bfc_offset = std::max( - line_.line_left_bfc_offset, - positioned_float.bfc_offset.line_offset + inline_margin_size - - margins.LineLeft(TextDirection::kLtr)); - } else { - line_.line_right_bfc_offset = - std::min(line_.line_right_bfc_offset, - positioned_float.bfc_offset.line_offset - - margins.LineLeft(TextDirection::kLtr)); - } + NGLayoutOpportunity opportunity = exclusion_space_->FindLayoutOpportunity( + {constraint_space_.BfcOffset().line_offset, bfc_block_offset}, + constraint_space_.AvailableSize().inline_size, NGLogicalSize()); + + DCHECK_EQ(bfc_block_offset, opportunity.rect.BlockStartOffset()); + + line_.line_opportunity = opportunity.ComputeLineLayoutOpportunity( + constraint_space_, line_.line_opportunity.line_block_size, + LayoutUnit()); - DCHECK_GE(line_.line_left_bfc_offset, LayoutUnit()); - DCHECK_GE(line_.line_right_bfc_offset, LayoutUnit()); DCHECK_GE(line_.AvailableWidth(), LayoutUnit()); } } @@ -783,7 +846,7 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, // be a break opportunity after the space. The break_iterator cannot // compute this because it considers break opportunities are before a run // of spaces. - const String& text = node_.Text(); + const String& text = Text(); if (offset_ < text.length() && IsBreakableSpace(text[offset_])) { item_result->can_break_after = true; return; @@ -858,7 +921,7 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleOverflow( #endif item_index_ = item_result->item_index; offset_ = item_result->end_offset; - node_.AssertOffset(item_index_, offset_); + items_data_.AssertOffset(item_index_, offset_); } else { Rewind(line_info, i + 1); } @@ -879,7 +942,7 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleOverflow( } // Let this line overflow. - // If there was a break opporunity, the overflow should stop there. + // If there was a break opportunity, the overflow should stop there. if (break_before) { Rewind(line_info, break_before); return LineBreakState::kTrailing; @@ -918,8 +981,7 @@ void NGLineBreaker::Rewind(NGLineInfo* line_info, unsigned new_end) { // paint invalidations, hit testing, etc. LayoutObject* NGLineBreaker::CurrentLayoutObject( const NGLineInfo& line_info) const { - const Vector<NGInlineItem>& items = - node_.Items(line_info.UseFirstLineStyle()); + const Vector<NGInlineItem>& items = line_info.ItemsData().items; DCHECK_LE(item_index_, items.size()); // Find the next item that has LayoutObject. Some items such as bidi controls // do not have LayoutObject. @@ -936,56 +998,6 @@ LayoutObject* NGLineBreaker::CurrentLayoutObject( return nullptr; } -// Truncate overflowing text and append ellipsis. -void NGLineBreaker::TruncateOverflowingText(NGLineInfo* line_info) { - // The ellipsis is styled according to the line style. - const Font& font = line_info->LineStyle().GetFont(); - const SimpleFontData* font_data = font.PrimaryFont(); - DCHECK(font_data); - String ellipsis = - font_data && font_data->GlyphForCharacter(kHorizontalEllipsisCharacter) - ? String(&kHorizontalEllipsisCharacter, 1) - : String(u"..."); - HarfBuzzShaper shaper(ellipsis.Characters16(), ellipsis.length()); - scoped_refptr<ShapeResult> shape_result = - shaper.Shape(&font, line_info->BaseDirection()); - - // Truncate the line to (available_width - ellipsis_width) using 'line-break: - // anywhere'. - unsigned saved_item_index = item_index_; - unsigned saved_offset = offset_; - override_break_anywhere_ = true; - break_iterator_.SetBreakType(LineBreakType::kBreakCharacter); - HandleOverflow(line_info, - line_.AvailableWidth() - shape_result->SnappedWidth()); - - // Find the LayoutObject this ellpsis is tied to. - LayoutObject* layout_object = CurrentLayoutObject(*line_info); - - // Restore item_index/offset to before HandleOverflow(). - item_index_ = saved_item_index; - offset_ = saved_offset; - - // Ellipsis should not have text decorations. Reset if it's set. - scoped_refptr<const ComputedStyle> style = &line_info->LineStyle(); - if (style->TextDecorationsInEffect() != TextDecoration::kNone) { - scoped_refptr<ComputedStyle> ellipsis_style = - ComputedStyle::CreateAnonymousStyleWithDisplay(*style, - EDisplay::kInline); - ellipsis_style->ResetTextDecoration(); - ellipsis_style->ClearAppliedTextDecorations(); - style = std::move(ellipsis_style); - } - - // The ellipsis should appear at the logical end of the line. - // This is stored seprately from other results so that it can be appended - // after bidi reorder. - NGTextFragmentBuilder builder(node_, constraint_space_.GetWritingMode()); - builder.SetText(layout_object, ellipsis, style, true /* is_ellipsis_style */, - std::move(shape_result)); - SetLineEndFragment(builder.ToTextFragment(), line_info); -} - void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) { current_style_ = &style; @@ -1041,7 +1053,7 @@ void NGLineBreaker::MoveToNextOf(const NGInlineItemResult& item_result) { scoped_refptr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken( const NGLineInfo& line_info, std::unique_ptr<const NGInlineLayoutStateStack> state_stack) const { - const Vector<NGInlineItem>& items = node_.Items(); + const Vector<NGInlineItem>& items = Items(); if (item_index_ >= items.size()) return NGInlineBreakToken::Create(node_); return NGInlineBreakToken::Create( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h index 98e15a95b37..b0838c40a67 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h @@ -6,7 +6,7 @@ #define NGLineBreaker_h #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h" +#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h" @@ -17,10 +17,12 @@ namespace blink { class Hyphenation; +class NGContainerFragmentBuilder; class NGInlineBreakToken; class NGInlineItem; class NGInlineLayoutStateStack; struct NGPositionedFloat; +struct NGUnpositionedFloat; // The line breaker needs to know which mode its in to properly handle floats. enum class NGLineBreakerMode { kContent, kMinContent, kMaxContent }; @@ -38,14 +40,15 @@ class CORE_EXPORT NGLineBreaker { const NGConstraintSpace&, Vector<NGPositionedFloat>*, Vector<scoped_refptr<NGUnpositionedFloat>>*, + NGContainerFragmentBuilder* container_builder, NGExclusionSpace*, unsigned handled_float_index, const NGInlineBreakToken* = nullptr); - ~NGLineBreaker() = default; + ~NGLineBreaker(); // Compute the next line break point and produces NGInlineItemResults for // the line. - bool NextLine(const NGLayoutOpportunity&, NGLineInfo*); + bool NextLine(const NGLineLayoutOpportunity& line_opportunity, NGLineInfo*); // Create an NGInlineBreakToken for the last line returned by NextLine(). scoped_refptr<NGInlineBreakToken> CreateBreakToken( @@ -63,15 +66,19 @@ class CORE_EXPORT NGLineBreaker { struct LineData { STACK_ALLOCATED(); + LineData(NGInlineNode node, const NGInlineBreakToken* break_token); + // The current position from inline_start. Unlike NGInlineLayoutAlgorithm // that computes position in visual order, this position in logical order. LayoutUnit position; - // The current opportunity. - NGLayoutOpportunity opportunity; + NGLineLayoutOpportunity line_opportunity; + + // True if this line is the "first formatted line". + // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line + bool is_first_formatted_line; - LayoutUnit line_left_bfc_offset; - LayoutUnit line_right_bfc_offset; + bool use_first_line_style; // We don't create "certain zero-height line boxes". // https://drafts.csswg.org/css2/visuren.html#phantom-line-box @@ -85,16 +92,20 @@ class CORE_EXPORT NGLineBreaker { bool is_after_forced_break = false; LayoutUnit AvailableWidth() const { - DCHECK_GE(line_right_bfc_offset, line_left_bfc_offset); - return line_right_bfc_offset - line_left_bfc_offset; + return line_opportunity.AvailableInlineSize(); } bool CanFit() const { return position <= AvailableWidth(); } bool CanFit(LayoutUnit extra) const { return position + extra <= AvailableWidth(); } + bool CanFloatFit(LayoutUnit extra) const { + return position + extra <= line_opportunity.AvailableFloatInlineSize(); + } }; - const String& Text() const { return break_iterator_.GetString(); } + const String& Text() const { return items_data_.text_content; } + const Vector<NGInlineItem>& Items() const { return items_data_.items; } + NGInlineItemResult* AddItem(const NGInlineItem&, unsigned end_offset, NGInlineItemResults*); @@ -104,7 +115,7 @@ class CORE_EXPORT NGLineBreaker { void BreakLine(NGLineInfo*); - void PrepareNextLine(const NGLayoutOpportunity&, NGLineInfo*); + void PrepareNextLine(const NGLineLayoutOpportunity&, NGLineInfo*); void UpdatePosition(const NGInlineItemResults&); void ComputeLineLocation(NGLineInfo*) const; @@ -128,6 +139,7 @@ class CORE_EXPORT NGLineBreaker { LayoutUnit available_width, NGLineInfo*); LineBreakState HandleTrailingSpaces(const NGInlineItem&, NGLineInfo*); + void RemoveTrailingCollapsibleSpace(NGLineInfo*); void AppendHyphen(const NGInlineItem& item, NGLineInfo*); LineBreakState HandleControlItem(const NGInlineItem&, @@ -137,7 +149,7 @@ class CORE_EXPORT NGLineBreaker { LineBreakState, NGLineInfo*); void HandleAtomicInline(const NGInlineItem&, NGLineInfo*); - void HandleFloat(const NGInlineItem&, NGInlineItemResult*); + void HandleFloat(const NGInlineItem&, NGLineInfo*, NGInlineItemResult*); void HandleOpenTag(const NGInlineItem&, NGInlineItemResult*); void HandleCloseTag(const NGInlineItem&, NGInlineItemResults*); @@ -147,33 +159,33 @@ class CORE_EXPORT NGLineBreaker { void Rewind(NGLineInfo*, unsigned new_end); LayoutObject* CurrentLayoutObject(const NGLineInfo&) const; - void TruncateOverflowingText(NGLineInfo*); void SetCurrentStyle(const ComputedStyle&); void MoveToNextOf(const NGInlineItem&); void MoveToNextOf(const NGInlineItemResult&); - bool IsFirstFormattedLine() const; - void ComputeBaseDirection(); + void ComputeBaseDirection(const NGLineInfo&); + bool IsTrailing(const NGInlineItem&, const NGLineInfo&) const; LineData line_; NGInlineNode node_; + const NGInlineItemsData& items_data_; + NGLineBreakerMode mode_; const NGConstraintSpace& constraint_space_; Vector<NGPositionedFloat>* positioned_floats_; Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats_; + NGContainerFragmentBuilder* container_builder_; /* May be nullptr */ NGExclusionSpace* exclusion_space_; scoped_refptr<const ComputedStyle> current_style_; unsigned item_index_ = 0; unsigned offset_ = 0; - bool previous_line_had_forced_break_ = false; - LayoutUnit bfc_line_offset_; - LayoutUnit bfc_block_offset_; LazyLineBreakIterator break_iterator_; HarfBuzzShaper shaper_; ShapeResultSpacing<String> spacing_; + bool previous_line_had_forced_break_ = false; const Hyphenation* hyphenation_ = nullptr; // Keep track of handled float items. See HandleFloat(). diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc index 9cf39096049..30c5402b812 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" namespace blink { @@ -46,15 +47,14 @@ class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest { Vector<NGInlineItemResults> lines; NGExclusionSpace exclusion_space; - NGLayoutOpportunity opportunity; - opportunity.rect = - NGBfcRect(NGBfcOffset(), {available_width, LayoutUnit::Max()}); + NGLineLayoutOpportunity line_opportunity(available_width); NGLineInfo line_info; while (!break_token || !break_token->IsFinished()) { NGLineBreaker line_breaker(node, NGLineBreakerMode::kContent, *space, &positioned_floats, &unpositioned_floats, + /* container_builder */ nullptr, &exclusion_space, 0u, break_token.get()); - if (!line_breaker.NextLine(opportunity, &line_info)) + if (!line_breaker.NextLine(line_opportunity, &line_info)) break; break_token = line_breaker.CreateBreakToken(line_info, nullptr); @@ -69,8 +69,11 @@ namespace { String ToString(NGInlineItemResults line, NGInlineNode node) { StringBuilder builder; + const String& text = node.ItemsData(false).text_content; for (const auto& item_result : line) { - builder.Append(node.Text(item_result.start_offset, item_result.end_offset)); + builder.Append( + StringView(text, item_result.start_offset, + item_result.end_offset - item_result.start_offset)); } return builder.ToString(); } @@ -181,7 +184,7 @@ TEST_F(NGLineBreakerTest, OverflowMargin) { </style> <div id=container><span>123 456</span> 789</div> )HTML"); - const Vector<NGInlineItem>& items = node.Items(); + const Vector<NGInlineItem>& items = node.ItemsData(false).items; // While "123 456" can fit in a line, "456" has a right margin that cannot // fit. Since "456" and its right margin is not breakable, "456" should be on diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.cc index 44d25987ad9..b83d812d685 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.cc @@ -15,6 +15,9 @@ NGLineHeightMetrics::NGLineHeightMetrics(const ComputedStyle& style, Initialize(font_data->GetFontMetrics(), baseline_type); } +NGLineHeightMetrics::NGLineHeightMetrics(const ComputedStyle& style) + : NGLineHeightMetrics(style, style.GetFontBaseline()) {} + NGLineHeightMetrics::NGLineHeightMetrics(const FontMetrics& font_metrics, FontBaseline baseline_type) { Initialize(font_metrics, baseline_type); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h index 8c8c57dc515..978c170044e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h @@ -25,6 +25,7 @@ struct NGLineHeightMetrics { // Compute from ComputedStyle, using the font metrics of the prikmary font. // The leading is not included. + NGLineHeightMetrics(const ComputedStyle&); NGLineHeightMetrics(const ComputedStyle&, FontBaseline); // Compute from FontMetrics. The leading is not included. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc new file mode 100644 index 00000000000..ae76600c718 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc @@ -0,0 +1,201 @@ +// 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/ng/inline/ng_line_truncator.h" + +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h" +#include "third_party/blink/renderer/platform/fonts/font_baseline.h" +#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h" + +namespace blink { + +namespace { + +// Create the style to use for the ellipsis characters. +// +// The ellipsis is styled according to the line style. +// https://drafts.csswg.org/css-ui/#ellipsing-details +scoped_refptr<const ComputedStyle> CreateEllipsisStyle( + scoped_refptr<const ComputedStyle> line_style) { + if (line_style->TextDecorationsInEffect() == TextDecoration::kNone) + return line_style; + + // Ellipsis should not have text decorations. Reset if it's set. + // This is not defined, but 4 impls do this. + scoped_refptr<ComputedStyle> ellipsis_style = + ComputedStyle::CreateAnonymousStyleWithDisplay(*line_style, + EDisplay::kInline); + ellipsis_style->ResetTextDecoration(); + ellipsis_style->ClearAppliedTextDecorations(); + return ellipsis_style; +} + +} // namespace + +NGLineTruncator::NGLineTruncator(NGInlineNode& node, + const NGLineInfo& line_info) + : node_(node), + line_style_(&line_info.LineStyle()), + available_width_(line_info.AvailableWidth()), + line_direction_(line_info.BaseDirection()) {} + +LayoutUnit NGLineTruncator::TruncateLine( + LayoutUnit line_width, + NGLineBoxFragmentBuilder::ChildList* line_box) { + // Shape the ellipsis and compute its inline size. + scoped_refptr<const ComputedStyle> ellipsis_style = + CreateEllipsisStyle(line_style_); + const Font& font = ellipsis_style->GetFont(); + const SimpleFontData* font_data = font.PrimaryFont(); + DCHECK(font_data); + String ellipsis_text = + font_data && font_data->GlyphForCharacter(kHorizontalEllipsisCharacter) + ? String(&kHorizontalEllipsisCharacter, 1) + : String(u"..."); + HarfBuzzShaper shaper(ellipsis_text.Characters16(), ellipsis_text.length()); + scoped_refptr<ShapeResult> ellipsis_shape_result = + shaper.Shape(&font, line_direction_); + LayoutUnit ellipsis_width = ellipsis_shape_result->SnappedWidth(); + + // Loop children from the logical last to the logical first to determine where + // to place the ellipsis. Children maybe truncated or moved as part of the + // process. + LayoutUnit ellipsis_inline_offset; + const NGPhysicalFragment* ellipsized_fragment = nullptr; + if (IsLtr(line_direction_)) { + NGLineBoxFragmentBuilder::Child* first_child = line_box->FirstInFlowChild(); + for (auto it = line_box->rbegin(); it != line_box->rend(); it++) { + auto& child = *it; + if (base::Optional<LayoutUnit> candidate = EllipsisOffset( + line_width, ellipsis_width, &child == first_child, &child)) { + ellipsis_inline_offset = candidate.value(); + ellipsized_fragment = child.PhysicalFragment(); + DCHECK(ellipsized_fragment); + break; + } + } + } else { + NGLineBoxFragmentBuilder::Child* first_child = line_box->LastInFlowChild(); + ellipsis_inline_offset = available_width_ - ellipsis_width; + for (auto& child : *line_box) { + if (base::Optional<LayoutUnit> candidate = EllipsisOffset( + line_width, ellipsis_width, &child == first_child, &child)) { + ellipsis_inline_offset = candidate.value(); + ellipsized_fragment = child.PhysicalFragment(); + DCHECK(ellipsized_fragment); + break; + } + } + } + + // Abort if ellipsis could not be placed. + if (!ellipsized_fragment) + return line_width; + + // Now the offset of the ellpisis is determined. Place the ellpisis into the + // line box. + NGTextFragmentBuilder builder(node_, line_style_->GetWritingMode()); + DCHECK(ellipsized_fragment->GetLayoutObject() && + ellipsized_fragment->GetLayoutObject()->IsInline()); + builder.SetText(ellipsized_fragment->GetLayoutObject(), ellipsis_text, + ellipsis_style, true /* is_ellipsis_style */, + std::move(ellipsis_shape_result)); + FontBaseline baseline_type = line_style_->GetFontBaseline(); + NGLineHeightMetrics ellipsis_metrics(font_data->GetFontMetrics(), + baseline_type); + line_box->AddChild( + builder.ToTextFragment(), + NGLogicalOffset{ellipsis_inline_offset, -ellipsis_metrics.ascent}, + ellipsis_width, 0); + return std::max(ellipsis_inline_offset + ellipsis_width, line_width); +} + +// Return the offset to place the ellipsis. +// +// This function may truncate or move the child so that the ellipsis can fit. +base::Optional<LayoutUnit> NGLineTruncator::EllipsisOffset( + LayoutUnit line_width, + LayoutUnit ellipsis_width, + bool is_first_child, + NGLineBoxFragmentBuilder::Child* child) { + // Leave out-of-flow children as is. + if (!child->HasInFlowFragment()) + return base::nullopt; + + // Can't place ellipsis if this child is completely outside of the box. + DCHECK_GE(line_width, child->offset.inline_offset + child->inline_size); + LayoutUnit child_inline_offset = + IsLtr(line_direction_) + ? child->offset.inline_offset + : line_width - (child->offset.inline_offset + child->inline_size); + LayoutUnit space_for_child = available_width_ - child_inline_offset; + if (space_for_child <= 0) + return base::nullopt; + + // If not all of this child can fit, try to truncate. + space_for_child -= ellipsis_width; + if (space_for_child < child->inline_size && + !TruncateChild(space_for_child, is_first_child, child)) { + // This child maybe partially visible. When it can't be truncated, move it + // out so that none of this child should be visible. + child->offset.inline_offset = line_width; + return base::nullopt; + } + + return IsLtr(line_direction_) + ? child->offset.inline_offset + child->inline_size + : child->offset.inline_offset - ellipsis_width; +} + +// Truncate the specified child. Returns true if truncated successfully, false +// otherwise. +// +// Note that this function may return true even if it can't fit the child when +// |is_first_child|, because the spec defines that the first character or atomic +// inline-level element on a line must be clipped rather than ellipsed. +// https://drafts.csswg.org/css-ui/#text-overflow +bool NGLineTruncator::TruncateChild(LayoutUnit space_for_child, + bool is_first_child, + NGLineBoxFragmentBuilder::Child* child) { + // If the space is not enough, try the next child. + if (space_for_child <= 0 && !is_first_child) + return false; + + // Only text fragments can be truncated. + if (!child->fragment) + return is_first_child; + auto& fragment = ToNGPhysicalTextFragment(*child->fragment); + const ShapeResult* shape_result = fragment.TextShapeResult(); + if (!shape_result) + return is_first_child; + + // Compute the offset to truncate. + unsigned new_length = shape_result->OffsetToFit( + IsLtr(line_direction_) ? space_for_child + : shape_result->Width() - space_for_child, + line_direction_); + if (!new_length || new_length == fragment.Length()) { + if (!is_first_child) + return false; + new_length = !new_length ? 1 : new_length - 1; + } + + // Truncate the text fragment. + child->fragment = line_direction_ == shape_result->Direction() + ? fragment.TrimText(fragment.StartOffset(), + fragment.StartOffset() + new_length) + : fragment.TrimText(fragment.StartOffset() + new_length, + fragment.EndOffset()); + LayoutUnit new_inline_size = line_style_->IsHorizontalWritingMode() + ? child->fragment->Size().width + : child->fragment->Size().height; + DCHECK_LE(new_inline_size, child->inline_size); + if (UNLIKELY(IsRtl(line_direction_))) + child->offset.inline_offset += child->inline_size - new_inline_size; + child->inline_size = new_inline_size; + return true; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h new file mode 100644 index 00000000000..adfcd22ad9c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_TRUNCATOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_TRUNCATOR_H_ + +#include "base/optional.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" +#include "third_party/blink/renderer/platform/text/text_direction.h" + +namespace blink { + +class NGLineInfo; + +// A class to truncate lines and place ellipsis, invoked by the CSS +// 'text-overflow: ellipsis' property. +// https://drafts.csswg.org/css-ui/#overflow-ellipsis +class CORE_EXPORT NGLineTruncator final { + STACK_ALLOCATED(); + + public: + NGLineTruncator(NGInlineNode& node, const NGLineInfo& line_info); + + // Truncate |line_box| and place ellipsis. Returns the new inline-size of the + // |line_box|. + // + // |line_box| should be after bidi reorder, but before box fragments are + // created. + LayoutUnit TruncateLine(LayoutUnit line_width, + NGLineBoxFragmentBuilder::ChildList* line_box); + + private: + base::Optional<LayoutUnit> EllipsisOffset(LayoutUnit line_width, + LayoutUnit ellipsis_width, + bool is_first_child, + NGLineBoxFragmentBuilder::Child*); + bool TruncateChild(LayoutUnit space_for_this_child, + bool is_first_child, + NGLineBoxFragmentBuilder::Child* child); + + NGInlineNode& node_; + scoped_refptr<const ComputedStyle> line_style_; + LayoutUnit available_width_; + TextDirection line_direction_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_TRUNCATOR_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.cc new file mode 100644 index 00000000000..d4c9f60a712 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.cc @@ -0,0 +1,31 @@ +// 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/ng/inline/ng_line_utils.h" + +#include "third_party/blink/renderer/core/editing/position_with_affinity.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" + +namespace blink { + +const NGPaintFragment* NGContainingLineBoxOf( + const PositionWithAffinity& position) { + const NGCaretPosition caret_position = ComputeNGCaretPosition(position); + if (caret_position.IsNull()) + return nullptr; + return caret_position.fragment->ContainerLineBox(); +} + +bool InSameNGLineBox(const PositionWithAffinity& position1, + const PositionWithAffinity& position2) { + const NGPaintFragment* line_box1 = NGContainingLineBoxOf(position1); + if (!line_box1) + return false; + + const NGPaintFragment* line_box2 = NGContainingLineBoxOf(position2); + return line_box1 == line_box2; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h new file mode 100644 index 00000000000..e77e8dbde26 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_ + +#include "third_party/blink/renderer/core/editing/forward.h" + +namespace blink { + +class NGPaintFragment; + +// Returns the NG line box fragment containing the caret position of the given +// position. Returns false if the position is not in Layout NG, or does not +// have any caret position. +const NGPaintFragment* NGContainingLineBoxOf(const PositionWithAffinity&); + +// Returns true if the caret positions of the two positions are in the same NG +// line box. Returns false in all other cases. +bool InSameNGLineBox(const PositionWithAffinity&, const PositionWithAffinity&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc index ff4406cef52..46e7077f983 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/editing/position.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/platform/text/character.h" namespace blink { @@ -247,13 +248,13 @@ NGMappingUnitRange NGOffsetMapping::GetMappingUnitsForDOMRange( return {result_begin, result_end}; } -Optional<unsigned> NGOffsetMapping::GetTextContentOffset( +base::Optional<unsigned> NGOffsetMapping::GetTextContentOffset( const Position& position) const { DCHECK(NGOffsetMapping::AcceptsPosition(position)) << position; if (IsNonAtomicInline(*position.AnchorNode())) { auto iter = ranges_.find(position.AnchorNode()); if (iter == ranges_.end()) - return WTF::nullopt; + return base::nullopt; DCHECK_NE(iter->value.first, iter->value.second) << position; if (position.IsBeforeAnchor()) return units_[iter->value.first].TextContentStart(); @@ -262,7 +263,7 @@ Optional<unsigned> NGOffsetMapping::GetTextContentOffset( const NGOffsetMappingUnit* unit = GetMappingUnitForPosition(position); if (!unit) - return WTF::nullopt; + return base::nullopt; return unit->ConvertDOMOffsetToTextContent(ToNodeOffsetPair(position).second); } @@ -339,13 +340,13 @@ bool NGOffsetMapping::IsAfterNonCollapsedContent( unit->GetType() != NGOffsetMappingUnitType::kCollapsed; } -Optional<UChar> NGOffsetMapping::GetCharacterBefore( +base::Optional<UChar> NGOffsetMapping::GetCharacterBefore( const Position& position) const { DCHECK(NGOffsetMapping::AcceptsPosition(position)); DCHECK(!IsNonAtomicInline(*position.AnchorNode())) << position; - Optional<unsigned> text_content_offset = GetTextContentOffset(position); + base::Optional<unsigned> text_content_offset = GetTextContentOffset(position); if (!text_content_offset || !*text_content_offset) - return WTF::nullopt; + return base::nullopt; return text_[*text_content_offset - 1]; } @@ -385,4 +386,15 @@ Position NGOffsetMapping::GetLastPosition(unsigned offset) const { return CreatePositionForOffsetMapping(node, dom_offset); } +bool NGOffsetMapping::HasBidiControlCharactersOnly(unsigned start, + unsigned end) const { + DCHECK_LE(start, end); + DCHECK_LE(end, text_.length()); + for (unsigned i = start; i < end; ++i) { + if (!Character::IsBidiControl(text_[i])) + return false; + } + return true; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h index f4ea52ba8f5..efb2769eb30 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h @@ -5,12 +5,13 @@ #ifndef NGOffsetMapping_h #define NGOffsetMapping_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/editing/forward.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -18,7 +19,6 @@ namespace blink { class LayoutBlockFlow; class LayoutObject; -class Node; enum class NGOffsetMappingUnitType { kIdentity, kCollapsed, kExpanded }; @@ -134,7 +134,7 @@ class CORE_EXPORT NGOffsetMapping { // Returns the text content offset corresponding to the given position. // Returns nullopt when the position is not laid out in this context. - Optional<unsigned> GetTextContentOffset(const Position&) const; + base::Optional<unsigned> GetTextContentOffset(const Position&) const; // Starting from the given position, searches for non-collapsed content in // the anchor node in forward/backward direction and returns the position @@ -151,7 +151,7 @@ class CORE_EXPORT NGOffsetMapping { // Maps the given position to a text content offset, and then returns the text // content character before the offset. Returns nullopt if it does not exist. - Optional<UChar> GetCharacterBefore(const Position&) const; + base::Optional<UChar> GetCharacterBefore(const Position&) const; // ------ Mapping APIs from text content to DOM ------ @@ -172,6 +172,12 @@ class CORE_EXPORT NGOffsetMapping { // TODO(xiaochengh): Add offset-to-DOM APIs skipping generated contents. + // ------ APIs inspecting the text content string ------ + + // Returns false if all characters in [start, end) of |text_| are bidi + // control charcters. Returns true otherwise. + bool HasBidiControlCharactersOnly(unsigned start, unsigned end) const; + private: // The NGOffsetMappingUnits of the inline formatting context in osrted order. UnitVector units_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h index 8451f04475a..716d3ac760e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h @@ -5,9 +5,9 @@ #ifndef NGOffsetMappingBuilder_h #define NGOffsetMappingBuilder_h +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -80,7 +80,7 @@ class CORE_EXPORT NGOffsetMappingBuilder { ~SourceNodeScope(); private: - AutoReset<const LayoutObject*> auto_reset_; + base::AutoReset<const LayoutObject*> auto_reset_; #if DCHECK_IS_ON() NGOffsetMappingBuilder* builder_ = nullptr; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc index 39232f8ad19..1036a000106 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc @@ -40,7 +40,7 @@ class NGOffsetMappingTest : public NGLayoutTest { } bool IsOffsetMappingStored() const { - return layout_block_flow_->GetNGInlineNodeData()->offset_mapping_.get(); + return layout_block_flow_->GetNGInlineNodeData()->offset_mapping.get(); } const LayoutText* GetLayoutTextUnder(const char* parent_id) { @@ -53,7 +53,8 @@ class NGOffsetMappingTest : public NGLayoutTest { return GetOffsetMapping().GetMappingUnitForPosition(position); } - Optional<unsigned> GetTextContentOffset(const Position& position) const { + base::Optional<unsigned> GetTextContentOffset( + const Position& position) const { return GetOffsetMapping().GetTextContentOffset(position); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc index 10d79eb6c43..e341ddffdae 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc @@ -5,26 +5,61 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { +namespace { + +static const NGPhysicalFragment* LastLogicalLeafExceptLinebreakInternal( + const NGPhysicalFragment& runner, + TextDirection direction) { + if (runner.IsText()) { + if (ToNGPhysicalTextFragment(runner).IsLineBreak()) + return nullptr; + return &runner; + } + if (!runner.IsContainer() || runner.IsBlockLayoutRoot()) + return &runner; + const auto& children = ToNGPhysicalContainerFragment(runner).Children(); + for (size_t i = 0; i < children.size(); i++) { + // TODO(xiaochengh): This isn't correct for mixed Bidi. Fix it. Besides, we + // should compute and store it during layout. + // We want a logical last child in a line. + const size_t index = + direction == TextDirection::kLtr ? (children.size() - 1 - i) : i; + const NGPhysicalFragment* child = children[index].get(); + DCHECK(child); + if (const NGPhysicalFragment* candidate = + LastLogicalLeafExceptLinebreakInternal(*child, direction)) + return candidate; + } + return nullptr; +} + +} // namespace + NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment( const ComputedStyle& style, + NGStyleVariant style_variant, NGPhysicalSize size, Vector<scoped_refptr<NGPhysicalFragment>>& children, const NGPhysicalOffsetRect& contents_visual_rect, + const NGPhysicalOffsetRect& scrollable_overflow, const NGLineHeightMetrics& metrics, TextDirection base_direction, scoped_refptr<NGBreakToken> break_token) : NGPhysicalContainerFragment(nullptr, style, + style_variant, size, kFragmentLineBox, 0, children, contents_visual_rect, std::move(break_token)), + scrollable_overflow_(scrollable_overflow), metrics_(metrics) { base_direction_ = static_cast<unsigned>(base_direction); } @@ -80,6 +115,13 @@ const NGPhysicalFragment* NGPhysicalLineBoxFragment::LastLogicalLeaf() const { return runner; } +const NGPhysicalFragment* +NGPhysicalLineBoxFragment::LastLogicalLeafIgnoringLineBreak() const { + if (Children().IsEmpty()) + return nullptr; + return LastLogicalLeafExceptLinebreakInternal(*this, this->BaseDirection()); +} + bool NGPhysicalLineBoxFragment::HasSoftWrapToNextLine() const { DCHECK(BreakToken()); DCHECK(BreakToken()->IsInlineType()); @@ -87,9 +129,4 @@ bool NGPhysicalLineBoxFragment::HasSoftWrapToNextLine() const { return !break_token.IsFinished() && !break_token.IsForcedBreak(); } -PositionWithAffinity NGPhysicalLineBoxFragment::PositionForPoint( - const NGPhysicalOffset& point) const { - return PositionForPointInInlineLevelBox(point); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h index 02989a0955e..b92b910d2b3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h @@ -17,9 +17,11 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final public: // This modifies the passed-in children vector. NGPhysicalLineBoxFragment(const ComputedStyle&, + NGStyleVariant style_variant, NGPhysicalSize size, Vector<scoped_refptr<NGPhysicalFragment>>& children, const NGPhysicalOffsetRect& contents_visual_rect, + const NGPhysicalOffsetRect& scrollable_overflow, const NGLineHeightMetrics&, TextDirection base_direction, scoped_refptr<NGBreakToken> break_token = nullptr); @@ -39,24 +41,31 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final // VisualRect of itself including contents, in the local coordinate. NGPhysicalOffsetRect VisualRectWithContents() const; + // Scrollable overflow. including contents, in the local coordinate. + NGPhysicalOffsetRect ScrollableOverflow() const { + return scrollable_overflow_; + } + // Returns the first/last leaf fragment in the line in logical order. Returns // nullptr if the line box is empty. const NGPhysicalFragment* FirstLogicalLeaf() const; const NGPhysicalFragment* LastLogicalLeaf() const; + // Returns the last leaf fragment in the line in logical order except line + // break. Returns nullptr if such fragment doesn't exist. + const NGPhysicalFragment* LastLogicalLeafIgnoringLineBreak() const; // Whether the content soft-wraps to the next line. bool HasSoftWrapToNextLine() const; - PositionWithAffinity PositionForPoint(const NGPhysicalOffset&) const final; - scoped_refptr<NGPhysicalFragment> CloneWithoutOffset() const { Vector<scoped_refptr<NGPhysicalFragment>> children_copy(children_); return base::AdoptRef(new NGPhysicalLineBoxFragment( - Style(), size_, children_copy, contents_visual_rect_, metrics_, - BaseDirection(), break_token_)); + Style(), StyleVariant(), size_, children_copy, contents_visual_rect_, + scrollable_overflow_, metrics_, BaseDirection(), break_token_)); } private: + NGPhysicalOffsetRect scrollable_overflow_; NGLineHeightMetrics metrics_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc index cd44fd8ea05..331fb87d70b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc @@ -59,6 +59,7 @@ TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafInSimpleText) { "</div>"); EXPECT_TEXT_FRAGMENT("foo", GetLineBox()->FirstLogicalLeaf()); EXPECT_TEXT_FRAGMENT("bar", GetLineBox()->LastLogicalLeaf()); + EXPECT_TEXT_FRAGMENT("bar", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); } TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafInRtlText) { @@ -69,6 +70,7 @@ TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafInRtlText) { "</bdo>"); EXPECT_TEXT_FRAGMENT("foo", GetLineBox()->FirstLogicalLeaf()); EXPECT_TEXT_FRAGMENT("bar", GetLineBox()->LastLogicalLeaf()); + EXPECT_TEXT_FRAGMENT("bar", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); } TEST_F(NGPhysicalLineBoxFragmentTest, @@ -81,6 +83,7 @@ TEST_F(NGPhysicalLineBoxFragmentTest, "</div>"); EXPECT_TEXT_FRAGMENT("f", GetLineBox()->FirstLogicalLeaf()); EXPECT_TEXT_FRAGMENT("r", GetLineBox()->LastLogicalLeaf()); + EXPECT_TEXT_FRAGMENT("r", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); } TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafWithInlineBlock) { @@ -92,12 +95,26 @@ TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafWithInlineBlock) { "</div>"); EXPECT_BOX_FRAGMENT("foo", GetLineBox()->FirstLogicalLeaf()); EXPECT_BOX_FRAGMENT("baz", GetLineBox()->LastLogicalLeaf()); + EXPECT_BOX_FRAGMENT("baz", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); } TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafWithImages) { SetBodyInnerHTML("<div id=root><img id=img1>foo<img id=img2></div>"); EXPECT_BOX_FRAGMENT("img1", GetLineBox()->FirstLogicalLeaf()); EXPECT_BOX_FRAGMENT("img2", GetLineBox()->LastLogicalLeaf()); + EXPECT_BOX_FRAGMENT("img2", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); +} + +TEST_F(NGPhysicalLineBoxFragmentTest, LastLogicalLeafSoftWrap) { + SetBodyInnerHTML("<div id=root style='width: 2em'>foo bar</div>"); + EXPECT_TEXT_FRAGMENT("foo", GetLineBox()->LastLogicalLeaf()); + EXPECT_TEXT_FRAGMENT("foo", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); +} + +TEST_F(NGPhysicalLineBoxFragmentTest, LastLogicalLeafHardWrap) { + SetBodyInnerHTML("<div id=root>foo<br>bar</div>"); + EXPECT_TEXT_FRAGMENT("\n", GetLineBox()->LastLogicalLeaf()); + EXPECT_TEXT_FRAGMENT("foo", GetLineBox()->LastLogicalLeafIgnoringLineBreak()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc index 848ed377dd8..d75f591cc0f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc @@ -5,16 +5,31 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/dom/node.h" -#include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/core/layout/layout_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h" +#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { +// Convert logical cooridnate to local physical coordinate. +NGPhysicalOffsetRect NGPhysicalTextFragment::ConvertToLocal( + const LayoutRect& logical_rect) const { + switch (LineOrientation()) { + case NGLineOrientation::kHorizontal: + return NGPhysicalOffsetRect(logical_rect); + case NGLineOrientation::kClockWiseVertical: + return {{size_.width - logical_rect.MaxY(), logical_rect.X()}, + {logical_rect.Height(), logical_rect.Width()}}; + case NGLineOrientation::kCounterClockWiseVertical: + return {{logical_rect.Y(), size_.height - logical_rect.MaxX()}, + {logical_rect.Height(), logical_rect.Width()}}; + } + NOTREACHED(); + return NGPhysicalOffsetRect(logical_rect); +} + // Compute the inline position from text offset, in logical coordinate relative // to this fragment. LayoutUnit NGPhysicalTextFragment::InlinePositionForOffset( @@ -75,8 +90,8 @@ NGPhysicalOffsetRect NGPhysicalTextFragment::LocalRect( } NGPhysicalOffsetRect NGPhysicalTextFragment::SelfVisualRect() const { - if (!shape_result_) - return {}; + if (UNLIKELY(!shape_result_)) + return LocalRect(); // Glyph bounds is in logical coordinate, origin at the alphabetic baseline. LayoutRect visual_rect = EnclosingLayoutRect(shape_result_->Bounds()); @@ -84,8 +99,7 @@ NGPhysicalOffsetRect NGPhysicalTextFragment::SelfVisualRect() const { // Make the origin at the logical top of this fragment. const ComputedStyle& style = Style(); const Font& font = style.GetFont(); - const SimpleFontData* font_data = font.PrimaryFont(); - if (font_data) { + if (const SimpleFontData* font_data = font.PrimaryFont()) { visual_rect.SetY(visual_rect.Y() + font_data->GetFontMetrics().FixedAscent( kAlphabeticBaseline)); } @@ -111,26 +125,38 @@ NGPhysicalOffsetRect NGPhysicalTextFragment::SelfVisualRect() const { if (ShadowList* text_shadow = style.TextShadow()) { LayoutRectOutsets text_shadow_logical_outsets = - LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()) - .LineOrientationOutsets(style.GetWritingMode()); + LineOrientationLayoutRectOutsets( + LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()), + style.GetWritingMode()); text_shadow_logical_outsets.ClampNegativeToZero(); visual_rect.Expand(text_shadow_logical_outsets); } visual_rect = LayoutRect(EnclosingIntRect(visual_rect)); - switch (LineOrientation()) { - case NGLineOrientation::kHorizontal: - return NGPhysicalOffsetRect(visual_rect); - case NGLineOrientation::kClockWiseVertical: - return {{size_.width - visual_rect.MaxY(), visual_rect.X()}, - {visual_rect.Height(), visual_rect.Width()}}; - case NGLineOrientation::kCounterClockWiseVertical: - return {{visual_rect.Y(), size_.height - visual_rect.MaxX()}, - {visual_rect.Height(), visual_rect.Width()}}; - } - NOTREACHED(); - return {}; + // Uniting the frame rect ensures that non-ink spaces such side bearings, or + // even space characters, are included in the visual rect for decorations. + NGPhysicalOffsetRect local_visual_rect = ConvertToLocal(visual_rect); + local_visual_rect.Unite(LocalRect()); + return local_visual_rect; +} + +scoped_refptr<NGPhysicalFragment> NGPhysicalTextFragment::TrimText( + unsigned new_start_offset, + unsigned new_end_offset) const { + DCHECK(shape_result_); + DCHECK_GE(new_start_offset, StartOffset()); + DCHECK_GT(new_end_offset, new_start_offset); + DCHECK_LE(new_end_offset, EndOffset()); + scoped_refptr<ShapeResult> new_shape_result = + shape_result_->SubRange(new_start_offset, new_end_offset); + LayoutUnit new_inline_size = new_shape_result->SnappedWidth(); + return base::AdoptRef(new NGPhysicalTextFragment( + layout_object_, Style(), static_cast<NGStyleVariant>(style_variant_), + TextType(), text_, new_start_offset, new_end_offset, + IsHorizontal() ? NGPhysicalSize{new_inline_size, size_.height} + : NGPhysicalSize{size_.width, new_inline_size}, + LineOrientation(), EndEffect(), std::move(new_shape_result))); } scoped_refptr<NGPhysicalFragment> NGPhysicalTextFragment::CloneWithoutOffset() @@ -164,15 +190,24 @@ unsigned NGPhysicalTextFragment::TextOffsetForPoint( StartOffset(); } -PositionWithAffinity NGPhysicalTextFragment::PositionForPoint( - const NGPhysicalOffset& point) const { - if (IsAnonymousText()) - return PositionWithAffinity(); - const unsigned text_offset = TextOffsetForPoint(point); - const Position position = - NGOffsetMapping::GetFor(GetLayoutObject())->GetFirstPosition(text_offset); - // TODO(xiaochengh): Adjust TextAffinity. - return PositionWithAffinity(position, TextAffinity::kDownstream); +UBiDiLevel NGPhysicalTextFragment::BidiLevel() const { + // TODO(xiaochengh): Make the implementation more efficient with, e.g., + // binary search and/or LayoutNGText::InlineItems(). + const auto& items = InlineItemsOfContainingBlock(); + const NGInlineItem* containing_item = std::find_if( + items.begin(), items.end(), [this](const NGInlineItem& item) { + return item.StartOffset() <= StartOffset() && + item.EndOffset() >= EndOffset(); + }); + DCHECK(containing_item); + DCHECK_NE(containing_item, items.end()); + return containing_item->BidiLevel(); +} + +TextDirection NGPhysicalTextFragment::ResolvedDirection() const { + if (TextShapeResult()) + return TextShapeResult()->Direction(); + return NGPhysicalFragment::ResolvedDirection(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h index eea1dddd920..f6921d10546 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" -#include "third_party/blink/renderer/platform/fonts/font_baseline.h" #include "third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" @@ -16,8 +15,6 @@ namespace blink { -class ShapeResult; - struct NGPhysicalOffsetRect; enum class AdjustMidCluster; @@ -104,9 +101,6 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { bool IsHorizontal() const { return LineOrientation() == NGLineOrientation::kHorizontal; } - FontBaseline BaselineType() const { - return IsHorizontal() ? kAlphabeticBaseline : kIdeographicBaseline; - } // Compute the inline position from text offset, in logical coordinate // relative to this fragment. @@ -116,6 +110,7 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { // Start and end offsets must be between StartOffset() and EndOffset(). NGPhysicalOffsetRect LocalRect(unsigned start_offset, unsigned end_offset) const; + using NGPhysicalFragment::LocalRect; // The visual bounding box that includes glpyh bounding box and CSS // properties, in local coordinates. @@ -125,6 +120,11 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { return static_cast<NGTextEndEffect>(end_effect_); } + // Create a new fragment that has part of the text of this fragment. + // All other properties are the same as this fragment. + scoped_refptr<NGPhysicalFragment> TrimText(unsigned start_offset, + unsigned end_offset) const; + scoped_refptr<NGPhysicalFragment> CloneWithoutOffset() const; NGTextFragmentPaintInfo PaintInfo() const { @@ -139,13 +139,16 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { // Returns the text offset in the fragment placed closest to the given point. unsigned TextOffsetForPoint(const NGPhysicalOffset&) const; - PositionWithAffinity PositionForPoint(const NGPhysicalOffset&) const override; + UBiDiLevel BidiLevel() const override; + TextDirection ResolvedDirection() const override; private: LayoutUnit InlinePositionForOffset(unsigned offset, LayoutUnit (*round)(float), AdjustMidCluster) const; + NGPhysicalOffsetRect ConvertToLocal(const LayoutRect&) const; + // The text of NGInlineNode; i.e., of a parent block. The text for this // fragment is a substring(start_offset_, end_offset_) of this string. const String text_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc index 8852bb2da74..9ee319a8d3c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc @@ -37,13 +37,14 @@ NGTextFragmentBuilder::NGTextFragmentBuilder(NGInlineNode node, void NGTextFragmentBuilder::SetItem( NGPhysicalTextFragment::NGTextType text_type, + const NGInlineItemsData& items_data, NGInlineItemResult* item_result, LayoutUnit line_height) { DCHECK(item_result); DCHECK(item_result->item->Style()); text_type_ = text_type; - text_ = inline_node_.Text(); + text_ = items_data.text_content; item_index_ = item_result->item_index; start_offset_ = item_result->start_offset; end_offset_ = item_result->end_offset; @@ -71,11 +72,8 @@ void NGTextFragmentBuilder::SetText( end_offset_ = shape_result->EndIndexForResult(); SetStyle(style, is_ellipsis_style ? NGStyleVariant::kEllipsis : NGStyleVariant::kStandard); - FontBaseline baseline_type = style->IsHorizontalWritingMode() - ? kAlphabeticBaseline - : kIdeographicBaseline; size_ = {shape_result->SnappedWidth(), - NGLineHeightMetrics(*style, baseline_type).LineHeight()}; + NGLineHeightMetrics(*style).LineHeight()}; shape_result_ = std::move(shape_result); layout_object_ = layout_object; end_effect_ = NGTextEndEffect::kNone; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h index b53ca079a0b..68efb30de15 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h @@ -26,6 +26,7 @@ class CORE_EXPORT NGTextFragmentBuilder final : public NGBaseFragmentBuilder { // NOTE: Takes ownership of the shape result within the item result. void SetItem(NGPhysicalTextFragment::NGTextType, + const NGInlineItemsData&, NGInlineItemResult*, LayoutUnit line_height); void SetText(LayoutObject*, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc index 8a0ad1a3f46..6b81fd15240 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc @@ -7,9 +7,11 @@ #include "third_party/blink/renderer/core/layout/layout_analyzer.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.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" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" namespace blink { @@ -81,8 +83,8 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() { container_style->GetWritingMode(), container_style->Direction()); // Compute ContainingBlock logical size. - // OverrideContainingBlockLogicalWidth/Height are used by e.g. grid layout. - // Override sizes are padding box size, not border box, so we must add + // OverrideContainingBlockContentLogicalWidth/Height are used by e.g. grid + // layout. Override sizes are padding box size, not border box, so we must add // borders and scrollbars to compensate. NGBoxStrut borders_and_scrollbars = ComputeBorders(*constraint_space, *container_style) + @@ -97,14 +99,14 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() { // object is really managed by legacy layout). LayoutUnit container_border_box_logical_width; LayoutUnit container_border_box_logical_height; - if (HasOverrideContainingBlockLogicalWidth()) { + if (HasOverrideContainingBlockContentLogicalWidth()) { container_border_box_logical_width = OverrideContainingBlockContentLogicalWidth() + borders_and_scrollbars.InlineSum(); } else { container_border_box_logical_width = container->LogicalWidth(); } - if (HasOverrideContainingBlockLogicalHeight()) { + if (HasOverrideContainingBlockContentLogicalHeight()) { container_border_box_logical_height = OverrideContainingBlockContentLogicalHeight() + borders_and_scrollbars.BlockSum(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc new file mode 100644 index 00000000000..5db1077417f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc @@ -0,0 +1,49 @@ +// 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/ng/layout_ng_flexible_box.h" + +#include "third_party/blink/renderer/core/layout/layout_analyzer.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" + +namespace blink { + +LayoutNGFlexibleBox::LayoutNGFlexibleBox(Element* element) + : LayoutBlock(element) {} + +void LayoutNGFlexibleBox::UpdateBlockLayout(bool relayout_children) { + LayoutAnalyzer::BlockScope analyzer(*this); + + scoped_refptr<NGConstraintSpace> constraint_space = + NGConstraintSpace::CreateFromLayoutObject(*this); + + scoped_refptr<NGLayoutResult> result = + NGBlockNode(this).Layout(*constraint_space); + + for (NGOutOfFlowPositionedDescendant descendant : + result->OutOfFlowPositionedDescendants()) + descendant.node.UseOldOutOfFlowPositioning(); + + NGPhysicalBoxFragment* fragment = + ToNGPhysicalBoxFragment(result->PhysicalFragment().get()); + + // Pasted from layout_ng_block_flow. TODO(dgrogan): Factor a utility method. + const LayoutBlock* containing_block = ContainingBlock(); + NGPhysicalOffset physical_offset; + if (containing_block) { + NGPhysicalSize containing_block_size(containing_block->Size().Width(), + containing_block->Size().Height()); + NGLogicalOffset logical_offset(LogicalLeft(), LogicalTop()); + physical_offset = logical_offset.ConvertToPhysical( + constraint_space->GetWritingMode(), constraint_space->Direction(), + containing_block_size, fragment->Size()); + } + fragment->SetOffset(physical_offset); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h new file mode 100644 index 00000000000..6ff73e9c01f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h @@ -0,0 +1,31 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_FLEXIBLE_BOX_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_FLEXIBLE_BOX_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h" + +namespace blink { + +class CORE_EXPORT LayoutNGFlexibleBox : public LayoutBlock { + public: + explicit LayoutNGFlexibleBox(Element*); + + void UpdateBlockLayout(bool relayout_children) override; + + bool IsFlexibleBox() const final { return true; } + const char* GetName() const override { return "LayoutNGFlexibleBox"; } + + protected: + bool IsOfType(LayoutObjectType type) const override { + return type == kLayoutObjectNGFlexibleBox || LayoutBlock::IsOfType(type); + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_FLEXIBLE_BOX_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc index c32f0ceb87b..19c0da2d6fd 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc @@ -9,16 +9,29 @@ #include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/core/layout/hit_test_location.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/layout_table_caption.h" +#include "third_party/blink/renderer/core/layout/layout_table_cell.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h" #include "third_party/blink/renderer/core/paint/ng/ng_block_flow_painter.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" namespace blink { template <typename Base> +LayoutNGMixin<Base>::LayoutNGMixin(Element* element) : Base(element) { + static_assert( + std::is_base_of<LayoutBlockFlow, Base>::value, + "Base class of LayoutNGMixin must be LayoutBlockFlow or derived class."); +} + +template <typename Base> LayoutNGMixin<Base>::~LayoutNGMixin() = default; template <typename Base> @@ -27,6 +40,11 @@ bool LayoutNGMixin<Base>::IsOfType(LayoutObject::LayoutObjectType type) const { } template <typename Base> +NGInlineNodeData* LayoutNGMixin<Base>::TakeNGInlineNodeData() { + return ng_inline_node_data_.release(); +} + +template <typename Base> NGInlineNodeData* LayoutNGMixin<Base>::GetNGInlineNodeData() const { DCHECK(ng_inline_node_data_); return ng_inline_node_data_.get(); @@ -56,26 +74,43 @@ void LayoutNGMixin<Base>::AddOverflowFromChildren() { // Add overflow from the last layout cycle. if (Base::ChildrenInline()) { if (const NGPhysicalBoxFragment* physical_fragment = CurrentFragment()) { - bool has_width = - physical_fragment->Style().OverflowX() != EOverflow::kHidden; - bool has_height = - physical_fragment->Style().OverflowY() != EOverflow::kHidden; - if (has_width || has_height) { + // LayoutOverflow is only computed if overflow is not hidden + if (physical_fragment->Style().OverflowX() != EOverflow::kHidden || + physical_fragment->Style().OverflowY() != EOverflow::kHidden) { + // inline-end LayoutOverflow padding spec is still undecided: + // https://github.com/w3c/csswg-drafts/issues/129 + // For backwards compatibility, if container clips overflow, + // padding is added to the inline-end for inline children. + base::Optional<NGPhysicalBoxStrut> padding_strut; + if (Base::HasOverflowClip()) { + padding_strut = + NGBoxStrut(LayoutUnit(), Base::PaddingEnd(), LayoutUnit(), + LayoutUnit()) + .ConvertToPhysical(Base::StyleRef().GetWritingMode(), + Base::StyleRef().Direction()); + } + NGPhysicalOffsetRect children_overflow; for (const auto& child : physical_fragment->Children()) { - NGPhysicalOffsetRect child_rect(child->Offset(), child->Size()); - if (!has_width) - child_rect.size.width = LayoutUnit(); - if (!has_height) - child_rect.size.height = LayoutUnit(); - Base::AddLayoutOverflow(child_rect.ToLayoutRect()); + NGPhysicalOffsetRect child_scrollable_overflow = + child->ScrollableOverflow(); + child_scrollable_overflow.offset += child->Offset(); + if (child->IsLineBox() && padding_strut) { + child_scrollable_overflow.Expand(*padding_strut); + } + children_overflow.Unite(child_scrollable_overflow); } + Base::AddLayoutOverflow(children_overflow.ToLayoutFlippedRect( + physical_fragment->Style(), physical_fragment->Size())); } - + Base::AddSelfVisualOverflow( + physical_fragment->SelfVisualRect().ToLayoutFlippedRect( + physical_fragment->Style(), physical_fragment->Size())); // TODO(kojii): If |RecalcOverflowAfterStyleChange()|, we need to // re-compute glyph bounding box. How to detect it and how to re-compute // is TBD. Base::AddContentsVisualOverflow( - physical_fragment->ContentsVisualRect().ToLayoutRect()); + physical_fragment->ContentsVisualRect().ToLayoutFlippedRect( + physical_fragment->Style(), physical_fragment->Size())); // TODO(kojii): The above code computes visual overflow only, we fallback // to LayoutBlock for AddLayoutOverflow() for now. It doesn't compute // correctly without RootInlineBox though. @@ -84,14 +119,24 @@ void LayoutNGMixin<Base>::AddOverflowFromChildren() { Base::AddOverflowFromChildren(); } +template <typename Base> +void LayoutNGMixin<Base>::AddOutlineRects( + Vector<LayoutRect>& rects, + const LayoutPoint& additional_offset, + LayoutObject::IncludeBlockVisualOverflowOrNot include_block_overflows) + const { + Base::AddOutlineRects(rects, additional_offset, include_block_overflows); + if (CurrentFragment()) { + CurrentFragment()->AddSelfOutlineRects(&rects, additional_offset); + } +} + // Retrieve NGBaseline from the current fragment. template <typename Base> const NGBaseline* LayoutNGMixin<Base>::FragmentBaseline( NGBaselineAlgorithmType type) const { if (const NGPhysicalFragment* physical_fragment = CurrentFragment()) { - FontBaseline baseline_type = Base::IsHorizontalWritingMode() - ? kAlphabeticBaseline - : kIdeographicBaseline; + FontBaseline baseline_type = Base::StyleRef().GetFontBaseline(); return ToNGPhysicalBoxFragment(physical_fragment) ->Baseline({type, baseline_type}); } @@ -131,13 +176,26 @@ scoped_refptr<NGLayoutResult> LayoutNGMixin<Base>::CachedLayoutResult( return nullptr; if (constraint_space != *cached_constraint_space_) return nullptr; - if (cached_constraint_space_->UnpositionedFloats().size() || - cached_result_->UnpositionedFloats().size()) + // The checks above should be enough to bail if layout is incomplete, but + // let's verify: + DCHECK(IsBlockLayoutComplete(*cached_constraint_space_, *cached_result_)); + // If we used to contain abspos items, we can't reuse the fragment, because + // we can't be sure that the list of items hasn't changed (as we bubble them + // up during layout). In the case of newly-added abspos items to this + // containing block, we will handle those by the NeedsLayout check above for + // now. + // TODO(layout-ng): Come up with a better solution for this + if (cached_result_->OutOfFlowPositionedDescendants().size()) return nullptr; return cached_result_->CloneWithoutOffset(); } template <typename Base> +const NGConstraintSpace* LayoutNGMixin<Base>::CachedConstraintSpace() const { + return cached_constraint_space_.get(); +} + +template <typename Base> void LayoutNGMixin<Base>::SetCachedLayoutResult( const NGConstraintSpace& constraint_space, NGBreakToken* break_token, @@ -146,6 +204,8 @@ void LayoutNGMixin<Base>::SetCachedLayoutResult( // We can't cache these yet return; } + if (constraint_space.IsIntermediateLayout()) + return; cached_constraint_space_ = &constraint_space; cached_result_ = layout_result; @@ -167,26 +227,9 @@ void LayoutNGMixin<Base>::SetPaintFragment( Base::SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kSubtree); } -static Vector<NGPaintFragment*> GetNGPaintFragmentsInternal( - NGPaintFragment* paint, - const LayoutObject& layout_object) { - if (!paint) - return Vector<NGPaintFragment*>(); - Vector<NGPaintFragment*> fragments; - if (paint->GetLayoutObject() == &layout_object) - fragments.push_back(paint); - for (const auto& child : paint->Children()) { - const auto& result = - GetNGPaintFragmentsInternal(child.get(), layout_object); - fragments.AppendVector(result); - } - return fragments; -} - template <typename Base> -Vector<NGPaintFragment*> LayoutNGMixin<Base>::GetPaintFragments( - const LayoutObject& layout_object) const { - return GetNGPaintFragmentsInternal(PaintFragment(), layout_object); +void LayoutNGMixin<Base>::ClearPaintFragment() { + paint_fragment_ = nullptr; } template <typename Base> @@ -223,8 +266,10 @@ bool LayoutNGMixin<Base>::NodeAtPoint( return LayoutBlockFlow::NodeAtPoint(result, location_in_container, accumulated_offset, action); } - - LayoutPoint adjusted_location = accumulated_offset + Base::Location(); + LayoutPoint offset = PaintFragment()->PhysicalFragment().IsPlacedByLayoutNG() + ? PaintFragment()->Offset().ToLayoutPoint() + : Base::Location(); + LayoutPoint adjusted_location = accumulated_offset + offset; if (!RootScrollerUtil::IsEffective(*this)) { // Check if we need to do anything at all. // If we have clipping, then we can't have any spillout. @@ -235,6 +280,10 @@ bool LayoutNGMixin<Base>::NodeAtPoint( if (!location_in_container.Intersects(overflow_box)) return false; } + if (Base::IsInSelfHitTestingPhase(action) && Base::HasOverflowClip() && + Base::HitTestOverflowControl(result, location_in_container, + adjusted_location)) + return true; return NGBlockFlowPainter(*this).NodeAtPoint(result, location_in_container, accumulated_offset, @@ -254,16 +303,17 @@ PositionWithAffinity LayoutNGMixin<Base>::PositionForPoint( if (!Base::ChildrenInline()) return LayoutBlock::PositionForPoint(point); - if (!CurrentFragment()) + if (!PaintFragment()) return Base::CreatePositionWithAffinity(0); const PositionWithAffinity ng_position = - CurrentFragment()->PositionForPoint(NGPhysicalOffset(point)); + PaintFragment()->PositionForPoint(NGPhysicalOffset(point)); if (ng_position.IsNotNull()) return ng_position; return Base::CreatePositionWithAffinity(0); } +template class LayoutNGMixin<LayoutTableCaption>; template class LayoutNGMixin<LayoutTableCell>; template class LayoutNGMixin<LayoutBlockFlow>; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h index e2812400e48..3f7511fd9b9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h @@ -7,30 +7,30 @@ #include <type_traits> -#include "third_party/blink/renderer/core/layout/layout_table_cell.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" -#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" -#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_box_model_object.h" namespace blink { +enum class NGBaselineAlgorithmType; class NGBreakToken; +class NGConstraintSpace; class NGLayoutResult; +class NGPaintFragment; +class NGPhysicalFragment; +struct NGBaseline; +struct NGInlineNodeData; // This mixin holds code shared between LayoutNG subclasses of // LayoutBlockFlow. template <typename Base> class CORE_TEMPLATE_CLASS_EXPORT LayoutNGMixin : public Base { - static_assert( - std::is_base_of<LayoutBlockFlow, Base>::value, - "Base class of LayoutNGMixin must be LayoutBlockFlow or derived class."); - public: - explicit LayoutNGMixin(Element* element) : Base(element) {} + explicit LayoutNGMixin(Element* element); ~LayoutNGMixin() override; + NGInlineNodeData* TakeNGInlineNodeData() override; NGInlineNodeData* GetNGInlineNodeData() const override; void ResetNGInlineNodeData() override; bool HasNGInlineNodeData() const override { @@ -57,6 +57,7 @@ class CORE_TEMPLATE_CLASS_EXPORT LayoutNGMixin : public Base { scoped_refptr<NGLayoutResult> CachedLayoutResult( const NGConstraintSpace&, NGBreakToken*) const override; + const NGConstraintSpace* CachedConstraintSpace() const override; void SetCachedLayoutResult(const NGConstraintSpace&, NGBreakToken*, @@ -68,15 +69,18 @@ class CORE_TEMPLATE_CLASS_EXPORT LayoutNGMixin : public Base { return paint_fragment_.get(); } void SetPaintFragment(scoped_refptr<const NGPhysicalFragment>) override; - void ClearPaintFragment() { paint_fragment_ = nullptr; } - Vector<NGPaintFragment*> GetPaintFragments( - const LayoutObject&) const override; + void ClearPaintFragment() override; protected: bool IsOfType(LayoutObject::LayoutObjectType) const override; void AddOverflowFromChildren() override; + void AddOutlineRects( + Vector<LayoutRect>&, + const LayoutPoint& additional_offset, + LayoutObject::IncludeBlockVisualOverflowOrNot) const override; + const NGPhysicalBoxFragment* CurrentFragment() const override; const NGBaseline* FragmentBaseline(NGBaselineAlgorithmType) const; @@ -90,11 +94,6 @@ class CORE_TEMPLATE_CLASS_EXPORT LayoutNGMixin : public Base { friend class NGBaseLayoutAlgorithmTest; }; -extern template class CORE_EXTERN_TEMPLATE_EXPORT - LayoutNGMixin<LayoutTableCell>; -extern template class CORE_EXTERN_TEMPLATE_EXPORT - LayoutNGMixin<LayoutBlockFlow>; - } // namespace blink #endif // LayoutNGMixin_h diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc new file mode 100644 index 00000000000..1ec5ed14bb6 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc @@ -0,0 +1,49 @@ +// 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/ng/layout_ng_table_caption.h" + +#include "third_party/blink/renderer/core/layout/layout_analyzer.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" + +namespace blink { + +LayoutNGTableCaption::LayoutNGTableCaption(Element* element) + : LayoutNGMixin<LayoutTableCaption>(element) {} + +void LayoutNGTableCaption::UpdateBlockLayout(bool relayout_children) { + LayoutAnalyzer::BlockScope analyzer(*this); + + DCHECK(!IsOutOfFlowPositioned()) << "Out of flow captions are blockified."; + + scoped_refptr<NGConstraintSpace> constraint_space = + NGConstraintSpace::CreateFromLayoutObject(*this); + + scoped_refptr<NGLayoutResult> result = + NGBlockNode(this).Layout(*constraint_space); + + // Tell legacy layout there were abspos descendents we couldn't place. We know + // we have to pass up to legacy here because this method is legacy's entry + // point to LayoutNG. If our parent were LayoutNG, it wouldn't have called + // UpdateBlockLayout, it would have packaged this LayoutObject into + // NGBlockNode and called Layout on that. + for (NGOutOfFlowPositionedDescendant descendant : + result->OutOfFlowPositionedDescendants()) + descendant.node.UseOldOutOfFlowPositioning(); + + // The parent table sometimes changes the caption's position after laying it + // out. So there's no point in setting the fragment's offset here; + // NGBoxFragmentPainter::Paint will have to handle it until table layout is + // implemented in NG, in which case that algorithm will set each child's + // offsets. See https://crbug.com/788590 for more info. + DCHECK(!result->PhysicalFragment()->IsPlacedByLayoutNG()) + << "Only a table should be placing table caption fragments and the ng " + "table algorithm doesn't exist yet!"; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.h new file mode 100644 index 00000000000..cacadfd46fc --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_TABLE_CAPTION_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_TABLE_CAPTION_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_table_caption.h" +#include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h" + +namespace blink { + +class CORE_EXPORT LayoutNGTableCaption final + : public LayoutNGMixin<LayoutTableCaption> { + public: + explicit LayoutNGTableCaption(Element*); + + void UpdateBlockLayout(bool relayout_children) override; + + const char* GetName() const override { return "LayoutNGTableCaption"; } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_TABLE_CAPTION_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc index 346cc36c6bf..e9e3423485d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc @@ -5,8 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.h" #include "third_party/blink/renderer/core/layout/layout_analyzer.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.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_positioned_descendant.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" namespace blink { @@ -16,8 +20,7 @@ LayoutNGTableCell::LayoutNGTableCell(Element* element) void LayoutNGTableCell::UpdateBlockLayout(bool relayout_children) { LayoutAnalyzer::BlockScope analyzer(*this); - SetOverrideLogicalContentWidth( - (LogicalWidth() - BorderAndPaddingLogicalWidth()).ClampNegativeToZero()); + SetOverrideLogicalWidth(LogicalWidth()); scoped_refptr<NGConstraintSpace> constraint_space = NGConstraintSpace::CreateFromLayoutObject(*this); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.cc b/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.cc index f18e7fa24be..6f504c9b9da 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.cc @@ -4,7 +4,6 @@ #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" -#include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" namespace blink { @@ -15,7 +14,7 @@ namespace blink { // LayoutMultiColumnSpannerPlaceholder. NG needs to skip these special // objects. The actual content is inside the flow thread. -LayoutObject* GetLayoutObjectForFirstChildNode(LayoutBlockFlow* parent) { +LayoutObject* GetLayoutObjectForFirstChildNode(LayoutBlock* parent) { LayoutObject* child = parent->FirstChild(); if (!child) return nullptr; @@ -38,7 +37,7 @@ LayoutObject* GetLayoutObjectForParentNode(LayoutObject* object) { return parent; } -bool AreNGBlockFlowChildrenInline(const LayoutBlockFlow* block) { +bool AreNGBlockFlowChildrenInline(const LayoutBlock* block) { if (block->ChildrenInline()) return true; if (const auto* first_child = block->FirstChild()) { @@ -49,14 +48,15 @@ bool AreNGBlockFlowChildrenInline(const LayoutBlockFlow* block) { } bool IsManagedByLayoutNG(const LayoutObject& object) { - if (!object.IsLayoutNGMixin()) + if (!object.IsLayoutNGMixin() && !object.IsLayoutNGFlexibleBox()) return false; const auto* containing_block = object.ContainingBlock(); if (!containing_block) return false; if (containing_block->IsLayoutFlowThread()) containing_block = containing_block->ContainingBlock(); - return containing_block && containing_block->IsLayoutNGMixin(); + return containing_block && (containing_block->IsLayoutNGMixin() || + containing_block->IsLayoutNGFlexibleBox()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h b/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h index 64bc4f57362..ab67358c6c0 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h @@ -7,13 +7,13 @@ namespace blink { -class LayoutBlockFlow; +class LayoutBlock; class LayoutObject; // Return the layout object that should be the first child NGLayoutInputNode of // |parent|. Normally this will just be the first layout object child, but there // are certain layout objects that should be skipped for NG. -LayoutObject* GetLayoutObjectForFirstChildNode(LayoutBlockFlow*); +LayoutObject* GetLayoutObjectForFirstChildNode(LayoutBlock*); // Return the layout object that should be the parent NGLayoutInputNode of // |object|. Normally this will just be the parent layout object, but there @@ -22,7 +22,7 @@ LayoutObject* GetLayoutObjectForParentNode(LayoutObject*); // Return true if the NGLayoutInputNode children of the NGLayoutInputNode // established by |block| will be inline; see LayoutObject::ChildrenInline(). -bool AreNGBlockFlowChildrenInline(const LayoutBlockFlow*); +bool AreNGBlockFlowChildrenInline(const LayoutBlock*); // Return true if the layout object is a LayoutNG object that is managed by the // LayoutNG engine (i.e. its containing block is a LayoutNG object as well). diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc index 0bcf2bf175c..fbd11c2a09d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc @@ -38,8 +38,9 @@ void LayoutNGListMarkerImage::ComputeSVGIntrinsicSizingInfoByDefaultSize( font_data->GetFontMetrics().Ascent() / LayoutUnit(2); FloatSize default_size(bullet_width, bullet_width); default_size.Scale(1 / Style()->EffectiveZoom()); - LayoutSize svg_image_size = - RoundedLayoutSize(ToSVGImage(image)->ConcreteObjectSize(default_size)); + FloatSize concrete_size = ToSVGImage(image)->ConcreteObjectSize(default_size); + concrete_size.Scale(Style()->EffectiveZoom() * ImageDevicePixelRatio()); + LayoutSize svg_image_size(RoundedLayoutSize(concrete_size)); intrinsic_sizing_info.size.SetWidth(svg_image_size.Width()); intrinsic_sizing_info.size.SetHeight(svg_image_size.Height()); @@ -47,19 +48,21 @@ void LayoutNGListMarkerImage::ComputeSVGIntrinsicSizingInfoByDefaultSize( intrinsic_sizing_info.has_height = true; } -bool LayoutNGListMarkerImage::GetNestedIntrinsicSizingInfo( +void LayoutNGListMarkerImage::ComputeIntrinsicSizingInfo( IntrinsicSizingInfo& intrinsic_sizing_info) const { - if (!LayoutImage::GetNestedIntrinsicSizingInfo(intrinsic_sizing_info)) - return false; + LayoutImage::ComputeIntrinsicSizingInfo(intrinsic_sizing_info); - // For SVG image, if GetNestedIntrinsicSizingInfo successfully and the size is - // empty, we need to compute the intrinsic size by setting a default size. + // If this is an SVG image without intrinsic width and height, + // compute an intrinsic size using the concrete object size resolved + // with a default object size of 1em x 1em. + // TODO(fs): Apply this more generally to all images (CSS <image> values) + // that have no intrinsic width or height. I.e just always compute the + // concrete object size here. if (intrinsic_sizing_info.size.IsEmpty() && CachedImage()) { Image* image = CachedImage()->GetImage(); if (image && image->IsSVGImage()) ComputeSVGIntrinsicSizingInfoByDefaultSize(intrinsic_sizing_info); } - return true; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h index 59cc8766a59..050b8ae5ee4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h @@ -21,7 +21,7 @@ class CORE_EXPORT LayoutNGListMarkerImage final : public LayoutImage { bool IsOfType(LayoutObjectType) const override; void ComputeSVGIntrinsicSizingInfoByDefaultSize(IntrinsicSizingInfo&) const; - bool GetNestedIntrinsicSizingInfo(IntrinsicSizingInfo&) const final; + void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const final; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGListMarkerImage, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc index 499092699aa..cfd4aad7ddd 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc @@ -5,8 +5,6 @@ #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" #include "third_party/blink/renderer/core/layout/layout_list_marker.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" @@ -41,24 +39,28 @@ LayoutUnit NGUnpositionedListMarker::InlineOffset( } scoped_refptr<NGLayoutResult> NGUnpositionedListMarker::Layout( - const NGConstraintSpace& space) const { + const NGConstraintSpace& space, + FontBaseline baseline_type) const { DCHECK(marker_layout_object_); NGBlockNode marker_node(marker_layout_object_); scoped_refptr<NGLayoutResult> marker_layout_result = - marker_node.LayoutAtomicInline(space, space.UseFirstLineStyle()); + marker_node.LayoutAtomicInline(space, baseline_type, + space.UseFirstLineStyle()); DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment()); return marker_layout_result; } bool NGUnpositionedListMarker::AddToBox( const NGConstraintSpace& space, + FontBaseline baseline_type, const NGPhysicalFragment& content, NGLogicalOffset* content_offset, NGFragmentBuilder* container_builder) const { + // Baselines from two different writing-mode cannot be aligned. + if (UNLIKELY(space.GetWritingMode() != content.Style().GetWritingMode())) + return false; + // Compute the baseline of the child content. - FontBaseline baseline_type = IsHorizontalWritingMode(space.GetWritingMode()) - ? kAlphabeticBaseline - : kIdeographicBaseline; NGLineHeightMetrics content_metrics; if (content.IsLineBox()) { content_metrics = ToNGPhysicalLineBoxFragment(content).Metrics(); @@ -66,7 +68,7 @@ bool NGUnpositionedListMarker::AddToBox( NGBoxFragment content_fragment(space.GetWritingMode(), ToNGPhysicalBoxFragment(content)); content_metrics = content_fragment.BaselineMetricsWithoutSynthesize( - {NGBaselineAlgorithmType::kFirstLine, baseline_type}, space); + {NGBaselineAlgorithmType::kFirstLine, baseline_type}); // If this child content does not have any line boxes, the list marker // should be aligned to the first line box of next child. @@ -76,7 +78,8 @@ bool NGUnpositionedListMarker::AddToBox( } // Layout the list marker. - scoped_refptr<NGLayoutResult> marker_layout_result = Layout(space); + scoped_refptr<NGLayoutResult> marker_layout_result = + Layout(space, baseline_type); DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment()); const NGPhysicalBoxFragment& marker_physical_fragment = ToNGPhysicalBoxFragment(*marker_layout_result->PhysicalFragment()); @@ -108,9 +111,11 @@ bool NGUnpositionedListMarker::AddToBox( LayoutUnit NGUnpositionedListMarker::AddToBoxWithoutLineBoxes( const NGConstraintSpace& space, + FontBaseline baseline_type, NGFragmentBuilder* container_builder) const { // Layout the list marker. - scoped_refptr<NGLayoutResult> marker_layout_result = Layout(space); + scoped_refptr<NGLayoutResult> marker_layout_result = + Layout(space, baseline_type); DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment()); const NGPhysicalBoxFragment& marker_physical_fragment = ToNGPhysicalBoxFragment(*marker_layout_result->PhysicalFragment()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h index a532a0eeb22..30e19bf3693 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h @@ -7,6 +7,7 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/fonts/font_baseline.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { @@ -45,6 +46,7 @@ class CORE_EXPORT NGUnpositionedListMarker final { // that the child content does not have a baseline to align to, and that // caller should try next child, or "WithoutLineBoxes" version. bool AddToBox(const NGConstraintSpace&, + FontBaseline, const NGPhysicalFragment& content, NGLogicalOffset* content_offset, NGFragmentBuilder*) const; @@ -53,13 +55,15 @@ class CORE_EXPORT NGUnpositionedListMarker final { // boxes. // Returns the block size of the list marker. LayoutUnit AddToBoxWithoutLineBoxes(const NGConstraintSpace&, + FontBaseline, NGFragmentBuilder*) const; private: bool IsImage() const; LayoutUnit InlineOffset(const LayoutUnit marker_inline_size) const; - scoped_refptr<NGLayoutResult> Layout(const NGConstraintSpace&) const; + scoped_refptr<NGLayoutResult> Layout(const NGConstraintSpace&, + FontBaseline) const; LayoutNGListMarker* marker_layout_object_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc index 94159196a42..4da55760b6b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc @@ -6,7 +6,6 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/length_functions.h" @@ -50,38 +49,41 @@ bool IsTopDominant(const WritingMode container_writing_mode, LayoutUnit ResolveWidth(const Length& width, const NGConstraintSpace& space, const ComputedStyle& style, - const Optional<MinMaxSize>& child_minmax, - LengthResolveType resolve_type) { - if (space.GetWritingMode() == WritingMode::kHorizontalTb) - return ResolveInlineLength(space, style, child_minmax, width, resolve_type); + const base::Optional<MinMaxSize>& child_minmax, + LengthResolveType type) { + if (space.GetWritingMode() == WritingMode::kHorizontalTb) { + return ResolveInlineLength(space, style, child_minmax, width, type, + LengthResolvePhase::kLayout); + } LayoutUnit computed_width = child_minmax.has_value() ? child_minmax->max_size : LayoutUnit(); - return ResolveBlockLength(space, style, style.Width(), computed_width, - resolve_type); + return ResolveBlockLength(space, style, width, computed_width, type, + LengthResolvePhase::kLayout); } LayoutUnit ResolveHeight(const Length& height, const NGConstraintSpace& space, const ComputedStyle& style, - const Optional<MinMaxSize>& child_minmax, - LengthResolveType resolve_type) { - if (space.GetWritingMode() != WritingMode::kHorizontalTb) - return ResolveInlineLength(space, style, child_minmax, height, - resolve_type); + const base::Optional<MinMaxSize>& child_minmax, + LengthResolveType type) { + if (space.GetWritingMode() != WritingMode::kHorizontalTb) { + return ResolveInlineLength(space, style, child_minmax, height, type, + LengthResolvePhase::kLayout); + } LayoutUnit computed_height = child_minmax.has_value() ? child_minmax->max_size : LayoutUnit(); - return ResolveBlockLength(space, style, height, computed_height, - resolve_type); + return ResolveBlockLength(space, style, height, computed_height, type, + LengthResolvePhase::kLayout); } // Available size can is maximum length Element can have without overflowing // container bounds. The position of Element's edges will determine // how much space there is available. LayoutUnit ComputeAvailableWidth(LayoutUnit container_width, - const Optional<LayoutUnit>& left, - const Optional<LayoutUnit>& right, - const Optional<LayoutUnit>& margin_left, - const Optional<LayoutUnit>& margin_right, + const base::Optional<LayoutUnit>& left, + const base::Optional<LayoutUnit>& right, + const base::Optional<LayoutUnit>& margin_left, + const base::Optional<LayoutUnit>& margin_right, const NGStaticPosition& static_position) { LayoutUnit available_width = container_width; DCHECK(!left || !right); @@ -100,12 +102,13 @@ LayoutUnit ComputeAvailableWidth(LayoutUnit container_width, return (available_width - margins).ClampNegativeToZero(); } -LayoutUnit ComputeAvailableHeight(LayoutUnit container_height, - const Optional<LayoutUnit>& top, - const Optional<LayoutUnit>& bottom, - const Optional<LayoutUnit>& margin_top, - const Optional<LayoutUnit>& margin_bottom, - const NGStaticPosition& static_position) { +LayoutUnit ComputeAvailableHeight( + LayoutUnit container_height, + const base::Optional<LayoutUnit>& top, + const base::Optional<LayoutUnit>& bottom, + const base::Optional<LayoutUnit>& margin_top, + const base::Optional<LayoutUnit>& margin_bottom, + const NGStaticPosition& static_position) { LayoutUnit available_height = container_height; DCHECK(!top || !bottom); if (!top && !bottom) { @@ -145,30 +148,30 @@ LayoutUnit VerticalBorderPadding(const NGConstraintSpace& space, // https://www.w3.org/TR/css-position-3/#abs-non-replaced-width void ComputeAbsoluteHorizontal(const NGConstraintSpace& space, const ComputedStyle& style, - const Optional<LayoutUnit>& incoming_width, + const base::Optional<LayoutUnit>& incoming_width, const NGStaticPosition& static_position, - const Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSize>& child_minmax, const WritingMode container_writing_mode, const TextDirection container_direction, NGAbsolutePhysicalPosition* position) { NGLogicalSize percentage_logical = space.PercentageResolutionSize(); NGPhysicalSize percentage_physical = percentage_logical.ConvertToPhysical(space.GetWritingMode()); - Optional<LayoutUnit> margin_left; + base::Optional<LayoutUnit> margin_left; if (!style.MarginLeft().IsAuto()) margin_left = ValueForLength(style.MarginLeft(), percentage_logical.inline_size); - Optional<LayoutUnit> margin_right; + base::Optional<LayoutUnit> margin_right; if (!style.MarginRight().IsAuto()) margin_right = ValueForLength(style.MarginRight(), percentage_logical.inline_size); - Optional<LayoutUnit> left; + base::Optional<LayoutUnit> left; if (!style.Left().IsAuto()) left = ValueForLength(style.Left(), percentage_physical.width); - Optional<LayoutUnit> right; + base::Optional<LayoutUnit> right; if (!style.Right().IsAuto()) right = ValueForLength(style.Right(), percentage_physical.width); - Optional<LayoutUnit> width = incoming_width; + base::Optional<LayoutUnit> width = incoming_width; NGPhysicalSize container_size = space.AvailableSize().ConvertToPhysical(space.GetWritingMode()); DCHECK_NE(container_size.width, NGSizeIndefinite); @@ -281,16 +284,12 @@ void ComputeAbsoluteHorizontal(const NGConstraintSpace& space, // If calculated width is outside of min/max constraints, // rerun the algorithm with constrained width. - Optional<LayoutUnit> min_width; - if (!style.MinWidth().IsAuto()) - min_width = ResolveWidth(style.MinWidth(), space, style, child_minmax, - LengthResolveType::kMinSize); - Optional<LayoutUnit> max_width; - if (!style.MaxWidth().IsMaxSizeNone()) - max_width = ResolveWidth(style.MaxWidth(), space, style, child_minmax, - LengthResolveType::kMaxSize); - if (width != ConstrainByMinMax(*width, min_width, max_width)) { - width = ConstrainByMinMax(*width, min_width, max_width); + LayoutUnit min = ResolveWidth(style.MinWidth(), space, style, child_minmax, + LengthResolveType::kMinSize); + LayoutUnit max = ResolveWidth(style.MaxWidth(), space, style, child_minmax, + LengthResolveType::kMaxSize); + if (width != ConstrainByMinMax(*width, min, max)) { + width = ConstrainByMinMax(*width, min, max); // Because this function only changes "width" when it's not already // set, it is safe to recursively call ourselves here because on the // second call it is guaranteed to be within min..max. @@ -312,9 +311,9 @@ void ComputeAbsoluteHorizontal(const NGConstraintSpace& space, // https://www.w3.org/TR/css-position-3/#abs-non-replaced-height void ComputeAbsoluteVertical(const NGConstraintSpace& space, const ComputedStyle& style, - const Optional<LayoutUnit>& incoming_height, + const base::Optional<LayoutUnit>& incoming_height, const NGStaticPosition& static_position, - const Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSize>& child_minmax, const WritingMode container_writing_mode, const TextDirection container_direction, NGAbsolutePhysicalPosition* position) { @@ -322,22 +321,22 @@ void ComputeAbsoluteVertical(const NGConstraintSpace& space, NGPhysicalSize percentage_physical = percentage_logical.ConvertToPhysical(space.GetWritingMode()); - Optional<LayoutUnit> margin_top; + base::Optional<LayoutUnit> margin_top; if (!style.MarginTop().IsAuto()) margin_top = ValueForLength(style.MarginTop(), percentage_logical.inline_size); - Optional<LayoutUnit> margin_bottom; + base::Optional<LayoutUnit> margin_bottom; if (!style.MarginBottom().IsAuto()) margin_bottom = ValueForLength(style.MarginBottom(), percentage_logical.inline_size); - Optional<LayoutUnit> top; + base::Optional<LayoutUnit> top; if (!style.Top().IsAuto()) top = ValueForLength(style.Top(), percentage_physical.height); - Optional<LayoutUnit> bottom; + base::Optional<LayoutUnit> bottom; if (!style.Bottom().IsAuto()) bottom = ValueForLength(style.Bottom(), percentage_physical.height); LayoutUnit border_padding = VerticalBorderPadding(space, style); - Optional<LayoutUnit> height = incoming_height; + base::Optional<LayoutUnit> height = incoming_height; NGPhysicalSize container_size = space.AvailableSize().ConvertToPhysical(space.GetWritingMode()); @@ -449,16 +448,12 @@ void ComputeAbsoluteVertical(const NGConstraintSpace& space, } // If calculated height is outside of min/max constraints, // rerun the algorithm with constrained width. - Optional<LayoutUnit> min_height; - if (!style.MinHeight().IsAuto()) - min_height = ResolveHeight(style.MinHeight(), space, style, child_minmax, - LengthResolveType::kMinSize); - Optional<LayoutUnit> max_height; - if (!style.MaxHeight().IsMaxSizeNone()) - max_height = ResolveHeight(style.MaxHeight(), space, style, child_minmax, - LengthResolveType::kMaxSize); - if (height != ConstrainByMinMax(*height, min_height, max_height)) { - height = ConstrainByMinMax(*height, min_height, max_height); + LayoutUnit min = ResolveHeight(style.MinHeight(), space, style, child_minmax, + LengthResolveType::kMinSize); + LayoutUnit max = ResolveHeight(style.MaxHeight(), space, style, child_minmax, + LengthResolveType::kMaxSize); + if (height != ConstrainByMinMax(*height, min, max)) { + height = ConstrainByMinMax(*height, min, max); // Because this function only changes "height" when it's not already // set, it is safe to recursively call ourselves here because on the // second call it is guaranteed to be within min..max. @@ -502,13 +497,13 @@ NGAbsolutePhysicalPosition ComputePartialAbsoluteWithChildInlineSize( const NGConstraintSpace& space, const ComputedStyle& style, const NGStaticPosition& static_position, - const Optional<MinMaxSize>& child_minmax, - const Optional<NGLogicalSize>& replaced_size, + const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<NGLogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction) { NGAbsolutePhysicalPosition position; if (style.IsHorizontalWritingMode()) { - Optional<LayoutUnit> width; + base::Optional<LayoutUnit> width; if (!style.Width().IsAuto()) { width = ResolveWidth(style.Width(), space, style, child_minmax, LengthResolveType::kContentSize); @@ -519,7 +514,7 @@ NGAbsolutePhysicalPosition ComputePartialAbsoluteWithChildInlineSize( child_minmax, container_writing_mode, container_direction, &position); } else { - Optional<LayoutUnit> height; + base::Optional<LayoutUnit> height; if (!style.Height().IsAuto()) { height = ResolveHeight(style.Height(), space, style, child_minmax, LengthResolveType::kContentSize); @@ -537,8 +532,8 @@ void ComputeFullAbsoluteWithChildBlockSize( const NGConstraintSpace& space, const ComputedStyle& style, const NGStaticPosition& static_position, - const Optional<LayoutUnit>& child_block_size, - const Optional<NGLogicalSize>& replaced_size, + const base::Optional<LayoutUnit>& child_block_size, + const base::Optional<NGLogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction, NGAbsolutePhysicalPosition* position) { @@ -546,12 +541,12 @@ void ComputeFullAbsoluteWithChildBlockSize( // unknown, or fully computed, there is no minmax. // To express this, a 'fixed' minmax is created where // min and max are the same. - Optional<MinMaxSize> child_minmax; + base::Optional<MinMaxSize> child_minmax; if (child_block_size.has_value()) { child_minmax = MinMaxSize{*child_block_size, *child_block_size}; } if (style.IsHorizontalWritingMode()) { - Optional<LayoutUnit> height; + base::Optional<LayoutUnit> height; if (!style.Height().IsAuto()) { height = ResolveHeight(style.Height(), space, style, child_minmax, LengthResolveType::kContentSize); @@ -562,7 +557,7 @@ void ComputeFullAbsoluteWithChildBlockSize( container_writing_mode, container_direction, position); } else { - Optional<LayoutUnit> width; + base::Optional<LayoutUnit> width; if (!style.Width().IsAuto()) { width = ResolveWidth(style.Width(), space, style, child_minmax, LengthResolveType::kContentSize); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h index 5e4fe6a5e4c..ede1012089f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h @@ -5,12 +5,12 @@ #ifndef NGAbsoluteUtils_h #define NGAbsoluteUtils_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h" #include "third_party/blink/renderer/platform/layout_unit.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { @@ -54,8 +54,8 @@ ComputePartialAbsoluteWithChildInlineSize( const NGConstraintSpace& space, const ComputedStyle& style, const NGStaticPosition&, - const Optional<MinMaxSize>& child_minmax, - const Optional<NGLogicalSize>& replaced_size, + const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<NGLogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction); @@ -64,8 +64,8 @@ CORE_EXPORT void ComputeFullAbsoluteWithChildBlockSize( const NGConstraintSpace& space, const ComputedStyle& style, const NGStaticPosition&, - const Optional<LayoutUnit>& child_block_size, - const Optional<NGLogicalSize>& replaced_size, + const base::Optional<LayoutUnit>& child_block_size, + const base::Optional<NGLogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction, NGAbsolutePhysicalPosition* position); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc index 8d9fc740317..c070173984f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc @@ -118,8 +118,8 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { LayoutUnit width = container_size_.inline_size - left - margin_left - right - margin_right; - Optional<MinMaxSize> estimated_inline; - Optional<LayoutUnit> estimated_block; + base::Optional<MinMaxSize> estimated_inline; + base::Optional<LayoutUnit> estimated_block; MinMaxSize minmax_60{LayoutUnit(60) + border_padding, LayoutUnit(60) + border_padding}; @@ -148,7 +148,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); estimated_inline = minmax_60; p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(minmax_60.min_size, p.size.width); EXPECT_EQ(LayoutUnit(0), p.inset.left); @@ -159,13 +159,13 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { estimated_inline = minmax_60; p = ComputePartialAbsoluteWithChildInlineSize( *ltr_space_, *style_, static_right_position, estimated_inline, - WTF::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); + base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(minmax_60.min_size, p.size.width); EXPECT_EQ(container_size_.inline_size, p.inset.right); // All auto + RTL. p = ComputePartialAbsoluteWithChildInlineSize( - *rtl_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *rtl_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(minmax_60.min_size, p.size.width); EXPECT_EQ(container_size_.inline_size - minmax_60.min_size, p.inset.right); @@ -175,7 +175,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); LayoutUnit margin_space = (container_size_.inline_size - left - right - p.size.width) / 2; @@ -189,7 +189,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { estimated_inline.reset(); ComputeFullAbsoluteWithChildBlockSize( *vertical_lr_space_, *style_, static_position, estimated_block, - WTF::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); + base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(left + margin_space, p.inset.left); EXPECT_EQ(right + margin_space, p.inset.right); @@ -200,7 +200,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { estimated_inline.reset(); ComputeFullAbsoluteWithChildBlockSize( *vertical_rl_space_, *style_, static_position, estimated_block, - WTF::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); + base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(left + margin_space, p.inset.left); EXPECT_EQ(right + margin_space, p.inset.right); @@ -208,7 +208,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { SetHorizontalStyle(left, NGAuto, LayoutUnit(200), NGAuto, right); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(left, p.inset.left); EXPECT_EQ(-left, p.inset.right); @@ -218,7 +218,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { WritingMode::kHorizontalTb); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *rtl_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *rtl_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kRtl); EXPECT_EQ(-right, p.inset.left); EXPECT_EQ(right, p.inset.right); @@ -228,7 +228,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); estimated_inline = minmax_60; p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(minmax_60.min_size, p.size.width); @@ -237,7 +237,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(margin_left, p.inset.left); EXPECT_EQ(container_size_.inline_size - margin_left - width, p.inset.right); @@ -247,7 +247,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *rtl_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *rtl_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(margin_left, p.inset.left); EXPECT_EQ(container_size_.inline_size - margin_left - width, p.inset.right); @@ -257,7 +257,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); estimated_inline = minmax_60; p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ( container_size_.inline_size - minmax_60.min_size - left - margin_left, @@ -269,7 +269,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(left + margin_left, p.inset.left); @@ -281,7 +281,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(left + margin_left, p.inset.left); style_->SetBoxSizing(EBoxSizing::kBorderBox); @@ -291,7 +291,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(right + margin_right, p.inset.right); @@ -300,7 +300,7 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(width, p.size.width); } @@ -335,7 +335,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { style_->SetBorderRightWidth(0); NGAbsolutePhysicalPosition p; - Optional<LayoutUnit> auto_height; + base::Optional<LayoutUnit> auto_height; MinMaxSize minmax_60{LayoutUnit(60), LayoutUnit(60)}; NGStaticPosition static_position{NGStaticPosition::kTopLeft, @@ -352,14 +352,14 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(60); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(*auto_height, p.size.height); EXPECT_EQ(LayoutUnit(0), p.inset.top); // All auto, static position bottom ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position_bottom, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position_bottom, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(container_size_.block_size, p.inset.bottom); @@ -368,7 +368,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); LayoutUnit margin_space = (container_size_.block_size - top - height - bottom) / 2; @@ -380,7 +380,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { WritingMode::kVerticalLr); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); p = ComputePartialAbsoluteWithChildInlineSize( - *vertical_lr_space_, *style_, static_position, minmax_60, WTF::nullopt, + *vertical_lr_space_, *style_, static_position, minmax_60, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(top + margin_space, p.inset.top); EXPECT_EQ(bottom + margin_space, p.inset.bottom); @@ -390,7 +390,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { WritingMode::kVerticalRl); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); p = ComputePartialAbsoluteWithChildInlineSize( - *vertical_rl_space_, *style_, static_position, minmax_60, WTF::nullopt, + *vertical_rl_space_, *style_, static_position, minmax_60, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(top + margin_space, p.inset.top); EXPECT_EQ(bottom + margin_space, p.inset.bottom); @@ -399,7 +399,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { SetVerticalStyle(top, NGAuto, LayoutUnit(300), NGAuto, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(top, p.inset.top); EXPECT_EQ(-top, p.inset.bottom); @@ -409,7 +409,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(60); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(*auto_height, p.size.height); @@ -418,7 +418,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(margin_top, p.inset.top); EXPECT_EQ(container_size_.block_size - margin_top - height, p.inset.bottom); @@ -428,7 +428,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(20); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(border_padding, p.size.height); @@ -437,7 +437,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(70); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(*auto_height, p.size.height); @@ -446,7 +446,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(top + margin_top, p.inset.top); @@ -455,7 +455,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(bottom + margin_bottom, p.inset.bottom); @@ -464,7 +464,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(height, p.size.height); } @@ -488,39 +488,39 @@ TEST_F(NGAbsoluteUtilsTest, MinMax) { // width < min gets set to min. SetHorizontalStyle(NGAuto, NGAuto, LayoutUnit(5), NGAuto, NGAuto); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(min, p.size.width); // width > max gets set to max. SetHorizontalStyle(NGAuto, NGAuto, LayoutUnit(200), NGAuto, NGAuto); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(max, p.size.width); // Unspecified width becomes minmax, gets clamped to min. SetHorizontalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); p = ComputePartialAbsoluteWithChildInlineSize( - *ltr_space_, *style_, static_position, estimated_inline, WTF::nullopt, + *ltr_space_, *style_, static_position, estimated_inline, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); EXPECT_EQ(min, p.size.width); // HEIGHT TESTS - Optional<LayoutUnit> auto_height; + base::Optional<LayoutUnit> auto_height; // height < min gets set to min. SetVerticalStyle(NGAuto, NGAuto, LayoutUnit(5), NGAuto, NGAuto); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(min, p.size.height); // height > max gets set to max. SetVerticalStyle(NGAuto, NGAuto, LayoutUnit(200), NGAuto, NGAuto); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(max, p.size.height); @@ -528,7 +528,7 @@ TEST_F(NGAbsoluteUtilsTest, MinMax) { SetVerticalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); auto_height = LayoutUnit(20); ComputeFullAbsoluteWithChildBlockSize( - *ltr_space_, *style_, static_position, auto_height, WTF::nullopt, + *ltr_space_, *style_, static_position, auto_height, base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); EXPECT_EQ(min, p.size.width); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.cc index 356a4863457..09fff586050 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.cc @@ -4,6 +4,8 @@ #include "third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h" +#include "third_party/blink/renderer/core/style/computed_style.h" + namespace blink { NGBaseFragmentBuilder::NGBaseFragmentBuilder( @@ -12,7 +14,8 @@ NGBaseFragmentBuilder::NGBaseFragmentBuilder( TextDirection direction) : style_(std::move(style)), writing_mode_(writing_mode), - direction_(direction) { + direction_(direction), + style_variant_(NGStyleVariant::kStandard) { DCHECK(style_); } @@ -22,6 +25,12 @@ NGBaseFragmentBuilder::NGBaseFragmentBuilder(WritingMode writing_mode, NGBaseFragmentBuilder::~NGBaseFragmentBuilder() = default; +NGBaseFragmentBuilder& NGBaseFragmentBuilder::SetStyleVariant( + NGStyleVariant style_variant) { + style_variant_ = style_variant; + return *this; +} + NGBaseFragmentBuilder& NGBaseFragmentBuilder::SetStyle( scoped_refptr<const ComputedStyle> style, NGStyleVariant style_variant) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h index ea0e8495a82..62dcd9510ca 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h @@ -8,13 +8,14 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h" -#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { +class ComputedStyle; + class CORE_EXPORT NGBaseFragmentBuilder { STACK_ALLOCATED(); public: @@ -24,6 +25,7 @@ class CORE_EXPORT NGBaseFragmentBuilder { DCHECK(style_); return *style_; } + NGBaseFragmentBuilder& SetStyleVariant(NGStyleVariant); NGBaseFragmentBuilder& SetStyle(scoped_refptr<const ComputedStyle>, NGStyleVariant); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h index b88822c3282..c377e3ac66b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h @@ -7,10 +7,8 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -19,6 +17,8 @@ namespace blink { class Element; +class LayoutNGBlockFlow; +class NGPhysicalBoxFragment; // Base class for all LayoutNG Algorithms unit test classes. typedef bool TestParamLayoutNG; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc index b20e516c46a..5b315e8cada 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" namespace blink { namespace { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc index 3f03f269bcb..baa5f80f16e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc @@ -8,55 +8,69 @@ #include <memory> #include <utility> +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.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" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { namespace { -// Returns if a child may be affected by its clear property. I.e. it will -// actually clear a float. -bool ClearanceMayAffectLayout( - const NGExclusionSpace& exclusion_space, - const Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats, - const ComputedStyle& child_style) { - EClear clear = child_style.Clear(); - bool should_clear_left = (clear == EClear::kBoth || clear == EClear::kLeft); - bool should_clear_right = (clear == EClear::kBoth || clear == EClear::kRight); - - if (exclusion_space.HasLeftFloat() && should_clear_left) - return true; - - if (exclusion_space.HasRightFloat() && should_clear_right) - return true; - - auto should_clear_pred = - [&](const scoped_refptr<const NGUnpositionedFloat>& unpositioned_float) { - return (unpositioned_float->IsLeft() && should_clear_left) || - (unpositioned_float->IsRight() && should_clear_right); - }; +// Return true if a child is to be cleared past adjoining floats. These are +// floats that would otherwise (if 'clear' were 'none') be pulled down by the +// BFC offset of the child. If the child is to clear floats, though, we +// obviously need separate the child from the floats and move it past them, +// since that's what clearance is all about. This means that if we have any such +// floats to clear, we know for sure that we get clearance, even before layout. +inline bool HasClearancePastAdjoiningFloats(NGFloatTypes adjoining_floats, + const ComputedStyle& child_style) { + return ToFloatTypes(child_style.Clear()) & adjoining_floats; +} - if (std::any_of(unpositioned_floats.begin(), unpositioned_floats.end(), - should_clear_pred)) +// Adjust BFC block offset for clearance, if applicable. Return true of +// clearance was applied. +// +// Clearance applies either when the BFC block offset calculated simply isn't +// past all relevant floats, *or* when we have already determined that we're +// directly preceded by clearance. +// +// The latter is the case when we need to force ourselves past floats that would +// otherwise be adjoining, were it not for the predetermined clearance. +// Clearance inhibits margin collapsing and acts as spacing before the +// block-start margin of the child. It needs to be exactly what takes the +// block-start border edge of the cleared block adjacent to the block-end outer +// edge of the "bottommost" relevant float. +// +// We cannot reliably calculate the actual clearance amount at this point, +// because 1) this block right here may actually be a descendant of the block +// that is to be cleared, and 2) we may not yet have separated the margin before +// and after the clearance. None of this matters, though, because we know where +// to place this block if clearance applies: exactly at the ConstraintSpace's +// ClearanceOffset(). +bool ApplyClearance(const NGConstraintSpace& constraint_space, + LayoutUnit* bfc_block_offset) { + if (constraint_space.HasClearanceOffset() && + (*bfc_block_offset < constraint_space.ClearanceOffset() || + constraint_space.ShouldForceClearance())) { + *bfc_block_offset = constraint_space.ClearanceOffset(); return true; - + } return false; } @@ -112,7 +126,11 @@ NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm(NGBlockNode node, is_resuming_(break_token && !break_token->IsBreakBefore()), exclusion_space_(new NGExclusionSpace(space.ExclusionSpace())) {} -Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( +// Define the destructor here, so that we can forward-declare more in the +// header. +NGBlockLayoutAlgorithm::~NGBlockLayoutAlgorithm() = default; + +base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( const MinMaxSizeInput& input) const { MinMaxSize sizes; @@ -164,14 +182,11 @@ Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( // an anonymous box that contains all line boxes. // |NextSibling| returns the next block sibling, or nullptr, skipping all // following inline siblings and descendants. - child_sizes = child.ComputeMinMaxSize(child_input); - } else { - Optional<MinMaxSize> child_minmax; - if (NeedMinMaxSizeForContentContribution(child_style)) - child_minmax = child.ComputeMinMaxSize(child_input); - child_sizes = - ComputeMinAndMaxContentContribution(child_style, child_minmax); + child.ComputeMinMaxSize(Style().GetWritingMode(), child_input); + } else { + child_sizes = ComputeMinAndMaxContentContribution( + Style().GetWritingMode(), child, child_input); } // Determine the max inline contribution of the child. @@ -244,7 +259,7 @@ NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset( NGLayoutInputNode child, const NGFragment& fragment, const NGBoxStrut& child_margins, - const WTF::Optional<NGBfcOffset>& known_fragment_offset) { + const base::Optional<NGBfcOffset>& known_fragment_offset) { if (known_fragment_offset) { return LogicalFromBfcOffsets( fragment, known_fragment_offset.value(), ContainerBfcOffset(), @@ -257,7 +272,7 @@ NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset( if (child.IsInline()) { LayoutUnit offset = LineOffsetForTextAlign(Style().GetTextAlign(), Style().Direction(), - child_available_size_.inline_size); + child_available_size_.inline_size, LayoutUnit()); if (IsRtl(Style().Direction())) offset = child_available_size_.inline_size - offset; inline_offset += offset; @@ -271,7 +286,7 @@ NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset( } scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { - WTF::Optional<MinMaxSize> min_max_size; + base::Optional<MinMaxSize> min_max_size; if (NeedMinMaxSize(ConstraintSpace(), Style())) { MinMaxSizeInput zero_input; min_max_size = ComputeMinMaxSize(zero_input); @@ -293,47 +308,79 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { // Anonymous constraint spaces are auto-sized. Don't let that affect // block-axis percentage resolution. - if (ConstraintSpace().IsAnonymous()) + if (ConstraintSpace().IsAnonymous() || Node().IsAnonymous()) child_percentage_size_ = ConstraintSpace().PercentageResolutionSize(); else child_percentage_size_ = adjusted_size; + if (ConstraintSpace().IsFixedSizeBlock() && + !ConstraintSpace().FixedSizeBlockIsDefinite()) + child_percentage_size_.block_size = NGSizeIndefinite; container_builder_.SetInlineSize(size.inline_size); - // If we have a list of unpositioned floats as input to this layout, we'll - // need to abort once our BFC offset is resolved. Additionally the - // FloatsBfcOffset() must not be present in this case. - unpositioned_floats_ = ConstraintSpace().UnpositionedFloats(); - abort_when_bfc_resolved_ = !unpositioned_floats_.IsEmpty(); - if (abort_when_bfc_resolved_) - DCHECK(!ConstraintSpace().FloatsBfcOffset()); + if (NGFloatTypes float_types = ConstraintSpace().AdjoiningFloatTypes()) { + DCHECK(!ConstraintSpace().IsNewFormattingContext()); + DCHECK(!container_builder_.BfcOffset()); + + // If there were preceding adjoining floats, they will be affected when the + // BFC offset gets resolved or updated. We then need to roll back and + // re-layout those floats with the new BFC offset, once the BFC offset is + // updated. + abort_when_bfc_offset_updated_ = true; + + container_builder_.AddAdjoiningFloatTypes(float_types); + } // If we are resuming from a break token our start border and padding is // within a previous fragment. - intrinsic_block_size_ = + LayoutUnit content_edge = is_resuming_ ? LayoutUnit() : border_scrollbar_padding_.block_start; - NGMarginStrut input_margin_strut = ConstraintSpace().MarginStrut(); - - LayoutUnit input_bfc_block_offset = - ConstraintSpace().BfcOffset().block_offset; - - // Margins collapsing: - // Do not collapse margins between parent and its child if there is - // border/padding between them. - if (border_scrollbar_padding_.block_start) { - input_bfc_block_offset += input_margin_strut.Sum(); - bool updated = MaybeUpdateFragmentBfcOffset(input_bfc_block_offset); + NGPreviousInflowPosition previous_inflow_position = { + ConstraintSpace().BfcOffset().block_offset, LayoutUnit(), + ConstraintSpace().MarginStrut(), + /* empty_block_affected_by_clearance */ false}; - if (updated && abort_when_bfc_resolved_) { - container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); + // Margins collapsing: Do not collapse margins between parent and its child if + // there is border/padding between them. Then we can and must resolve the BFC + // offset now. Also, if this is a new formatting context, or if we're resuming + // layout from a break token, we need to resolve the BFC offset now. Margin + // struts cannot pass from one fragment to another if they are generated by + // the same block; they must be dealt with at the first fragment. + if (border_scrollbar_padding_.block_start || is_resuming_ || + ConstraintSpace().IsNewFormattingContext()) { + if (!ResolveBfcOffset(&previous_inflow_position)) { + // There should be no preceding content that depends on the BFC offset of + // a new formatting context block, and likewise when resuming from a break + // token. + DCHECK(!ConstraintSpace().IsNewFormattingContext()); + DCHECK(!is_resuming_); return container_builder_.Abort(NGLayoutResult::kBfcOffsetResolved); } + // Move to the content edge. This is where the first child should be placed. + previous_inflow_position.bfc_block_offset += content_edge; + previous_inflow_position.logical_block_offset = content_edge; + } + +#if DCHECK_IS_ON() + // If this is a new formatting context, we should definitely be at the origin + // here. If we're resuming from a break token (for a block that doesn't + // establish a new formatting context), that may not be the case, + // though. There may e.g. be clearance involved, or inline-start margins. + if (ConstraintSpace().IsNewFormattingContext()) + DCHECK_EQ(container_builder_.BfcOffset().value(), NGBfcOffset()); + // If this is a new formatting context, or if we're resuming from a break + // token, no margin strut must be lingering around at this point. + if (ConstraintSpace().IsNewFormattingContext() || is_resuming_) + DCHECK(ConstraintSpace().MarginStrut().IsEmpty()); - // We reset the block offset here as it may have been effected by clearance. - input_bfc_block_offset = ContainerBfcOffset().block_offset; - input_margin_strut = NGMarginStrut(); + if (!container_builder_.BfcOffset()) { + // New formatting contexts, and where we have an empty block affected by + // clearance should already have their BFC offset resolved. + DCHECK(!previous_inflow_position.empty_block_affected_by_clearance); + DCHECK(!ConstraintSpace().IsNewFormattingContext()); } +#endif // If this node is a quirky container, (we are in quirks mode and either a // table cell or body), we set our margin strut to a mode where it only @@ -346,32 +393,10 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { // In the above example <p>'s & <h1>'s margins are ignored as they are // quirky, and we only consider <div>'s 10px margin. if (node_.IsQuirkyContainer()) - input_margin_strut.is_quirky_container_start = true; - - // If a new formatting context hits the margin collapsing if-branch above - // then the BFC offset is still {} as the margin strut from the constraint - // space must also be empty. - // If we are resuming layout from a break token the same rule applies. Margin - // struts cannot pass through break tokens (unless it's a break token before - // the first fragment (the one we're about to create)). - if (ConstraintSpace().IsNewFormattingContext() || is_resuming_) { - MaybeUpdateFragmentBfcOffset(input_bfc_block_offset); - DCHECK(input_margin_strut.IsEmpty()); -#if DCHECK_IS_ON() - // If this is a new formatting context, we should definitely be at the - // origin here. If we're resuming at a fragmented block (that doesn't - // establish a new formatting context), that may not be the case, - // though. There may e.g. be clearance involved, or inline-start margins. - if (ConstraintSpace().IsNewFormattingContext()) - DCHECK_EQ(container_builder_.BfcOffset().value(), NGBfcOffset()); -#endif - } + previous_inflow_position.margin_strut.is_quirky_container_start = true; - input_bfc_block_offset += intrinsic_block_size_; + intrinsic_block_size_ = content_edge; - NGPreviousInflowPosition previous_inflow_position = { - input_bfc_block_offset, intrinsic_block_size_, input_margin_strut, - /* empty_block_affected_by_clearance */ false}; scoped_refptr<NGBreakToken> previous_inline_break_token; NGBlockChildIterator child_iterator(Node().FirstChild(), BreakToken()); @@ -408,7 +433,6 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { if (!success) { // We need to abort the layout, as our BFC offset was resolved. - container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); return container_builder_.Abort(NGLayoutResult::kBfcOffsetResolved); } if (container_builder_.DidBreak() && IsFragmentainerOutOfSpace()) @@ -418,32 +442,15 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { } NGMarginStrut end_margin_strut = previous_inflow_position.margin_strut; - LayoutUnit end_bfc_block_offset = previous_inflow_position.bfc_block_offset; // If the current layout is a new formatting context, we need to encapsulate // all of our floats. if (ConstraintSpace().IsNewFormattingContext()) { - // We can use the BFC coordinates, as we are a new formatting context. - DCHECK_EQ(container_builder_.BfcOffset().value(), NGBfcOffset()); - - WTF::Optional<LayoutUnit> float_end_offset = - exclusion_space_->ClearanceOffset(EClear::kBoth); - - // We only update the size of this fragment if we need to grow to - // encapsulate the floats. - if (float_end_offset && float_end_offset.value() > end_bfc_block_offset) { - end_margin_strut = NGMarginStrut(); - end_bfc_block_offset = float_end_offset.value(); - intrinsic_block_size_ = - std::max(intrinsic_block_size_, float_end_offset.value()); - } + intrinsic_block_size_ = + std::max(intrinsic_block_size_, + exclusion_space_->ClearanceOffset(EClear::kBoth)); } - // There are still a couple of opportunities to find something solid for this - // block to hang on to, if we haven't already been able to do so. Keep track - // of this, so that we can abort layout if necessary. - bool bfc_updated = false; - // The end margin strut of an in-flow fragment contributes to the size of the // current fragment if: // - There is block-end border/scrollbar/padding. @@ -462,8 +469,6 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { LayoutUnit margin_strut_sum = node_.IsQuirkyContainer() ? end_margin_strut.QuirkyContainerSum() : end_margin_strut.Sum(); - end_bfc_block_offset += margin_strut_sum; - if (!container_builder_.BfcOffset()) { // If we have collapsed through the block start and all children (if any), // now is the time to determine the BFC offset, because finally we have @@ -471,8 +476,9 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { // for instance). If we're a new formatting context, though, we shouldn't // be here, because then the offset should already have been determined. DCHECK(!ConstraintSpace().IsNewFormattingContext()); - bfc_updated = MaybeUpdateFragmentBfcOffset(end_bfc_block_offset); - DCHECK(bfc_updated); + if (!ResolveBfcOffset(&previous_inflow_position)) + return container_builder_.Abort(NGLayoutResult::kBfcOffsetResolved); + DCHECK(container_builder_.BfcOffset()); } else { // The trailing margin strut will be part of our intrinsic block size, but // only if there is something that separates the end margin strut from the @@ -503,25 +509,12 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { // have a break token, it means that we know the blocks' position even if // they're empty; it will be at the very start of the fragmentainer. if (!container_builder_.BfcOffset() && (size.block_size || BreakToken())) { - end_bfc_block_offset += end_margin_strut.Sum(); - bfc_updated = MaybeUpdateFragmentBfcOffset(end_bfc_block_offset); - DCHECK(bfc_updated); - } - - if (bfc_updated && abort_when_bfc_resolved_) { - // New formatting contexts, and where we have an empty block affected by - // clearance should already have their BFC offset resolved, and shouldn't - // enter this branch. - DCHECK(!previous_inflow_position.empty_block_affected_by_clearance); - DCHECK(!ConstraintSpace().IsNewFormattingContext()); - - container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); - return container_builder_.Abort(NGLayoutResult::kBfcOffsetResolved); + if (!ResolveBfcOffset(&previous_inflow_position)) + return container_builder_.Abort(NGLayoutResult::kBfcOffsetResolved); + DCHECK(container_builder_.BfcOffset()); } if (container_builder_.BfcOffset()) { - PositionPendingFloats(end_bfc_block_offset); - // Do not collapse margins between the last in-flow child and bottom margin // of its parent if the parent has height != auto. if (!Style().LogicalHeight().IsAuto()) { @@ -556,18 +549,24 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { .Run(); } - // If we have any unpositioned floats at this stage, need to tell our parent - // about this, so that we get relayout with a forced BFC offset. +#if DCHECK_IS_ON() + // If we have any unpositioned floats at this stage, our parent will pick up + // this by examining adjoining float types returned, so that we get relayout + // with a forced BFC offset once it's known. if (!unpositioned_floats_.IsEmpty()) { DCHECK(!container_builder_.BfcOffset()); - container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); + DCHECK(container_builder_.AdjoiningFloatTypes()); } +#endif PropagateBaselinesFromChildren(); DCHECK(exclusion_space_); container_builder_.SetExclusionSpace(std::move(exclusion_space_)); + if (ConstraintSpace().UseFirstLineStyle()) + container_builder_.SetStyleVariant(NGStyleVariant::kFirstLine); + return container_builder_.ToBoxFragment(); } @@ -603,7 +602,8 @@ void NGBlockLayoutAlgorithm::HandleFloat( origin_inline_offset, ConstraintSpace().BfcOffset().line_offset, margins, child, child_break_token); - unpositioned_floats_.push_back(std::move(unpositioned_float)); + AddUnpositionedFloat(&unpositioned_floats_, &container_builder_, + unpositioned_float); // If there is a break token for a float we must be resuming layout, we must // always know our position in the BFC. @@ -636,17 +636,71 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext( const ComputedStyle& child_style = child.Style(); const TextDirection direction = ConstraintSpace().Direction(); + bool has_clearance_past_adjoining_floats = HasClearancePastAdjoiningFloats( + container_builder_.AdjoiningFloatTypes(), child_style); NGInflowChildData child_data = - ComputeChildData(*previous_inflow_position, child, child_break_token); - + ComputeChildData(*previous_inflow_position, child, child_break_token, + has_clearance_past_adjoining_floats); + + // If the child has a block-start margin, and the BFC offset is still + // unresolved, and we have preceding adjoining floats, things get complicated + // here. Depending on whether the child fits beside the floats, the margin may + // or may not be adjoining with the current margin strut. This affects the + // position of the preceding adjoining floats. We may have to resolve the BFC + // offset once with the child's margin tentatively adjoining, then realize + // that the child isn't going to fit beside the floats at the current + // position, and therefore re-resolve the BFC offset with the child's margin + // non-adjoining. This is akin to clearance. NGMarginStrut adjoining_margin_strut(previous_inflow_position->margin_strut); adjoining_margin_strut.Append(child_data.margins.block_start, child_style.HasMarginBeforeQuirk()); - - LayoutUnit initial_child_bfc_offset_estimate = + LayoutUnit adjoining_bfc_offset_estimate = child_data.bfc_offset_estimate.block_offset + adjoining_margin_strut.Sum(); - LayoutUnit child_bfc_offset_estimate = initial_child_bfc_offset_estimate; + LayoutUnit non_adjoining_bfc_offset_estimate = + child_data.bfc_offset_estimate.block_offset + + previous_inflow_position->margin_strut.Sum(); + LayoutUnit child_bfc_offset_estimate = adjoining_bfc_offset_estimate; + bool bfc_offset_already_resolved = false; + bool child_determined_bfc_offset = false; + bool child_margin_got_separated = false; + bool had_pending_floats = false; + + if (!container_builder_.BfcOffset()) { + had_pending_floats = !unpositioned_floats_.IsEmpty(); + + if (ConstraintSpace().FloatsBfcOffset()) { + // This is not the first time we're here. We already have a suggested BFC + // offset. + bfc_offset_already_resolved = true; + NGBfcOffset bfc_offset = *ConstraintSpace().FloatsBfcOffset(); + child_bfc_offset_estimate = bfc_offset.block_offset; + // We require that the BFC offset be the one we'd get with either margins + // adjoining or margins separated. Anything else is a bug. + DCHECK(bfc_offset.block_offset == adjoining_bfc_offset_estimate || + bfc_offset.block_offset == non_adjoining_bfc_offset_estimate); + // Figure out if the child margin has already got separated from the + // margin strut or not. + child_margin_got_separated = + bfc_offset.block_offset != adjoining_bfc_offset_estimate; + } else if (has_clearance_past_adjoining_floats) { + child_bfc_offset_estimate = previous_inflow_position->NextBorderEdge(); + child_margin_got_separated = true; + } + + // The BFC offset of this container gets resolved because of this child. + child_determined_bfc_offset = true; + if (!ResolveBfcOffset(previous_inflow_position, + child_bfc_offset_estimate)) { + // If we need to abort here, it means that we had preceding unpositioned + // floats. This is only expected if we're here for the first time. + DCHECK(!bfc_offset_already_resolved); + return false; + } + + // We reset the block offset here as it may have been affected by clearance. + child_bfc_offset_estimate = ContainerBfcOffset().block_offset; + } // If the child has a non-zero block-start margin, our initial estimate will // be that any pending floats will be flush (block-start-wise) with this @@ -659,7 +713,9 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext( // ignore this margin, which may cause them to end up at completely different // positions than initially estimated. In other words, we'll need another // layout pass if this happens. - bool abort_if_cleared = child_data.margins.block_start != LayoutUnit(); + bool abort_if_cleared = child_data.margins.block_start != LayoutUnit() && + !child_margin_got_separated && + child_determined_bfc_offset; NGLayoutOpportunity opportunity; scoped_refptr<NGLayoutResult> layout_result; std::tie(layout_result, opportunity) = @@ -669,39 +725,43 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext( if (!layout_result) { DCHECK(abort_if_cleared); // Layout got aborted, because the child got pushed down by floats, and we - // had pending floats that we tentatively positioned incorrectly, due to a - // margin that shouldn't have affected them. Try again without the child's - // margin. This re-layout *must* produce a fragment and opportunity which - // fits within the exclusion space. + // may have had pending floats that we tentatively positioned incorrectly + // (since the child's margin shouldn't have affected them). Try again + // without the child's margin. So, we need another layout pass. Figure out + // if we can do it right away from here, or if we have to roll back and + // reposition floats first. + if (child_determined_bfc_offset) { + // The BFC offset was calculated when we got to this child, with the + // child's margin adjoining. Since that turned out to be wrong, re-resolve + // the BFC offset without the child's margin. + LayoutUnit old_offset = container_builder_.BfcOffset()->block_offset; + container_builder_.ResetBfcOffset(); + ResolveBfcOffset(previous_inflow_position, + non_adjoining_bfc_offset_estimate); + if ((bfc_offset_already_resolved || had_pending_floats) && + old_offset != container_builder_.BfcOffset()->block_offset) { + // The first BFC offset resolution turned out to be wrong, and we + // positioned preceding adjacent floats based on that. Now we have to + // roll back and position them at the correct offset. The only expected + // incorrect estimate is with the child's margin adjoining. Any other + // incorrect estimate will result in failed layout. + DCHECK_EQ(old_offset, adjoining_bfc_offset_estimate); + return false; + } + } + DCHECK_GT(opportunity.rect.start_offset.block_offset, child_bfc_offset_estimate); - NGMarginStrut non_adjoining_margin_strut( - previous_inflow_position->margin_strut); - child_bfc_offset_estimate = child_data.bfc_offset_estimate.block_offset + - non_adjoining_margin_strut.Sum(); - - // Make sure that we don't move below the previously found layout - // opportunity. This could otherwise happen if the child has negative - // margins. - child_bfc_offset_estimate = std::min( - child_bfc_offset_estimate, opportunity.rect.start_offset.block_offset); + child_bfc_offset_estimate = non_adjoining_bfc_offset_estimate; + child_margin_got_separated = true; + // We can re-layout the child right away. This re-layout *must* produce a + // fragment and opportunity which fits within the exclusion space. std::tie(layout_result, opportunity) = LayoutNewFormattingContext( child, child_break_token, child_data, child_bfc_offset_estimate, /* abort_if_cleared */ false); } DCHECK(layout_result->PhysicalFragment()); - - // We now know the childs BFC offset, try and update our own if needed. - bool updated = MaybeUpdateFragmentBfcOffset(child_bfc_offset_estimate); - - if (updated && abort_when_bfc_resolved_) - return false; - - // Position any pending floats if we've just updated our BFC offset. - PositionPendingFloats(child_bfc_offset_estimate); - - DCHECK(layout_result->PhysicalFragment()); const auto& physical_fragment = *layout_result->PhysicalFragment(); NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment); @@ -720,8 +780,9 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext( if (ConstraintSpace().HasBlockFragmentation()) { bool is_pushed_by_floats = - layout_result->IsPushedByFloats() || - child_bfc_offset.block_offset > initial_child_bfc_offset_estimate; + child_margin_got_separated || + child_bfc_offset.block_offset > child_bfc_offset_estimate || + layout_result->IsPushedByFloats(); if (BreakBeforeChild(child, *layout_result, logical_offset.block_offset, is_pushed_by_floats)) return true; @@ -758,28 +819,18 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( const TextDirection direction = ConstraintSpace().Direction(); const WritingMode writing_mode = ConstraintSpace().GetWritingMode(); - // Position all the pending floats into a temporary exclusion space. This - // *doesn't* place them into our output exclusion space yet, as we don't know - // where the child will be positioned, and hence what *out* BFC offset is. - // If we already know our BFC offset, this won't have any affect. - NGExclusionSpace tmp_exclusion_space(*exclusion_space_); - PositionFloats(child_origin_block_offset, child_origin_block_offset, - unpositioned_floats_, ConstraintSpace(), &tmp_exclusion_space); - LayoutUnit child_bfc_line_offset = ConstraintSpace().BfcOffset().line_offset + border_scrollbar_padding_.LineLeft(direction) + child_data.margins.LineLeft(direction); // The origin offset is where we should start looking for layout - // opportunities. It needs to be adjusted by the child's clearance, in - // addition to the parent's (if we don't know our BFC offset yet). + // opportunities. It needs to be adjusted by the child's clearance. NGBfcOffset origin_offset = {child_bfc_line_offset, child_origin_block_offset}; - AdjustToClearance(tmp_exclusion_space.ClearanceOffset(child.Style().Clear()), + AdjustToClearance(exclusion_space_->ClearanceOffset(child.Style().Clear()), &origin_offset); - if (!container_builder_.BfcOffset()) - AdjustToClearance(ConstraintSpace().ClearanceOffset(), &origin_offset); + DCHECK(container_builder_.BfcOffset()); // Before we lay out, figure out how much inline space we have available at // the start block offset estimate (the child is not allowed to overlap with @@ -793,7 +844,7 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( LayoutUnit inline_margin = child_data.margins.InlineSum(); LayoutUnit inline_size = (child_available_size_.inline_size - inline_margin).ClampNegativeToZero(); - NGLayoutOpportunity opportunity = tmp_exclusion_space.FindLayoutOpportunity( + NGLayoutOpportunity opportunity = exclusion_space_->FindLayoutOpportunity( origin_offset, inline_size, NGLogicalSize()); scoped_refptr<NGLayoutResult> layout_result; @@ -807,7 +858,8 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( if (abort_if_cleared && origin_offset.block_offset < opportunity.rect.BlockStartOffset()) { // Abort if we got pushed downwards. We need to adjust - // child_origin_block_offset and try again. + // child_origin_block_offset, reposition any floats affected by that, and + // try again. layout_result = nullptr; break; } @@ -829,7 +881,7 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( // Now find a layout opportunity where the fragment is actually going to // fit. NGFragment fragment(writing_mode, *layout_result->PhysicalFragment()); - opportunity = tmp_exclusion_space.FindLayoutOpportunity( + opportunity = exclusion_space_->FindLayoutOpportunity( origin_offset, inline_size, fragment.Size()); } while (origin_offset.block_offset < opportunity.rect.BlockStartOffset()); @@ -849,102 +901,53 @@ bool NGBlockLayoutAlgorithm::HandleInflow( bool is_non_empty_inline = child.IsInline() && !ToNGInlineNode(child).IsEmptyInline(); - // TODO(ikilpatrick): We may only want to position pending floats if there is - // something that we *might* clear in the unpositioned list. E.g. we may - // clear an already placed left float, but the unpositioned list may only have - // right floats. - bool should_position_pending_floats = + bool has_clearance_past_adjoining_floats = child.IsBlock() && - ClearanceMayAffectLayout(*exclusion_space_, unpositioned_floats_, - child.Style()); + HasClearancePastAdjoiningFloats(container_builder_.AdjoiningFloatTypes(), + child.Style()); - // There are two conditions where we need to position our pending floats: - // 1. If the child will be affected by clearance. + // If we can separate the previous margin strut from what is to follow, do + // that. Then we're able to resolve *our* BFC offset and position any pending + // floats. There are two situations where this is necessary: + // 1. If the child is to be cleared by adjoining floats. // 2. If the child is a non-empty inline. - // This collapses the previous margin strut, and optionally resolves *our* - // BFC offset. - if (should_position_pending_floats || is_non_empty_inline) { - LayoutUnit origin_point_block_offset = - previous_inflow_position->bfc_block_offset + - previous_inflow_position->margin_strut.Sum(); - bool updated = MaybeUpdateFragmentBfcOffset(origin_point_block_offset); - - if (updated && abort_when_bfc_resolved_) + if (has_clearance_past_adjoining_floats || is_non_empty_inline) { + if (!ResolveBfcOffset(previous_inflow_position)) return false; - - // If our BFC offset was updated we may have been affected by clearance - // ourselves. We need to adjust the origin point to accomodate this. - if (updated) - origin_point_block_offset = - std::max(origin_point_block_offset, - ConstraintSpace().ClearanceOffset().value_or(LayoutUnit())); - - bool positioned_direct_child_floats = !unpositioned_floats_.IsEmpty(); - - PositionPendingFloats(origin_point_block_offset); - - // If we positioned float which are direct children, or the child is a - // non-empty inline, we need to artificially "reset" the previous inflow - // position, e.g. we clear the margin strut, and set the offset to our - // block-start border edge. - // - // This behaviour is similar to if we had block-start border or padding. - if ((positioned_direct_child_floats && updated) || is_non_empty_inline) { - // We must have no border/scrollbar/padding here otherwise our BFC offset - // would already be resolved. - if (!is_non_empty_inline) - DCHECK_EQ(border_scrollbar_padding_.block_start, LayoutUnit()); - - previous_inflow_position->bfc_block_offset = origin_point_block_offset; - previous_inflow_position->margin_strut = NGMarginStrut(); - previous_inflow_position->logical_block_offset = LayoutUnit(); - } } // Perform layout on the child. NGInflowChildData child_data = - ComputeChildData(*previous_inflow_position, child, child_break_token); + ComputeChildData(*previous_inflow_position, child, child_break_token, + has_clearance_past_adjoining_floats); scoped_refptr<NGConstraintSpace> child_space = CreateConstraintSpaceForChild(child, child_data, child_available_size_); scoped_refptr<NGLayoutResult> layout_result = child.Layout(*child_space, child_break_token); + base::Optional<NGBfcOffset> child_bfc_offset = layout_result->BfcOffset(); + // TODO(layout-dev): A more optimal version of this is to set + // relayout_child_when_bfc_resolved only if the child tree itself _added_ any + // floats that it failed to position. Currently, we risk relaying out the + // parent block for no reason, because we're not able to make this + // distinction. + bool relayout_child_when_bfc_resolved = + layout_result->AdjoiningFloatTypes() && !child_bfc_offset && + !child_space->FloatsBfcOffset(); bool is_empty_block = IsEmptyBlock(child, *layout_result); - // If we don't know our BFC offset yet, we need to copy the list of - // unpositioned floats from the child's layout result. - // - // If the child had any unpositioned floats, we need to abort our layout if - // we resolve our BFC offset. - // - // If we are a new formatting context, the child will get re-laid out once it - // has been positioned. - // - // TODO(ikilpatrick): a more optimal version of this is to set - // abort_when_bfc_resolved_, if the child tree _added_ any floats. - if (!container_builder_.BfcOffset()) { - unpositioned_floats_ = layout_result->UnpositionedFloats(); - abort_when_bfc_resolved_ |= !layout_result->UnpositionedFloats().IsEmpty(); - if (child_space->FloatsBfcOffset()) - DCHECK(layout_result->UnpositionedFloats().IsEmpty()); - // If our BFC offset is unknown, and the child got pushed down by floats, so - // will we. - if (layout_result->IsPushedByFloats()) - container_builder_.SetIsPushedByFloats(); - } - // A child may have aborted its layout if it resolved its BFC offset. If // we don't have a BFC offset yet, we need to propagate the abortion up // to our parent. if (layout_result->Status() == NGLayoutResult::kBfcOffsetResolved && !container_builder_.BfcOffset()) { - MaybeUpdateFragmentBfcOffset( - layout_result->BfcOffset().value().block_offset); - - // NOTE: Unlike other aborts, we don't try check if we *should* abort with - // abort_when_bfc_resolved_, this is simply propagating an abort up to a - // node which is able to restart the layout (a node that has resolved its - // BFC offset). + // There's no need to do anything apart from resolving the BFC offset here, + // so make sure that it aborts before trying to position floats or anything + // like that, which would just be waste of time. This is simply propagating + // an abort up to a node which is able to restart the layout (a node that + // has resolved its BFC offset). + abort_when_bfc_offset_updated_ = true; + ResolveBfcOffset(previous_inflow_position, child_bfc_offset->block_offset); return false; } @@ -967,22 +970,50 @@ bool NGBlockLayoutAlgorithm::HandleInflow( // We try and position the child within the block formatting context. This // may cause our BFC offset to be resolved, in which case we should abort our // layout if needed. - WTF::Optional<NGBfcOffset> child_bfc_offset; - if (layout_result->BfcOffset()) { - if (!PositionWithBfcOffset(layout_result->BfcOffset().value(), - &child_bfc_offset)) + bool has_clearance = layout_result->IsPushedByFloats(); + if (!child_bfc_offset) { + if (!has_clearance && child_space->HasClearanceOffset() && + child.Style().Clear() != EClear::kNone) { + // This is an empty block child that we collapsed through, so we have to + // detect clearance manually. See if the child's hypothetical border edge + // is past the relevant floats. If it's not, we need to apply clearance + // before it. + LayoutUnit child_block_offset_estimate = + previous_inflow_position->bfc_block_offset + + layout_result->EndMarginStrut().Sum(); + if (child_block_offset_estimate < child_space->ClearanceOffset() || + child_space->ShouldForceClearance()) + has_clearance = empty_block_affected_by_clearance = true; + } + } + if (has_clearance) { + // The child has clearance. Clearance inhibits margin collapsing and acts as + // spacing before the block-start margin of the child. Our BFC offset is + // therefore resolvable, and if it hasn't already been resolved, we'll do it + // now to separate the child's collapsed margin from this container. + if (!ResolveBfcOffset(previous_inflow_position)) return false; - } else { + } + if (!child_bfc_offset) { + DCHECK(is_empty_block); // Layout wasn't able to determine the BFC offset of the child. This has to // mean that the child is empty (block-size-wise). - DCHECK(is_empty_block); if (container_builder_.BfcOffset()) { // Since we know our own BFC offset, though, we can calculate that of the // child as well. child_bfc_offset = PositionEmptyChildWithParentBfc( - child, *child_space, child_data, *layout_result, - &empty_block_affected_by_clearance); + child, *child_space, child_data, *layout_result); } + } else if (!has_clearance) { + // We shouldn't have any pending floats here, since an in-flow child found + // its BFC offset. + DCHECK(unpositioned_floats_.IsEmpty()); + + // The child's BFC offset is known, and since there's no clearance, this + // container will get the same offset, unless it has already been resolved. + if (!ResolveBfcOffset(previous_inflow_position, + child_bfc_offset->block_offset)) + return false; } // We need to re-layout a child if it was affected by clearance in order to @@ -1020,7 +1051,7 @@ bool NGBlockLayoutAlgorithm::HandleInflow( // - It has some unpositioned floats. // - It was affected by clearance. if ((layout_result->Status() == NGLayoutResult::kBfcOffsetResolved || - !layout_result->UnpositionedFloats().IsEmpty() || + relayout_child_when_bfc_resolved || empty_block_affected_by_clearance_needs_relayout) && child_bfc_offset) { scoped_refptr<NGConstraintSpace> new_child_space = @@ -1028,11 +1059,45 @@ bool NGBlockLayoutAlgorithm::HandleInflow( child_bfc_offset); layout_result = child.Layout(*new_child_space, child_break_token); + if (layout_result->Status() == NGLayoutResult::kBfcOffsetResolved) { + // Even a second layout pass may abort, if the BFC offset initially + // calculated turned out to be wrong. This happens when we discover that + // an in-flow block-level descendant that establishes a new formatting + // context doesn't fit beside the floats at its initial position. Allow + // one more pass. + child_bfc_offset = layout_result->BfcOffset(); + DCHECK(child_bfc_offset); + new_child_space = CreateConstraintSpaceForChild( + child, child_data, child_available_size_, child_bfc_offset); + layout_result = child.Layout(*new_child_space, child_break_token); + } + DCHECK_EQ(layout_result->Status(), NGLayoutResult::kSuccess); DCHECK(layout_result->ExclusionSpace()); exclusion_space_ = std::make_unique<NGExclusionSpace>(*layout_result->ExclusionSpace()); + relayout_child_when_bfc_resolved = false; + } + + // If we don't know our BFC offset yet, and the child stumbled into something + // that needs it (unable to position floats when the BFC offset is unknown), + // we need abort layout once we manage to resolve it, and relayout. Note that + // this check is performed after the optional second layout pass above, since + // we may have been able to resolve our BFC offset (e.g. due to clearance) and + // position any descendant floats in the second pass. In particular, when it + // comes to clearance of empty blocks, if we just applied it and resolved the + // BFC offset to separate the margins before and after clearance, we cannot + // abort and re-layout this block, or clearance would be lost. + // + // If we are a new formatting context, the child will get re-laid out once it + // has been positioned. + if (!container_builder_.BfcOffset()) { + abort_when_bfc_offset_updated_ |= relayout_child_when_bfc_resolved; + // If our BFC offset is unknown, and the child got pushed down by floats, + // so will we. + if (layout_result->IsPushedByFloats()) + container_builder_.SetIsPushedByFloats(); } // A line-box may have a list of floats which we add as children. @@ -1073,6 +1138,9 @@ bool NGBlockLayoutAlgorithm::HandleInflow( intrinsic_block_size_ = std::max(intrinsic_block_size_, logical_offset.block_offset + fragment.BlockSize()); + } else if (!container_builder_.BfcOffset()) { + container_builder_.AddAdjoiningFloatTypes( + layout_result->AdjoiningFloatTypes()); } container_builder_.AddChild(layout_result, logical_offset); @@ -1094,7 +1162,8 @@ bool NGBlockLayoutAlgorithm::HandleInflow( NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData( const NGPreviousInflowPosition& previous_inflow_position, NGLayoutInputNode child, - const NGBreakToken* child_break_token) { + const NGBreakToken* child_break_token, + bool force_clearance) { DCHECK(child); DCHECK(!child.IsFloating()); @@ -1114,14 +1183,14 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData( margins.LineLeft(ConstraintSpace().Direction()), previous_inflow_position.bfc_block_offset}; - return {child_bfc_offset, margin_strut, margins}; + return {child_bfc_offset, margin_strut, margins, force_clearance}; } NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition( const NGPreviousInflowPosition& previous_inflow_position, const NGLayoutInputNode child, const NGInflowChildData& child_data, - const WTF::Optional<NGBfcOffset>& child_bfc_offset, + const base::Optional<NGBfcOffset>& child_bfc_offset, const NGLogicalOffset& logical_offset, const NGLayoutResult& layout_result, const NGFragment& fragment, @@ -1133,6 +1202,10 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition( bool is_empty_block = IsEmptyBlock(child, layout_result); if (is_empty_block) { + // The default behaviour for empty blocks is they just pass through the + // previous inflow position. + child_end_bfc_block_offset = previous_inflow_position.bfc_block_offset; + if (empty_block_affected_by_clearance) { // If an empty block was affected by clearance (that is it got pushed // down past a float), we need to do something slightly bizarre. @@ -1144,18 +1217,46 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition( // Another way of thinking about this is that when you *add* back the // margin strut, you end up with the same position as you started with. // - // This behaviour isn't known to be in any CSS specification. - child_end_bfc_block_offset = child_bfc_offset.value().block_offset - - layout_result.EndMarginStrut().Sum(); - logical_block_offset = - logical_offset.block_offset - layout_result.EndMarginStrut().Sum(); - } else { - // The default behaviour for empty blocks is they just pass through the - // previous inflow position. - child_end_bfc_block_offset = previous_inflow_position.bfc_block_offset; - logical_block_offset = previous_inflow_position.logical_block_offset; + // This is essentially what the spec refers to as clearance [1], and, + // while we normally don't have to calculate it directly, in the case of + // an empty cleared child like here, we actually have to. + // + // We have to calculate clearance for empty cleared children, because we + // need the margin that's between the clearance and this block to collapse + // correctly with subsequent content. This is something that needs to take + // place after the margin strut preceding and following the clearance have + // been separated. Clearance may be positive, negative or zero, depending + // on what it takes to (hypothetically) place this child just below the + // last relevant float. Since the margins before and after the clearance + // have been separated, we may have to pull the child back, and that's an + // example of negative clearance. + // + // (In the other case, when a cleared child is non-empty (i.e. when we + // don't end up here), we don't need to explicitly calculate clearance, + // because then we just place its border edge where it should be and we're + // done with it.) + // + // [1] https://www.w3.org/TR/CSS22/visuren.html#flow-control + + // First move past the margin that is to precede the clearance. It will + // not participate in any subsequent margin collapsing. + LayoutUnit margin_before_clearance = + previous_inflow_position.margin_strut.Sum(); + child_end_bfc_block_offset += margin_before_clearance; + + // Calculate and apply actual clearance. + LayoutUnit clearance = child_bfc_offset.value().block_offset - + layout_result.EndMarginStrut().Sum() - + previous_inflow_position.NextBorderEdge(); + child_end_bfc_block_offset += clearance; } + // The logical block offset needs to go through exactly the same change as + // the BFC block offset here. + logical_block_offset = previous_inflow_position.logical_block_offset + + child_end_bfc_block_offset - + previous_inflow_position.bfc_block_offset; + if (!container_builder_.BfcOffset()) { DCHECK_EQ(child_end_bfc_block_offset, ConstraintSpace().BfcOffset().block_offset); @@ -1190,27 +1291,11 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition( empty_or_sibling_empty_affected_by_clearance}; } -bool NGBlockLayoutAlgorithm::PositionWithBfcOffset( - const NGBfcOffset& bfc_offset, - WTF::Optional<NGBfcOffset>* child_bfc_offset) { - LayoutUnit bfc_block_offset = bfc_offset.block_offset; - bool updated = MaybeUpdateFragmentBfcOffset(bfc_block_offset); - - if (updated && abort_when_bfc_resolved_) - return false; - - PositionPendingFloats(bfc_block_offset); - - *child_bfc_offset = bfc_offset; - return true; -} - NGBfcOffset NGBlockLayoutAlgorithm::PositionEmptyChildWithParentBfc( const NGLayoutInputNode& child, const NGConstraintSpace& child_space, const NGInflowChildData& child_data, - const NGLayoutResult& layout_result, - bool* has_clearance) const { + const NGLayoutResult& layout_result) const { DCHECK(IsEmptyBlock(child, layout_result)); // The child must be an in-flow zero-block-size fragment, use its end margin @@ -1225,11 +1310,10 @@ NGBfcOffset NGBlockLayoutAlgorithm::PositionEmptyChildWithParentBfc( if (child.IsInline()) { child_bfc_offset.line_offset += LineOffsetForTextAlign(Style().GetTextAlign(), Style().Direction(), - child_available_size_.inline_size); + child_available_size_.inline_size, LayoutUnit()); } - *has_clearance = - AdjustToClearance(child_space.ClearanceOffset(), &child_bfc_offset); + ApplyClearance(child_space, &child_bfc_offset.block_offset); return child_bfc_offset; } @@ -1551,12 +1635,12 @@ NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( // TODO(ikilpatrick): Move the auto margins calculation for different writing // modes to post-layout. if (!child.IsFloating() && !child.CreatesNewFormattingContext()) { - WTF::Optional<MinMaxSize> sizes; + base::Optional<MinMaxSize> sizes; if (NeedMinMaxSize(*space, child_style)) { // We only want to guess the child's size here, so preceding floats are of // no interest. MinMaxSizeInput zero_input; - sizes = child.ComputeMinMaxSize(zero_input); + sizes = child.ComputeMinMaxSize(child_style.GetWritingMode(), zero_input); } LayoutUnit child_inline_size = @@ -1575,7 +1659,7 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( const NGLayoutInputNode child, const NGInflowChildData& child_data, const NGLogicalSize child_available_size, - const WTF::Optional<NGBfcOffset> floats_bfc_offset) { + const base::Optional<NGBfcOffset> floats_bfc_offset) { NGConstraintSpaceBuilder space_builder(ConstraintSpace()); NGLogicalSize available_size(child_available_size); @@ -1600,8 +1684,11 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( .SetBfcOffset(child_data.bfc_offset_estimate) .SetMarginStrut(child_data.margin_strut); - if (!is_new_fc) + if (!is_new_fc) { space_builder.SetExclusionSpace(*exclusion_space_); + space_builder.SetAdjoiningFloatTypes( + container_builder_.AdjoiningFloatTypes()); + } if (!container_builder_.BfcOffset() && ConstraintSpace().FloatsBfcOffset()) { space_builder.SetFloatsBfcOffset( @@ -1612,26 +1699,17 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( if (floats_bfc_offset) space_builder.SetFloatsBfcOffset(floats_bfc_offset); - if (!is_new_fc && !floats_bfc_offset) { - space_builder.SetUnpositionedFloats(unpositioned_floats_); - } - WritingMode writing_mode; - Optional<LayoutUnit> clearance_offset; - if (!constraint_space_.IsNewFormattingContext()) - clearance_offset = ConstraintSpace().ClearanceOffset(); + LayoutUnit clearance_offset = constraint_space_.IsNewFormattingContext() + ? LayoutUnit::Min() + : ConstraintSpace().ClearanceOffset(); if (child.IsInline()) { writing_mode = Style().GetWritingMode(); } else { const ComputedStyle& child_style = child.Style(); LayoutUnit child_clearance_offset = exclusion_space_->ClearanceOffset(child_style.Clear()); - if (clearance_offset) { - clearance_offset = - std::max(clearance_offset.value(), child_clearance_offset); - } else { - clearance_offset = child_clearance_offset; - } + clearance_offset = std::max(clearance_offset, child_clearance_offset); space_builder.SetIsShrinkToFit(ShouldShrinkToFit(Style(), child_style)); space_builder.SetTextDirection(child_style.Direction()); writing_mode = child_style.GetWritingMode(); @@ -1639,13 +1717,12 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( // PositionListMarker() requires a first line baseline. if (container_builder_.UnpositionedListMarker()) { space_builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kFirstLine, - IsHorizontalWritingMode(constraint_space_.GetWritingMode()) - ? kAlphabeticBaseline - : kIdeographicBaseline}); + {NGBaselineAlgorithmType::kFirstLine, Style().GetFontBaseline()}); } } space_builder.SetClearanceOffset(clearance_offset); + if (child_data.force_clearance) + space_builder.SetShouldForceClearance(); LayoutUnit space_available; if (ConstraintSpace().HasBlockFragmentation()) { @@ -1668,7 +1745,6 @@ NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( space_builder.SetFragmentainerSpaceAtBfcStart(space_available); space_builder.SetFragmentationType( ConstraintSpace().BlockFragmentationType()); - return space_builder.ToConstraintSpace(writing_mode); } @@ -1734,20 +1810,59 @@ void NGBlockLayoutAlgorithm::PropagateBaselinesFromChildren() { } } -bool NGBlockLayoutAlgorithm::MaybeUpdateFragmentBfcOffset( +bool NGBlockLayoutAlgorithm::ResolveBfcOffset( + NGPreviousInflowPosition* previous_inflow_position, LayoutUnit bfc_block_offset) { - if (container_builder_.BfcOffset()) - return false; + if (container_builder_.BfcOffset()) { + DCHECK(unpositioned_floats_.IsEmpty()); + return true; + } NGBfcOffset bfc_offset(ConstraintSpace().BfcOffset().line_offset, bfc_block_offset); - if (AdjustToClearance(ConstraintSpace().ClearanceOffset(), &bfc_offset)) + if (ApplyClearance(ConstraintSpace(), &bfc_offset.block_offset)) container_builder_.SetIsPushedByFloats(); container_builder_.SetBfcOffset(bfc_offset); + container_builder_.ResetAdjoiningFloatTypes(); + + if (NeedsAbortOnBfcOffsetChange()) + return false; + + // If our BFC offset was updated, we may have been affected by clearance + // ourselves. We need to adjust the origin point to accomodate this. + bfc_block_offset = bfc_offset.block_offset; + + PositionPendingFloats(bfc_block_offset); + + // Reset the previous inflow position. Clear the margin strut and set the + // offset to our block-start border edge. + // + // We'll now end up at the block-start border edge. If the BFC offset was + // resolved due to a block-start border or padding, that must be added by the + // caller, for subsequent layout to continue at the right position. Whether we + // need to add border+padding or not isn't something we should determine here, + // so it must be dealt with as part of initializing the layout algorithm. + previous_inflow_position->bfc_block_offset = bfc_block_offset; + previous_inflow_position->logical_block_offset = LayoutUnit(); + previous_inflow_position->margin_strut = NGMarginStrut(); return true; } +bool NGBlockLayoutAlgorithm::NeedsAbortOnBfcOffsetChange() const { + DCHECK(container_builder_.BfcOffset()); + if (!abort_when_bfc_offset_updated_) + return false; + // If no previous BFC offset was set, we need to abort. + if (!ConstraintSpace().FloatsBfcOffset()) + return true; + // If the previous BFC offset matches the new one, we can continue. Otherwise, + // we need to abort. + LayoutUnit old_bfc_block_offset = + ConstraintSpace().FloatsBfcOffset()->block_offset; + return container_builder_.BfcOffset()->block_offset != old_bfc_block_offset; +} + void NGBlockLayoutAlgorithm::PositionPendingFloats( LayoutUnit origin_block_offset) { DCHECK(container_builder_.BfcOffset() || ConstraintSpace().FloatsBfcOffset()) @@ -1860,8 +1975,9 @@ void NGBlockLayoutAlgorithm::PositionOrPropagateListMarker( return; container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker()); } - if (list_marker.AddToBox(constraint_space_, *layout_result.PhysicalFragment(), - content_offset, &container_builder_)) + if (list_marker.AddToBox(constraint_space_, Style().GetFontBaseline(), + *layout_result.PhysicalFragment(), content_offset, + &container_builder_)) return; // If the list marker could not be positioned against this child because it @@ -1877,7 +1993,7 @@ void NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes() { // Position the list marker without aligning to line boxes. LayoutUnit marker_block_size = container_builder_.UnpositionedListMarker().AddToBoxWithoutLineBoxes( - constraint_space_, &container_builder_); + constraint_space_, Style().GetFontBaseline(), &container_builder_); container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker()); // Whether the list marker should affect the block size or not is not diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h index 4ed60269ff6..8a51fde1456 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h @@ -19,9 +19,15 @@ class NGConstraintSpace; class NGFragment; class NGLayoutResult; -// This struct is used for communicating to a child the position of the -// previous inflow child. +// This struct is used for communicating to a child the position of the previous +// inflow child. This will be used to calculate the position of the next child. struct NGPreviousInflowPosition { + // Return the BFC offset of the next block-start border edge we'd get if we + // commit pending margins. + LayoutUnit NextBorderEdge() const { + return bfc_block_offset + margin_strut.Sum(); + } + LayoutUnit bfc_block_offset; LayoutUnit logical_block_offset; NGMarginStrut margin_strut; @@ -34,6 +40,7 @@ struct NGInflowChildData { NGBfcOffset bfc_offset_estimate; NGMarginStrut margin_strut; NGBoxStrut margins; + bool force_clearance; }; // A class for general block layout (e.g. a <div> with no special style). @@ -52,7 +59,10 @@ class CORE_EXPORT NGBlockLayoutAlgorithm const NGConstraintSpace& space, NGBlockBreakToken* break_token = nullptr); - Optional<MinMaxSize> ComputeMinMaxSize(const MinMaxSizeInput&) const override; + ~NGBlockLayoutAlgorithm() override; + + base::Optional<MinMaxSize> ComputeMinMaxSize( + const MinMaxSizeInput&) const override; scoped_refptr<NGLayoutResult> Layout() override; private: @@ -64,27 +74,24 @@ class CORE_EXPORT NGBlockLayoutAlgorithm const NGLayoutInputNode child, const NGInflowChildData& child_data, const NGLogicalSize child_available_size, - const WTF::Optional<NGBfcOffset> floats_bfc_offset = WTF::nullopt); + const base::Optional<NGBfcOffset> floats_bfc_offset = base::nullopt); // @return Estimated BFC offset for the "to be layout" child. NGInflowChildData ComputeChildData(const NGPreviousInflowPosition&, NGLayoutInputNode, - const NGBreakToken* child_break_token); + const NGBreakToken* child_break_token, + bool force_clearance); NGPreviousInflowPosition ComputeInflowPosition( const NGPreviousInflowPosition& previous_inflow_position, const NGLayoutInputNode child, const NGInflowChildData& child_data, - const WTF::Optional<NGBfcOffset>& child_bfc_offset, + const base::Optional<NGBfcOffset>& child_bfc_offset, const NGLogicalOffset& logical_offset, const NGLayoutResult& layout_result, const NGFragment& fragment, bool empty_block_affected_by_clearance); - // Positions the fragment that knows its BFC offset. - bool PositionWithBfcOffset(const NGBfcOffset& bfc_offset, - WTF::Optional<NGBfcOffset>* child_bfc_offset); - // Position an empty child using the parent BFC offset. // The fragment doesn't know its offset, but we can still calculate its BFC // position because the parent fragment's BFC is known. @@ -96,8 +103,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm const NGLayoutInputNode& child, const NGConstraintSpace& child_space, const NGInflowChildData& child_data, - const NGLayoutResult&, - bool* has_clearance) const; + const NGLayoutResult&) const; void HandleOutOfFlowPositioned(const NGPreviousInflowPosition&, NGBlockNode); void HandleFloat(const NGPreviousInflowPosition&, @@ -178,8 +184,29 @@ class CORE_EXPORT NGBlockLayoutAlgorithm const NGPhysicalFragment*, LayoutUnit child_offset); - // Updates the fragment's BFC offset if it's not already set. - bool MaybeUpdateFragmentBfcOffset(LayoutUnit bfc_block_offset); + // If still unresolved, resolve the fragment's BFC offset. + // + // This includes applying clearance, so the bfc_block_offset passed won't be + // the final BFC offset, if it wasn't large enough to get past all relevant + // floats. The updated BFC offset can be read out with ContainerBfcOffset(). + // + // In addition to resolving our BFC offset, this will also position pending + // floats, and update our in-flow layout state. Returns false if resolving the + // BFC offset resulted in needing to abort layout. It will always return true + // otherwise. If the BFC offset was already resolved, this method does nothing + // (and returns true). + bool ResolveBfcOffset(NGPreviousInflowPosition*, LayoutUnit bfc_block_offset); + + // A very common way to resolve the BFC offset is to simply commit the pending + // margin, so here's a convenience overload for that. + bool ResolveBfcOffset(NGPreviousInflowPosition* previous_inflow_position) { + return ResolveBfcOffset(previous_inflow_position, + previous_inflow_position->NextBorderEdge()); + } + + // Return true if the BFC offset has changed and this means that we need to + // abort layout. + bool NeedsAbortOnBfcOffsetChange() const; // Positions pending floats starting from {@origin_block_offset}. void PositionPendingFloats(LayoutUnit origin_block_offset); @@ -201,7 +228,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm NGLayoutInputNode child, const NGFragment&, const NGBoxStrut& child_margins, - const WTF::Optional<NGBfcOffset>& known_fragment_offset); + const base::Optional<NGBfcOffset>& known_fragment_offset); // Computes default content size for HTML and BODY elements in quirks mode. // Returns NGSizeIndefinite in all other cases. @@ -231,7 +258,14 @@ class CORE_EXPORT NGBlockLayoutAlgorithm // Set if we're resuming layout of a node that has already produced fragments. bool is_resuming_; - bool abort_when_bfc_resolved_; + // Set when we're to abort if the BFC offset gets resolved or updated. + // Sometimes we walk past elements (i.e. floats) that depend on the BFC offset + // being known (in order to position and lay themselves out properly). When + // this happens, and we finally manage to resolve (or update) the BFC offset + // at some subsequent element, we need to check if this flag is set, and abort + // layout if it is. + bool abort_when_bfc_offset_updated_ = false; + bool has_processed_first_child_ = false; std::unique_ptr<NGExclusionSpace> exclusion_space_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc index 7d4e30976bd..fc1e2732a14 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc @@ -31,7 +31,6 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest { protected: void SetUp() override { NGBaseLayoutAlgorithmTest::SetUp(); - style_ = ComputedStyle::Create(); } scoped_refptr<NGPhysicalBoxFragment> RunBlockLayoutAlgorithm( @@ -67,7 +66,13 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest { return fragment->DumpFragmentTree(flags); } - scoped_refptr<ComputedStyle> style_; + scoped_refptr<ComputedStyle> MutableStyleForElement(Element* element) { + DCHECK(element->GetLayoutObject()); + scoped_refptr<ComputedStyle> mutable_style = + ComputedStyle::Clone(element->GetLayoutObject()->StyleRef()); + element->GetLayoutObject()->SetStyleInternal(mutable_style); + return mutable_style; + } }; TEST_F(NGBlockLayoutAlgorithmTest, FixedSize) { @@ -383,7 +388,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase3) { scoped_refptr<const NGPhysicalBoxFragment> fragment; auto run_test = [&](const Length& container_height) { Element* container = GetDocument().getElementById("container"); - container->MutableComputedStyle()->SetHeight(container_height); + MutableStyleForElement(container)->SetHeight(container_height); container->GetLayoutObject()->SetNeedsLayout(""); std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( GetDocument().getElementsByTagName("html")->item(0)); @@ -435,7 +440,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase4) { scoped_refptr<const NGPhysicalBoxFragment> fragment; auto run_test = [&](const Length& container_padding_top) { Element* container = GetDocument().getElementById("container"); - container->MutableComputedStyle()->SetPaddingTop(container_padding_top); + MutableStyleForElement(container)->SetPaddingTop(container_padding_top); container->GetLayoutObject()->SetNeedsLayout(""); std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( GetDocument().getElementsByTagName("html")->item(0)); @@ -722,19 +727,20 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsEmptyBlockWithClearance) { const Length& inflow_margin_top) { // Set the style of the elements we care about. Element* zero_top = GetDocument().getElementById("zero-top"); - zero_top->MutableComputedStyle()->SetMarginBottom(zero_top_margin_bottom); + MutableStyleForElement(zero_top)->SetMarginBottom(zero_top_margin_bottom); zero_top->GetLayoutObject()->SetNeedsLayout(""); Element* zero_inner = GetDocument().getElementById("zero-inner"); - zero_inner->MutableComputedStyle()->SetMarginTop(zero_inner_margin_top); - zero_inner->MutableComputedStyle()->SetMarginBottom( - zero_inner_margin_bottom); + scoped_refptr<ComputedStyle> zero_inner_style = + MutableStyleForElement(zero_inner); + zero_inner_style->SetMarginTop(zero_inner_margin_top); + zero_inner_style->SetMarginBottom(zero_inner_margin_bottom); zero_inner->GetLayoutObject()->SetNeedsLayout(""); Element* zero_element = GetDocument().getElementById("zero"); - zero_element->MutableComputedStyle()->SetMarginBottom(zero_margin_bottom); + MutableStyleForElement(zero_element)->SetMarginBottom(zero_margin_bottom); zero_element->GetLayoutObject()->SetNeedsLayout(""); Element* inflow_element = GetDocument().getElementById("inflow"); - inflow_element->MutableComputedStyle()->SetMarginTop(inflow_margin_top); + MutableStyleForElement(inflow_element)->SetMarginTop(inflow_margin_top); inflow_element->GetLayoutObject()->SetNeedsLayout(""); GetDocument().View()->UpdateAllLifecyclePhases(); @@ -1283,7 +1289,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFragmentsWithClear) { scoped_refptr<const NGPhysicalBoxFragment> fragment; auto run_with_clearance = [&](EClear clear_value) { Element* el_with_clear = GetDocument().getElementById("clearance"); - el_with_clear->MutableComputedStyle()->SetClear(clear_value); + MutableStyleForElement(el_with_clear)->SetClear(clear_value); el_with_clear->GetLayoutObject()->SetNeedsLayout(""); std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( GetDocument().getElementsByTagName("html")->item(0)); @@ -2127,7 +2133,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, scoped_refptr<const NGPhysicalBoxFragment> fragment; auto run_test = [&](const Length& block_width) { Element* new_fc_block = GetDocument().getElementById("new-fc"); - new_fc_block->MutableComputedStyle()->SetWidth(block_width); + MutableStyleForElement(new_fc_block)->SetWidth(block_width); new_fc_block->GetLayoutObject()->SetNeedsLayout(""); std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( GetDocument().getElementsByTagName("html")->item(0)); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index 92ed7e92724..e30210096e9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc @@ -9,11 +9,8 @@ #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h" #include "third_party/blink/renderer/core/layout/layout_multi_column_set.h" -#include "third_party/blink/renderer/core/layout/layout_table.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" @@ -26,8 +23,10 @@ #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h" +#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -125,6 +124,8 @@ void UpdateLegacyMultiColumnFlowThread( // handles such cases. void CopyFragmentDataToLayoutBoxForInlineChildren( const NGPhysicalContainerFragment& container, + LayoutUnit initial_container_width, + bool initial_container_is_flipped, NGPhysicalOffset offset = {}) { for (const auto& child : container.Children()) { if (child->IsContainer()) { @@ -135,7 +136,13 @@ void CopyFragmentDataToLayoutBoxForInlineChildren( LayoutObject* layout_object = child->GetLayoutObject(); if (layout_object && layout_object->IsBox()) { LayoutBox& layout_box = ToLayoutBox(*layout_object); - layout_box.SetLocation(child_offset.ToLayoutPoint()); + NGPhysicalOffset maybe_flipped_offset = child_offset; + if (initial_container_is_flipped) { + maybe_flipped_offset.left = initial_container_width - + child->Size().width - + maybe_flipped_offset.left; + } + layout_box.SetLocation(maybe_flipped_offset.ToLayoutPoint()); } // The Location() of inline LayoutObject is relative to the @@ -144,7 +151,8 @@ void CopyFragmentDataToLayoutBoxForInlineChildren( // to its descendants in this case. if (!child->IsBlockLayoutRoot()) { CopyFragmentDataToLayoutBoxForInlineChildren( - ToNGPhysicalContainerFragment(*child), child_offset); + ToNGPhysicalContainerFragment(*child), initial_container_width, + initial_container_is_flipped, child_offset); } } } @@ -169,9 +177,30 @@ scoped_refptr<NGLayoutResult> NGBlockNode::Layout( layout_result = ToLayoutBlockFlow(box_)->CachedLayoutResult( constraint_space, break_token); if (layout_result) { - block_flow->ClearPaintFragment(); - if (first_child && first_child.IsInline()) - block_flow->SetPaintFragment(layout_result->PhysicalFragment()); + // TODO(layoutng): Figure out why these two call can't be inside the + // !constraint_space.IsIntermediateLayout() block below. + UpdateShapeOutsideInfoIfNeeded( + constraint_space.PercentageResolutionSize().inline_size); + // We may need paint invalidation even if we can reuse layout, as our + // paint offset/visual rect may have changed due to relative + // positioning changes. Otherwise we fail fast/css/ + // fast/css/relative-positioned-block-with-inline-ancestor-and-parent + // -dynamic.html + // TODO(layoutng): See if we can optimize this. When we natively + // support relative positioning in NG we can probably remove this, + box_->SetMayNeedPaintInvalidation(); + + // We have to re-set the cached result here, because it is used for + // LayoutNGMixin::CurrentFragment and therefore has to be up-to-date. + // In particular, that fragment would have an incorrect offset if we + // don't re-set the result here. + ToLayoutBlockFlow(box_)->SetCachedLayoutResult( + constraint_space, break_token, layout_result); + if (!constraint_space.IsIntermediateLayout()) { + block_flow->ClearPaintFragment(); + if (first_child && first_child.IsInline()) + block_flow->SetPaintFragment(layout_result->PhysicalFragment()); + } return layout_result; } } @@ -181,16 +210,21 @@ scoped_refptr<NGLayoutResult> NGBlockNode::Layout( if (block_flow) { block_flow->SetCachedLayoutResult(constraint_space, break_token, layout_result); - block_flow->ClearPaintFragment(); + if (layout_result->Status() == NGLayoutResult::kSuccess && + !constraint_space.IsIntermediateLayout()) + block_flow->ClearPaintFragment(); } - if (layout_result->Status() == NGLayoutResult::kSuccess && - layout_result->UnpositionedFloats().IsEmpty()) { + if (IsBlockLayoutComplete(constraint_space, *layout_result)) { DCHECK(layout_result->PhysicalFragment()); if (block_flow && first_child && first_child.IsInline()) { - CopyFragmentDataToLayoutBoxForInlineChildren( - ToNGPhysicalBoxFragment(*layout_result->PhysicalFragment())); + NGBoxStrut scrollbars = GetScrollbarSizes(); + CopyFragmentDataToLayoutBoxForInlineChildren( + ToNGPhysicalBoxFragment(*layout_result->PhysicalFragment()), + layout_result->PhysicalFragment()->Size().width - + scrollbars.block_start, + Style().IsFlippedBlocksWritingMode()); block_flow->SetPaintFragment(layout_result->PhysicalFragment()); } @@ -204,6 +238,7 @@ scoped_refptr<NGLayoutResult> NGBlockNode::Layout( } MinMaxSize NGBlockNode::ComputeMinMaxSize( + WritingMode container_writing_mode, const MinMaxSizeInput& input, const NGConstraintSpace* constraint_space) { MinMaxSize sizes; @@ -224,21 +259,35 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( NGConstraintSpaceBuilder(Style().GetWritingMode(), InitialContainingBlockSize()) .SetTextDirection(Style().Direction()) + .SetIsIntermediateLayout(true) + .SetFloatsBfcOffset(NGBfcOffset()) .ToConstraintSpace(Style().GetWritingMode()); if (!constraint_space) constraint_space = zero_constraint_space.get(); - // TODO(cbiesinger): For orthogonal children, we need to always synthesize. + if (!IsParallelWritingMode(container_writing_mode, + Style().GetWritingMode())) { + scoped_refptr<NGLayoutResult> layout_result = Layout(*constraint_space); + DCHECK_EQ(layout_result->Status(), NGLayoutResult::kSuccess); + NGBoxFragment fragment( + container_writing_mode, + ToNGPhysicalBoxFragment(*layout_result->PhysicalFragment())); + sizes.min_size = sizes.max_size = fragment.Size().inline_size; + return sizes; + } + + // TODO(layout-ng): We need to make sure to use the right algorithm NGBlockLayoutAlgorithm minmax_algorithm(*this, *constraint_space); - Optional<MinMaxSize> maybe_sizes = minmax_algorithm.ComputeMinMaxSize(input); + base::Optional<MinMaxSize> maybe_sizes = + minmax_algorithm.ComputeMinMaxSize(input); if (maybe_sizes.has_value()) return *maybe_sizes; // Have to synthesize this value. scoped_refptr<NGLayoutResult> layout_result = Layout(*zero_constraint_space); NGBoxFragment min_fragment( - Style().GetWritingMode(), + container_writing_mode, ToNGPhysicalBoxFragment(*layout_result->PhysicalFragment())); sizes.min_size = min_fragment.Size().inline_size; @@ -249,11 +298,12 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( .SetTextDirection(Style().Direction()) .SetAvailableSize({LayoutUnit::Max(), LayoutUnit()}) .SetPercentageResolutionSize({LayoutUnit(), LayoutUnit()}) + .SetIsIntermediateLayout(true) .ToConstraintSpace(Style().GetWritingMode()); layout_result = Layout(*infinite_constraint_space); NGBoxFragment max_fragment( - Style().GetWritingMode(), + container_writing_mode, ToNGPhysicalBoxFragment(*layout_result->PhysicalFragment())); sizes.max_size = max_fragment.Size().inline_size; return sizes; @@ -261,18 +311,17 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( NGBoxStrut NGBlockNode::GetScrollbarSizes() const { NGPhysicalBoxStrut sizes; - const ComputedStyle* style = GetLayoutObject()->Style(); - if (!style->IsOverflowVisible()) { - const LayoutBox* box = ToLayoutBox(GetLayoutObject()); - LayoutUnit vertical = LayoutUnit(box->VerticalScrollbarWidth()); - LayoutUnit horizontal = LayoutUnit(box->HorizontalScrollbarHeight()); + const ComputedStyle& style = box_->StyleRef(); + if (!style.IsOverflowVisible()) { + LayoutUnit vertical = LayoutUnit(box_->VerticalScrollbarWidth()); + LayoutUnit horizontal = LayoutUnit(box_->HorizontalScrollbarHeight()); sizes.bottom = horizontal; - if (box->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) + if (box_->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) sizes.left = vertical; else sizes.right = vertical; } - return sizes.ConvertToLogical(style->GetWritingMode(), style->Direction()); + return sizes.ConvertToLogical(style.GetWritingMode(), style.Direction()); } NGLayoutInputNode NGBlockNode::NextSibling() const { @@ -285,21 +334,23 @@ NGLayoutInputNode NGBlockNode::NextSibling() const { } NGLayoutInputNode NGBlockNode::FirstChild() const { - auto* block = ToLayoutBlockFlow(box_); + auto* block = ToLayoutBlock(box_); auto* child = GetLayoutObjectForFirstChildNode(block); if (!child) return nullptr; if (AreNGBlockFlowChildrenInline(block)) - return NGInlineNode(block); + return NGInlineNode(ToLayoutBlockFlow(block)); return NGBlockNode(ToLayoutBox(child)); } bool NGBlockNode::CanUseNewLayout(const LayoutBox& box) { DCHECK(RuntimeEnabledFeatures::LayoutNGEnabled()); + if (box.StyleRef().ForceLegacyLayout()) + return false; // When the style has |ForceLegacyLayout|, it's usually not LayoutNGMixin, // but anonymous block can be. - return box.IsLayoutNGMixin() && !box.StyleRef().ForceLegacyLayout(); + return box.IsLayoutNGMixin() || box.IsLayoutNGFlexibleBox(); } bool NGBlockNode::CanUseNewLayout() const { @@ -315,6 +366,9 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( const NGConstraintSpace& constraint_space, const NGLayoutResult& layout_result) { DCHECK(layout_result.PhysicalFragment()); + if (constraint_space.IsIntermediateLayout()) + return; + const NGPhysicalBoxFragment& physical_fragment = ToNGPhysicalBoxFragment(*layout_result.PhysicalFragment()); @@ -338,9 +392,12 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( } logical_height += fragment_logical_size.block_size; intrinsic_content_logical_height += layout_result.IntrinsicBlockSize(); - NGBoxStrut border_scrollbar_padding = - ComputeBorders(constraint_space, Style()) + - ComputePadding(constraint_space, Style()) + GetScrollbarSizes(); + + NGBoxStrut borders = ComputeBorders(constraint_space, Style()); + NGBoxStrut scrollbars = GetScrollbarSizes(); + NGBoxStrut padding = ComputePadding(constraint_space, Style()); + NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding; + if (IsLastFragment(physical_fragment)) intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum(); box_->SetLogicalHeight(logical_height); @@ -382,14 +439,17 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( } // |ComputeOverflow()| below calls |AddOverflowFromChildren()|, which - // computes visual overflow from |RootInlineBox| if |ChildrenInline()|. - block->ComputeOverflow(intrinsic_block_size - - border_scrollbar_padding.block_end); + // computes visual overflow from |RootInlineBox| if |ChildrenInline()| + block->ComputeOverflow(intrinsic_block_size - borders.block_end - + scrollbars.BlockSum()); } box_->UpdateAfterLayout(); box_->ClearNeedsLayout(); + UpdateShapeOutsideInfoIfNeeded( + constraint_space.PercentageResolutionSize().inline_size); + if (box_->IsLayoutBlockFlow()) { LayoutBlockFlow* block_flow = ToLayoutBlockFlow(box_); block_flow->UpdateIsSelfCollapsing(); @@ -463,7 +523,9 @@ void NGBlockNode::CopyChildFragmentPosition( // LegacyLayout flips vertical-rl horizontal coordinates before paint. // NGLayout flips X location for LegacyLayout compatibility. if (containing_block->StyleRef().IsFlippedBlocksWritingMode()) { - LayoutUnit container_width = containing_block->Size().Width(); + NGBoxStrut scrollbars = GetScrollbarSizes(); + LayoutUnit container_width = + containing_block->Size().Width() - scrollbars.block_start; layout_box->SetX(container_width - fragment.Offset().left - additional_offset.left - fragment.Size().width); } else { @@ -487,6 +549,7 @@ void NGBlockNode::CopyChildFragmentPosition( scoped_refptr<NGLayoutResult> NGBlockNode::LayoutAtomicInline( const NGConstraintSpace& parent_constraint_space, + FontBaseline baseline_type, bool use_first_line_style) { NGConstraintSpaceBuilder space_builder(parent_constraint_space); space_builder.SetUseFirstLineStyle(use_first_line_style); @@ -495,10 +558,7 @@ scoped_refptr<NGLayoutResult> NGBlockNode::LayoutAtomicInline( // would synthesize box-baseline. if (NGBaseline::ShouldPropagateBaselines(ToLayoutBox(GetLayoutObject()))) { space_builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kAtomicInline, - IsHorizontalWritingMode(parent_constraint_space.GetWritingMode()) - ? FontBaseline::kAlphabeticBaseline - : FontBaseline::kIdeographicBaseline}); + {NGBaselineAlgorithmType::kAtomicInline, baseline_type}); } const ComputedStyle& style = Style(); @@ -541,18 +601,11 @@ scoped_refptr<NGLayoutResult> NGBlockNode::RunOldLayout( box_->SetOverrideContainingBlockContentLogicalHeight(inline_size); } - // TODO(layout-ng): Does this handle scrollbars correctly? if (constraint_space.IsFixedSizeInline()) { - box_->SetOverrideLogicalContentWidth( - (constraint_space.AvailableSize().inline_size - - box_->BorderAndPaddingLogicalWidth()) - .ClampNegativeToZero()); + box_->SetOverrideLogicalWidth(constraint_space.AvailableSize().inline_size); } if (constraint_space.IsFixedSizeBlock()) { - box_->SetOverrideLogicalContentHeight( - (constraint_space.AvailableSize().block_size - - box_->BorderAndPaddingLogicalHeight()) - .ClampNegativeToZero()); + box_->SetOverrideLogicalHeight(constraint_space.AvailableSize().block_size); } if (box_->IsLayoutNGMixin() && box_->NeedsLayout()) { @@ -577,6 +630,8 @@ scoped_refptr<NGLayoutResult> NGBlockNode::RunOldLayout( std::make_unique<NGExclusionSpace>(constraint_space.ExclusionSpace())); CopyBaselinesFromOldLayout(constraint_space, &builder); + UpdateShapeOutsideInfoIfNeeded( + constraint_space.PercentageResolutionSize().inline_size); return builder.ToBoxFragment(); } @@ -588,44 +643,74 @@ void NGBlockNode::CopyBaselinesFromOldLayout( if (requests.IsEmpty()) return; + if (UNLIKELY(constraint_space.GetWritingMode() != Style().GetWritingMode())) + return; + for (const auto& request : requests) { switch (request.algorithm_type) { - case NGBaselineAlgorithmType::kAtomicInline: - AddAtomicInlineBaselineFromOldLayout( - request, constraint_space.UseFirstLineStyle(), builder); + case NGBaselineAlgorithmType::kAtomicInline: { + LayoutUnit position = + AtomicInlineBaselineFromOldLayout(request, constraint_space); + if (position != -1) + builder->AddBaseline(request, position); break; + } case NGBaselineAlgorithmType::kFirstLine: { LayoutUnit position = box_->FirstLineBoxBaseline(); - if (position != -1) { + if (position != -1) builder->AddBaseline(request, position); - } break; } } } } -void NGBlockNode::AddAtomicInlineBaselineFromOldLayout( +LayoutUnit NGBlockNode::AtomicInlineBaselineFromOldLayout( const NGBaselineRequest& request, - bool is_first_line, - NGFragmentBuilder* builder) { - // Block-level boxes do not have atomic inline baseline. - // This includes form controls when 'display:block' is applied. - if (box_->IsLayoutBlock() && !box_->IsInline()) - return; + const NGConstraintSpace& constraint_space) { + LineDirectionMode line_direction = box_->IsHorizontalWritingMode() + ? LineDirectionMode::kHorizontalLine + : LineDirectionMode::kVerticalLine; + + // If this is an inline box, use |BaselinePosition()|. Some LayoutObject + // classes override it assuming inline layout calls |BaselinePosition()|. + if (box_->IsInline()) { + LayoutUnit position = LayoutUnit(box_->BaselinePosition( + request.baseline_type, constraint_space.UseFirstLineStyle(), + line_direction, kPositionOnContainingLine)); + + // BaselinePosition() uses margin edge for atomic inlines. Subtract + // margin-over so that the position is relative to the border box. + if (box_->IsAtomicInlineLevel()) + position -= box_->MarginOver(); + + return position; + } - LineDirectionMode line_direction = - IsHorizontalWritingMode(builder->GetWritingMode()) - ? LineDirectionMode::kHorizontalLine - : LineDirectionMode::kVerticalLine; - LayoutUnit position = LayoutUnit(box_->BaselinePosition( - request.baseline_type, is_first_line, line_direction)); + // If this is a block box, use |InlineBlockBaseline()|. When an inline block + // has block children, their inline block baselines need to be propagated. + return box_->InlineBlockBaseline(line_direction); +} - // BaselinePosition() uses margin edge for atomic inlines. - if (box_->IsAtomicInlineLevel()) - position -= box_->MarginOver(); +// Floats can optionally have a shape area, specifed by "shape-outside". The +// current shape machinery requires setting the size of the float after layout +// in the parents writing mode. +void NGBlockNode::UpdateShapeOutsideInfoIfNeeded( + LayoutUnit percentage_resolution_inline_size) { + if (!box_->IsFloating() || !box_->GetShapeOutsideInfo()) + return; - builder->AddBaseline(request, position); + // TODO(ikilpatrick): Ideally this should be moved to a NGLayoutResult + // computing the shape area. There may be an issue with the new fragmentation + // model and computing the correct sizes of shapes. + ShapeOutsideInfo* shape_outside = box_->GetShapeOutsideInfo(); + LayoutBlock* containing_block = box_->ContainingBlock(); + shape_outside->SetReferenceBoxLogicalSize( + containing_block->IsHorizontalWritingMode() + ? box_->Size() + : box_->Size().TransposedSize()); + shape_outside->SetPercentageResolutionInlineSize( + percentage_resolution_inline_size); } void NGBlockNode::UseOldOutOfFlowPositioning() { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h index 05f50c3cbfa..7120a177c1b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" +#include "third_party/blink/renderer/platform/fonts/font_baseline.h" namespace blink { @@ -46,7 +47,8 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { // min/max calculation (e.g. the node that will undergo shrink-to-fit). This // constraint space will not be passed on to children. If no constraint space // is specified, a zero-sized one will be used. - MinMaxSize ComputeMinMaxSize(const MinMaxSizeInput&, + MinMaxSize ComputeMinMaxSize(WritingMode container_writing_mode, + const MinMaxSizeInput&, const NGConstraintSpace* = nullptr); NGBoxStrut GetScrollbarSizes() const; @@ -55,6 +57,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { // Layout an atomic inline; e.g., inline block. scoped_refptr<NGLayoutResult> LayoutAtomicInline(const NGConstraintSpace&, + FontBaseline, bool use_first_line_style); // Runs layout on the underlying LayoutObject and creates a fragment for the @@ -89,9 +92,11 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { const NGPhysicalOffset& additional_offset = NGPhysicalOffset()); void CopyBaselinesFromOldLayout(const NGConstraintSpace&, NGFragmentBuilder*); - void AddAtomicInlineBaselineFromOldLayout(const NGBaselineRequest&, - bool, - NGFragmentBuilder*); + LayoutUnit AtomicInlineBaselineFromOldLayout(const NGBaselineRequest&, + const NGConstraintSpace&); + + void UpdateShapeOutsideInfoIfNeeded( + LayoutUnit percentage_resolution_inline_size); }; DEFINE_TYPE_CASTS(NGBlockNode, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc index 95c934ddc10..202a5744538 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc @@ -159,7 +159,8 @@ TEST_F(NGBlockNodeForTest, MinAndMaxContent) { const int kWidth = 30; NGBlockNode box(ToLayoutBox(GetLayoutObjectByElementId("box"))); - MinMaxSize sizes = box.ComputeMinMaxSize(MinMaxSizeInput()); + MinMaxSize sizes = + box.ComputeMinMaxSize(WritingMode::kHorizontalTb, MinMaxSizeInput()); EXPECT_EQ(LayoutUnit(kWidth), sizes.min_size); EXPECT_EQ(LayoutUnit(kWidth), sizes.max_size); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc index 3ee4744c3ce..b5a5bf1bb27 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc @@ -6,7 +6,6 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -14,29 +13,36 @@ namespace blink { NGLineHeightMetrics NGBoxFragment::BaselineMetricsWithoutSynthesize( - const NGBaselineRequest& request, - const NGConstraintSpace& constraint_space) const { + const NGBaselineRequest& request) const { + // For "leaf" theme objects, let the theme decide what the baseline position + // is. The theme baseline wins over the propagated baselines. const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_); - bool is_parallel_writing_mode = - IsParallelWritingMode(constraint_space.GetWritingMode(), - physical_fragment.Style().GetWritingMode()); - if (is_parallel_writing_mode) { - // Find the baseline from the computed results. - if (const NGBaseline* baseline = physical_fragment.Baseline(request)) { - LayoutUnit ascent = baseline->offset; - LayoutUnit descent = BlockSize() - ascent; + DCHECK(physical_fragment_.GetLayoutObject()); + const LayoutBox& layout_box = + ToLayoutBox(*physical_fragment_.GetLayoutObject()); + const ComputedStyle& style = physical_fragment.Style(); + if (style.HasAppearance() && + !LayoutTheme::GetTheme().IsControlContainer(style.Appearance())) { + return NGLineHeightMetrics( + BlockSize() + layout_box.MarginOver() + + LayoutTheme::GetTheme().BaselinePositionAdjustment(style), + layout_box.MarginUnder()); + } - // For replaced elements, inline-block elements, and inline-table - // elements, the height is the height of their margin box. - // https://drafts.csswg.org/css2/visudet.html#line-height - LayoutBox* layout_box = ToLayoutBox(physical_fragment_.GetLayoutObject()); - if (layout_box->IsAtomicInlineLevel()) { - ascent += layout_box->MarginOver(); - descent += layout_box->MarginUnder(); - } + // Check if we have a propagated baseline. + if (const NGBaseline* baseline = physical_fragment.Baseline(request)) { + LayoutUnit ascent = baseline->offset; + LayoutUnit descent = BlockSize() - ascent; - return NGLineHeightMetrics(ascent, descent); + // For replaced elements, inline-block elements, and inline-table + // elements, the height is the height of their margin box. + // https://drafts.csswg.org/css2/visudet.html#line-height + if (layout_box.IsAtomicInlineLevel()) { + ascent += layout_box.MarginOver(); + descent += layout_box.MarginUnder(); } + + return NGLineHeightMetrics(ascent, descent); } return NGLineHeightMetrics(); @@ -45,36 +51,31 @@ NGLineHeightMetrics NGBoxFragment::BaselineMetricsWithoutSynthesize( NGLineHeightMetrics NGBoxFragment::BaselineMetrics( const NGBaselineRequest& request, const NGConstraintSpace& constraint_space) const { - NGLineHeightMetrics metrics = - BaselineMetricsWithoutSynthesize(request, constraint_space); - if (!metrics.IsEmpty()) - return metrics; + // Try to compute the baseline if the writing-modes are the same. + if (constraint_space.GetWritingMode() == GetWritingMode()) { + NGLineHeightMetrics metrics = BaselineMetricsWithoutSynthesize(request); + if (!metrics.IsEmpty()) + return metrics; + } // The baseline type was not found. This is either this box should synthesize // box-baseline without propagating from children, or caller forgot to add // baseline requests to constraint space when it called Layout(). LayoutUnit block_size = BlockSize(); - const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_); - const ComputedStyle& style = physical_fragment.Style(); - LayoutBox* layout_box = ToLayoutBox(physical_fragment_.GetLayoutObject()); - if (style.HasAppearance() && - !LayoutTheme::GetTheme().IsControlContainer(style.Appearance())) { - return NGLineHeightMetrics( - block_size + layout_box->MarginOver() + - LayoutTheme::GetTheme().BaselinePositionAdjustment(style), - layout_box->MarginUnder()); - } - // If atomic inline, use the margin box. See above. - if (layout_box->IsAtomicInlineLevel()) { + const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_); + DCHECK(physical_fragment_.GetLayoutObject()); + const LayoutBox& layout_box = + ToLayoutBox(*physical_fragment_.GetLayoutObject()); + if (layout_box.IsAtomicInlineLevel()) { bool is_parallel_writing_mode = IsParallelWritingMode(constraint_space.GetWritingMode(), physical_fragment.Style().GetWritingMode()); if (is_parallel_writing_mode) - block_size += layout_box->MarginLogicalHeight(); + block_size += layout_box.MarginLogicalHeight(); else - block_size += layout_box->MarginLogicalWidth(); + block_size += layout_box.MarginLogicalWidth(); } if (request.baseline_type == kAlphabeticBaseline) diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h index 3f3b3dbe9b4..93c08fc9c10 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h @@ -12,7 +12,6 @@ namespace blink { -class NGPhysicalBoxFragment; struct NGBaselineRequest; struct NGLineHeightMetrics; @@ -31,8 +30,7 @@ class CORE_EXPORT NGBoxFragment final : public NGFragment { // not have any baselines, while the other version synthesize the baseline // from the box. NGLineHeightMetrics BaselineMetricsWithoutSynthesize( - const NGBaselineRequest&, - const NGConstraintSpace&) const; + const NGBaselineRequest&) const; NGLineHeightMetrics BaselineMetrics(const NGBaselineRequest&, const NGConstraintSpace&) const; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc index 4c260973cf0..2f25a62eb2b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h" #include <algorithm> +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" @@ -12,6 +13,7 @@ #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" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { @@ -44,26 +46,20 @@ LayoutUnit ConstrainColumnBlockSize(LayoutUnit size, LayoutUnit extra = border_scrollbar_padding.BlockSum(); size += extra; - Optional<LayoutUnit> max_length; const ComputedStyle& style = node.Style(); - Length logical_max_height = style.LogicalMaxHeight(); - if (!logical_max_height.IsMaxSizeNone()) { - max_length = ResolveBlockLength(space, style, logical_max_height, size, - LengthResolveType::kMaxSize); - } + LayoutUnit max = ResolveBlockLength(space, style, style.LogicalMaxHeight(), + size, LengthResolveType::kMaxSize, + LengthResolvePhase::kLayout); LayoutUnit extent = ResolveBlockLength(space, style, style.LogicalHeight(), - size, LengthResolveType::kContentSize); + size, LengthResolveType::kContentSize, + LengthResolvePhase::kLayout); if (extent != NGSizeIndefinite) { // A specified height/width will just constrain the maximum length. - if (max_length.has_value()) - max_length = std::min(max_length.value(), extent); - else - max_length = extent; + max = std::min(max, extent); } // Constrain and convert the value back to content-box. - if (max_length.has_value()) - size = std::min(size, max_length.value()); + size = std::min(size, max); return size - extra; } @@ -75,7 +71,7 @@ NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(NGBlockNode node, : NGLayoutAlgorithm(node, space, ToNGBlockBreakToken(break_token)) {} scoped_refptr<NGLayoutResult> NGColumnLayoutAlgorithm::Layout() { - Optional<MinMaxSize> min_max_size; + base::Optional<MinMaxSize> min_max_size; if (NeedMinMaxSize(ConstraintSpace(), Style())) min_max_size = ComputeMinMaxSize(MinMaxSizeInput()); NGBoxStrut border_scrollbar_padding = @@ -193,11 +189,11 @@ scoped_refptr<NGLayoutResult> NGColumnLayoutAlgorithm::Layout() { return container_builder_.ToBoxFragment(); } -Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize( +base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize( const MinMaxSizeInput& input) const { // First calculate the min/max sizes of columns. NGBlockLayoutAlgorithm algorithm(Node(), ConstraintSpace()); - Optional<MinMaxSize> min_max_sizes = algorithm.ComputeMinMaxSize(input); + base::Optional<MinMaxSize> min_max_sizes = algorithm.ComputeMinMaxSize(input); DCHECK(min_max_sizes.has_value()); MinMaxSize sizes = min_max_sizes.value(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h index 1c07e5e99ef..19262a4bc7d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h @@ -5,7 +5,6 @@ #ifndef NGColumnLayoutAlgorithm_h #define NGColumnLayoutAlgorithm_h -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" @@ -15,6 +14,7 @@ class NGBlockNode; class NGBlockBreakToken; class NGBreakToken; class NGConstraintSpace; +struct NGLogicalSize; class CORE_EXPORT NGColumnLayoutAlgorithm : public NGLayoutAlgorithm<NGBlockNode, @@ -27,7 +27,8 @@ class CORE_EXPORT NGColumnLayoutAlgorithm scoped_refptr<NGLayoutResult> Layout() override; - Optional<MinMaxSize> ComputeMinMaxSize(const MinMaxSizeInput&) const override; + base::Optional<MinMaxSize> ComputeMinMaxSize( + const MinMaxSizeInput&) const override; private: NGLogicalSize CalculateColumnSize(const NGLogicalSize& content_box_size); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc index 99bdc8955f9..5d5f59ec473 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc @@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" namespace blink { namespace { @@ -1971,13 +1972,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, MinMax) { ASSERT_TRUE(layout_object); ASSERT_TRUE(layout_object->IsBox()); NGBlockNode node = NGBlockNode(ToLayoutBox(layout_object)); - ComputedStyle* style = layout_object->MutableStyle(); + scoped_refptr<ComputedStyle> style = + ComputedStyle::Clone(layout_object->StyleRef()); + layout_object->SetStyle(style); scoped_refptr<NGConstraintSpace> space = ConstructBlockLayoutTestConstraintSpace( WritingMode::kHorizontalTb, TextDirection::kLtr, NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite)); NGColumnLayoutAlgorithm algorithm(node, *space.get()); - Optional<MinMaxSize> size; + base::Optional<MinMaxSize> size; MinMaxSizeInput zero_input; // Both column-count and column-width set. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc index 6448236c189..e6c27f43071 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc @@ -8,25 +8,13 @@ #include <memory> #include "third_party/blink/renderer/core/layout/layout_block.h" +#include "third_party/blink/renderer/core/layout/layout_flexible_box.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" namespace blink { -namespace { - -bool ShouldComputeBaseline(const LayoutBox& box) { - if (box.IsLayoutBlock() && - ToLayoutBlock(box).UseLogicalBottomMarginEdgeForInlineBlockBaseline()) - return false; - if (box.IsWritingModeRoot()) - return false; - return true; -} - -} // namespace - NGConstraintSpace::NGConstraintSpace( WritingMode writing_mode, bool is_orthogonal_writing_mode_root, @@ -39,20 +27,21 @@ NGConstraintSpace::NGConstraintSpace( LayoutUnit fragmentainer_space_at_bfc_start, bool is_fixed_size_inline, bool is_fixed_size_block, + bool fixed_size_block_is_definite, bool is_shrink_to_fit, - bool is_inline_direction_triggers_scrollbar, - bool is_block_direction_triggers_scrollbar, + bool is_intermediate_layout, NGFragmentationType block_direction_fragmentation_type, bool separate_leading_fragmentainer_margins, bool is_new_fc, bool is_anonymous, bool use_first_line_style, + bool should_force_clearance, + NGFloatTypes adjoining_floats, const NGMarginStrut& margin_strut, const NGBfcOffset& bfc_offset, - const WTF::Optional<NGBfcOffset>& floats_bfc_offset, + const base::Optional<NGBfcOffset>& floats_bfc_offset, const NGExclusionSpace& exclusion_space, - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats, - const WTF::Optional<LayoutUnit>& clearance_offset, + LayoutUnit clearance_offset, Vector<NGBaselineRequest>& baseline_requests) : available_size_(available_size), percentage_resolution_size_(percentage_resolution_size), @@ -63,17 +52,17 @@ NGConstraintSpace::NGConstraintSpace( fragmentainer_space_at_bfc_start_(fragmentainer_space_at_bfc_start), is_fixed_size_inline_(is_fixed_size_inline), is_fixed_size_block_(is_fixed_size_block), + fixed_size_block_is_definite_(fixed_size_block_is_definite), is_shrink_to_fit_(is_shrink_to_fit), - is_inline_direction_triggers_scrollbar_( - is_inline_direction_triggers_scrollbar), - is_block_direction_triggers_scrollbar_( - is_block_direction_triggers_scrollbar), + is_intermediate_layout_(is_intermediate_layout), block_direction_fragmentation_type_(block_direction_fragmentation_type), separate_leading_fragmentainer_margins_( separate_leading_fragmentainer_margins), is_new_fc_(is_new_fc), is_anonymous_(is_anonymous), use_first_line_style_(use_first_line_style), + should_force_clearance_(should_force_clearance), + adjoining_floats_(adjoining_floats), writing_mode_(static_cast<unsigned>(writing_mode)), is_orthogonal_writing_mode_root_(is_orthogonal_writing_mode_root), direction_(static_cast<unsigned>(direction)), @@ -82,7 +71,6 @@ NGConstraintSpace::NGConstraintSpace( floats_bfc_offset_(floats_bfc_offset), exclusion_space_(std::make_unique<NGExclusionSpace>(exclusion_space)), clearance_offset_(clearance_offset) { - unpositioned_floats_.swap(unpositioned_floats); baseline_requests_.swap(baseline_requests); } @@ -92,42 +80,62 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpace::CreateFromLayoutObject( bool parallel_containing_block = IsParallelWritingMode( box.ContainingBlock()->StyleRef().GetWritingMode(), writing_mode); bool fixed_inline = false, fixed_block = false; + bool fixed_block_is_definite = true; LayoutUnit available_logical_width; - if (parallel_containing_block) - available_logical_width = box.ContainingBlockLogicalWidthForContent(); - else - available_logical_width = box.PerpendicularContainingBlockLogicalHeight(); + if (parallel_containing_block && + box.HasOverrideContainingBlockContentLogicalWidth()) { + // Grid layout sets OverrideContainingBlockContentLogicalWidth|Height + available_logical_width = box.OverrideContainingBlockContentLogicalWidth(); + } else if (!parallel_containing_block && + box.HasOverrideContainingBlockContentLogicalHeight()) { + available_logical_width = box.OverrideContainingBlockContentLogicalHeight(); + } else { + if (parallel_containing_block) + available_logical_width = box.ContainingBlockLogicalWidthForContent(); + else + available_logical_width = box.PerpendicularContainingBlockLogicalHeight(); + } available_logical_width = std::max(LayoutUnit(), available_logical_width); LayoutUnit available_logical_height; - if (!box.Parent()) { - available_logical_height = box.View()->ViewLogicalHeightForPercentages(); - } else if (box.ContainingBlock()) { - if (parallel_containing_block) { - available_logical_height = - box.ContainingBlock() - ->AvailableLogicalHeightForPercentageComputation(); - } else { - available_logical_height = box.ContainingBlockLogicalWidthForContent(); + if (parallel_containing_block && + box.HasOverrideContainingBlockContentLogicalHeight()) { + // Grid layout sets OverrideContainingBlockContentLogicalWidth|Height + available_logical_height = + box.OverrideContainingBlockContentLogicalHeight(); + } else if (!parallel_containing_block && + box.HasOverrideContainingBlockContentLogicalWidth()) { + available_logical_height = box.OverrideContainingBlockContentLogicalWidth(); + } else { + if (!box.Parent()) { + available_logical_height = box.View()->ViewLogicalHeightForPercentages(); + } else if (box.ContainingBlock()) { + if (parallel_containing_block) { + available_logical_height = + box.ContainingBlock() + ->AvailableLogicalHeightForPercentageComputation(); + } else { + available_logical_height = box.ContainingBlockLogicalWidthForContent(); + } } } NGLogicalSize percentage_size = {available_logical_width, available_logical_height}; NGLogicalSize available_size = percentage_size; - // When we have an override size, the available_logical_{width,height} will be - // used as the final size of the box, so it has to include border and - // padding. - if (box.HasOverrideLogicalContentWidth()) { - available_size.inline_size = - box.BorderAndPaddingLogicalWidth() + box.OverrideLogicalContentWidth(); + if (box.HasOverrideLogicalWidth()) { + available_size.inline_size = box.OverrideLogicalWidth(); fixed_inline = true; } - if (box.HasOverrideLogicalContentHeight()) { - available_size.block_size = box.BorderAndPaddingLogicalHeight() + - box.OverrideLogicalContentHeight(); + if (box.HasOverrideLogicalHeight()) { + available_size.block_size = box.OverrideLogicalHeight(); fixed_block = true; } + if (box.IsFlexItem() && fixed_block) { + fixed_block_is_definite = + ToLayoutFlexibleBox(box.Parent()) + ->UseOverrideLogicalHeightForPerentageResolution(box); + } bool is_new_fc = true; // TODO(ikilpatrick): This DCHECK needs to be enabled once we've switched @@ -149,26 +157,25 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpace::CreateFromLayoutObject( NGConstraintSpaceBuilder builder(writing_mode, initial_containing_block_size); - if (ShouldComputeBaseline(box)) { - FontBaseline baseline_type = IsHorizontalWritingMode(writing_mode) - ? kAlphabeticBaseline - : kIdeographicBaseline; + if (!box.IsWritingModeRoot()) { // Add all types because we don't know which baselines will be requested. - builder - .AddBaselineRequest( - {NGBaselineAlgorithmType::kAtomicInline, baseline_type}) - .AddBaselineRequest( - {NGBaselineAlgorithmType::kFirstLine, baseline_type}); + FontBaseline baseline_type = box.StyleRef().GetFontBaseline(); + bool synthesize_inline_block_baseline = + box.IsLayoutBlock() && + ToLayoutBlock(box).UseLogicalBottomMarginEdgeForInlineBlockBaseline(); + if (!synthesize_inline_block_baseline) { + builder.AddBaselineRequest( + {NGBaselineAlgorithmType::kAtomicInline, baseline_type}); + } + builder.AddBaselineRequest( + {NGBaselineAlgorithmType::kFirstLine, baseline_type}); } return builder.SetAvailableSize(available_size) .SetPercentageResolutionSize(percentage_size) - .SetIsInlineDirectionTriggersScrollbar( - box.StyleRef().OverflowInlineDirection() == EOverflow::kAuto) - .SetIsBlockDirectionTriggersScrollbar( - box.StyleRef().OverflowBlockDirection() == EOverflow::kAuto) .SetIsFixedSizeInline(fixed_inline) .SetIsFixedSizeBlock(fixed_block) + .SetFixedSizeBlockIsDefinite(fixed_block_is_definite) .SetIsShrinkToFit( box.SizesLogicalWidthToFitContent(box.StyleRef().LogicalWidth())) .SetIsNewFormattingContext(is_new_fc) @@ -199,12 +206,6 @@ NGFragmentationType NGConstraintSpace::BlockFragmentationType() const { } bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const { - // TODO(cbiesinger): For simplicity and performance, for now, we only - // consider two constraint spaces equal if neither one has unpositioned - // floats. We should consider changing this in the future. - if (unpositioned_floats_.size() || other.unpositioned_floats_.size()) - return false; - if (exclusion_space_ && other.exclusion_space_ && *exclusion_space_ != *other.exclusion_space_) return false; @@ -221,16 +222,15 @@ bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const { is_fixed_size_inline_ == other.is_fixed_size_inline_ && is_fixed_size_block_ == other.is_fixed_size_block_ && is_shrink_to_fit_ == other.is_shrink_to_fit_ && - is_inline_direction_triggers_scrollbar_ == - other.is_inline_direction_triggers_scrollbar_ && - is_block_direction_triggers_scrollbar_ == - other.is_block_direction_triggers_scrollbar_ && + is_intermediate_layout_ == other.is_intermediate_layout_ && block_direction_fragmentation_type_ == other.block_direction_fragmentation_type_ && is_new_fc_ == other.is_new_fc_ && separate_leading_fragmentainer_margins_ == other.separate_leading_fragmentainer_margins_ && is_anonymous_ == other.is_anonymous_ && + should_force_clearance_ == other.should_force_clearance_ && + adjoining_floats_ == other.adjoining_floats_ && writing_mode_ == other.writing_mode_ && direction_ == other.direction_ && margin_strut_ == other.margin_strut_ && @@ -245,15 +245,14 @@ bool NGConstraintSpace::operator!=(const NGConstraintSpace& other) const { } String NGConstraintSpace::ToString() const { - return String::Format( - "Offset: %s,%s Size: %sx%s Clearance: %s", - bfc_offset_.line_offset.ToString().Ascii().data(), - bfc_offset_.block_offset.ToString().Ascii().data(), - AvailableSize().inline_size.ToString().Ascii().data(), - AvailableSize().block_size.ToString().Ascii().data(), - clearance_offset_.has_value() - ? clearance_offset_.value().ToString().Ascii().data() - : "none"); + return String::Format("Offset: %s,%s Size: %sx%s Clearance: %s", + bfc_offset_.line_offset.ToString().Ascii().data(), + bfc_offset_.block_offset.ToString().Ascii().data(), + AvailableSize().inline_size.ToString().Ascii().data(), + AvailableSize().block_size.ToString().Ascii().data(), + HasClearanceOffset() + ? ClearanceOffset().ToString().Ascii().data() + : "none"); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index f3bd0b7c848..2fc95583958 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h @@ -5,6 +5,7 @@ #ifndef NGConstraintSpace_h #define NGConstraintSpace_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h" @@ -12,10 +13,9 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" -#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -116,18 +116,6 @@ class CORE_EXPORT NGConstraintSpace final // Also note this is true only when the document has ':first-line' rules. bool UseFirstLineStyle() const { return use_first_line_style_; } - // Whether exceeding the AvailableSize() triggers the presence of a scrollbar - // for the indicated direction. - // If exceeded the current layout should be aborted and invoked again with a - // constraint space modified to reserve space for a scrollbar. - bool IsInlineDirectionTriggersScrollbar() const { - return is_inline_direction_triggers_scrollbar_; - } - - bool IsBlockDirectionTriggersScrollbar() const { - return is_block_direction_triggers_scrollbar_; - } - // Some layout modes “stretch” their children to a fixed size (e.g. flex, // grid). These flags represented whether a layout needs to produce a // fragment that satisfies a fixed constraint in the inline and block @@ -139,10 +127,20 @@ class CORE_EXPORT NGConstraintSpace final bool IsFixedSizeBlock() const { return is_fixed_size_block_; } + // Whether a fixed block size should be considered definite. + bool FixedSizeBlockIsDefinite() const { + return fixed_size_block_is_definite_; + } + // Whether an auto inline-size should be interpreted as shrink-to-fit // (ie. fit-content). This is used for inline-block, floats, etc. bool IsShrinkToFit() const { return is_shrink_to_fit_; } + // Whether this constraint space is used for an intermediate layout in a + // multi-pass layout. In such a case, we should not copy back the resulting + // layout data to the legacy tree or create a paint fragment from it. + bool IsIntermediateLayout() const { return is_intermediate_layout_; } + // If specified a layout should produce a Fragment which fragments at the // blockSize if possible. NGFragmentationType BlockFragmentationType() const; @@ -170,28 +168,62 @@ class CORE_EXPORT NGConstraintSpace final // - block_start border or padding in the current layout. // - Text content, atomic inlines, (see NGLineBreaker). // - The current layout having a block_size. + // - Clearance before a child. NGBfcOffset BfcOffset() const { return bfc_offset_; } // If present, and the current layout hasn't resolved its BFC offset yet (see // BfcOffset), the layout should position all of its unpositioned floats at - // this offset. + // this offset. This value is the BFC offset that we calculated in the + // previous pass, a pass which aborted once the BFC offset got resolved, + // because we had walked past content (i.e. floats) that depended on it being + // resolved. // // This value should be propogated to child layouts if the current layout // hasn't resolved its BFC offset yet. // - // This value is calculated *after* an initial pass of the tree, this value - // should only be present during the second pass. - WTF::Optional<NGBfcOffset> FloatsBfcOffset() const { + // This value is calculated *after* an initial pass of the tree, and should + // only be present during subsequent passes. + base::Optional<NGBfcOffset> FloatsBfcOffset() const { return floats_bfc_offset_; } - const Vector<scoped_refptr<NGUnpositionedFloat>>& UnpositionedFloats() const { - return unpositioned_floats_; + // Return the types (none, left, right, both) of preceding adjoining + // floats. These are floats that are added while the in-flow BFC offset is + // still unknown. The floats may or may not be unpositioned (pending). That + // depends on which layout pass we're in. They are typically positioned if + // FloatsBfcOffset() is known. Adjoining floats should be treated differently + // when calculating clearance on a block with adjoining block-start margin. + // (in such cases we will know up front that the block will need clearance, + // since, if it doesn't, the float will be pulled along with the block, and + // the block will fail to clear). + NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; } + + bool HasClearanceOffset() const { + return clearance_offset_ != LayoutUnit::Min(); } + LayoutUnit ClearanceOffset() const { return clearance_offset_; } - WTF::Optional<LayoutUnit> ClearanceOffset() const { - return clearance_offset_; - } + // Return true if the fragment needs to have clearance applied to it, + // regardless of its hypothetical position. The fragment will then go exactly + // below the relevant floats. This happens when a cleared child gets separated + // from floats that would otherwise be adjoining; example: + // + // <div id="container"> + // <div id="float" style="float:left; width:100px; height:100px;"></div> + // <div id="clearee" style="clear:left; margin-top:12345px;">text</div> + // </div> + // + // Clearance separates #clearee from #container, and #float is positioned at + // the block-start content edge of #container. Without clearance, margins + // would have been adjoining and the large margin on #clearee would have + // pulled both #container and #float along with it. No margin, no matter how + // large, would ever be able to pull #clearee below the float then. But we + // have clearance, the margins are separated, and in this case we know that we + // have clearance even before we have laid out (because of the adjoing + // float). So it would just be wrong to check for clearance when we position + // #clearee. Nothing can prevent clearance here. A large margin on the cleared + // child will be canceled out with negative clearance. + bool ShouldForceClearance() const { return should_force_clearance_; } const Vector<NGBaselineRequest>& BaselineRequests() const { return baseline_requests_; @@ -205,33 +237,33 @@ class CORE_EXPORT NGConstraintSpace final private: friend class NGConstraintSpaceBuilder; // Default constructor. - NGConstraintSpace( - WritingMode, - bool is_orthogonal_writing_mode_root, - TextDirection, - NGLogicalSize available_size, - NGLogicalSize percentage_resolution_size, - LayoutUnit parent_percentage_resolution_inline_size, - NGPhysicalSize initial_containing_block_size, - LayoutUnit fragmentainer_block_size, - LayoutUnit fragmentainer_space_at_bfc_start, - bool is_fixed_size_inline, - bool is_fixed_size_block, - bool is_shrink_to_fit, - bool is_inline_direction_triggers_scrollbar, - bool is_block_direction_triggers_scrollbar, - NGFragmentationType block_direction_fragmentation_type, - bool separate_leading_fragmentainer_margins_, - bool is_new_fc, - bool is_anonymous, - bool use_first_line_style, - const NGMarginStrut& margin_strut, - const NGBfcOffset& bfc_offset, - const WTF::Optional<NGBfcOffset>& floats_bfc_offset, - const NGExclusionSpace& exclusion_space, - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats, - const WTF::Optional<LayoutUnit>& clearance_offset, - Vector<NGBaselineRequest>& baseline_requests); + NGConstraintSpace(WritingMode, + bool is_orthogonal_writing_mode_root, + TextDirection, + NGLogicalSize available_size, + NGLogicalSize percentage_resolution_size, + LayoutUnit parent_percentage_resolution_inline_size, + NGPhysicalSize initial_containing_block_size, + LayoutUnit fragmentainer_block_size, + LayoutUnit fragmentainer_space_at_bfc_start, + bool is_fixed_size_inline, + bool is_fixed_size_block, + bool fixed_size_block_is_definite, + bool is_shrink_to_fit, + bool is_intermediate_layout, + NGFragmentationType block_direction_fragmentation_type, + bool separate_leading_fragmentainer_margins_, + bool is_new_fc, + bool is_anonymous, + bool use_first_line_style, + bool should_force_clearance, + NGFloatTypes adjoining_floats, + const NGMarginStrut& margin_strut, + const NGBfcOffset& bfc_offset, + const base::Optional<NGBfcOffset>& floats_bfc_offset, + const NGExclusionSpace& exclusion_space, + LayoutUnit clearance_offset, + Vector<NGBaselineRequest>& baseline_requests); NGLogicalSize available_size_; NGLogicalSize percentage_resolution_size_; @@ -243,11 +275,10 @@ class CORE_EXPORT NGConstraintSpace final unsigned is_fixed_size_inline_ : 1; unsigned is_fixed_size_block_ : 1; + unsigned fixed_size_block_is_definite_ : 1; unsigned is_shrink_to_fit_ : 1; - - unsigned is_inline_direction_triggers_scrollbar_ : 1; - unsigned is_block_direction_triggers_scrollbar_ : 1; + unsigned is_intermediate_layout_ : 1; unsigned block_direction_fragmentation_type_ : 2; unsigned separate_leading_fragmentainer_margins_ : 1; @@ -258,6 +289,8 @@ class CORE_EXPORT NGConstraintSpace final unsigned is_anonymous_ : 1; unsigned use_first_line_style_ : 1; + unsigned should_force_clearance_ : 1; + unsigned adjoining_floats_ : 2; // NGFloatTypes unsigned writing_mode_ : 3; unsigned is_orthogonal_writing_mode_root_ : 1; @@ -265,11 +298,10 @@ class CORE_EXPORT NGConstraintSpace final NGMarginStrut margin_strut_; NGBfcOffset bfc_offset_; - WTF::Optional<NGBfcOffset> floats_bfc_offset_; + base::Optional<NGBfcOffset> floats_bfc_offset_; const std::unique_ptr<const NGExclusionSpace> exclusion_space_; - WTF::Optional<LayoutUnit> clearance_offset_; - Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_; + LayoutUnit clearance_offset_; Vector<NGBaselineRequest> baseline_requests_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc index 9e60a1dc247..f1ed8bb3579 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc @@ -13,26 +13,13 @@ NGConstraintSpaceBuilder::NGConstraintSpaceBuilder( : NGConstraintSpaceBuilder(parent_space.GetWritingMode(), parent_space.InitialContainingBlockSize()) { parent_percentage_resolution_size_ = parent_space.PercentageResolutionSize(); + is_intermediate_layout_ = parent_space.IsIntermediateLayout(); } NGConstraintSpaceBuilder::NGConstraintSpaceBuilder(WritingMode writing_mode, NGPhysicalSize icb_size) : initial_containing_block_size_(icb_size), - fragmentainer_block_size_(NGSizeIndefinite), - fragmentainer_space_at_bfc_start_(NGSizeIndefinite), - parent_writing_mode_(static_cast<unsigned>(writing_mode)), - is_fixed_size_inline_(false), - is_fixed_size_block_(false), - is_shrink_to_fit_(false), - is_inline_direction_triggers_scrollbar_(false), - is_block_direction_triggers_scrollbar_(false), - fragmentation_type_(kFragmentNone), - separate_leading_fragmentainer_margins_(false), - is_new_fc_(false), - is_anonymous_(false), - use_first_line_style_(false), - text_direction_(static_cast<unsigned>(TextDirection::kLtr)), - exclusion_space_(nullptr) {} + parent_writing_mode_(writing_mode) {} NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetAvailableSize( NGLogicalSize available_size) { @@ -48,7 +35,7 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetPercentageResolutionSize( NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetTextDirection( TextDirection text_direction) { - text_direction_ = static_cast<unsigned>(text_direction); + text_direction_ = text_direction; return *this; } @@ -65,13 +52,13 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetBfcOffset( } NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetFloatsBfcOffset( - const WTF::Optional<NGBfcOffset>& floats_bfc_offset) { + const base::Optional<NGBfcOffset>& floats_bfc_offset) { floats_bfc_offset_ = floats_bfc_offset; return *this; } NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetClearanceOffset( - const WTF::Optional<LayoutUnit>& clearance_offset) { + LayoutUnit clearance_offset) { clearance_offset_ = clearance_offset; return *this; } @@ -94,25 +81,21 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetIsFixedSizeBlock( return *this; } -NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetIsShrinkToFit( - bool shrink_to_fit) { - is_shrink_to_fit_ = shrink_to_fit; +NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetFixedSizeBlockIsDefinite( + bool fixed_size_block_is_definite) { + fixed_size_block_is_definite_ = fixed_size_block_is_definite; return *this; } -NGConstraintSpaceBuilder& -NGConstraintSpaceBuilder::SetIsInlineDirectionTriggersScrollbar( - bool is_inline_direction_triggers_scrollbar) { - is_inline_direction_triggers_scrollbar_ = - is_inline_direction_triggers_scrollbar; +NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetIsShrinkToFit( + bool shrink_to_fit) { + is_shrink_to_fit_ = shrink_to_fit; return *this; } -NGConstraintSpaceBuilder& -NGConstraintSpaceBuilder::SetIsBlockDirectionTriggersScrollbar( - bool is_block_direction_triggers_scrollbar) { - is_block_direction_triggers_scrollbar_ = - is_block_direction_triggers_scrollbar; +NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetIsIntermediateLayout( + bool is_intermediate_layout) { + is_intermediate_layout_ = is_intermediate_layout; return *this; } @@ -140,12 +123,6 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetUseFirstLineStyle( return *this; } -NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetUnpositionedFloats( - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats) { - unpositioned_floats_ = unpositioned_floats; - return *this; -} - void NGConstraintSpaceBuilder::AddBaselineRequests( const Vector<NGBaselineRequest>& requests) { DCHECK(baseline_requests_.IsEmpty()); @@ -166,17 +143,23 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpaceBuilder::ToConstraintSpace( WritingMode out_writing_mode) { // Whether the child and the containing block are parallel to each other. // Example: vertical-rl and vertical-lr - bool is_in_parallel_flow = IsParallelWritingMode( - static_cast<WritingMode>(parent_writing_mode_), out_writing_mode); + bool is_in_parallel_flow = + IsParallelWritingMode(parent_writing_mode_, out_writing_mode); NGLogicalSize available_size = available_size_; NGLogicalSize percentage_resolution_size = percentage_resolution_size_; NGLogicalSize parent_percentage_resolution_size = parent_percentage_resolution_size_.value_or(percentage_resolution_size); + bool is_fixed_size_inline = is_fixed_size_inline_; + bool is_fixed_size_block = is_fixed_size_block_; + bool fixed_size_block_is_definite = fixed_size_block_is_definite_; if (!is_in_parallel_flow) { available_size.Flip(); percentage_resolution_size.Flip(); parent_percentage_resolution_size.Flip(); + is_fixed_size_inline = is_fixed_size_block_; + is_fixed_size_block = is_fixed_size_inline_; + fixed_size_block_is_definite = true; } // If inline size is indefinite, use size of initial containing block. @@ -202,56 +185,34 @@ scoped_refptr<NGConstraintSpace> NGConstraintSpaceBuilder::ToConstraintSpace( DEFINE_STATIC_LOCAL(NGExclusionSpace, empty_exclusion_space, ()); - // Reset things that do not pass the Formatting Context boundary. - if (is_new_fc_) - DCHECK(unpositioned_floats_.IsEmpty()); + DCHECK(!is_new_fc_ || !adjoining_floats_); const NGExclusionSpace& exclusion_space = (is_new_fc_ || !exclusion_space_) ? empty_exclusion_space : *exclusion_space_; NGBfcOffset bfc_offset = is_new_fc_ ? NGBfcOffset() : bfc_offset_; NGMarginStrut margin_strut = is_new_fc_ ? NGMarginStrut() : margin_strut_; - WTF::Optional<LayoutUnit> clearance_offset = - is_new_fc_ ? WTF::nullopt : clearance_offset_; - WTF::Optional<NGBfcOffset> floats_bfc_offset = - is_new_fc_ ? WTF::nullopt : floats_bfc_offset_; + LayoutUnit clearance_offset = + is_new_fc_ ? LayoutUnit::Min() : clearance_offset_; + base::Optional<NGBfcOffset> floats_bfc_offset = + is_new_fc_ ? base::nullopt : floats_bfc_offset_; if (floats_bfc_offset) { floats_bfc_offset = NGBfcOffset( {bfc_offset.line_offset, floats_bfc_offset.value().block_offset}); } - if (is_in_parallel_flow) { - return base::AdoptRef(new NGConstraintSpace( - static_cast<WritingMode>(out_writing_mode), false, - static_cast<TextDirection>(text_direction_), available_size, - percentage_resolution_size, - parent_percentage_resolution_size.inline_size, - initial_containing_block_size_, fragmentainer_block_size_, - fragmentainer_space_at_bfc_start_, is_fixed_size_inline_, - is_fixed_size_block_, is_shrink_to_fit_, - is_inline_direction_triggers_scrollbar_, - is_block_direction_triggers_scrollbar_, - static_cast<NGFragmentationType>(fragmentation_type_), - separate_leading_fragmentainer_margins_, is_new_fc_, is_anonymous_, - use_first_line_style_, margin_strut, bfc_offset, floats_bfc_offset, - exclusion_space, unpositioned_floats_, clearance_offset, - baseline_requests_)); - } return base::AdoptRef(new NGConstraintSpace( - out_writing_mode, true, static_cast<TextDirection>(text_direction_), - available_size, percentage_resolution_size, - parent_percentage_resolution_size.inline_size, + out_writing_mode, !is_in_parallel_flow, text_direction_, available_size, + percentage_resolution_size, parent_percentage_resolution_size.inline_size, initial_containing_block_size_, fragmentainer_block_size_, - fragmentainer_space_at_bfc_start_, is_fixed_size_block_, - is_fixed_size_inline_, is_shrink_to_fit_, - is_block_direction_triggers_scrollbar_, - is_inline_direction_triggers_scrollbar_, - static_cast<NGFragmentationType>(fragmentation_type_), + fragmentainer_space_at_bfc_start_, is_fixed_size_inline, + is_fixed_size_block, fixed_size_block_is_definite, is_shrink_to_fit_, + is_intermediate_layout_, fragmentation_type_, separate_leading_fragmentainer_margins_, is_new_fc_, is_anonymous_, - use_first_line_style_, margin_strut, bfc_offset, floats_bfc_offset, - exclusion_space, unpositioned_floats_, clearance_offset, - baseline_requests_)); + use_first_line_style_, should_force_clearance_, adjoining_floats_, + margin_strut, bfc_offset, floats_bfc_offset, exclusion_space, + clearance_offset, baseline_requests_)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h index 9f4ab1f2b8b..a23c2d926bb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h @@ -5,17 +5,21 @@ #ifndef NGConstraintSpaceBuilder_h #define NGConstraintSpaceBuilder_h +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h" +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/core/layout/ng/ng_exclusion.h" -#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/platform/text/text_direction.h" +#include "third_party/blink/renderer/platform/text/writing_mode.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { +class NGExclusionSpace; + class CORE_EXPORT NGConstraintSpaceBuilder final { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: // NOTE: This constructor doesn't act like a copy-constructor, it uses the @@ -44,13 +48,13 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { NGConstraintSpaceBuilder& SetIsFixedSizeInline(bool is_fixed_size_inline); NGConstraintSpaceBuilder& SetIsFixedSizeBlock(bool is_fixed_size_block); + NGConstraintSpaceBuilder& SetFixedSizeBlockIsDefinite( + bool fixed_size_block_is_definite); NGConstraintSpaceBuilder& SetIsShrinkToFit(bool shrink_to_fit); - NGConstraintSpaceBuilder& SetIsInlineDirectionTriggersScrollbar( - bool is_inline_direction_triggers_scrollbar); - NGConstraintSpaceBuilder& SetIsBlockDirectionTriggersScrollbar( - bool is_block_direction_triggers_scrollbar); + NGConstraintSpaceBuilder& SetIsIntermediateLayout( + bool is_intermediate_layout); NGConstraintSpaceBuilder& SetFragmentationType(NGFragmentationType); @@ -63,17 +67,23 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { NGConstraintSpaceBuilder& SetIsAnonymous(bool is_anonymous); NGConstraintSpaceBuilder& SetUseFirstLineStyle(bool use_first_line_style); - NGConstraintSpaceBuilder& SetUnpositionedFloats( - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats); + NGConstraintSpaceBuilder& SetAdjoiningFloatTypes(NGFloatTypes floats) { + adjoining_floats_ = floats; + return *this; + } NGConstraintSpaceBuilder& SetMarginStrut(const NGMarginStrut& margin_strut); NGConstraintSpaceBuilder& SetBfcOffset(const NGBfcOffset& bfc_offset); NGConstraintSpaceBuilder& SetFloatsBfcOffset( - const WTF::Optional<NGBfcOffset>& floats_bfc_offset); + const base::Optional<NGBfcOffset>& floats_bfc_offset); - NGConstraintSpaceBuilder& SetClearanceOffset( - const WTF::Optional<LayoutUnit>& clearance_offset); + NGConstraintSpaceBuilder& SetClearanceOffset(LayoutUnit clearance_offset); + + NGConstraintSpaceBuilder& SetShouldForceClearance() { + should_force_clearance_ = true; + return *this; + } NGConstraintSpaceBuilder& SetExclusionSpace( const NGExclusionSpace& exclusion_space); @@ -96,30 +106,31 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { NGLogicalSize available_size_; // Relative to parent_writing_mode_. NGLogicalSize percentage_resolution_size_; - Optional<NGLogicalSize> parent_percentage_resolution_size_; + base::Optional<NGLogicalSize> parent_percentage_resolution_size_; NGPhysicalSize initial_containing_block_size_; - LayoutUnit fragmentainer_block_size_; - LayoutUnit fragmentainer_space_at_bfc_start_; - - unsigned parent_writing_mode_ : 3; - unsigned is_fixed_size_inline_ : 1; - unsigned is_fixed_size_block_ : 1; - unsigned is_shrink_to_fit_ : 1; - unsigned is_inline_direction_triggers_scrollbar_ : 1; - unsigned is_block_direction_triggers_scrollbar_ : 1; - unsigned fragmentation_type_ : 2; - unsigned separate_leading_fragmentainer_margins_ : 1; - unsigned is_new_fc_ : 1; - unsigned is_anonymous_ : 1; - unsigned use_first_line_style_ : 1; - unsigned text_direction_ : 1; + LayoutUnit fragmentainer_block_size_ = NGSizeIndefinite; + LayoutUnit fragmentainer_space_at_bfc_start_ = NGSizeIndefinite; + + WritingMode parent_writing_mode_; + NGFragmentationType fragmentation_type_ = kFragmentNone; + NGFloatTypes adjoining_floats_ = kFloatTypeNone; + TextDirection text_direction_ = TextDirection::kLtr; + bool is_fixed_size_inline_ = false; + bool is_fixed_size_block_ = false; + bool fixed_size_block_is_definite_ = true; + bool is_shrink_to_fit_ = false; + bool is_intermediate_layout_ = false; + bool separate_leading_fragmentainer_margins_ = false; + bool is_new_fc_ = false; + bool is_anonymous_ = false; + bool use_first_line_style_ = false; + bool should_force_clearance_ = false; NGMarginStrut margin_strut_; NGBfcOffset bfc_offset_; - WTF::Optional<NGBfcOffset> floats_bfc_offset_; - const NGExclusionSpace* exclusion_space_; - WTF::Optional<LayoutUnit> clearance_offset_; - Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_; + base::Optional<NGBfcOffset> floats_bfc_offset_; + const NGExclusionSpace* exclusion_space_ = nullptr; + LayoutUnit clearance_offset_; Vector<NGBaselineRequest> baseline_requests_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc index eb667bad00b..d3c783914e8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc @@ -5,9 +5,9 @@ #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { @@ -45,12 +45,6 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::SetExclusionSpace( return *this; } -NGContainerFragmentBuilder& NGContainerFragmentBuilder::SwapUnpositionedFloats( - Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats) { - unpositioned_floats_.swap(*unpositioned_floats); - return *this; -} - NGContainerFragmentBuilder& NGContainerFragmentBuilder::SetUnpositionedListMarker( const NGUnpositionedListMarker& marker) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h index 84948148997..448360170e1 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_base_fragment_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -23,7 +24,6 @@ class ComputedStyle; class NGExclusionSpace; class NGLayoutResult; class NGPhysicalFragment; -struct NGUnpositionedFloat; class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder { STACK_ALLOCATED(); @@ -39,17 +39,18 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder { // The NGBfcOffset is where this fragment was positioned within the BFC. If // it is not set, this fragment may be placed anywhere within the BFC. - const WTF::Optional<NGBfcOffset>& BfcOffset() const { return bfc_offset_; } + const base::Optional<NGBfcOffset>& BfcOffset() const { return bfc_offset_; } NGContainerFragmentBuilder& SetBfcOffset(const NGBfcOffset&); + NGContainerFragmentBuilder& ResetBfcOffset() { + bfc_offset_.reset(); + return *this; + } NGContainerFragmentBuilder& SetEndMarginStrut(const NGMarginStrut&); NGContainerFragmentBuilder& SetExclusionSpace( std::unique_ptr<const NGExclusionSpace> exclusion_space); - NGContainerFragmentBuilder& SwapUnpositionedFloats( - Vector<scoped_refptr<NGUnpositionedFloat>>*); - const NGUnpositionedListMarker& UnpositionedListMarker() const { return unpositioned_list_marker_; } @@ -121,6 +122,16 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder { } bool IsPushedByFloats() const { return is_pushed_by_floats_; } + NGContainerFragmentBuilder& ResetAdjoiningFloatTypes() { + adjoining_floats_ = kFloatTypeNone; + return *this; + } + NGContainerFragmentBuilder& AddAdjoiningFloatTypes(NGFloatTypes floats) { + adjoining_floats_ |= floats; + return *this; + } + NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; } + #ifndef NDEBUG String ToString() const; #endif @@ -170,14 +181,10 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder { NGLogicalSize size_; - WTF::Optional<NGBfcOffset> bfc_offset_; + base::Optional<NGBfcOffset> bfc_offset_; NGMarginStrut end_margin_strut_; std::unique_ptr<const NGExclusionSpace> exclusion_space_; - // Floats that need to be positioned by the next in-flow fragment that can - // determine its block position in space. - Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_; - Vector<NGOutOfFlowPositionedCandidate> oof_positioned_candidates_; Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_; @@ -186,6 +193,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGBaseFragmentBuilder { Vector<scoped_refptr<NGPhysicalFragment>> children_; Vector<NGLogicalOffset> offsets_; + NGFloatTypes adjoining_floats_ = kFloatTypeNone; + bool has_last_resort_break_ = false; bool is_pushed_by_floats_ = false; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_exclusion.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_exclusion.h index 554a8fef5f3..a22f4409e6a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_exclusion.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_exclusion.h @@ -12,23 +12,41 @@ namespace blink { +class LayoutBox; + +struct CORE_EXPORT NGExclusionShapeData { + NGExclusionShapeData(const LayoutBox* layout_box, + const NGBoxStrut& margins, + const NGBoxStrut& shape_insets) + : layout_box(layout_box), margins(margins), shape_insets(shape_insets) {} + + const LayoutBox* layout_box; + const NGBoxStrut margins; + const NGBoxStrut shape_insets; +}; + // Struct that represents an exclusion. This currently is just a float but // we've named it an exclusion to potentially support other types in the future. struct CORE_EXPORT NGExclusion : public RefCounted<NGExclusion> { - static scoped_refptr<NGExclusion> Create(const NGBfcRect& rect, - const EFloat type) { - return base::AdoptRef(new NGExclusion(rect, type)); + static scoped_refptr<NGExclusion> Create( + const NGBfcRect& rect, + const EFloat type, + std::unique_ptr<NGExclusionShapeData> shape_data = nullptr) { + return base::AdoptRef(new NGExclusion(rect, type, std::move(shape_data))); } const NGBfcRect rect; const EFloat type; + const std::unique_ptr<NGExclusionShapeData> shape_data; bool operator==(const NGExclusion& other) const; bool operator!=(const NGExclusion& other) const { return !(*this == other); } private: - NGExclusion(const NGBfcRect& rect, const EFloat type) - : rect(rect), type(type) {} + NGExclusion(const NGBfcRect& rect, + const EFloat type, + std::unique_ptr<NGExclusionShapeData> shape_data) + : rect(rect), type(type), shape_data(std::move(shape_data)) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc index 3d82603bf5e..4445b9b5f0a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc @@ -4,16 +4,13 @@ #include "third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h" -#include <algorithm> +#include <memory> #include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h" #include "third_party/blink/renderer/core/layout/layout_box.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.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" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -30,7 +27,7 @@ scoped_refptr<NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { << "Don't support that yet"; LayoutUnit container_logical_width = ComputeInlineSizeForFragment( - ConstraintSpace(), Style(), /* MinMaxSize */ WTF::nullopt); + ConstraintSpace(), Style(), /* MinMaxSize */ base::nullopt); Vector<FlexItem> flex_items; for (NGLayoutInputNode child = Node().FirstChild(); child; @@ -39,11 +36,15 @@ scoped_refptr<NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { continue; // Assume row flexbox with no orthogonal items, which lets us just use // MinMaxSize for flex base size. An orthogonal item would need full layout. + // TODO(layout-ng): Now that ComputeMinMaxSize takes a writing mode, this + // should be easy to fix by just passing an appropriate constraint space to + // ComputeMinMaxSize. DCHECK(IsParallelWritingMode(Node().Style().GetWritingMode(), child.Style().GetWritingMode())) << "Orthogonal items aren't supported yet."; MinMaxSizeInput zero_input; - MinMaxSize min_max_sizes = child.ComputeMinMaxSize(zero_input); + MinMaxSize min_max_sizes = + child.ComputeMinMaxSize(ConstraintSpace().GetWritingMode(), zero_input); LayoutUnit flex_base_content_size; if (child.Style().FlexBasis().IsAuto() && child.Style().Width().IsAuto()) { @@ -124,19 +125,13 @@ scoped_refptr<NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { {flex_item.desired_location.X(), flex_item.desired_location.Y()}); } } - // TODO(dgrogan): This line is only needed because we erroneously tell the - // parent block layout algorithm that the flexbox doesn't create a new BFC, so - // a DCHECK is triggered. Remove this line after adding a LayoutNGFlexibleBox - // class and returning it from LayoutObject::CreateLayoutObject(). - container_builder_.SetExclusionSpace( - std::make_unique<NGExclusionSpace>(ConstraintSpace().ExclusionSpace())); return container_builder_.ToBoxFragment(); } -Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( +base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( const MinMaxSizeInput& input) const { // TODO(dgrogan): Implement this. - return WTF::nullopt; + return base::nullopt; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h index 9df0e0b12cf..1b079c744dc 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h @@ -25,7 +25,8 @@ class CORE_EXPORT NGFlexLayoutAlgorithm scoped_refptr<NGLayoutResult> Layout() override; - Optional<MinMaxSize> ComputeMinMaxSize(const MinMaxSizeInput&) const override; + base::Optional<MinMaxSize> ComputeMinMaxSize( + const MinMaxSizeInput&) const override; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc index 7badb8b057f..0adf9fbfd3b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc @@ -4,13 +4,18 @@ #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { @@ -37,7 +42,7 @@ NGLayoutOpportunity FindLayoutOpportunityForFloat( LayoutUnit inline_size) { NGBfcOffset adjusted_origin_point = AdjustToTopEdgeAlignmentRule(exclusion_space, origin_offset); - WTF::Optional<LayoutUnit> clearance_offset = + LayoutUnit clearance_offset = exclusion_space.ClearanceOffset(unpositioned_float.ClearType()); AdjustToClearance(clearance_offset, &adjusted_origin_point); @@ -51,26 +56,6 @@ NGLayoutOpportunity FindLayoutOpportunityForFloat( float_size); } -// Creates an exclusion from the fragment that will be placed in the provided -// layout opportunity. -scoped_refptr<NGExclusion> CreateExclusion( - const NGFragment& fragment, - const NGBfcOffset& float_margin_bfc_offset, - const NGBoxStrut& margins, - EFloat type) { - // TODO(ikilpatrick): Don't include the block-start margin of a float which - // has fragmented. - NGBfcOffset start_offset = float_margin_bfc_offset; - - NGBfcOffset end_offset( - /* line_offset */ start_offset.line_offset + - (fragment.InlineSize() + margins.InlineSum()).ClampNegativeToZero(), - /* block_offset */ start_offset.block_offset + - (fragment.BlockSize() + margins.BlockSum()).ClampNegativeToZero()); - - return NGExclusion::Create(NGBfcRect(start_offset, end_offset), type); -} - scoped_refptr<NGConstraintSpace> CreateConstraintSpaceForFloatFromBuilder( const NGUnpositionedFloat& unpositioned_float, NGConstraintSpaceBuilder& builder) { @@ -115,6 +100,86 @@ CreateConstraintSpaceForFloatForInlineSizeCalculation( return CreateConstraintSpaceForFloatFromBuilder(unpositioned_float, builder); } +std::unique_ptr<NGExclusionShapeData> CreateExclusionShapeData( + const NGBoxStrut& margins, + const LayoutBox* layout_box, + const NGUnpositionedFloat& unpositioned_float, + const NGConstraintSpace& parent_space, + TextDirection direction) { + DCHECK(layout_box->GetShapeOutsideInfo()); + + // We make the margins on the shape-data relative to line-left/line-right. + NGBoxStrut new_margins(margins.LineLeft(direction), + margins.LineRight(direction), margins.block_start, + margins.block_end); + NGBoxStrut shape_insets; + + const ComputedStyle& style = layout_box->StyleRef(); + switch (style.ShapeOutside()->CssBox()) { + case CSSBoxType::kMissing: + case CSSBoxType::kMargin: + shape_insets -= new_margins; + break; + case CSSBoxType::kBorder: + break; + case CSSBoxType::kPadding: + shape_insets = + ComputeBorders(*CreateConstraintSpaceForFloatForInlineSizeCalculation( + unpositioned_float, parent_space), + style) + .ConvertToPhysical(style.GetWritingMode(), style.Direction()) + .ConvertToLogical(parent_space.GetWritingMode(), + TextDirection::kLtr); + break; + case CSSBoxType::kContent: + const scoped_refptr<NGConstraintSpace> space = + CreateConstraintSpaceForFloatForInlineSizeCalculation( + unpositioned_float, parent_space); + NGBoxStrut border_padding = + ComputeBorders(*space, style) + ComputePadding(*space, style); + shape_insets = + border_padding + .ConvertToPhysical(style.GetWritingMode(), style.Direction()) + .ConvertToLogical(parent_space.GetWritingMode(), + TextDirection::kLtr); + break; + } + + return std::make_unique<NGExclusionShapeData>(layout_box, new_margins, + shape_insets); +} + +// Creates an exclusion from the fragment that will be placed in the provided +// layout opportunity. +scoped_refptr<NGExclusion> CreateExclusion( + const NGFragment& fragment, + const NGBfcOffset& float_margin_bfc_offset, + const NGBoxStrut& margins, + const LayoutBox* layout_box, + const NGUnpositionedFloat& unpositioned_float, + const NGConstraintSpace& parent_space, + TextDirection direction, + EFloat type) { + // TODO(ikilpatrick): Don't include the block-start margin of a float which + // has fragmented. + NGBfcOffset start_offset = float_margin_bfc_offset; + + NGBfcOffset end_offset( + /* line_offset */ start_offset.line_offset + + (fragment.InlineSize() + margins.InlineSum()).ClampNegativeToZero(), + /* block_offset */ start_offset.block_offset + + (fragment.BlockSize() + margins.BlockSum()).ClampNegativeToZero()); + + std::unique_ptr<NGExclusionShapeData> shape_data = + layout_box->GetShapeOutsideInfo() + ? CreateExclusionShapeData(margins, layout_box, unpositioned_float, + parent_space, direction) + : nullptr; + + return NGExclusion::Create(NGBfcRect(start_offset, end_offset), type, + std::move(shape_data)); +} + } // namespace LayoutUnit ComputeInlineSizeForUnpositionedFloat( @@ -130,6 +195,7 @@ LayoutUnit ComputeInlineSizeForUnpositionedFloat( // If we've already performed layout on the unpositioned float, just return // the cached value. if (unpositioned_float->layout_result) { + // TODO(layout-ng): Should this use IsParallelWritingMode()? DCHECK(!is_same_writing_mode); DCHECK(unpositioned_float->layout_result->PhysicalFragment()); return NGFragment(parent_space.GetWritingMode(), @@ -148,10 +214,11 @@ LayoutUnit ComputeInlineSizeForUnpositionedFloat( // because NG cannot figure out the size of such objects on its own, // especially not for tables. if (is_same_writing_mode && unpositioned_float->node.CanUseNewLayout()) { - WTF::Optional<MinMaxSize> min_max_size; + base::Optional<MinMaxSize> min_max_size; if (NeedMinMaxSize(*space.get(), style)) { MinMaxSizeInput zero_input; // Floats do not intrude into floats. - min_max_size = unpositioned_float->node.ComputeMinMaxSize(zero_input); + min_max_size = unpositioned_float->node.ComputeMinMaxSize( + style.GetWritingMode(), zero_input); } return ComputeInlineSizeForFragment(*space.get(), style, min_max_size); } @@ -222,18 +289,6 @@ NGPositionedFloat PositionFloat(LayoutUnit origin_block_offset, NGFragment float_fragment(parent_space.GetWritingMode(), *layout_result->PhysicalFragment()); - // TODO(glebl): This should check for infinite opportunity instead. - // TODO(ikilpatrick): Remove. - if (opportunity.rect.IsEmpty()) { - // Because of the implementation specific of the layout opportunity iterator - // an empty opportunity can mean 2 things: - // - search for layout opportunities is exhausted. - // - opportunity has an infinite size. That's because CS is infinite. - opportunity = NGLayoutOpportunity(NGBfcRect( - /* start_offset */ NGBfcOffset(), /* end_offset */ NGBfcOffset( - float_fragment.InlineSize(), float_fragment.BlockSize()))); - } - LayoutUnit float_margin_box_inline_size = float_fragment.InlineSize() + unpositioned_float->margins.InlineSum(); @@ -247,6 +302,8 @@ NGPositionedFloat PositionFloat(LayoutUnit origin_block_offset, // Add the float as an exclusion. scoped_refptr<NGExclusion> exclusion = CreateExclusion( float_fragment, float_margin_bfc_offset, unpositioned_float->margins, + ToLayoutBox(unpositioned_float->node.GetLayoutObject()), + *unpositioned_float, parent_space, parent_space.Direction(), unpositioned_float->IsRight() ? EFloat::kRight : EFloat::kLeft); exclusion_space->Add(std::move(exclusion)); @@ -278,4 +335,31 @@ const Vector<NGPositionedFloat> PositionFloats( return positioned_floats; } +void AddUnpositionedFloat( + Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats, + NGContainerFragmentBuilder* fragment_builder, + scoped_refptr<NGUnpositionedFloat> unpositioned_float) { + if (fragment_builder && !fragment_builder->BfcOffset()) { + fragment_builder->AddAdjoiningFloatTypes( + unpositioned_float->IsLeft() ? kFloatTypeLeft : kFloatTypeRight); + } + unpositioned_floats->push_back(std::move(unpositioned_float)); +} + +NGFloatTypes ToFloatTypes(EClear clear) { + switch (clear) { + default: + NOTREACHED(); + FALLTHROUGH; + case EClear::kNone: + return kFloatTypeNone; + case EClear::kLeft: + return kFloatTypeLeft; + case EClear::kRight: + return kFloatTypeRight; + case EClear::kBoth: + return kFloatTypeBoth; + }; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h index 2fd9b00bd72..10973f6693a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h @@ -7,16 +7,26 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/style/computed_style_constants.h" #include "third_party/blink/renderer/platform/layout_unit.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { class NGConstraintSpace; +class NGContainerFragmentBuilder; class NGExclusionSpace; struct NGPositionedFloat; struct NGUnpositionedFloat; +enum NGFloatTypeValue { + kFloatTypeNone = 0b00, + kFloatTypeLeft = 0b01, + kFloatTypeRight = 0b10, + kFloatTypeBoth = 0b11 +}; +typedef int NGFloatTypes; + // Returns the inline size (relative to {@code parent_space}) of the // unpositioned float. If the float is in a different writing mode, this will // perform a layout. @@ -42,6 +52,15 @@ CORE_EXPORT const Vector<NGPositionedFloat> PositionFloats( const NGConstraintSpace& space, NGExclusionSpace* exclusion_space); +// Add a pending float to the list. It will be committed (positioned) once we +// have resolved the BFC offset. +void AddUnpositionedFloat( + Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats, + NGContainerFragmentBuilder* fragment_builder, + scoped_refptr<NGUnpositionedFloat> unpositioned_float); + +NGFloatTypes ToFloatTypes(EClear clear); + } // namespace blink #endif // NGFloatsUtils_h diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.cc index 93823b4247f..028a6d2283e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.cc @@ -13,7 +13,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -285,7 +284,7 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() { scoped_refptr<NGPhysicalBoxFragment> fragment = base::AdoptRef(new NGPhysicalBoxFragment( - layout_object_, Style(), physical_size, children_, + layout_object_, Style(), style_variant_, physical_size, children_, padding_.ConvertToPhysical(GetWritingMode(), Direction()) .SnapToDevicePixels(), contents_visual_rect, baselines_, BoxType(), is_old_layout_root_, @@ -295,11 +294,10 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::ToBoxFragment() { return base::AdoptRef(new NGLayoutResult( std::move(fragment), oof_positioned_descendants_, positioned_floats, - unpositioned_floats_, unpositioned_list_marker_, - std::move(exclusion_space_), bfc_offset_, end_margin_strut_, - intrinsic_block_size_, minimal_space_shortage_, initial_break_before_, - previous_break_after_, has_forced_break_, is_pushed_by_floats_, - NGLayoutResult::kSuccess)); + unpositioned_list_marker_, std::move(exclusion_space_), bfc_offset_, + end_margin_strut_, intrinsic_block_size_, minimal_space_shortage_, + initial_break_before_, previous_break_after_, has_forced_break_, + is_pushed_by_floats_, adjoining_floats_, NGLayoutResult::kSuccess)); } scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort( @@ -308,9 +306,9 @@ scoped_refptr<NGLayoutResult> NGFragmentBuilder::Abort( Vector<NGPositionedFloat> positioned_floats; return base::AdoptRef(new NGLayoutResult( nullptr, oof_positioned_descendants, positioned_floats, - unpositioned_floats_, NGUnpositionedListMarker(), nullptr, bfc_offset_, - end_margin_strut_, LayoutUnit(), LayoutUnit(), EBreakBetween::kAuto, - EBreakBetween::kAuto, false, false, status)); + NGUnpositionedListMarker(), nullptr, bfc_offset_, end_margin_strut_, + LayoutUnit(), LayoutUnit(), EBreakBetween::kAuto, EBreakBetween::kAuto, + false, false, kFloatTypeNone, status)); } // Finds FragmentPairs that define inline containing blocks. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h index e2c79e6241c..478b43e42d4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h @@ -8,15 +8,14 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_rect.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" -#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" -#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" + namespace blink { class NGPhysicalFragment; @@ -40,8 +39,6 @@ class CORE_EXPORT NGFragmentBuilder final : public NGContainerFragmentBuilder { ~NGFragmentBuilder() override; - using WeakBoxList = PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>>; - NGFragmentBuilder& SetIntrinsicBlockSize(LayoutUnit); NGFragmentBuilder& SetPadding(const NGBoxStrut&); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc index 03b89dc7989..4f94e8f7805 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "third_party/blink/renderer/core/exported/web_view_impl.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" @@ -54,7 +54,9 @@ TEST_F(NGInlineLayoutTest, BlockWithSingleTextNode) { EXPECT_TRUE(result); String expected_text("Hello World!"); - EXPECT_EQ(expected_text, ToNGInlineNode(node.FirstChild()).Text(0, 12)); + NGInlineNode first_child = ToNGInlineNode(node.FirstChild()); + EXPECT_EQ(expected_text, + StringView(first_child.ItemsData(false).text_content, 0, 12)); } TEST_F(NGInlineLayoutTest, BlockWithTextAndAtomicInline) { @@ -80,7 +82,9 @@ TEST_F(NGInlineLayoutTest, BlockWithTextAndAtomicInline) { String expected_text("Hello "); expected_text.append(kObjectReplacementCharacter); expected_text.append("."); - EXPECT_EQ(expected_text, ToNGInlineNode(node.FirstChild()).Text(0, 8)); + NGInlineNode first_child = ToNGInlineNode(node.FirstChild()); + EXPECT_EQ(expected_text, + StringView(first_child.ItemsData(false).text_content, 0, 8)); // Delete the line box tree to avoid leaks in the test. block_flow->DeleteLineBoxTree(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h index 71f5fb54f7e..428255389d8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h @@ -5,11 +5,11 @@ #ifndef NGLayoutAlgorithm_h #define NGLayoutAlgorithm_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { @@ -56,8 +56,9 @@ class CORE_EXPORT NGLayoutAlgorithm { // account. If the return value is empty, the caller is expected to synthesize // this value from the overflow rect returned from Layout called with an // available width of 0 and LayoutUnit::max(), respectively. - virtual Optional<MinMaxSize> ComputeMinMaxSize(const MinMaxSizeInput&) const { - return WTF::nullopt; + virtual base::Optional<MinMaxSize> ComputeMinMaxSize( + const MinMaxSizeInput&) const { + return base::nullopt; } protected: diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc index ced495d4277..63cb0d76d03 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc @@ -9,7 +9,6 @@ #include "third_party/blink/renderer/core/layout/min_max_size.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" @@ -42,7 +41,8 @@ void AppendNodeToString(NGLayoutInputNode node, } if (node.IsInline()) { - for (const NGInlineItem& inline_item : ToNGInlineNode(node).Items()) { + const auto& items = ToNGInlineNode(node).ItemsData(false).items; + for (const NGInlineItem& inline_item : items) { string_builder->Append(indent_builder.ToString()); string_builder->Append(inline_item.ToString()); string_builder->Append("\n"); @@ -95,6 +95,10 @@ bool NGLayoutInputNode::IsListMarker() const { return IsBlock() && box_->IsLayoutNGListMarker(); } +bool NGLayoutInputNode::IsAnonymous() const { + return box_->IsAnonymous(); +} + bool NGLayoutInputNode::IsQuirkyContainer() const { return box_->GetDocument().InQuirksMode() && (box_->IsBody() || box_->IsTableCell()); @@ -128,16 +132,18 @@ scoped_refptr<NGLayoutResult> NGLayoutInputNode::Layout( } MinMaxSize NGLayoutInputNode::ComputeMinMaxSize( + WritingMode writing_mode, const MinMaxSizeInput& input, const NGConstraintSpace* space) { return IsInline() ? ToNGInlineNode(*this).ComputeMinMaxSize(input) - : ToNGBlockNode(*this).ComputeMinMaxSize(input, space); + : ToNGBlockNode(*this).ComputeMinMaxSize(writing_mode, + input, space); } void NGLayoutInputNode::IntrinsicSize( NGLogicalSize* default_intrinsic_size, - Optional<LayoutUnit>* computed_inline_size, - Optional<LayoutUnit>* computed_block_size, + base::Optional<LayoutUnit>* computed_inline_size, + base::Optional<LayoutUnit>* computed_block_size, NGLogicalSize* aspect_ratio) const { DCHECK(IsReplaced()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h index bdf26b58bc0..4ac0d3ec824 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h @@ -5,9 +5,10 @@ #ifndef NGLayoutInputNode_h #define NGLayoutInputNode_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/layout_unit.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" +#include "third_party/blink/renderer/platform/text/writing_mode.h" namespace blink { @@ -59,6 +60,7 @@ class CORE_EXPORT NGLayoutInputNode { bool ShouldBeConsideredAsReplaced() const; bool IsListItem() const; bool IsListMarker() const; + bool IsAnonymous() const; // If the node is a quirky container for margin collapsing, see: // https://html.spec.whatwg.org/#margin-collapsing-quirks @@ -70,7 +72,8 @@ class CORE_EXPORT NGLayoutInputNode { // Performs layout on this input node, will return the layout result. scoped_refptr<NGLayoutResult> Layout(const NGConstraintSpace&, NGBreakToken*); - MinMaxSize ComputeMinMaxSize(const MinMaxSizeInput&, + MinMaxSize ComputeMinMaxSize(WritingMode, + const MinMaxSizeInput&, const NGConstraintSpace* = nullptr); // Returns intrinsic sizing information for replaced elements. @@ -79,8 +82,8 @@ class CORE_EXPORT NGLayoutInputNode { // computations: LayoutReplaced::IntrinsicSizingInfo, // and LayoutReplaced::IntrinsicSize. void IntrinsicSize(NGLogicalSize* default_intrinsic_size, - Optional<LayoutUnit>* computed_inline_size, - Optional<LayoutUnit>* computed_block_size, + base::Optional<LayoutUnit>* computed_inline_size, + base::Optional<LayoutUnit>* computed_block_size, NGLogicalSize* aspect_ratio) const; // Returns the next sibling. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index 724aec2a0fa..5982eb02b4c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc @@ -9,7 +9,6 @@ #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" -#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" namespace blink { @@ -17,10 +16,9 @@ NGLayoutResult::NGLayoutResult( scoped_refptr<NGPhysicalFragment> physical_fragment, Vector<NGOutOfFlowPositionedDescendant>& oof_positioned_descendants, Vector<NGPositionedFloat>& positioned_floats, - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats, const NGUnpositionedListMarker& unpositioned_list_marker, std::unique_ptr<const NGExclusionSpace> exclusion_space, - const WTF::Optional<NGBfcOffset> bfc_offset, + const base::Optional<NGBfcOffset> bfc_offset, const NGMarginStrut end_margin_strut, const LayoutUnit intrinsic_block_size, LayoutUnit minimal_space_shortage, @@ -28,6 +26,7 @@ NGLayoutResult::NGLayoutResult( EBreakBetween final_break_after, bool has_forced_break, bool is_pushed_by_floats, + NGFloatTypes adjoining_floats, NGLayoutResultStatus status) : physical_fragment_(std::move(physical_fragment)), unpositioned_list_marker_(unpositioned_list_marker), @@ -40,22 +39,20 @@ NGLayoutResult::NGLayoutResult( final_break_after_(final_break_after), has_forced_break_(has_forced_break), is_pushed_by_floats_(is_pushed_by_floats), + adjoining_floats_(adjoining_floats), status_(status) { oof_positioned_descendants_.swap(oof_positioned_descendants); positioned_floats_.swap(positioned_floats); - unpositioned_floats_.swap(unpositioned_floats); } -// Keep the implementation of the destructor here, to avoid dependencies on -// NGUnpositionedFloat in the header file. +// Define the destructor here, so that we can forward-declare more in the +// header. NGLayoutResult::~NGLayoutResult() = default; scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const { Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants( oof_positioned_descendants_); Vector<NGPositionedFloat> positioned_floats(positioned_floats_); - Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats( - unpositioned_floats_); std::unique_ptr<const NGExclusionSpace> exclusion_space; // TODO(layoutng) Replace this with DCHECK(exclusion_space_) when // callers guarantee exclusion_space_ != null. @@ -64,10 +61,10 @@ scoped_refptr<NGLayoutResult> NGLayoutResult::CloneWithoutOffset() const { } return base::AdoptRef(new NGLayoutResult( physical_fragment_->CloneWithoutOffset(), oof_positioned_descendants, - positioned_floats, unpositioned_floats, unpositioned_list_marker_, - std::move(exclusion_space), bfc_offset_, end_margin_strut_, - intrinsic_block_size_, minimal_space_shortage_, initial_break_before_, - final_break_after_, has_forced_break_, is_pushed_by_floats_, Status())); + positioned_floats, unpositioned_list_marker_, std::move(exclusion_space), + bfc_offset_, end_margin_strut_, intrinsic_block_size_, + minimal_space_shortage_, initial_break_before_, final_break_after_, + has_forced_break_, is_pushed_by_floats_, adjoining_floats_, Status())); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index 9112e0d94b8..89aa6cce96a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" @@ -19,7 +20,6 @@ namespace blink { class NGExclusionSpace; struct NGPositionedFloat; -struct NGUnpositionedFloat; // The NGLayoutResult stores the resulting data from layout. This includes // geometry information in form of a NGPhysicalFragment, which is kept around @@ -59,18 +59,6 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { return positioned_floats_; } - // List of floats that need to be positioned by the next in-flow child that - // can determine its position in space. - // Use case example where it may be needed: - // <div><float></div> - // <div style="margin-top: 10px; height: 20px"></div> - // The float cannot be positioned right away inside of the 1st div because - // the vertical position is not known at that moment. It will be known only - // after the 2nd div collapses its margin with its parent. - const Vector<scoped_refptr<NGUnpositionedFloat>>& UnpositionedFloats() const { - return unpositioned_floats_; - } - const NGUnpositionedListMarker& UnpositionedListMarker() const { return unpositioned_list_marker_; } @@ -83,7 +71,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { return static_cast<NGLayoutResultStatus>(status_); } - const WTF::Optional<NGBfcOffset>& BfcOffset() const { return bfc_offset_; } + const base::Optional<NGBfcOffset>& BfcOffset() const { return bfc_offset_; } const NGMarginStrut EndMarginStrut() const { return end_margin_strut_; } @@ -109,40 +97,48 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { // of floats. bool IsPushedByFloats() const { return is_pushed_by_floats_; } + // Return the types (none, left, right, both) of preceding adjoining + // floats. These are floats that are added while the in-flow BFC offset is + // still unknown. The floats may or may not be unpositioned (pending). That + // depends on which layout pass we're in. Adjoining floats should be treated + // differently when calculating clearance on a block with adjoining + // block-start margin (in such cases we will know up front that the block will + // need clearance, since, if it doesn't, the float will be pulled along with + // the block, and the block will fail to clear). + NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; } + scoped_refptr<NGLayoutResult> CloneWithoutOffset() const; private: friend class NGFragmentBuilder; friend class NGLineBoxFragmentBuilder; - NGLayoutResult( - scoped_refptr<NGPhysicalFragment> physical_fragment, - Vector<NGOutOfFlowPositionedDescendant>& - out_of_flow_positioned_descendants, - Vector<NGPositionedFloat>& positioned_floats, - Vector<scoped_refptr<NGUnpositionedFloat>>& unpositioned_floats, - const NGUnpositionedListMarker& unpositioned_list_marker, - std::unique_ptr<const NGExclusionSpace> exclusion_space, - const WTF::Optional<NGBfcOffset> bfc_offset, - const NGMarginStrut end_margin_strut, - const LayoutUnit intrinsic_block_size, - LayoutUnit minimal_space_shortage, - EBreakBetween initial_break_before, - EBreakBetween final_break_after, - bool has_forced_break, - bool is_pushed_by_floats, - NGLayoutResultStatus status); + NGLayoutResult(scoped_refptr<NGPhysicalFragment> physical_fragment, + Vector<NGOutOfFlowPositionedDescendant>& + out_of_flow_positioned_descendants, + Vector<NGPositionedFloat>& positioned_floats, + const NGUnpositionedListMarker& unpositioned_list_marker, + std::unique_ptr<const NGExclusionSpace> exclusion_space, + const base::Optional<NGBfcOffset> bfc_offset, + const NGMarginStrut end_margin_strut, + const LayoutUnit intrinsic_block_size, + LayoutUnit minimal_space_shortage, + EBreakBetween initial_break_before, + EBreakBetween final_break_after, + bool has_forced_break, + bool is_pushed_by_floats, + NGFloatTypes adjoining_floats, + NGLayoutResultStatus status); scoped_refptr<NGPhysicalFragment> physical_fragment_; Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_; Vector<NGPositionedFloat> positioned_floats_; - Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_; NGUnpositionedListMarker unpositioned_list_marker_; const std::unique_ptr<const NGExclusionSpace> exclusion_space_; - const WTF::Optional<NGBfcOffset> bfc_offset_; + const base::Optional<NGBfcOffset> bfc_offset_; const NGMarginStrut end_margin_strut_; const LayoutUnit intrinsic_block_size_; const LayoutUnit minimal_space_shortage_; @@ -153,6 +149,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { unsigned has_forced_break_ : 1; unsigned is_pushed_by_floats_ : 1; + unsigned adjoining_floats_ : 2; // NGFloatTypes unsigned status_ : 1; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_test.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_test.h index 40a8ffb3f48..7f881af1fa1 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_test.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_test.h @@ -7,7 +7,6 @@ #include "third_party/blink/renderer/core/frame/local_frame_client.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc new file mode 100644 index 00000000000..029a7e4bcef --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc @@ -0,0 +1,23 @@ +// 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/ng/ng_layout_utils.h" + +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" + +namespace blink { + +bool IsBlockLayoutComplete(const NGConstraintSpace& space, + const NGLayoutResult& result) { + if (result.Status() != NGLayoutResult::kSuccess) + return false; + if (space.IsIntermediateLayout()) + return false; + // Check that we're done positioning pending floats. + return !result.AdjoiningFloatTypes() || result.BfcOffset() || + space.FloatsBfcOffset(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h new file mode 100644 index 00000000000..f93fc5e8f43 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_ + +namespace blink { + +class NGConstraintSpace; +class NGLayoutResult; + +// Return true if layout is considered complete. In some cases we require more +// than one layout pass. +// This function never considers intermediate layouts +// (space,IsIntermediateLayout()) to be complete. +bool IsBlockLayoutComplete(const NGConstraintSpace&, const NGLayoutResult&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc index d9e42256902..06afd4f1d9f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc @@ -5,19 +5,17 @@ #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include <algorithm> +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_table_cell.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/layout_unit.h" #include "third_party/blink/renderer/platform/length.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { -// TODO(layout-ng): -// - replaced calculations -// - Take scrollbars into account bool NeedMinMaxSize(const NGConstraintSpace& constraint_space, const ComputedStyle& style) { @@ -33,18 +31,30 @@ bool NeedMinMaxSize(const ComputedStyle& style) { style.LogicalMaxWidth().IsIntrinsic(); } -bool NeedMinMaxSizeForContentContribution(const ComputedStyle& style) { - return style.LogicalWidth().IsIntrinsicOrAuto() || - style.LogicalMinWidth().IsIntrinsic() || - style.LogicalMaxWidth().IsIntrinsic(); +bool NeedMinMaxSizeForContentContribution(WritingMode mode, + const ComputedStyle& style) { + // During the intrinsic sizes pass percentages/calc() are defined to behave + // like 'auto'. As a result we need to calculate the intrinsic sizes for any + // children with percentages. E.g. + // <div style="float:left;"> + // <div style="width:30%;">text text</div> + // </div> + if (mode == WritingMode::kHorizontalTb) { + return style.Width().IsIntrinsicOrAuto() || + style.Width().IsPercentOrCalc() || style.MinWidth().IsIntrinsic() || + style.MaxWidth().IsIntrinsic(); + } + return style.Height().IsIntrinsicOrAuto() || + style.Height().IsPercentOrCalc() || style.MinHeight().IsIntrinsic() || + style.MaxHeight().IsIntrinsic(); } LayoutUnit ResolveInlineLength(const NGConstraintSpace& constraint_space, const ComputedStyle& style, - const WTF::Optional<MinMaxSize>& min_and_max, + const base::Optional<MinMaxSize>& min_and_max, const Length& length, - LengthResolveType type) { - DCHECK(!length.IsMaxSizeNone()); + LengthResolveType type, + LengthResolvePhase phase) { DCHECK_GE(constraint_space.AvailableSize().inline_size, LayoutUnit()); DCHECK_GE(constraint_space.PercentageResolutionSize().inline_size, LayoutUnit()); @@ -53,12 +63,29 @@ LayoutUnit ResolveInlineLength(const NGConstraintSpace& constraint_space, if (constraint_space.IsAnonymous()) return constraint_space.AvailableSize().inline_size; + if (length.IsMaxSizeNone()) { + DCHECK_EQ(type, LengthResolveType::kMaxSize); + return LayoutUnit::Max(); + } + NGBoxStrut border_and_padding = ComputeBorders(constraint_space, style) + ComputePadding(constraint_space, style); if (type == LengthResolveType::kMinSize && length.IsAuto()) return border_and_padding.InlineSum(); + // Check if we shouldn't resolve a percentage/calc() if we are in the + // intrinsic sizes phase. + if (phase == LengthResolvePhase::kIntrinsic && length.IsPercentOrCalc()) { + // min-width/min-height should be "0", i.e. no min limit is applied. + if (type == LengthResolveType::kMinSize) + return border_and_padding.InlineSum(); + + // max-width/max-height becomes "infinity", i.e. no max limit is applied. + if (type == LengthResolveType::kMaxSize) + return LayoutUnit::Max(); + } + switch (length.GetType()) { case kAuto: case kFillAvailable: { @@ -116,27 +143,16 @@ LayoutUnit ResolveBlockLength(const NGConstraintSpace& constraint_space, const ComputedStyle& style, const Length& length, LayoutUnit content_size, - LengthResolveType type) { - DCHECK(!length.IsMaxSizeNone()); + LengthResolveType type, + LengthResolvePhase phase) { DCHECK_EQ(constraint_space.GetWritingMode(), style.GetWritingMode()); if (constraint_space.IsAnonymous()) return content_size; - if (length.IsPercentOrCalc() && - constraint_space.PercentageResolutionSize().block_size == - NGSizeIndefinite) { - // We're unable to resolve this percentage, since there's nothing to resolve - // it against. Height/width becomes 'auto', so we can just return the - // content size. Min-height/min-width becomes 0. Max-height/max-width - // becomes 'none', which means that we shouldn't impose any max limit, so - // return "infinity". - if (type == LengthResolveType::kContentSize) - return content_size; - if (type == LengthResolveType::kMaxSize) - return LayoutUnit::Max(); - DCHECK_EQ(type, LengthResolveType::kMinSize); - return LayoutUnit(); + if (length.IsMaxSizeNone()) { + DCHECK_EQ(type, LengthResolveType::kMaxSize); + return LayoutUnit::Max(); } NGBoxStrut border_and_padding = ComputeBorders(constraint_space, style) + @@ -145,6 +161,28 @@ LayoutUnit ResolveBlockLength(const NGConstraintSpace& constraint_space, if (type == LengthResolveType::kMinSize && length.IsAuto()) return border_and_padding.BlockSum(); + bool is_percentage_indefinite = + constraint_space.PercentageResolutionSize().block_size == + NGSizeIndefinite; + + // Check if we can't/shouldn't resolve a percentage/calc() - because the + // percentage resolution size is indefinite or because we are in the + // intrinsic sizes phase. + if ((phase == LengthResolvePhase::kIntrinsic || is_percentage_indefinite) && + length.IsPercentOrCalc()) { + // min-width/min-height should be "0", i.e. no min limit is applied. + if (type == LengthResolveType::kMinSize) + return border_and_padding.BlockSum(); + + // max-width/max-height becomes "infinity", i.e. no max limit is applied. + if (type == LengthResolveType::kMaxSize) + return LayoutUnit::Max(); + + // width/height becomes "auto", so we can just return the content size. + DCHECK_EQ(type, LengthResolveType::kContentSize); + return content_size; + } + switch (length.GetType()) { case kFillAvailable: { LayoutUnit content_size = constraint_space.AvailableSize().block_size; @@ -224,49 +262,109 @@ LayoutUnit ResolveMarginPaddingLength(const NGConstraintSpace& constraint_space, } MinMaxSize ComputeMinAndMaxContentContribution( + WritingMode writing_mode, const ComputedStyle& style, - const WTF::Optional<MinMaxSize>& min_and_max) { + const base::Optional<MinMaxSize>& min_and_max) { // Synthesize a zero-sized constraint space for passing to // ResolveInlineLength. - WritingMode writing_mode = style.GetWritingMode(); + // The constraint space's writing mode has to match the style, so we can't + // use the passed-in mode here. NGConstraintSpaceBuilder builder( - writing_mode, + style.GetWritingMode(), /* icb_size */ {NGSizeIndefinite, NGSizeIndefinite}); scoped_refptr<NGConstraintSpace> space = - builder.ToConstraintSpace(writing_mode); + builder.ToConstraintSpace(style.GetWritingMode()); + + LayoutUnit content_size = + min_and_max ? min_and_max->max_size : NGSizeIndefinite; MinMaxSize computed_sizes; - Length inline_size = style.LogicalWidth(); - if (inline_size.IsAuto()) { + Length inline_size = writing_mode == WritingMode::kHorizontalTb + ? style.Width() + : style.Height(); + if (inline_size.IsAuto() || inline_size.IsPercentOrCalc()) { CHECK(min_and_max.has_value()); computed_sizes = *min_and_max; } else { - computed_sizes.min_size = computed_sizes.max_size = - ResolveInlineLength(*space, style, min_and_max, inline_size, - LengthResolveType::kContentSize); + if (IsParallelWritingMode(writing_mode, style.GetWritingMode())) { + computed_sizes.min_size = computed_sizes.max_size = ResolveInlineLength( + *space, style, min_and_max, inline_size, + LengthResolveType::kContentSize, LengthResolvePhase::kIntrinsic); + } else { + computed_sizes.min_size = computed_sizes.max_size = ResolveBlockLength( + *space, style, inline_size, content_size, + LengthResolveType::kContentSize, LengthResolvePhase::kIntrinsic); + } } - Length max_length = style.LogicalMaxWidth(); - if (!max_length.IsMaxSizeNone()) { - LayoutUnit max = ResolveInlineLength(*space, style, min_and_max, max_length, - LengthResolveType::kMaxSize); - computed_sizes.min_size = std::min(computed_sizes.min_size, max); - computed_sizes.max_size = std::min(computed_sizes.max_size, max); + Length max_length = writing_mode == WritingMode::kHorizontalTb + ? style.MaxWidth() + : style.MaxHeight(); + LayoutUnit max; + if (IsParallelWritingMode(writing_mode, style.GetWritingMode())) { + max = ResolveInlineLength(*space, style, min_and_max, max_length, + LengthResolveType::kMaxSize, + LengthResolvePhase::kIntrinsic); + } else { + max = ResolveBlockLength(*space, style, max_length, content_size, + LengthResolveType::kMaxSize, + LengthResolvePhase::kIntrinsic); + } + computed_sizes.min_size = std::min(computed_sizes.min_size, max); + computed_sizes.max_size = std::min(computed_sizes.max_size, max); + + Length min_length = writing_mode == WritingMode::kHorizontalTb + ? style.MinWidth() + : style.MinHeight(); + LayoutUnit min; + if (IsParallelWritingMode(writing_mode, style.GetWritingMode())) { + min = ResolveInlineLength(*space, style, min_and_max, min_length, + LengthResolveType::kMinSize, + LengthResolvePhase::kIntrinsic); + } else { + min = ResolveBlockLength(*space, style, min_length, content_size, + LengthResolveType::kMinSize, + LengthResolvePhase::kIntrinsic); } - - LayoutUnit min = - ResolveInlineLength(*space, style, min_and_max, style.LogicalMinWidth(), - LengthResolveType::kMinSize); computed_sizes.min_size = std::max(computed_sizes.min_size, min); computed_sizes.max_size = std::max(computed_sizes.max_size, min); return computed_sizes; } +MinMaxSize ComputeMinAndMaxContentContribution( + WritingMode writing_mode, + NGLayoutInputNode node, + const MinMaxSizeInput& input, + const NGConstraintSpace* constraint_space) { + base::Optional<MinMaxSize> minmax; + if (NeedMinMaxSizeForContentContribution(writing_mode, node.Style())) { + scoped_refptr<NGConstraintSpace> adjusted_constraint_space; + if (constraint_space) { + // TODO(layout-ng): Check if our constraint space produces spec-compliant + // outputs. + // It is important to set a floats bfc offset so that we don't get a + // partial layout. It is also important that we shrink to fit, by + // definition. + NGConstraintSpaceBuilder builder(*constraint_space); + builder.SetAvailableSize(constraint_space->AvailableSize()) + .SetFloatsBfcOffset(NGBfcOffset()) + .SetIsShrinkToFit(true); + adjusted_constraint_space = + builder.ToConstraintSpace(node.Style().GetWritingMode()); + constraint_space = adjusted_constraint_space.get(); + } + minmax = node.ComputeMinMaxSize(writing_mode, input, constraint_space); + } + + return ComputeMinAndMaxContentContribution(writing_mode, node.Style(), + minmax); +} + LayoutUnit ComputeInlineSizeForFragment( const NGConstraintSpace& space, const ComputedStyle& style, - const WTF::Optional<MinMaxSize>& min_and_max) { + const base::Optional<MinMaxSize>& min_and_max) { if (space.IsFixedSizeInline()) return space.AvailableSize().inline_size; @@ -274,20 +372,17 @@ LayoutUnit ComputeInlineSizeForFragment( if (logical_width.IsAuto() && space.IsShrinkToFit()) logical_width = Length(kFitContent); - LayoutUnit extent = - ResolveInlineLength(space, style, min_and_max, logical_width, - LengthResolveType::kContentSize); - - Optional<LayoutUnit> max_length; - if (!style.LogicalMaxWidth().IsMaxSizeNone()) { - max_length = - ResolveInlineLength(space, style, min_and_max, style.LogicalMaxWidth(), - LengthResolveType::kMaxSize); - } - Optional<LayoutUnit> min_length = - ResolveInlineLength(space, style, min_and_max, style.LogicalMinWidth(), - LengthResolveType::kMinSize); - return ConstrainByMinMax(extent, min_length, max_length); + LayoutUnit extent = ResolveInlineLength( + space, style, min_and_max, logical_width, LengthResolveType::kContentSize, + LengthResolvePhase::kLayout); + + LayoutUnit max = ResolveInlineLength( + space, style, min_and_max, style.LogicalMaxWidth(), + LengthResolveType::kMaxSize, LengthResolvePhase::kLayout); + LayoutUnit min = ResolveInlineLength( + space, style, min_and_max, style.LogicalMinWidth(), + LengthResolveType::kMinSize, LengthResolvePhase::kLayout); + return ConstrainByMinMax(extent, min, max); } LayoutUnit ComputeBlockSizeForFragment( @@ -298,39 +393,39 @@ LayoutUnit ComputeBlockSizeForFragment( return constraint_space.AvailableSize().block_size; if (style.Display() == EDisplay::kTableCell) { - // All handled by the table layout code or not applicable + // All handled by the table layout code or not applicable. return content_size; } - LayoutUnit extent = - ResolveBlockLength(constraint_space, style, style.LogicalHeight(), - content_size, LengthResolveType::kContentSize); + LayoutUnit extent = ResolveBlockLength( + constraint_space, style, style.LogicalHeight(), content_size, + LengthResolveType::kContentSize, LengthResolvePhase::kLayout); if (extent == NGSizeIndefinite) { DCHECK_EQ(content_size, NGSizeIndefinite); return extent; } - Optional<LayoutUnit> max_length; - if (!style.LogicalMaxHeight().IsMaxSizeNone()) { - max_length = - ResolveBlockLength(constraint_space, style, style.LogicalMaxHeight(), - content_size, LengthResolveType::kMaxSize); - } - Optional<LayoutUnit> min_length = - ResolveBlockLength(constraint_space, style, style.LogicalMinHeight(), - content_size, LengthResolveType::kMinSize); - return ConstrainByMinMax(extent, min_length, max_length); + + LayoutUnit max = ResolveBlockLength( + constraint_space, style, style.LogicalMaxHeight(), content_size, + LengthResolveType::kMaxSize, LengthResolvePhase::kLayout); + LayoutUnit min = ResolveBlockLength( + constraint_space, style, style.LogicalMinHeight(), content_size, + LengthResolveType::kMinSize, LengthResolvePhase::kLayout); + + return ConstrainByMinMax(extent, min, max); } // Computes size for a replaced element. -NGLogicalSize ComputeReplacedSize(const NGLayoutInputNode& node, - const NGConstraintSpace& space, - const Optional<MinMaxSize>& child_minmax) { +NGLogicalSize ComputeReplacedSize( + const NGLayoutInputNode& node, + const NGConstraintSpace& space, + const base::Optional<MinMaxSize>& child_minmax) { DCHECK(node.IsReplaced()); NGLogicalSize replaced_size; NGLogicalSize default_intrinsic_size; - Optional<LayoutUnit> computed_inline_size; - Optional<LayoutUnit> computed_block_size; + base::Optional<LayoutUnit> computed_inline_size; + base::Optional<LayoutUnit> computed_block_size; NGLogicalSize aspect_ratio; node.IntrinsicSize(&default_intrinsic_size, &computed_inline_size, @@ -354,16 +449,16 @@ NGLogicalSize ComputeReplacedSize(const NGLayoutInputNode& node, } else { // inline_size is computed from block_size. replaced_size.inline_size = - ResolveBlockLength(space, style, block_length, - default_intrinsic_size.block_size, - LengthResolveType::kContentSize) * + ResolveBlockLength( + space, style, block_length, default_intrinsic_size.block_size, + LengthResolveType::kContentSize, LengthResolvePhase::kLayout) * aspect_ratio.inline_size / aspect_ratio.block_size; } } else { // inline_size is resolved directly. - replaced_size.inline_size = - ResolveInlineLength(space, style, child_minmax, inline_length, - LengthResolveType::kContentSize); + replaced_size.inline_size = ResolveInlineLength( + space, style, child_minmax, inline_length, + LengthResolveType::kContentSize, LengthResolvePhase::kLayout); } // Compute block size @@ -381,13 +476,14 @@ NGLogicalSize ComputeReplacedSize(const NGLayoutInputNode& node, // block_size is computed from inline_size. replaced_size.block_size = ResolveInlineLength(space, style, child_minmax, inline_length, - LengthResolveType::kContentSize) * + LengthResolveType::kContentSize, + LengthResolvePhase::kLayout) * aspect_ratio.block_size / aspect_ratio.inline_size; } } else { replaced_size.block_size = ResolveBlockLength( space, style, block_length, default_intrinsic_size.block_size, - LengthResolveType::kContentSize); + LengthResolveType::kContentSize, LengthResolvePhase::kLayout); } return replaced_size; } @@ -600,7 +696,8 @@ void ApplyAutoMargins(const ComputedStyle& style, LayoutUnit LineOffsetForTextAlign(ETextAlign text_align, TextDirection direction, - LayoutUnit space_left) { + LayoutUnit space_left, + LayoutUnit trailing_spaces_width) { bool is_ltr = IsLtr(direction); if (text_align == ETextAlign::kStart || text_align == ETextAlign::kJustify) text_align = is_ltr ? ETextAlign::kLeft : ETextAlign::kRight; @@ -619,19 +716,25 @@ LayoutUnit LineOffsetForTextAlign(ETextAlign text_align, } case ETextAlign::kRight: case ETextAlign::kWebkitRight: { + // In RTL, trailing spaces appear on the left of the line. + if (UNLIKELY(!is_ltr)) + return space_left - trailing_spaces_width; // Wide lines spill out of the block based off direction. // So even if text-align is right, if direction is LTR, wide lines // should overflow out of the right side of the block. - if (space_left > LayoutUnit() || !is_ltr) + if (space_left > LayoutUnit()) return space_left; return LayoutUnit(); } case ETextAlign::kCenter: case ETextAlign::kWebkitCenter: { - if (is_ltr || space_left > LayoutUnit()) + if (is_ltr) return (space_left / 2).ClampNegativeToZero(); + // In RTL, trailing spaces appear on the left of the line. + if (space_left > LayoutUnit()) + return (space_left / 2).ClampNegativeToZero() - trailing_spaces_width; // In RTL, wide lines should spill out to the left, same as kRight. - return space_left; + return space_left - trailing_spaces_width; } default: NOTREACHED(); @@ -640,13 +743,9 @@ LayoutUnit LineOffsetForTextAlign(ETextAlign text_align, } LayoutUnit ConstrainByMinMax(LayoutUnit length, - Optional<LayoutUnit> min, - Optional<LayoutUnit> max) { - if (max && length > max.value()) - length = max.value(); - if (min && length < min.value()) - length = min.value(); - return length; + LayoutUnit min, + LayoutUnit max) { + return std::max(min, std::min(length, max)); } NGBoxStrut CalculateBorderScrollbarPadding( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h index 7512770abf2..2960d4236fe 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h @@ -5,6 +5,7 @@ #ifndef NGLengthUtils_h #define NGLengthUtils_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/min_max_size.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" @@ -12,15 +13,25 @@ #include "third_party/blink/renderer/core/style/computed_style_constants.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { class ComputedStyle; class Length; +struct MinMaxSizeInput; class NGConstraintSpace; class NGBlockNode; class NGLayoutInputNode; +// LengthResolvePhase indicates what type of layout pass we are currently in. +// This changes how lengths are resolved. kIntrinsic must be used during the +// intrinsic sizes pass, and kLayout must be used during the layout pass. +enum class LengthResolvePhase { kIntrinsic, kLayout }; + +// LengthResolveType indicates what type length the function is being passed +// based on its CSS property. E.g. +// kMinSize - min-width / min-height +// kMaxSize - max-width / max-height +// kContentSize - width / height enum class LengthResolveType { kMinSize, kMaxSize, kContentSize }; // Whether the caller needs to compute min-content and max-content sizes to @@ -33,15 +44,20 @@ CORE_EXPORT bool NeedMinMaxSize(const ComputedStyle&); // Like NeedMinMaxSize, but for use when calling // ComputeMinAndMaxContentContribution. -CORE_EXPORT bool NeedMinMaxSizeForContentContribution(const ComputedStyle&); +// Because content contributions are commonly needed by a block's parent, +// we also take a writing mode here so we can check this in the parent's +// coordinate system. +CORE_EXPORT bool NeedMinMaxSizeForContentContribution(WritingMode mode, + const ComputedStyle&); // Convert an inline-axis length to a layout unit using the given constraint // space. CORE_EXPORT LayoutUnit ResolveInlineLength(const NGConstraintSpace&, const ComputedStyle&, - const WTF::Optional<MinMaxSize>&, + const base::Optional<MinMaxSize>&, const Length&, - LengthResolveType); + LengthResolveType, + LengthResolvePhase); // Convert a block-axis length to a layout unit using the given constraint // space and content size. @@ -49,7 +65,8 @@ CORE_EXPORT LayoutUnit ResolveBlockLength(const NGConstraintSpace&, const ComputedStyle&, const Length&, LayoutUnit content_size, - LengthResolveType); + LengthResolveType, + LengthResolvePhase); // Convert margin/border/padding length to a layout unit using the // given constraint space. @@ -63,9 +80,28 @@ CORE_EXPORT LayoutUnit ResolveMarginPaddingLength(const NGConstraintSpace&, // to zero) and that an auto inline size resolves to the respective min/max // content size. // Also, the min/max contribution does include the inline margins as well. +// Because content contributions are commonly needed by a block's parent, +// we also take a writing mode here so we can compute this in the parent's +// coordinate system. CORE_EXPORT MinMaxSize -ComputeMinAndMaxContentContribution(const ComputedStyle&, - const WTF::Optional<MinMaxSize>&); +ComputeMinAndMaxContentContribution(WritingMode writing_mode, + const ComputedStyle&, + const base::Optional<MinMaxSize>&); + +// A version of ComputeMinAndMaxContentContribution that does not require you +// to compute the min/max content size of the node. Instead, this function +// will compute it if necessary. +// writing_mode is the desired output writing mode (ie. often the writing mode +// of the parent); node is the node of which to compute the min/max content +// contribution. +// If a constraint space is provided, this function will convert it to the +// correct writing mode and otherwise make sure it is suitable for computing +// the desired value. +MinMaxSize ComputeMinAndMaxContentContribution( + WritingMode writing_mode, + NGLayoutInputNode node, + const MinMaxSizeInput& input, + const NGConstraintSpace* space = nullptr); // Resolves the given length to a layout unit, constraining it by the min // logical width and max logical width properties from the ComputedStyle @@ -73,7 +109,7 @@ ComputeMinAndMaxContentContribution(const ComputedStyle&, CORE_EXPORT LayoutUnit ComputeInlineSizeForFragment(const NGConstraintSpace&, const ComputedStyle&, - const WTF::Optional<MinMaxSize>&); + const base::Optional<MinMaxSize>&); // Resolves the given length to a layout unit, constraining it by the min // logical height and max logical height properties from the ComputedStyle @@ -83,9 +119,10 @@ CORE_EXPORT LayoutUnit ComputeBlockSizeForFragment(const NGConstraintSpace&, LayoutUnit content_size); // Computes intrinsic size for replaced elements. -CORE_EXPORT NGLogicalSize ComputeReplacedSize(const NGLayoutInputNode&, - const NGConstraintSpace&, - const Optional<MinMaxSize>&); +CORE_EXPORT NGLogicalSize +ComputeReplacedSize(const NGLayoutInputNode&, + const NGConstraintSpace&, + const base::Optional<MinMaxSize>&); // Based on available inline size, CSS computed column-width, CSS computed // column-count and CSS used column-gap, return CSS used column-count. @@ -150,11 +187,12 @@ CORE_EXPORT void ApplyAutoMargins(const ComputedStyle& child_style, // text-align, direction and amount of unused space. CORE_EXPORT LayoutUnit LineOffsetForTextAlign(ETextAlign, TextDirection, - LayoutUnit space_left); + LayoutUnit space_left, + LayoutUnit trailing_spaces_width); CORE_EXPORT LayoutUnit ConstrainByMinMax(LayoutUnit length, - Optional<LayoutUnit> min, - Optional<LayoutUnit> max); + LayoutUnit min, + LayoutUnit max); NGBoxStrut CalculateBorderScrollbarPadding( const NGConstraintSpace& constraint_space, @@ -163,7 +201,7 @@ NGBoxStrut CalculateBorderScrollbarPadding( inline NGLogicalSize CalculateBorderBoxSize( const NGConstraintSpace& constraint_space, const ComputedStyle& style, - const Optional<MinMaxSize>& min_and_max, + const base::Optional<MinMaxSize>& min_and_max, LayoutUnit block_content_size = NGSizeIndefinite) { return NGLogicalSize( ComputeInlineSizeForFragment(constraint_space, style, min_and_max), diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc index 5297591b7bb..63dbcd82766 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc @@ -41,21 +41,23 @@ class NGLengthUtilsTest : public testing::Test { LayoutUnit ResolveInlineLength( const Length& length, LengthResolveType type = LengthResolveType::kContentSize, - const WTF::Optional<MinMaxSize>& sizes = WTF::nullopt) { + LengthResolvePhase phase = LengthResolvePhase::kLayout, + const base::Optional<MinMaxSize>& sizes = base::nullopt) { scoped_refptr<NGConstraintSpace> constraint_space = ConstructConstraintSpace(200, 300); return ::blink::ResolveInlineLength(*constraint_space, *style_, sizes, - length, type); + length, type, phase); } LayoutUnit ResolveBlockLength( const Length& length, LengthResolveType type = LengthResolveType::kContentSize, + LengthResolvePhase phase = LengthResolvePhase::kLayout, LayoutUnit content_size = LayoutUnit()) { scoped_refptr<NGConstraintSpace> constraint_space = ConstructConstraintSpace(200, 300); return ::blink::ResolveBlockLength(*constraint_space, *style_, length, - content_size, type); + content_size, type, phase); } LayoutUnit ComputeInlineSizeForFragment( @@ -81,30 +83,39 @@ TEST_F(NGLengthUtilsTest, testResolveInlineLength) { EXPECT_EQ(LayoutUnit(60), ResolveInlineLength(Length(30, kPercent))); EXPECT_EQ(LayoutUnit(150), ResolveInlineLength(Length(150, kFixed))); EXPECT_EQ(LayoutUnit(0), - ResolveInlineLength(Length(kAuto), LengthResolveType::kMinSize)); + ResolveInlineLength(Length(kAuto), LengthResolveType::kMinSize, + LengthResolvePhase::kIntrinsic)); EXPECT_EQ(LayoutUnit(200), ResolveInlineLength(Length(kAuto))); EXPECT_EQ(LayoutUnit(200), ResolveInlineLength(Length(kFillAvailable))); - EXPECT_EQ(LayoutUnit(200), - ResolveInlineLength(Length(kAuto), LengthResolveType::kMaxSize)); - EXPECT_EQ(LayoutUnit(200), ResolveInlineLength(Length(kFillAvailable), - LengthResolveType::kMaxSize)); + EXPECT_EQ( + LayoutUnit::Max(), + ResolveInlineLength(Length(30, kPercent), LengthResolveType::kMaxSize, + LengthResolvePhase::kIntrinsic)); + EXPECT_EQ( + LayoutUnit(200), + ResolveInlineLength(Length(kFillAvailable), LengthResolveType::kMaxSize, + LengthResolvePhase::kIntrinsic)); MinMaxSize sizes; sizes.min_size = LayoutUnit(30); sizes.max_size = LayoutUnit(40); - EXPECT_EQ(LayoutUnit(30), - ResolveInlineLength(Length(kMinContent), - LengthResolveType::kContentSize, sizes)); - EXPECT_EQ(LayoutUnit(40), - ResolveInlineLength(Length(kMaxContent), - LengthResolveType::kContentSize, sizes)); - EXPECT_EQ(LayoutUnit(40), - ResolveInlineLength(Length(kFitContent), - LengthResolveType::kContentSize, sizes)); + EXPECT_EQ( + LayoutUnit(30), + ResolveInlineLength(Length(kMinContent), LengthResolveType::kContentSize, + LengthResolvePhase::kLayout, sizes)); + EXPECT_EQ( + LayoutUnit(40), + ResolveInlineLength(Length(kMaxContent), LengthResolveType::kContentSize, + LengthResolvePhase::kLayout, sizes)); + EXPECT_EQ( + LayoutUnit(40), + ResolveInlineLength(Length(kFitContent), LengthResolveType::kContentSize, + LengthResolvePhase::kLayout, sizes)); sizes.max_size = LayoutUnit(800); - EXPECT_EQ(LayoutUnit(200), - ResolveInlineLength(Length(kFitContent), - LengthResolveType::kContentSize, sizes)); + EXPECT_EQ( + LayoutUnit(200), + ResolveInlineLength(Length(kFitContent), LengthResolveType::kContentSize, + LengthResolvePhase::kLayout, sizes)); #if DCHECK_IS_ON() // This should fail a DCHECK. @@ -118,11 +129,8 @@ TEST_F(NGLengthUtilsTest, testResolveBlockLength) { EXPECT_EQ(LayoutUnit(0), ResolveBlockLength(Length(kAuto))); EXPECT_EQ(LayoutUnit(300), ResolveBlockLength(Length(kFillAvailable))); - EXPECT_EQ(LayoutUnit(0), - ResolveBlockLength(Length(kAuto), LengthResolveType::kContentSize)); - EXPECT_EQ(LayoutUnit(300), - ResolveBlockLength(Length(kFillAvailable), - LengthResolveType::kContentSize)); + EXPECT_EQ(LayoutUnit(0), ResolveBlockLength(Length(kAuto))); + EXPECT_EQ(LayoutUnit(300), ResolveBlockLength(Length(kFillAvailable))); } TEST_F(NGLengthUtilsTest, testComputeContentContribution) { @@ -130,43 +138,51 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { sizes.min_size = LayoutUnit(30); sizes.max_size = LayoutUnit(40); - MinMaxSize expected{LayoutUnit(), LayoutUnit()}; + MinMaxSize expected = sizes; style_->SetLogicalWidth(Length(30, kPercent)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); + expected = MinMaxSize{LayoutUnit(), LayoutUnit()}; style_->SetLogicalWidth(Length(kFillAvailable)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = MinMaxSize{LayoutUnit(150), LayoutUnit(150)}; style_->SetLogicalWidth(Length(150, kFixed)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = sizes; style_->SetLogicalWidth(Length(kAuto)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = MinMaxSize{LayoutUnit(430), LayoutUnit(440)}; style_->SetPaddingLeft(Length(400, kFixed)); auto sizes_padding400 = sizes; sizes_padding400 += LayoutUnit(400); - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding400)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding400)); - expected = MinMaxSize{LayoutUnit(100), LayoutUnit(100)}; + expected = MinMaxSize{LayoutUnit(30), LayoutUnit(40)}; style_->SetPaddingLeft(Length(0, kFixed)); style_->SetLogicalWidth(Length(CalculationValue::Create( PixelsAndPercent(100, -10), kValueRangeNonNegative))); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = MinMaxSize{LayoutUnit(30), LayoutUnit(35)}; style_->SetLogicalWidth(Length(kAuto)); style_->SetMaxWidth(Length(35, kFixed)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = MinMaxSize{LayoutUnit(80), LayoutUnit(80)}; style_->SetLogicalWidth(Length(50, kFixed)); style_->SetMinWidth(Length(80, kFixed)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); expected = MinMaxSize{LayoutUnit(150), LayoutUnit(150)}; style_ = ComputedStyle::Create(); @@ -174,35 +190,36 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { style_->SetPaddingLeft(Length(50, kFixed)); auto sizes_padding50 = sizes; sizes_padding50 += LayoutUnit(50); - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding50)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding50)); expected = MinMaxSize{LayoutUnit(100), LayoutUnit(100)}; style_->SetBoxSizing(EBoxSizing::kBorderBox); - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding50)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding50)); // Content size should never be below zero, even with box-sizing: border-box // and a large padding... expected = MinMaxSize{LayoutUnit(400), LayoutUnit(400)}; style_->SetPaddingLeft(Length(400, kFixed)); - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding400)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding400)); expected.min_size = expected.max_size = sizes.min_size + LayoutUnit(400); style_->SetLogicalWidth(Length(kMinContent)); - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding400)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding400)); style_->SetLogicalWidth(Length(100, kFixed)); style_->SetMaxWidth(Length(kMaxContent)); // Due to padding and box-sizing, width computes to 400px and max-width to // 440px, so the result is 400. expected = MinMaxSize{LayoutUnit(400), LayoutUnit(400)}; - EXPECT_EQ(expected, - ComputeMinAndMaxContentContribution(*style_, sizes_padding400)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes_padding400)); expected = MinMaxSize{LayoutUnit(40), LayoutUnit(40)}; style_->SetPaddingLeft(Length(0, kFixed)); - EXPECT_EQ(expected, ComputeMinAndMaxContentContribution(*style_, sizes)); + EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( + style_->GetWritingMode(), *style_, sizes)); } TEST_F(NGLengthUtilsTest, testComputeInlineSizeForFragment) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc index 25983fe0dcb..bd18926e320 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc @@ -8,11 +8,12 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" -#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.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_positioned_descendant.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -299,8 +300,8 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( .SetAvailableSize(container_info.content_size) .SetPercentageResolutionSize(container_info.content_size) .ToConstraintSpace(descendant_writing_mode); - Optional<MinMaxSize> min_max_size; - Optional<LayoutUnit> block_estimate; + base::Optional<MinMaxSize> min_max_size; + base::Optional<LayoutUnit> block_estimate; scoped_refptr<NGLayoutResult> layout_result = nullptr; @@ -311,11 +312,11 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( // This is a new formatting context, so whatever happened on the outside // doesn't concern us. MinMaxSizeInput zero_input; - min_max_size = - node.ComputeMinMaxSize(zero_input, descendant_constraint_space.get()); + min_max_size = node.ComputeMinMaxSize(descendant_writing_mode, zero_input, + descendant_constraint_space.get()); } - Optional<NGLogicalSize> replaced_size; + base::Optional<NGLogicalSize> replaced_size; if (descendant.node.IsReplaced()) { replaced_size = ComputeReplacedSize( descendant.node, *descendant_constraint_space, min_max_size); @@ -396,7 +397,7 @@ bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant( scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment( NGBlockNode descendant, const ContainingBlockInfo& container_info, - const Optional<LayoutUnit>& block_estimate, + const base::Optional<LayoutUnit>& block_estimate, const NGAbsolutePhysicalPosition node_position) { // As the block_estimate is always in the descendant's writing mode, we build // the constraint space in the descendant's writing mode. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h index 9f6eeb3faee..d5e0f691fe6 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h @@ -7,17 +7,20 @@ #include "third_party/blink/renderer/core/core_export.h" +#include "base/optional.h" +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h" #include "third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h" -#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" namespace blink { class ComputedStyle; +class LayoutObject; class NGBlockNode; class NGFragmentBuilder; class NGConstraintSpace; class NGLayoutResult; +struct NGOutOfFlowPositionedDescendant; // Helper class for positioning of out-of-flow blocks. // It should be used together with NGFragmentBuilder. @@ -79,7 +82,7 @@ class CORE_EXPORT NGOutOfFlowLayoutPart { scoped_refptr<NGLayoutResult> GenerateFragment( NGBlockNode node, const ContainingBlockInfo&, - const Optional<LayoutUnit>& block_estimate, + const base::Optional<LayoutUnit>& block_estimate, const NGAbsolutePhysicalPosition node_position); NGFragmentBuilder* container_builder_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc index c75b25b64ef..1fcbcd9b8dc 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc @@ -4,7 +4,8 @@ #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" -#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" +#include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc new file mode 100644 index 00000000000..f6b4a7edc95 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc @@ -0,0 +1,69 @@ +// 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/ng/ng_outline_utils.h" + +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/platform/geometry/layout_rect.h" + +namespace blink { + +class LayoutObject; + +void NGOutlineUtils::CollectDescendantOutlines( + const NGPhysicalBoxFragment& container, + const NGPhysicalOffset& paint_offset, + FragmentMap* anchor_fragment_map, + OutlineRectMap* outline_rect_map) { + DCHECK(anchor_fragment_map->IsEmpty()); + DCHECK(outline_rect_map->IsEmpty()); + if (!container.ChildrenInline()) + return; + for (auto& descendant : NGInlineFragmentTraversal::DescendantsOf(container)) { + if (!descendant.fragment->IsBox() || descendant.fragment->IsAtomicInline()) + continue; + + const ComputedStyle& descendant_style = descendant.fragment->Style(); + if (!descendant_style.HasOutline() || + descendant_style.Visibility() != EVisibility::kVisible) + continue; + if (descendant_style.OutlineStyleIsAuto() && + !LayoutTheme::GetTheme().ShouldDrawDefaultFocusRing( + descendant.fragment->GetNode(), descendant_style)) + continue; + + const LayoutObject* layout_object = descendant.fragment->GetLayoutObject(); + Vector<LayoutRect>* outline_rects; + auto iter = outline_rect_map->find(layout_object); + if (iter == outline_rect_map->end()) { + anchor_fragment_map->insert(layout_object, descendant.fragment.get()); + outline_rects = + &outline_rect_map->insert(layout_object, Vector<LayoutRect>()) + .stored_value->value; + } else { + outline_rects = &iter->value; + } + NGPhysicalOffsetRect outline( + paint_offset + descendant.offset_to_container_box, + descendant.fragment->Size()); + outline_rects->push_back(outline.ToLayoutRect()); + } +} + +NGPhysicalOffsetRect NGOutlineUtils::ComputeEnclosingOutline( + const ComputedStyle& style, + const Vector<LayoutRect>& outlines) { + LayoutRect combined_outline; + for (auto& outline : outlines) { + combined_outline.Unite(outline); + } + combined_outline.Inflate(style.OutlineOffset() + style.OutlineWidth()); + return NGPhysicalOffsetRect( + NGPhysicalOffset(combined_outline.X(), combined_outline.Y()), + NGPhysicalSize(combined_outline.Width(), combined_outline.Height())); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.h new file mode 100644 index 00000000000..d225d11947d --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUTLINE_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUTLINE_UTILS_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class ComputedStyle; +class LayoutObject; +class LayoutRect; +class NGPhysicalFragment; +class NGPhysicalBoxFragment; +struct NGPhysicalOffset; +struct NGPhysicalOffsetRect; + +class CORE_EXPORT NGOutlineUtils { + STATIC_ONLY(NGOutlineUtils); + + public: + using FragmentMap = HashMap<const LayoutObject*, const NGPhysicalFragment*>; + using OutlineRectMap = HashMap<const LayoutObject*, Vector<LayoutRect>>; + + static void CollectDescendantOutlines(const NGPhysicalBoxFragment& container, + const NGPhysicalOffset& paint_offset, + FragmentMap* anchor_fragment_map, + OutlineRectMap* outline_rect_map); + + // Union of all outline rectangles, including outline thickness. + static NGPhysicalOffsetRect ComputeEnclosingOutline( + const ComputedStyle& style, + const Vector<LayoutRect>& outlines); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUTLINE_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc index 7e86fa919c1..110e07f58f9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc @@ -12,6 +12,7 @@ #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" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { @@ -21,7 +22,7 @@ NGPageLayoutAlgorithm::NGPageLayoutAlgorithm(NGBlockNode node, : NGLayoutAlgorithm(node, space, ToNGBlockBreakToken(break_token)) {} scoped_refptr<NGLayoutResult> NGPageLayoutAlgorithm::Layout() { - Optional<MinMaxSize> min_max_size; + base::Optional<MinMaxSize> min_max_size; if (NeedMinMaxSize(ConstraintSpace(), Style())) min_max_size = ComputeMinMaxSize(MinMaxSizeInput()); NGBoxStrut border_scrollbar_padding = @@ -84,7 +85,7 @@ scoped_refptr<NGLayoutResult> NGPageLayoutAlgorithm::Layout() { return container_builder_.ToBoxFragment(); } -Optional<MinMaxSize> NGPageLayoutAlgorithm::ComputeMinMaxSize( +base::Optional<MinMaxSize> NGPageLayoutAlgorithm::ComputeMinMaxSize( const MinMaxSizeInput& input) const { NGBlockLayoutAlgorithm algorithm(Node(), ConstraintSpace()); return algorithm.ComputeMinMaxSize(input); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h index a8f4c406da8..86f0d67b003 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h @@ -28,7 +28,8 @@ class CORE_EXPORT NGPageLayoutAlgorithm scoped_refptr<NGLayoutResult> Layout() override; - Optional<MinMaxSize> ComputeMinMaxSize(const MinMaxSizeInput&) const override; + base::Optional<MinMaxSize> ComputeMinMaxSize( + const MinMaxSizeInput&) const override; private: scoped_refptr<NGConstraintSpace> CreateConstraintSpaceForPages( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index 16c291542fa..ff2e044f2f5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc @@ -5,15 +5,19 @@ #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/editing/position_with_affinity.h" -#include "third_party/blink/renderer/core/layout/layout_block.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h" namespace blink { NGPhysicalBoxFragment::NGPhysicalBoxFragment( LayoutObject* layout_object, const ComputedStyle& style, + NGStyleVariant style_variant, NGPhysicalSize size, Vector<scoped_refptr<NGPhysicalFragment>>& children, const NGPixelSnappedPhysicalBoxStrut& padding, @@ -25,6 +29,7 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment( scoped_refptr<NGBreakToken> break_token) : NGPhysicalContainerFragment(layout_object, style, + style_variant, size, kFragmentBox, box_type, @@ -36,6 +41,21 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment( DCHECK(baselines.IsEmpty()); // Ensure move semantics is used. is_old_layout_root_ = is_old_layout_root; border_edge_ = border_edges; + + // Compute visual contribution from descendant outlines. + NGOutlineUtils::FragmentMap anchor_fragment_map; + NGOutlineUtils::OutlineRectMap outline_rect_map; + NGOutlineUtils::CollectDescendantOutlines( + *this, NGPhysicalOffset(), &anchor_fragment_map, &outline_rect_map); + for (auto& anchor_iter : anchor_fragment_map) { + const NGPhysicalFragment* fragment = anchor_iter.value; + Vector<LayoutRect>* outline_rects = + &outline_rect_map.find(anchor_iter.key)->value; + descendant_outlines_.Unite(NGOutlineUtils::ComputeEnclosingOutline( + fragment->Style(), *outline_rects)); + } + GetLayoutObject()->SetOutlineMayBeAffectedByDescendants( + !descendant_outlines_.IsEmpty()); } const NGBaseline* NGPhysicalBoxFragment::Baseline( @@ -73,34 +93,74 @@ bool NGPhysicalBoxFragment::ShouldClipOverflow() const { ToLayoutBox(layout_object)->ShouldClipOverflow(); } -NGPhysicalOffsetRect NGPhysicalBoxFragment::SelfVisualRect() const { - const ComputedStyle& style = Style(); - if (!style.HasVisualOverflowingEffect()) - return {{}, Size()}; +LayoutRect NGPhysicalBoxFragment::OverflowClipRect( + const LayoutPoint& location, + OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const { + DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox()); + const LayoutBox* box = ToLayoutBox(GetLayoutObject()); + return box->OverflowClipRect(location, overlay_scrollbar_clip_behavior); +} +NGPhysicalOffsetRect NGPhysicalBoxFragment::ScrollableOverflow() const { + DCHECK(GetLayoutObject()); LayoutObject* layout_object = GetLayoutObject(); - DCHECK(layout_object); if (layout_object->IsBox()) { - LayoutRect visual_rect({}, Size().ToLayoutSize()); - visual_rect.Expand(style.BoxDecorationOutsets()); - - if (style.HasOutline()) { - Vector<LayoutRect> outline_rects; - // The result rects are in coordinates of this object's border box. - AddSelfOutlineRects(&outline_rects, LayoutPoint()); - LayoutRect rect = UnionRectEvenIfEmpty(outline_rects); - rect.Inflate(style.OutlineOutsetExtent()); - visual_rect.Unite(rect); + if (HasOverflowClip()) + return NGPhysicalOffsetRect({}, Size()); + // Legacy is the source of truth for overflow + return NGPhysicalOffsetRect( + ToLayoutBox(layout_object)->LayoutOverflowRect()); + } else if (layout_object->IsLayoutInline()) { + // Inline overflow is a union of child overflows. + NGPhysicalOffsetRect overflow({}, Size()); + for (const auto& child_fragment : Children()) { + NGPhysicalOffsetRect child_overflow = + child_fragment->ScrollableOverflow(); + child_overflow.offset += child_fragment->Offset(); + overflow.Unite(child_overflow); } - - return NGPhysicalOffsetRect(visual_rect); + return overflow; + } else { + NOTREACHED(); } + return NGPhysicalOffsetRect({}, Size()); +} + +IntSize NGPhysicalBoxFragment::ScrolledContentOffset() const { + DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox()); + const LayoutBox* box = ToLayoutBox(GetLayoutObject()); + return box->ScrolledContentOffset(); +} + +LayoutSize NGPhysicalBoxFragment::ScrollSize() const { + DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox()); + const LayoutBox* box = ToLayoutBox(GetLayoutObject()); + return LayoutSize(box->ScrollWidth(), box->ScrollHeight()); +} - // TODO(kojii): Implement for inline boxes. - DCHECK(layout_object->IsLayoutInline()); +NGPhysicalOffsetRect NGPhysicalBoxFragment::SelfVisualRect() const { + const ComputedStyle& style = Style(); LayoutRect visual_rect({}, Size().ToLayoutSize()); - visual_rect.Expand(style.BoxDecorationOutsets()); + DCHECK(GetLayoutObject()); + if (style.HasVisualOverflowingEffect()) { + if (GetLayoutObject()->IsBox()) { + visual_rect.Expand(style.BoxDecorationOutsets()); + if (style.HasOutline()) { + Vector<LayoutRect> outline_rects; + // The result rects are in coordinates of this object's border box. + AddSelfOutlineRects(&outline_rects, LayoutPoint()); + LayoutRect rect = UnionRectEvenIfEmpty(outline_rects); + rect.Inflate(style.OutlineOutsetExtent()); + visual_rect.Unite(rect); + } + } else { + // TODO(kojii): Implement for inline boxes. + DCHECK(GetLayoutObject()->IsLayoutInline()); + visual_rect.Expand(style.BoxDecorationOutsets()); + } + } + visual_rect.Unite(descendant_outlines_.ToLayoutRect()); return NGPhysicalOffsetRect(visual_rect); } @@ -108,19 +168,51 @@ void NGPhysicalBoxFragment::AddSelfOutlineRects( Vector<LayoutRect>* outline_rects, const LayoutPoint& additional_offset) const { DCHECK(outline_rects); - // TODO(kojii): Implement. This is quite incomplete yet. - - // bool include_block_visual_overflow = - // layout_object->OutlineRectsShouldIncludeBlockVisualOverflow(); - // LayoutRect outline_rect(additional_offset, Size().ToLayoutSize()); - // LayoutRect outline_rect = VisualRect(); - // outline_rect.MoveBy(additional_offset); - // outline_rect.Inflate(-Style().OutlineOffset()); - // outline_rect.Inflate(-Style().OutlineWidth()); - outline_rects->push_back(outline_rect); + + DCHECK(GetLayoutObject()); + if (!GetLayoutObject()->IsBox()) + return; + if (!Style().OutlineStyleIsAuto() || GetLayoutObject()->HasOverflowClip() || + ToLayoutBox(GetLayoutObject())->HasControlClip()) + return; + + // Focus outline includes chidlren + for (const auto& child : Children()) { + // List markers have no outline + if (child->IsListMarker()) + continue; + + if (child->IsLineBox()) { + // Traverse children of the linebox + Vector<NGPhysicalFragmentWithOffset> line_children = + NGInlineFragmentTraversal::DescendantsOf( + ToNGPhysicalLineBoxFragment(*child)); + for (const auto& line_child : line_children) { + Vector<LayoutRect> line_child_rects; + line_child_rects.push_back( + line_child.RectInContainerBox().ToLayoutRect()); + DCHECK(line_child.fragment->GetLayoutObject()); + line_child.fragment->GetLayoutObject()->LocalToAncestorRects( + line_child_rects, ToLayoutBoxModelObject(GetLayoutObject()), + child->Offset().ToLayoutPoint(), additional_offset); + if (!line_child_rects.IsEmpty()) + outline_rects->push_back(line_child_rects[0]); + } + } else { + DCHECK(child->GetLayoutObject()); + LayoutObject* child_layout = child->GetLayoutObject(); + Vector<LayoutRect> child_rects; + child_rects.push_back(child->VisualRectWithContents().ToLayoutRect()); + child_layout->LocalToAncestorRects( + child_rects, ToLayoutBoxModelObject(GetLayoutObject()), LayoutPoint(), + additional_offset); + if (!child_rects.IsEmpty()) + outline_rects->push_back(child_rects[0]); + } + } } NGPhysicalOffsetRect NGPhysicalBoxFragment::VisualRectWithContents() const { @@ -132,12 +224,19 @@ NGPhysicalOffsetRect NGPhysicalBoxFragment::VisualRectWithContents() const { return visual_rect; } -PositionWithAffinity NGPhysicalBoxFragment::PositionForPoint( - const NGPhysicalOffset& point) const { - if (!IsBlockFlow()) - return PositionForPointInInlineLevelBox(point); - - return PositionForPointInInlineFormattingContext(point); +UBiDiLevel NGPhysicalBoxFragment::BidiLevel() const { + // TODO(xiaochengh): Make the implementation more efficient. + DCHECK(IsInline()); + DCHECK(IsAtomicInline()); + const auto& inline_items = InlineItemsOfContainingBlock(); + const NGInlineItem* self_item = + std::find_if(inline_items.begin(), inline_items.end(), + [this](const NGInlineItem& item) { + return GetLayoutObject() == item.GetLayoutObject(); + }); + DCHECK(self_item); + DCHECK_NE(self_item, inline_items.end()); + return self_item->BidiLevel(); } scoped_refptr<NGPhysicalFragment> NGPhysicalBoxFragment::CloneWithoutOffset() @@ -146,9 +245,9 @@ scoped_refptr<NGPhysicalFragment> NGPhysicalBoxFragment::CloneWithoutOffset() Vector<NGBaseline> baselines_copy(baselines_); scoped_refptr<NGPhysicalFragment> physical_fragment = base::AdoptRef(new NGPhysicalBoxFragment( - layout_object_, Style(), size_, children_copy, padding_, - contents_visual_rect_, baselines_copy, BoxType(), is_old_layout_root_, - border_edge_, break_token_)); + layout_object_, Style(), StyleVariant(), size_, children_copy, + padding_, contents_visual_rect_, baselines_copy, BoxType(), + is_old_layout_root_, border_edge_, break_token_)); return physical_fragment; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h index 6bc38d92972..f2f8a3b88c7 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h" +#include "third_party/blink/renderer/platform/scroll/scroll_types.h" namespace blink { @@ -18,6 +19,7 @@ class CORE_EXPORT NGPhysicalBoxFragment final // This modifies the passed-in children vector. NGPhysicalBoxFragment(LayoutObject* layout_object, const ComputedStyle& style, + NGStyleVariant style_variant, NGPhysicalSize size, Vector<scoped_refptr<NGPhysicalFragment>>& children, const NGPixelSnappedPhysicalBoxStrut& padding, @@ -40,6 +42,17 @@ class CORE_EXPORT NGPhysicalBoxFragment final bool HasOverflowClip() const; bool ShouldClipOverflow() const; + NGPhysicalOffsetRect ScrollableOverflow() const; + + // TODO(layout-dev): These three methods delegate to legacy layout for now, + // update them to use LayoutNG based overflow information from the fragment + // and change them to use NG geometry types once LayoutNG supports overflow. + LayoutRect OverflowClipRect( + const LayoutPoint& location, + OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize) const; + IntSize ScrolledContentOffset() const; + LayoutSize ScrollSize() const; + // Visual rect of this box in the local coordinate. Does not include children // even if they overflow this box. NGPhysicalOffsetRect SelfVisualRect() const; @@ -50,13 +63,14 @@ class CORE_EXPORT NGPhysicalBoxFragment final void AddSelfOutlineRects(Vector<LayoutRect>*, const LayoutPoint& additional_offset) const; - PositionWithAffinity PositionForPoint(const NGPhysicalOffset&) const override; + UBiDiLevel BidiLevel() const override; scoped_refptr<NGPhysicalFragment> CloneWithoutOffset() const; private: Vector<NGBaseline> baselines_; NGPixelSnappedPhysicalBoxStrut padding_; + NGPhysicalOffsetRect descendant_outlines_; }; DEFINE_TYPE_CASTS(NGPhysicalBoxFragment, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc index 8d5989cba0f..d0b66f10ea9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc @@ -4,7 +4,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" -#include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc index bd925dd85e1..3c6fe5fbb87 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc @@ -4,57 +4,12 @@ #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h" -#include "third_party/blink/renderer/core/editing/position_with_affinity.h" -#include "third_party/blink/renderer/core/layout/layout_object.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h" -#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" -#include "third_party/blink/renderer/core/style/computed_style.h" - namespace blink { -namespace { - -NGLogicalOffset ChildLogicalOffsetInParent( - const NGPhysicalContainerFragment& parent, - const NGPhysicalFragment& child) { - return child.Offset().ConvertToLogical(parent.Style().GetWritingMode(), - parent.Style().Direction(), - parent.Size(), child.Size()); -} - -NGLogicalSize ChildLogicalSizeInParent( - const NGPhysicalContainerFragment& parent, - const NGPhysicalFragment& child) { - return NGFragment(parent.Style().GetWritingMode(), child).Size(); -} - -Optional<PositionWithAffinity> PositionForPointInChild( - const NGPhysicalFragment& child, - const NGPhysicalOffset& point) { - const NGPhysicalOffset& child_point = point - child.Offset(); - // We must fallback to legacy for old layout roots. We also fallback (to - // LayoutNGMixin::PositionForPoint()) for NG block layout, so that we can - // utilize LayoutBlock::PositionForPoint() that resolves the position in block - // layout. - // TODO(xiaochengh): Don't fallback to legacy for NG block layout. - const PositionWithAffinity result = - (child.IsBlockFlow() || child.IsOldLayoutRoot()) - ? child.GetLayoutObject()->PositionForPoint( - child_point.ToLayoutPoint()) - : child.PositionForPoint(child_point); - if (result.IsNotNull()) - return result; - return WTF::nullopt; -} - -} // namespace - NGPhysicalContainerFragment::NGPhysicalContainerFragment( LayoutObject* layout_object, const ComputedStyle& style, + NGStyleVariant style_variant, NGPhysicalSize size, NGFragmentType type, unsigned sub_type, @@ -63,7 +18,7 @@ NGPhysicalContainerFragment::NGPhysicalContainerFragment( scoped_refptr<NGBreakToken> break_token) : NGPhysicalFragment(layout_object, style, - NGStyleVariant::kStandard, + style_variant, size, type, sub_type, @@ -73,154 +28,4 @@ NGPhysicalContainerFragment::NGPhysicalContainerFragment( DCHECK(children.IsEmpty()); // Ensure move semantics is used. } -PositionWithAffinity -NGPhysicalContainerFragment::PositionForPointInInlineLevelBox( - const NGPhysicalOffset& point) const { - DCHECK(IsInline() || IsLineBox()) << ToString(); - DCHECK(!IsBlockFlow()) << ToString(); - - const NGLogicalOffset logical_point = point.ConvertToLogical( - Style().GetWritingMode(), Style().Direction(), Size(), NGPhysicalSize()); - const LayoutUnit inline_point = logical_point.inline_offset; - - // Stores the closest child before |point| in the inline direction. Used if we - // can't find any child |point| falls in to resolve the position. - const NGPhysicalFragment* closest_child_before = nullptr; - LayoutUnit closest_child_before_inline_offset = LayoutUnit::Min(); - - // Stores the closest child after |point| in the inline direction. Used if we - // can't find any child |point| falls in to resolve the position. - const NGPhysicalFragment* closest_child_after = nullptr; - LayoutUnit closest_child_after_inline_offset = LayoutUnit::Max(); - - for (const auto& child : children_) { - const LayoutUnit child_inline_min = - ChildLogicalOffsetInParent(*this, *child).inline_offset; - const LayoutUnit child_inline_max = - child_inline_min + ChildLogicalSizeInParent(*this, *child).inline_size; - - // Try to resolve if |point| falls in any child in inline direction. - if (inline_point >= child_inline_min && inline_point <= child_inline_max) { - if (auto child_position = PositionForPointInChild(*child, point)) - return child_position.value(); - continue; - } - - if (inline_point < child_inline_min) { - if (child_inline_min < closest_child_after_inline_offset) { - closest_child_after = child.get(); - closest_child_after_inline_offset = child_inline_min; - } - } - - if (inline_point > child_inline_max) { - if (child_inline_max > closest_child_before_inline_offset) { - closest_child_before = child.get(); - closest_child_before_inline_offset = child_inline_max; - } - } - } - - if (closest_child_after) { - if (auto child_position = - PositionForPointInChild(*closest_child_after, point)) - return child_position.value(); - } - - if (closest_child_before) { - if (auto child_position = - PositionForPointInChild(*closest_child_before, point)) - return child_position.value(); - } - - return PositionWithAffinity(); -} - -PositionWithAffinity -NGPhysicalContainerFragment::PositionForPointInInlineFormattingContext( - const NGPhysicalOffset& point) const { - DCHECK(IsBlockFlow()) << ToString(); - DCHECK(IsBox()) << ToString(); - DCHECK(ToNGPhysicalBoxFragment(this)->ChildrenInline()) << ToString(); - - const NGLogicalOffset logical_point = point.ConvertToLogical( - Style().GetWritingMode(), Style().Direction(), Size(), NGPhysicalSize()); - const LayoutUnit block_point = logical_point.block_offset; - - // Stores the closest line box child above |point| in the block direction. - // Used if we can't find any child |point| falls in to resolve the position. - const NGPhysicalLineBoxFragment* closest_line_before = nullptr; - LayoutUnit closest_line_before_block_offset = LayoutUnit::Min(); - - // Stores the closest line box child below |point| in the block direction. - // Used if we can't find any child |point| falls in to resolve the position. - const NGPhysicalLineBoxFragment* closest_line_after = nullptr; - LayoutUnit closest_line_after_block_offset = LayoutUnit::Max(); - - for (const auto& child : children_) { - // Try to resolve if |point| falls in a non-line-box child completely. - if (!child->IsLineBox()) { - if (point.left >= child->Offset().left && - point.left <= child->Offset().left + child->Size().width && - point.top >= child->Offset().top && - point.top <= child->Offset().top + child->Size().height) { - if (auto child_position = PositionForPointInChild(*child, point)) - return child_position.value(); - } - continue; - } - - if (!child->IsLineBox() || - ToNGPhysicalLineBoxFragment(*child).Children().IsEmpty()) - continue; - - const LayoutUnit line_min = - ChildLogicalOffsetInParent(*this, *child).block_offset; - const LayoutUnit line_max = - line_min + ChildLogicalSizeInParent(*this, *child).block_size; - - // Try to resolve if |point| falls in a line box in block direction. - // Hitting on line bottom doesn't count, to match legacy behavior. - // TODO(xiaochengh): Consider floats. - if (block_point >= line_min && block_point < line_max) { - if (auto child_position = PositionForPointInChild(*child, point)) - return child_position.value(); - continue; - } - - if (block_point < line_min) { - if (line_min < closest_line_after_block_offset) { - closest_line_after = ToNGPhysicalLineBoxFragment(child.get()); - closest_line_after_block_offset = line_min; - } - } - - if (block_point >= line_max) { - if (line_max > closest_line_before_block_offset) { - closest_line_before = ToNGPhysicalLineBoxFragment(child.get()); - closest_line_before_block_offset = line_max; - } - } - } - - if (closest_line_after) { - if (auto child_position = - PositionForPointInChild(*closest_line_after, point)) - return child_position.value(); - } - - if (closest_line_before) { - if (auto child_position = - PositionForPointInChild(*closest_line_before, point)) - return child_position.value(); - } - - // TODO(xiaochengh): Looking at only the closest lines may not be enough, - // when we have multiple lines full of pseudo elements. Fix it. - - // TODO(xiaochengh): Consider floats. - - return PositionWithAffinity(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h index be57b677a45..4d80ac63b5b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h @@ -7,8 +7,8 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -28,6 +28,7 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment { NGPhysicalContainerFragment( LayoutObject*, const ComputedStyle&, + NGStyleVariant, NGPhysicalSize, NGFragmentType, unsigned sub_type, @@ -35,11 +36,6 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment { const NGPhysicalOffsetRect& contents_visual_rect, scoped_refptr<NGBreakToken> = nullptr); - PositionWithAffinity PositionForPointInInlineLevelBox( - const NGPhysicalOffset&) const; - PositionWithAffinity PositionForPointInInlineFormattingContext( - const NGPhysicalOffset&) const; - Vector<scoped_refptr<NGPhysicalFragment>> children_; NGPhysicalOffsetRect contents_visual_rect_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc index c8e9c220604..675d046c7c4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc @@ -5,11 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" #include "third_party/blink/renderer/core/layout/layout_block.h" -#include "third_party/blink/renderer/core/layout/layout_object_inlines.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -241,9 +242,12 @@ void NGPhysicalFragment::Destroy() const { const ComputedStyle& NGPhysicalFragment::Style() const { DCHECK(style_); + // TODO(kojii): Returning |style_| locks the style at the layout time, and + // will not be updated when its base style is updated later. Line styles and + // ellipsis styles have this problem. if (!GetLayoutObject()) return *style_; - switch ((NGStyleVariant)style_variant_) { + switch (StyleVariant()) { case NGStyleVariant::kStandard: return *GetLayoutObject()->Style(); case NGStyleVariant::kFirstLine: @@ -285,7 +289,9 @@ bool NGPhysicalFragment::IsPlacedByLayoutNG() const { if (!layout_object_) return false; const LayoutBlock* container = layout_object_->ContainingBlock(); - return container && container->IsLayoutNGMixin(); + if (!container) + return false; + return container->IsLayoutNGMixin() || container->IsLayoutNGFlexibleBox(); } NGPixelSnappedPhysicalBoxStrut NGPhysicalFragment::BorderWidths() const { @@ -324,6 +330,19 @@ NGPhysicalOffsetRect NGPhysicalFragment::VisualRectWithContents() const { return {{}, Size()}; } +NGPhysicalOffsetRect NGPhysicalFragment::ScrollableOverflow() const { + switch (Type()) { + case NGPhysicalFragment::kFragmentBox: + return ToNGPhysicalBoxFragment(*this).ScrollableOverflow(); + case NGPhysicalFragment::kFragmentText: + return {{}, Size()}; + case NGPhysicalFragment::kFragmentLineBox: + return ToNGPhysicalLineBoxFragment(*this).ScrollableOverflow(); + } + NOTREACHED(); + return {{}, Size()}; +} + void NGPhysicalFragment::PropagateContentsVisualRect( NGPhysicalOffsetRect* parent_visual_rect) const { NGPhysicalOffsetRect visual_rect = VisualRectWithContents(); @@ -331,6 +350,38 @@ void NGPhysicalFragment::PropagateContentsVisualRect( parent_visual_rect->Unite(visual_rect); } +const Vector<NGInlineItem>& NGPhysicalFragment::InlineItemsOfContainingBlock() + const { + DCHECK(IsInline()); + DCHECK(GetLayoutObject()); + LayoutBlockFlow* block_flow = + GetLayoutObject()->Parent()->EnclosingNGBlockFlow(); + // TODO(xiaochengh): Code below is copied from ng_offset_mapping.cc with + // modification. Unify them. + DCHECK(block_flow); + DCHECK(block_flow->ChildrenInline()); + NGBlockNode block_node = NGBlockNode(block_flow); + DCHECK(block_node.CanUseNewLayout()); + NGLayoutInputNode node = block_node.FirstChild(); + DCHECK(node); + DCHECK(node.IsInline()); + + // TODO(xiaochengh): Handle ::first-line. + return ToNGInlineNode(node).ItemsData(false).items; +} + +UBiDiLevel NGPhysicalFragment::BidiLevel() const { + NOTREACHED(); + return 0; +} + +TextDirection NGPhysicalFragment::ResolvedDirection() const { + DCHECK(IsInline()); + DCHECK(IsText() || IsAtomicInline()); + // TODO(xiaochengh): Store direction in |base_direction_| flag. + return DirectionFromLevel(BidiLevel()); +} + scoped_refptr<NGPhysicalFragment> NGPhysicalFragment::CloneWithoutOffset() const { switch (Type()) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h index 9591e027133..1932d3ebefb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h @@ -9,17 +9,21 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/editing/forward.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h" +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h" -#include "third_party/blink/renderer/platform/geometry/layout_rect.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" + +#include <unicode/ubidi.h> namespace blink { class ComputedStyle; class LayoutObject; class Node; -struct NGPhysicalOffsetRect; +class NGBreakToken; +class NGInlineItem; struct NGPixelSnappedPhysicalBoxStrut; class PaintLayer; @@ -130,17 +134,28 @@ class CORE_EXPORT NGPhysicalFragment // Returns the border-box size. NGPhysicalSize Size() const { return size_; } + // Returns the rect in the local coordinate of this fragment; i.e., offset is + // (0, 0). + NGPhysicalOffsetRect LocalRect() const { return {{}, size_}; } + // Bitmask for border edges, see NGBorderEdges::Physical. unsigned BorderEdges() const { return border_edge_; } NGPixelSnappedPhysicalBoxStrut BorderWidths() const; // Returns the offset relative to the parent fragment's content-box. NGPhysicalOffset Offset() const { - DCHECK(is_placed_); + DCHECK(is_placed_) << "this=" << this << " for layout object " + << layout_object_; return offset_; } NGBreakToken* BreakToken() const { return break_token_.get(); } + NGStyleVariant StyleVariant() const { + return static_cast<NGStyleVariant>(style_variant_); + } + bool UsesFirstLineStyle() const { + return StyleVariant() == NGStyleVariant::kFirstLine; + } const ComputedStyle& Style() const; Node* GetNode() const; @@ -160,6 +175,9 @@ class CORE_EXPORT NGPhysicalFragment // VisualRect of itself including contents, in the local coordinate. NGPhysicalOffsetRect VisualRectWithContents() const; + // Scrollable overflow. including contents, in the local coordinate. + NGPhysicalOffsetRect ScrollableOverflow() const; + // Unite visual rect to propagate to parent's ContentsVisualRect. void PropagateContentsVisualRect(NGPhysicalOffsetRect*) const; @@ -172,8 +190,12 @@ class CORE_EXPORT NGPhysicalFragment bool IsPlaced() const { return is_placed_; } - virtual PositionWithAffinity PositionForPoint( - const NGPhysicalOffset&) const = 0; + // Returns the bidi level of a text or atomic inline fragment. + virtual UBiDiLevel BidiLevel() const; + + // Returns the resolved direction of a text or atomic inline fragment. Not to + // be confused with the CSS 'direction' property. + virtual TextDirection ResolvedDirection() const; scoped_refptr<NGPhysicalFragment> CloneWithoutOffset() const; @@ -208,6 +230,8 @@ class CORE_EXPORT NGPhysicalFragment unsigned sub_type, scoped_refptr<NGBreakToken> break_token = nullptr); + const Vector<NGInlineItem>& InlineItemsOfContainingBlock() const; + LayoutObject* layout_object_; scoped_refptr<const ComputedStyle> style_; NGPhysicalSize size_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.cc index 71fdb1aa748..ef9bec90325 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.cc @@ -13,4 +13,8 @@ NGPositionedFloat::NGPositionedFloat( const NGBfcOffset& bfc_offset) : layout_result(layout_result), bfc_offset(bfc_offset) {} +// Define the destructor here, so that we can forward-declare more in the header +// file. +NGPositionedFloat::~NGPositionedFloat() = default; + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h index 7078151c948..09ac96614e9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h @@ -5,16 +5,19 @@ #ifndef NGPositionedFloat_h #define NGPositionedFloat_h +#include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h" -#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" namespace blink { +class NGLayoutResult; + // Contains the information necessary for copying back data to a FloatingObject. struct CORE_EXPORT NGPositionedFloat { NGPositionedFloat(scoped_refptr<NGLayoutResult> layout_result, const NGBfcOffset& bfc_offset); + ~NGPositionedFloat(); scoped_refptr<NGLayoutResult> layout_result; NGBfcOffset bfc_offset; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc index f058af41ef3..948b54dc2a0 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc @@ -4,11 +4,11 @@ #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h" +#include "base/optional.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/length_functions.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { @@ -21,7 +21,7 @@ NGLogicalOffset ComputeRelativeOffset(const ComputedStyle& child_style, NGPhysicalSize container_size = container_logical_size.ConvertToPhysical(container_writing_mode); - Optional<LayoutUnit> left, right, top, bottom; + base::Optional<LayoutUnit> left, right, top, bottom; if (!child_style.Left().IsAuto()) left = ValueForLength(child_style.Left(), container_size.width); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc index 2bbc9546f75..a06aa67c30f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc @@ -21,11 +21,10 @@ bool ShouldShrinkToFit(const ComputedStyle& parent_style, !is_in_parallel_flow; } -bool AdjustToClearance(const WTF::Optional<LayoutUnit>& clearance_offset, - NGBfcOffset* offset) { +bool AdjustToClearance(LayoutUnit clearance_offset, NGBfcOffset* offset) { DCHECK(offset); - if (clearance_offset && clearance_offset.value() > offset->block_offset) { - offset->block_offset = clearance_offset.value(); + if (clearance_offset > offset->block_offset) { + offset->block_offset = clearance_offset; return true; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h index 71c92ac9cc3..66262ad16c3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h @@ -5,9 +5,9 @@ #ifndef NGSpaceUtils_h #define NGSpaceUtils_h +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/layout_unit.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" namespace blink { @@ -21,9 +21,8 @@ bool ShouldShrinkToFit(const ComputedStyle& parent_style, const ComputedStyle& style); // Adjusts {@code offset} to the clearance line. -CORE_EXPORT bool AdjustToClearance( - const WTF::Optional<LayoutUnit>& clearance_offset, - NGBfcOffset* offset); +CORE_EXPORT bool AdjustToClearance(LayoutUnit clearance_offset, + NGBfcOffset* offset); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc index 5e1c158c550..e8853136e82 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc @@ -4,10 +4,10 @@ #include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h" -#include "third_party/blink/renderer/core/layout/line/root_inline_box.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/font_metrics.h" namespace blink { @@ -16,7 +16,8 @@ int NGTextDecorationOffset::ComputeUnderlineOffsetForUnder( float text_decoration_thickness, FontVerticalPositionType position_type) const { LayoutUnit offset = LayoutUnit::Max(); - FontBaseline baseline_type = text_fragment_.BaselineType(); + const ComputedStyle& style = text_fragment_.Style(); + FontBaseline baseline_type = style.GetFontBaseline(); if (decorating_box_) { // TODO(eae): Replace with actual baseline once available. @@ -32,8 +33,7 @@ int NGTextDecorationOffset::ComputeUnderlineOffsetForUnder( if (offset == LayoutUnit::Max()) { // TODO(layout-dev): How do we compute the baseline offset with a // decorating_box? - const SimpleFontData* font_data = - text_fragment_.Style().GetFont().PrimaryFont(); + const SimpleFontData* font_data = style.GetFont().PrimaryFont(); if (!font_data) return 0; offset = font_data->GetFontMetrics().Ascent(baseline_type) - diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h index 91936a8b651..68f45a41e8f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h @@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_TEXT_DECORATION_OFFSET_H_ #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/api/line_layout_item.h" #include "third_party/blink/renderer/core/layout/text_decoration_offset_base.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.cc index ffab2fa7420..285bee17df4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.cc @@ -4,10 +4,31 @@ #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { +// Define the constructor and destructor here, so that we can forward-declare +// more in the header file. +NGUnpositionedFloat::NGUnpositionedFloat(const NGBoxStrut& margins, + const NGLogicalSize& available_size, + const NGLogicalSize& percentage_size, + LayoutUnit origin_bfc_line_offset, + LayoutUnit bfc_line_offset, + NGBlockNode node, + NGBlockBreakToken* token) + : node(node), + token(token), + available_size(available_size), + percentage_size(percentage_size), + origin_bfc_line_offset(origin_bfc_line_offset), + bfc_line_offset(bfc_line_offset), + margins(margins) {} + +NGUnpositionedFloat::~NGUnpositionedFloat() = default; + bool NGUnpositionedFloat::IsLeft() const { return node.Style().Floating() == EFloat::kLeft; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h index 82d662ab42a..761bcc657a2 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h @@ -8,13 +8,15 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" -#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" namespace blink { +class NGBlockBreakToken; +class NGLayoutResult; + // Struct that keeps all information needed to position floats in LayoutNG. struct CORE_EXPORT NGUnpositionedFloat : public RefCounted<NGUnpositionedFloat> { @@ -32,6 +34,8 @@ struct CORE_EXPORT NGUnpositionedFloat bfc_line_offset, node, token)); } + ~NGUnpositionedFloat(); + NGBlockNode node; scoped_refptr<NGBlockBreakToken> token; @@ -68,14 +72,7 @@ struct CORE_EXPORT NGUnpositionedFloat LayoutUnit origin_bfc_line_offset, LayoutUnit bfc_line_offset, NGBlockNode node, - NGBlockBreakToken* token) - : node(node), - token(token), - available_size(available_size), - percentage_size(percentage_size), - origin_bfc_line_offset(origin_bfc_line_offset), - bfc_line_offset(bfc_line_offset), - margins(margins) {} + NGBlockBreakToken* token); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc b/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc index 7ecd208a2c8..04e9d99d1ae 100644 --- a/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc @@ -18,16 +18,9 @@ namespace blink { using Corner = ScrollAnchor::Corner; -typedef bool TestParamRootLayerScrolling; -class ScrollAnchorTest - : public testing::WithParamInterface<TestParamRootLayerScrolling>, - private ScopedRootLayerScrollingForTest, - private ScopedScrollAnchoringForTest, - public RenderingTest { +class ScrollAnchorTest : public RenderingTest { public: - ScrollAnchorTest() - : ScopedRootLayerScrollingForTest(GetParam()), - ScopedScrollAnchoringForTest(true) {} + ScrollAnchorTest() {} protected: void Update() { @@ -82,16 +75,11 @@ class ScrollAnchorTest GetDocument().QuerySelectorAll(AtomicString(serialized.selector)); EXPECT_EQ(ele_list->length(), 1u); } - - private: - std::unique_ptr<ScopedScrollAnchoringForTest> scroll_anchoring_; }; -INSTANTIATE_TEST_CASE_P(All, ScrollAnchorTest, testing::Bool()); - // TODO(ymalik): Currently, this should be the first test in the file to avoid // failure when running with other tests. Dig into this more and fix. -TEST_P(ScrollAnchorTest, UMAMetricUpdated) { +TEST_F(ScrollAnchorTest, UMAMetricUpdated) { HistogramTester histogram_tester; SetBodyInnerHTML(R"HTML( <style> body { height: 1000px } div { height: 100px } </style> @@ -156,7 +144,7 @@ TEST_P(ScrollAnchorTest, UMAMetricUpdated) { // TODO(skobes): Convert this to web-platform-tests when visual viewport API is // launched (http://crbug.com/635031). -TEST_P(ScrollAnchorTest, VisualViewportAnchors) { +TEST_F(ScrollAnchorTest, VisualViewportAnchors) { SetBodyInnerHTML(R"HTML( <style> * { font-size: 1.2em; font-family: sans-serif; } @@ -196,7 +184,7 @@ TEST_P(ScrollAnchorTest, VisualViewportAnchors) { // Test that a non-anchoring scroll on scroller clears scroll anchors for all // parent scrollers. -TEST_P(ScrollAnchorTest, ClearScrollAnchorsOnAncestors) { +TEST_F(ScrollAnchorTest, ClearScrollAnchorsOnAncestors) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px } div { height: 200px } @@ -223,7 +211,7 @@ TEST_P(ScrollAnchorTest, ClearScrollAnchorsOnAncestors) { EXPECT_EQ(nullptr, GetScrollAnchor(viewport).AnchorObject()); } -TEST_P(ScrollAnchorTest, AncestorClearingWithSiblingReference) { +TEST_F(ScrollAnchorTest, AncestorClearingWithSiblingReference) { SetBodyInnerHTML(R"HTML( <style> .scroller { @@ -265,7 +253,7 @@ TEST_P(ScrollAnchorTest, AncestorClearingWithSiblingReference) { Update(); } -TEST_P(ScrollAnchorTest, FractionalOffsetsAreRoundedBeforeComparing) { +TEST_F(ScrollAnchorTest, FractionalOffsetsAreRoundedBeforeComparing) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px } </style> <div id='block1' style='height: 50.4px'>abc</div> @@ -282,7 +270,7 @@ TEST_P(ScrollAnchorTest, FractionalOffsetsAreRoundedBeforeComparing) { EXPECT_EQ(101, viewport->ScrollOffsetInt().Height()); } -TEST_P(ScrollAnchorTest, AvoidStickyAnchorWhichMovesWithScroll) { +TEST_F(ScrollAnchorTest, AvoidStickyAnchorWhichMovesWithScroll) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px } </style> <div id='block1' style='height: 50px'>abc</div> @@ -300,7 +288,7 @@ TEST_P(ScrollAnchorTest, AvoidStickyAnchorWhichMovesWithScroll) { EXPECT_EQ(60, viewport->ScrollOffsetInt().Height()); } -TEST_P(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) { +TEST_F(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; width: 500px; height: 400px; } @@ -336,7 +324,7 @@ TEST_P(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) { // Verify that a nested scroller with a div that has its own PaintLayer can be // removed without causing a crash. This test passes if it doesn't crash. -TEST_P(ScrollAnchorTest, RemoveScrollerWithLayerInScrollingDiv) { +TEST_F(ScrollAnchorTest, RemoveScrollerWithLayerInScrollingDiv) { SetBodyInnerHTML(R"HTML( <style> body { height: 2000px } @@ -377,7 +365,7 @@ TEST_P(ScrollAnchorTest, RemoveScrollerWithLayerInScrollingDiv) { Update(); } -TEST_P(ScrollAnchorTest, FlexboxDelayedClampingAlsoDelaysAdjustment) { +TEST_F(ScrollAnchorTest, FlexboxDelayedClampingAlsoDelaysAdjustment) { SetBodyInnerHTML(R"HTML( <style> html { overflow: hidden; } @@ -408,7 +396,7 @@ TEST_P(ScrollAnchorTest, FlexboxDelayedClampingAlsoDelaysAdjustment) { EXPECT_EQ(150, ScrollerForElement(scroller)->ScrollOffsetInt().Height()); } -TEST_P(ScrollAnchorTest, FlexboxDelayedAdjustmentRespectsSANACLAP) { +TEST_F(ScrollAnchorTest, FlexboxDelayedAdjustmentRespectsSANACLAP) { SetBodyInnerHTML(R"HTML( <style> html { overflow: hidden; } @@ -442,7 +430,7 @@ TEST_P(ScrollAnchorTest, FlexboxDelayedAdjustmentRespectsSANACLAP) { // TODO(skobes): Convert this to web-platform-tests when document.rootScroller // is launched (http://crbug.com/505516). -TEST_P(ScrollAnchorTest, NonDefaultRootScroller) { +TEST_F(ScrollAnchorTest, NonDefaultRootScroller) { SetBodyInnerHTML(R"HTML( <style> ::-webkit-scrollbar { @@ -500,7 +488,7 @@ TEST_P(ScrollAnchorTest, NonDefaultRootScroller) { // This test verifies that scroll anchoring is disabled when the document is in // printing mode. -TEST_P(ScrollAnchorTest, AnchoringDisabledForPrinting) { +TEST_F(ScrollAnchorTest, AnchoringDisabledForPrinting) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px } div { height: 100px } </style> <div id='block1'>abc</div> @@ -517,7 +505,7 @@ TEST_P(ScrollAnchorTest, AnchoringDisabledForPrinting) { EXPECT_EQ(nullptr, GetScrollAnchor(viewport).AnchorObject()); } -TEST_P(ScrollAnchorTest, SerializeAnchorSimple) { +TEST_F(ScrollAnchorTest, SerializeAnchorSimple) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -530,7 +518,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorSimple) { ValidateSerializedAnchor("#block2", LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorUsesTagname) { +TEST_F(ScrollAnchorTest, SerializeAnchorUsesTagname) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -545,7 +533,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorUsesTagname) { ValidateSerializedAnchor("#ancestor>span", LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorSetsIsAnchorBit) { +TEST_F(ScrollAnchorTest, SerializeAnchorSetsIsAnchorBit) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -573,7 +561,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorSetsIsAnchorBit) { ScrollLayoutViewport(ScrollOffset(0, 25)); } -TEST_P(ScrollAnchorTest, SerializeAnchorSetsSavedRelativeOffset) { +TEST_F(ScrollAnchorTest, SerializeAnchorSetsSavedRelativeOffset) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -590,7 +578,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorSetsSavedRelativeOffset) { EXPECT_EQ(LayoutViewport()->ScrollOffsetInt().Height(), 250); } -TEST_P(ScrollAnchorTest, SerializeAnchorUsesClassname) { +TEST_F(ScrollAnchorTest, SerializeAnchorUsesClassname) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -605,7 +593,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorUsesClassname) { ValidateSerializedAnchor("#ancestor>.barbaz", LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorUsesNthChild) { +TEST_F(ScrollAnchorTest, SerializeAnchorUsesNthChild) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -620,7 +608,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorUsesNthChild) { ValidateSerializedAnchor("#ancestor>:nth-child(2)", LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorUsesLeastSpecificSelector) { +TEST_F(ScrollAnchorTest, SerializeAnchorUsesLeastSpecificSelector) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -642,7 +630,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorUsesLeastSpecificSelector) { LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorWithNoIdAttribute) { +TEST_F(ScrollAnchorTest, SerializeAnchorWithNoIdAttribute) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -664,7 +652,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorWithNoIdAttribute) { LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorChangesWithScroll) { +TEST_F(ScrollAnchorTest, SerializeAnchorChangesWithScroll) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -688,7 +676,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorChangesWithScroll) { ValidateSerializedAnchor("#ancestor>.foobar", LayoutPoint(0, -1)); } -TEST_P(ScrollAnchorTest, SerializeAnchorVerticalWritingMode) { +TEST_F(ScrollAnchorTest, SerializeAnchorVerticalWritingMode) { SetBodyInnerHTML(R"HTML( <style> body { @@ -713,7 +701,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorVerticalWritingMode) { ValidateSerializedAnchor("html>body>.barbaz", LayoutPoint(-50, 0)); } -TEST_P(ScrollAnchorTest, SerializeAnchorQualifiedTagName) { +TEST_F(ScrollAnchorTest, SerializeAnchorQualifiedTagName) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -727,7 +715,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorQualifiedTagName) { ValidateSerializedAnchor("html>body>ns\\:div", LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorLimitsSelectorLength) { +TEST_F(ScrollAnchorTest, SerializeAnchorLimitsSelectorLength) { StringBuilder builder; builder.Append("<style> body { height: 1000px; margin: 0; }</style>"); builder.Append("<div style='height:100px'>foobar</div>"); @@ -744,7 +732,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorLimitsSelectorLength) { EXPECT_FALSE(serialized.IsValid()); } -TEST_P(ScrollAnchorTest, SerializeAnchorIgnoresDuplicatedId) { +TEST_F(ScrollAnchorTest, SerializeAnchorIgnoresDuplicatedId) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -762,7 +750,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorIgnoresDuplicatedId) { LayoutPoint(0, -50)); } -TEST_P(ScrollAnchorTest, SerializeAnchorFailsForPseudoElement) { +TEST_F(ScrollAnchorTest, SerializeAnchorFailsForPseudoElement) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -776,7 +764,7 @@ TEST_P(ScrollAnchorTest, SerializeAnchorFailsForPseudoElement) { EXPECT_FALSE(GetScrollAnchor(LayoutViewport()).AnchorObject()); } -TEST_P(ScrollAnchorTest, RestoreAnchorSimple) { +TEST_F(ScrollAnchorTest, RestoreAnchorSimple) { SetBodyInnerHTML( "<style> body { height: 1000px; margin: 0; } div { height: 100px } " "</style>" @@ -798,7 +786,7 @@ TEST_P(ScrollAnchorTest, RestoreAnchorSimple) { EXPECT_EQ(LayoutViewport()->ScrollOffsetInt().Height(), 50); } -TEST_P(ScrollAnchorTest, RestoreAnchorNonTrivialSelector) { +TEST_F(ScrollAnchorTest, RestoreAnchorNonTrivialSelector) { SetBodyInnerHTML(R"HTML( <style> body { height: 1000px; margin: 0; } @@ -828,7 +816,7 @@ TEST_P(ScrollAnchorTest, RestoreAnchorNonTrivialSelector) { EXPECT_EQ(LayoutViewport()->ScrollOffsetInt().Height(), 450); } -TEST_P(ScrollAnchorTest, RestoreAnchorFailsForInvalidSelectors) { +TEST_F(ScrollAnchorTest, RestoreAnchorFailsForInvalidSelectors) { SetBodyInnerHTML( "<style> body { height: 1000px; margin: 0; } div { height: 100px } " "</style>" @@ -856,7 +844,7 @@ TEST_P(ScrollAnchorTest, RestoreAnchorFailsForInvalidSelectors) { // Ensure that when the serialized selector refers to a non-box, non-text // element(meaning its corresponding LayoutObject can't be the anchor object) // that restoration will still succeed. -TEST_P(ScrollAnchorTest, RestoreAnchorSucceedsForNonBoxNonTextElement) { +TEST_F(ScrollAnchorTest, RestoreAnchorSucceedsForNonBoxNonTextElement) { SetBodyInnerHTML( "<style> body { height: 1000px; margin: 0; } div { height: 100px } " "</style>" @@ -880,7 +868,7 @@ TEST_P(ScrollAnchorTest, RestoreAnchorSucceedsForNonBoxNonTextElement) { ValidateSerializedAnchor("html>body>code", LayoutPoint(0, 0)); } -TEST_P(ScrollAnchorTest, RestoreAnchorSucceedsWhenScriptForbidden) { +TEST_F(ScrollAnchorTest, RestoreAnchorSucceedsWhenScriptForbidden) { SetBodyInnerHTML( "<style> body { height: 1000px; margin: 0; } div { height: 100px } " "</style>" diff --git a/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc b/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc index 56be7a43e97..4765f26297c 100644 --- a/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc @@ -18,11 +18,12 @@ #include "third_party/blink/renderer/core/layout/layout_scrollbar_part.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h" +#include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" @@ -31,12 +32,8 @@ namespace blink { namespace { -class ScrollbarsTest : public testing::WithParamInterface<bool>, - private ScopedRootLayerScrollingForTest, - public SimTest { +class ScrollbarsTest : public SimTest { public: - ScrollbarsTest() : ScopedRootLayerScrollingForTest(GetParam()) {} - HitTestResult HitTest(int x, int y) { return WebView().CoreHitTestResultAt(WebPoint(x, y)); } @@ -46,10 +43,10 @@ class ScrollbarsTest : public testing::WithParamInterface<bool>, } void HandleMouseMoveEvent(int x, int y) { - WebMouseEvent event( - WebInputEvent::kMouseMove, WebFloatPoint(x, y), WebFloatPoint(x, y), - WebPointerProperties::Button::kNoButton, 0, WebInputEvent::kNoModifiers, - CurrentTimeTicksInSeconds()); + WebMouseEvent event(WebInputEvent::kMouseMove, WebFloatPoint(x, y), + WebFloatPoint(x, y), + WebPointerProperties::Button::kNoButton, 0, + WebInputEvent::kNoModifiers, CurrentTimeTicks()); event.SetFrameScale(1); GetEventHandler().HandleMouseMoveEvent(event, Vector<WebMouseEvent>()); } @@ -58,7 +55,7 @@ class ScrollbarsTest : public testing::WithParamInterface<bool>, WebMouseEvent event( WebInputEvent::kMouseDown, WebFloatPoint(x, y), WebFloatPoint(x, y), WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicksInSeconds()); + WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicks()); event.SetFrameScale(1); GetEventHandler().HandleMousePressEvent(event); } @@ -67,7 +64,25 @@ class ScrollbarsTest : public testing::WithParamInterface<bool>, WebMouseEvent event( WebInputEvent::kMouseUp, WebFloatPoint(x, y), WebFloatPoint(x, y), WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicksInSeconds()); + WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicks()); + event.SetFrameScale(1); + GetEventHandler().HandleMouseReleaseEvent(event); + } + + void HandleMouseMiddlePressEvent(int x, int y) { + WebMouseEvent event( + WebInputEvent::kMouseDown, WebFloatPoint(x, y), WebFloatPoint(x, y), + WebPointerProperties::Button::kMiddle, 0, + WebInputEvent::Modifiers::kMiddleButtonDown, CurrentTimeTicks()); + event.SetFrameScale(1); + GetEventHandler().HandleMousePressEvent(event); + } + + void HandleMouseMiddleReleaseEvent(int x, int y) { + WebMouseEvent event( + WebInputEvent::kMouseUp, WebFloatPoint(x, y), WebFloatPoint(x, y), + WebPointerProperties::Button::kMiddle, 0, + WebInputEvent::Modifiers::kMiddleButtonDown, CurrentTimeTicks()); event.SetFrameScale(1); GetEventHandler().HandleMouseReleaseEvent(event); } @@ -76,7 +91,7 @@ class ScrollbarsTest : public testing::WithParamInterface<bool>, WebMouseEvent event( WebInputEvent::kMouseMove, WebFloatPoint(1, 1), WebFloatPoint(1, 1), WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicksInSeconds()); + WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicks()); event.SetFrameScale(1); GetEventHandler().HandleMouseLeaveEvent(event); } @@ -131,10 +146,7 @@ class ScrollbarsTestWithVirtualTimer : public ScrollbarsTest { } }; -INSTANTIATE_TEST_CASE_P(All, ScrollbarsTest, testing::Bool()); -INSTANTIATE_TEST_CASE_P(All, ScrollbarsTestWithVirtualTimer, testing::Bool()); - -TEST_P(ScrollbarsTest, DocumentStyleRecalcPreservesScrollbars) { +TEST_F(ScrollbarsTest, DocumentStyleRecalcPreservesScrollbars) { v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); WebView().Resize(WebSize(800, 600)); SimRequest request("https://example.com/test.html", "text/html"); @@ -174,7 +186,7 @@ class ScrollbarsWebViewClient : public FrameTestHelpers::TestWebViewClient { float device_scale_factor_; }; -TEST_P(ScrollbarsTest, ScrollbarSizeForUseZoomDSF) { +TEST_F(ScrollbarsTest, ScrollbarSizeForUseZoomDSF) { ScrollbarsWebViewClient client; client.set_device_scale_factor(1.f); @@ -236,11 +248,7 @@ TEST_P(ScrollbarsTest, ScrollbarSizeForUseZoomDSF) { // caused by trying to avoid the layout when overlays are enabled but not // checking whether the scrollbars should be custom - which do take up layout // space. https://crbug.com/668387. -TEST_P(ScrollbarsTest, CustomScrollbarsCauseLayoutOnExistenceChange) { - // The bug reproduces only with RLS off. When RLS ships we can keep the test - // but remove this setting. - ScopedRootLayerScrollingForTest turn_off_root_layer_scrolling(false); - +TEST_F(ScrollbarsTest, CustomScrollbarsCauseLayoutOnExistenceChange) { WebView().Resize(WebSize(800, 600)); SimRequest request("https://example.com/test.html", "text/html"); LoadURL("https://example.com/test.html"); @@ -292,11 +300,7 @@ TEST_P(ScrollbarsTest, CustomScrollbarsCauseLayoutOnExistenceChange) { ASSERT_FALSE(layout_viewport->HorizontalScrollbar()); } -TEST_P(ScrollbarsTest, TransparentBackgroundUsesDarkOverlayColorTheme) { - // The bug reproduces only with RLS off. When RLS ships we can keep the test - // but remove this setting. - ScopedRootLayerScrollingForTest turn_off_root_layer_scrolling(false); - +TEST_F(ScrollbarsTest, TransparentBackgroundUsesDarkOverlayColorTheme) { WebView().Resize(WebSize(800, 600)); WebView().SetBaseBackgroundColorOverride(SK_ColorTRANSPARENT); SimRequest request("https://example.com/test.html", "text/html"); @@ -322,7 +326,7 @@ TEST_P(ScrollbarsTest, TransparentBackgroundUsesDarkOverlayColorTheme) { layout_viewport->GetScrollbarOverlayColorTheme()); } -TEST_P(ScrollbarsTest, BodyBackgroundChangesOverlayColorTheme) { +TEST_F(ScrollbarsTest, BodyBackgroundChangesOverlayColorTheme) { v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); WebView().Resize(WebSize(800, 600)); SimRequest request("https://example.com/test.html", "text/html"); @@ -352,7 +356,7 @@ TEST_P(ScrollbarsTest, BodyBackgroundChangesOverlayColorTheme) { } // Ensure overlay scrollbar change to display:none correctly. -TEST_P(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) { +TEST_F(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); LoadURL("https://example.com/test.html"); @@ -396,11 +400,8 @@ TEST_P(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) { DCHECK(scrollable_root->VerticalScrollbar()->IsOverlayScrollbar()); // For PaintLayer Overlay Scrollbar we will remove the scrollbar when it is - // not necessary even with overflow:scroll. Should remove after RLS ships. - if (GetParam() == 0) - DCHECK(scrollable_root->HorizontalScrollbar()); - else - DCHECK(!scrollable_root->HorizontalScrollbar()); + // not necessary even with overflow:scroll. + DCHECK(!scrollable_root->HorizontalScrollbar()); // Set display:none. div->setAttribute(HTMLNames::classAttr, "noscrollbars"); @@ -424,7 +425,7 @@ TEST_P(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) { EXPECT_TRUE(scrollable_root->HorizontalScrollbar()->FrameRect().IsEmpty()); } -TEST_P(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { +TEST_F(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); LoadURL("https://example.com/test.html"); @@ -446,9 +447,9 @@ TEST_P(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { ScrollableArea* scrollable_area = ToLayoutBox(scrollable->GetLayoutObject())->GetScrollableArea(); DCHECK(scrollable_area->VerticalScrollbar()); - WebGestureEvent scroll_begin( - WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, - CurrentTimeTicksInSeconds(), kWebGestureDeviceTouchpad); + WebGestureEvent scroll_begin(WebInputEvent::kGestureScrollBegin, + WebInputEvent::kNoModifiers, CurrentTimeTicks(), + kWebGestureDeviceTouchpad); scroll_begin.SetPositionInWidget( WebFloatPoint(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2, scrollable->OffsetTop())); @@ -465,7 +466,7 @@ TEST_P(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { scroll_begin, &should_update_capture)); } -TEST_P(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) { +TEST_F(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) { WebView().Resize(WebSize(800, 600)); SimRequest request("https://example.com/test.html", "text/html"); @@ -534,7 +535,7 @@ TEST_P(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) { } // Ensure mouse cursor should be pointer when hovering over the scrollbar. -TEST_P(ScrollbarsTest, MouseOverScrollbarInCustomCursorElement) { +TEST_F(ScrollbarsTest, MouseOverScrollbarInCustomCursorElement) { WebView().Resize(WebSize(250, 250)); SimRequest request("https://example.com/test.html", "text/html"); @@ -579,7 +580,7 @@ TEST_P(ScrollbarsTest, MouseOverScrollbarInCustomCursorElement) { // Makes sure that mouse hover over an overlay scrollbar doesn't activate // elements below(except the Element that owns the scrollbar) unless the // scrollbar is faded out. -TEST_P(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { +TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { WebView().Resize(WebSize(20, 20)); SimRequest request("https://example.com/test.html", "text/html"); @@ -661,7 +662,7 @@ TEST_P(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { // Makes sure that mouse hover over an custom scrollbar doesn't change the // activate elements. -TEST_P(ScrollbarsTest, MouseOverCustomScrollbar) { +TEST_F(ScrollbarsTest, MouseOverCustomScrollbar) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -728,7 +729,7 @@ TEST_P(ScrollbarsTest, MouseOverCustomScrollbar) { // Makes sure that mouse hover over an overlay scrollbar doesn't hover iframe // below. -TEST_P(ScrollbarsTest, MouseOverScrollbarAndIFrame) { +TEST_F(ScrollbarsTest, MouseOverScrollbarAndIFrame) { WebView().Resize(WebSize(200, 200)); SimRequest main_resource("https://example.com/", "text/html"); @@ -811,7 +812,7 @@ TEST_P(ScrollbarsTest, MouseOverScrollbarAndIFrame) { // Makes sure that mouse hover over a scrollbar also hover the element owns the // scrollbar. -TEST_P(ScrollbarsTest, MouseOverScrollbarAndParentElement) { +TEST_F(ScrollbarsTest, MouseOverScrollbarAndParentElement) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); WebView().Resize(WebSize(200, 200)); @@ -898,7 +899,7 @@ TEST_P(ScrollbarsTest, MouseOverScrollbarAndParentElement) { } // Makes sure that mouse over a root scrollbar also hover the html element. -TEST_P(ScrollbarsTest, MouseOverRootScrollbar) { +TEST_F(ScrollbarsTest, MouseOverRootScrollbar) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); WebView().Resize(WebSize(200, 200)); @@ -932,7 +933,7 @@ TEST_P(ScrollbarsTest, MouseOverRootScrollbar) { EXPECT_EQ(document.HoverElement(), document.documentElement()); } -TEST_P(ScrollbarsTest, MouseReleaseUpdatesScrollbarHoveredPart) { +TEST_F(ScrollbarsTest, MouseReleaseUpdatesScrollbarHoveredPart) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -997,7 +998,7 @@ TEST_P(ScrollbarsTest, MouseReleaseUpdatesScrollbarHoveredPart) { EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kNoPart); } -TEST_P(ScrollbarsTest, +TEST_F(ScrollbarsTest, CustomScrollbarInOverlayScrollbarThemeWillNotCauseDCHECKFails) { WebView().Resize(WebSize(200, 200)); @@ -1026,7 +1027,7 @@ TEST_P(ScrollbarsTest, // Make sure root custom scrollbar can change by Emulator but div custom // scrollbar not. -TEST_P(ScrollbarsTest, CustomScrollbarChangeToMobileByEmulator) { +TEST_F(ScrollbarsTest, CustomScrollbarChangeToMobileByEmulator) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -1110,7 +1111,7 @@ TEST_P(ScrollbarsTest, CustomScrollbarChangeToMobileByEmulator) { } // Ensure custom scrollbar recreate when style owner change, -TEST_P(ScrollbarsTest, CustomScrollbarWhenStyleOwnerChange) { +TEST_F(ScrollbarsTest, CustomScrollbarWhenStyleOwnerChange) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -1167,10 +1168,10 @@ TEST_P(ScrollbarsTest, CustomScrollbarWhenStyleOwnerChange) { // Disable on Android since VirtualTime not work for Android. // http://crbug.com/633321 #if defined(OS_ANDROID) -TEST_P(ScrollbarsTestWithVirtualTimer, +TEST_F(ScrollbarsTestWithVirtualTimer, DISABLED_TestNonCompositedOverlayScrollbarsFade) { #else -TEST_P(ScrollbarsTestWithVirtualTimer, TestNonCompositedOverlayScrollbarsFade) { +TEST_F(ScrollbarsTestWithVirtualTimer, TestNonCompositedOverlayScrollbarsFade) { #endif TimeAdvance(); constexpr double kMockOverlayFadeOutDelayInSeconds = 5.0; @@ -1482,7 +1483,7 @@ TEST_P(ScrollbarAppearanceTest, HugeScrollingThumbPosition) { #endif // A body with width just under the window width should not have scrollbars. -TEST_P(ScrollbarsTest, WideBodyShouldNotHaveScrollbars) { +TEST_F(ScrollbarsTest, WideBodyShouldNotHaveScrollbars) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1506,7 +1507,7 @@ TEST_P(ScrollbarsTest, WideBodyShouldNotHaveScrollbars) { } // A body with height just under the window height should not have scrollbars. -TEST_P(ScrollbarsTest, TallBodyShouldNotHaveScrollbars) { +TEST_F(ScrollbarsTest, TallBodyShouldNotHaveScrollbars) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1531,7 +1532,7 @@ TEST_P(ScrollbarsTest, TallBodyShouldNotHaveScrollbars) { // A body with dimensions just barely inside the window dimensions should not // have scrollbars. -TEST_P(ScrollbarsTest, TallAndWideBodyShouldNotHaveScrollbars) { +TEST_F(ScrollbarsTest, TallAndWideBodyShouldNotHaveScrollbars) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1556,7 +1557,7 @@ TEST_P(ScrollbarsTest, TallAndWideBodyShouldNotHaveScrollbars) { // A body with dimensions equal to the window dimensions should not have // scrollbars. -TEST_P(ScrollbarsTest, BodySizeEqualWindowSizeShouldNotHaveScrollbars) { +TEST_F(ScrollbarsTest, BodySizeEqualWindowSizeShouldNotHaveScrollbars) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1581,7 +1582,7 @@ TEST_P(ScrollbarsTest, BodySizeEqualWindowSizeShouldNotHaveScrollbars) { // A body with percentage width extending beyond the window width should cause a // horizontal scrollbar. -TEST_P(ScrollbarsTest, WidePercentageBodyShouldHaveScrollbar) { +TEST_F(ScrollbarsTest, WidePercentageBodyShouldHaveScrollbar) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1607,7 +1608,7 @@ TEST_P(ScrollbarsTest, WidePercentageBodyShouldHaveScrollbar) { // Similar to |WidePercentageBodyShouldHaveScrollbar| but with a body height // equal to the window height. -TEST_P(ScrollbarsTest, WidePercentageAndTallBodyShouldHaveScrollbar) { +TEST_F(ScrollbarsTest, WidePercentageAndTallBodyShouldHaveScrollbar) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1633,7 +1634,7 @@ TEST_P(ScrollbarsTest, WidePercentageAndTallBodyShouldHaveScrollbar) { // A body with percentage height extending beyond the window height should cause // a vertical scrollbar. -TEST_P(ScrollbarsTest, TallPercentageBodyShouldHaveScrollbar) { +TEST_F(ScrollbarsTest, TallPercentageBodyShouldHaveScrollbar) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1659,7 +1660,7 @@ TEST_P(ScrollbarsTest, TallPercentageBodyShouldHaveScrollbar) { // Similar to |TallPercentageBodyShouldHaveScrollbar| but with a body width // equal to the window width. -TEST_P(ScrollbarsTest, TallPercentageAndWideBodyShouldHaveScrollbar) { +TEST_F(ScrollbarsTest, TallPercentageAndWideBodyShouldHaveScrollbar) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1685,7 +1686,7 @@ TEST_P(ScrollbarsTest, TallPercentageAndWideBodyShouldHaveScrollbar) { // A body with percentage dimensions extending beyond the window dimensions // should cause scrollbars. -TEST_P(ScrollbarsTest, TallAndWidePercentageBodyShouldHaveScrollbars) { +TEST_F(ScrollbarsTest, TallAndWidePercentageBodyShouldHaveScrollbars) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1709,7 +1710,7 @@ TEST_P(ScrollbarsTest, TallAndWidePercentageBodyShouldHaveScrollbars) { EXPECT_TRUE(layout_viewport->HorizontalScrollbar()); } -TEST_P(ScrollbarsTest, MouseOverIFrameScrollbar) { +TEST_F(ScrollbarsTest, MouseOverIFrameScrollbar) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); WebView().Resize(WebSize(800, 600)); @@ -1759,7 +1760,7 @@ TEST_P(ScrollbarsTest, MouseOverIFrameScrollbar) { EXPECT_EQ(document.HoverElement(), iframe); } -TEST_P(ScrollbarsTest, AutosizeTest) { +TEST_F(ScrollbarsTest, AutosizeTest) { // This test requires that scrollbars take up space. ScopedOverlayScrollbarsForTest overlay_scrollbars(false); @@ -1827,7 +1828,7 @@ TEST_P(ScrollbarsTest, AutosizeTest) { } } -TEST_P(ScrollbarsTest, +TEST_F(ScrollbarsTest, HideTheOverlayScrollbarNotCrashAfterPLSADisposedPaintLayer) { WebView().Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -1869,7 +1870,50 @@ TEST_P(ScrollbarsTest, EXPECT_FALSE(scrollable_div->ScrollbarsHiddenIfOverlay()); } -TEST_P(ScrollbarsTest, OverlayScrollbarHitTest) { +TEST_F(ScrollbarsTest, PLSADisposeShouldClearPointerInLayers) { + GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( + true); + WebView().Resize(WebSize(200, 200)); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + /* transform keeps the graphics layer */ + #div { width: 100px; height: 100px; will-change: transform; } + .scroller{ overflow: scroll; } + .big{ height: 2000px; } + /* positioned so we still keep the PaintLayer */ + .hide { overflow: visible; position: absolute; } + </style> + <div id='div' class='scroller' style='z-index:1'> + <div class='big'> + </div> + </div> + )HTML"); + Compositor().BeginFrame(); + + Document& document = GetDocument(); + Element* div = document.getElementById("div"); + PaintLayerScrollableArea* scrollable_div = + ToLayoutBox(div->GetLayoutObject())->GetScrollableArea(); + + ASSERT_TRUE(scrollable_div); + + PaintLayer* paint_layer = scrollable_div->Layer(); + ASSERT_TRUE(paint_layer); + + GraphicsLayer* graphics_layer = scrollable_div->LayerForScrolling(); + ASSERT_TRUE(graphics_layer); + + div->setAttribute(HTMLNames::classAttr, "hide"); + document.UpdateStyleAndLayout(); + + EXPECT_FALSE(paint_layer->GetScrollableArea()); + EXPECT_FALSE(graphics_layer->GetScrollableArea()); +} + +TEST_F(ScrollbarsTest, OverlayScrollbarHitTest) { WebView().Resize(WebSize(300, 300)); SimRequest main_resource("https://example.com/", "text/html"); @@ -1923,6 +1967,89 @@ TEST_P(ScrollbarsTest, OverlayScrollbarHitTest) { EXPECT_FALSE(hit_test_result.GetScrollbar()); } +TEST_F(ScrollbarsTest, AllowMiddleButtonPressOnScrollbar) { + ScopedOverlayScrollbarsForTest overlay_scrollbars(false); + WebView().Resize(WebSize(200, 200)); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + #big { + height: 800px; + } + </style> + <div id='big'> + </div> + )HTML"); + Compositor().BeginFrame(); + + ScrollableArea* scrollable_area = + WebView().MainFrameImpl()->GetFrameView()->LayoutViewportScrollableArea(); + + Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); + ASSERT_TRUE(scrollbar); + ASSERT_TRUE(scrollbar->Enabled()); + + // Not allow press scrollbar with middle button. + HandleMouseMoveEvent(195, 5); + HandleMouseMiddlePressEvent(195, 5); + EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); + HandleMouseMiddleReleaseEvent(195, 5); +} + +// Ensure Scrollbar not release press by middle button down. +TEST_F(ScrollbarsTest, MiddleDownShouldNotAffectScrollbarPress) { + ScopedOverlayScrollbarsForTest overlay_scrollbars(false); + WebView().Resize(WebSize(200, 200)); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + #big { + height: 800px; + } + </style> + <div id='big'> + </div> + )HTML"); + Compositor().BeginFrame(); + + ScrollableArea* scrollable_area = + WebView().MainFrameImpl()->GetFrameView()->LayoutViewportScrollableArea(); + + Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); + ASSERT_TRUE(scrollbar); + ASSERT_TRUE(scrollbar->Enabled()); + + // Press on scrollbar then move mouse out of scrollbar and middle click + // should not release the press state. Then relase mouse left button should + // release the scrollbar press state. + + // Move mouse to thumb. + HandleMouseMoveEvent(195, 5); + HandleMousePressEvent(195, 5); + EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); + + // Move mouse out of scrollbar with press. + WebMouseEvent event(WebInputEvent::kMouseMove, WebFloatPoint(5, 5), + WebFloatPoint(5, 5), WebPointerProperties::Button::kLeft, + 0, WebInputEvent::Modifiers::kLeftButtonDown, + CurrentTimeTicks()); + event.SetFrameScale(1); + GetEventHandler().HandleMouseLeaveEvent(event); + EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); + + // Middle click should not release scrollbar press state. + HandleMouseMiddlePressEvent(5, 5); + EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); + + // Middle button release should release scrollbar press state. + HandleMouseMiddleReleaseEvent(5, 5); + EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart); +} + class ScrollbarTrackMarginsTest : public ScrollbarsTest { public: void PrepareTest(const String& track_style) { @@ -1976,9 +2103,7 @@ class ScrollbarTrackMarginsTest : public ScrollbarsTest { LayoutScrollbarPart* vertical_track_ = nullptr; }; -INSTANTIATE_TEST_CASE_P(All, ScrollbarTrackMarginsTest, testing::Bool()); - -TEST_P(ScrollbarTrackMarginsTest, +TEST_F(ScrollbarTrackMarginsTest, CustomScrollbarFractionalMarginsWillNotCauseDCHECKFailure) { PrepareTest(R"CSS( ::-webkit-scrollbar-track { @@ -1994,7 +2119,7 @@ TEST_P(ScrollbarTrackMarginsTest, EXPECT_EQ(41, vertical_track_->MarginBottom()); } -TEST_P(ScrollbarTrackMarginsTest, +TEST_F(ScrollbarTrackMarginsTest, CustomScrollbarScaledMarginsWillNotCauseDCHECKFailure) { WebView().SetZoomFactorForDeviceScaleFactor(1.25f); diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/box_shape_test.cc b/chromium/third_party/blink/renderer/core/layout/shapes/box_shape_test.cc index dffb0d13788..371d08e0e4b 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/box_shape_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/shapes/box_shape_test.cc @@ -143,7 +143,7 @@ TEST_F(BoxShapeTest, getIntervals) { TEST_EXCLUDED_INTERVAL(shape, LayoutUnit(15), LayoutUnit(6), 0, 100); TEST_EXCLUDED_INTERVAL(shape, LayoutUnit(20), LayoutUnit(50), 0, 100); TEST_EXCLUDED_INTERVAL(shape, LayoutUnit(69), LayoutUnit(5), 0, 100); - TEST_EXCLUDED_INTERVAL(shape, LayoutUnit(85), LayoutUnit(10), 0, 97.320511f); + TEST_EXCLUDED_INTERVAL(shape, LayoutUnit(85), LayoutUnit(10), 0, 97.3125f); } } // anonymous namespace diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/polygon_shape.h b/chromium/third_party/blink/renderer/core/layout/shapes/polygon_shape.h index 7b073cc72be..97f76361219 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/polygon_shape.h +++ b/chromium/third_party/blink/renderer/core/layout/shapes/polygon_shape.h @@ -65,7 +65,7 @@ class OffsetPolygonEdge final : public VertexPair { class PolygonShape final : public Shape { public: PolygonShape(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fill_rule) - : Shape(), polygon_(std::move(vertices), fill_rule) {} + : Shape(), polygon_(std::move(vertices)) {} LayoutRect ShapeMarginLogicalBoundingBox() const override; bool IsEmpty() const override { return polygon_.IsEmpty(); } diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape.h b/chromium/third_party/blink/renderer/core/layout/shapes/shape.h index 778a89ee86f..b940726a77b 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape.h +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape.h @@ -51,8 +51,8 @@ struct LineSegment { logical_right(logical_right), is_valid(true) {} - float logical_left; - float logical_right; + LayoutUnit logical_left; + LayoutUnit logical_right; bool is_valid; }; diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc index d7737d02982..61f6ccb7d1d 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc @@ -30,6 +30,8 @@ #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" #include <memory> +#include "base/auto_reset.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h" #include "third_party/blink/renderer/core/layout/floating_objects.h" @@ -37,7 +39,6 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/platform/length_functions.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" namespace blink { @@ -49,10 +50,22 @@ CSSBoxType ReferenceBox(const ShapeValue& shape_value) { void ShapeOutsideInfo::SetReferenceBoxLogicalSize( LayoutSize new_reference_box_logical_size) { + const Document& document = layout_box_.GetDocument(); bool is_horizontal_writing_mode = layout_box_.ContainingBlock()->Style()->IsHorizontalWritingMode(); + + LayoutSize margin_box_for_use_counter = new_reference_box_logical_size; + if (is_horizontal_writing_mode) { + margin_box_for_use_counter.Expand(layout_box_.MarginWidth(), + layout_box_.MarginHeight()); + } else { + margin_box_for_use_counter.Expand(layout_box_.MarginHeight(), + layout_box_.MarginWidth()); + } + switch (ReferenceBox(*layout_box_.Style()->ShapeOutside())) { case CSSBoxType::kMargin: + UseCounter::Count(document, WebFeature::kShapeOutsideMarginBox); if (is_horizontal_writing_mode) new_reference_box_logical_size.Expand(layout_box_.MarginWidth(), layout_box_.MarginHeight()); @@ -61,16 +74,25 @@ void ShapeOutsideInfo::SetReferenceBoxLogicalSize( layout_box_.MarginWidth()); break; case CSSBoxType::kBorder: + UseCounter::Count(document, WebFeature::kShapeOutsideBorderBox); break; case CSSBoxType::kPadding: + UseCounter::Count(document, WebFeature::kShapeOutsidePaddingBox); if (is_horizontal_writing_mode) new_reference_box_logical_size.Shrink(layout_box_.BorderWidth(), layout_box_.BorderHeight()); else new_reference_box_logical_size.Shrink(layout_box_.BorderHeight(), layout_box_.BorderWidth()); + + if (new_reference_box_logical_size != margin_box_for_use_counter) { + UseCounter::Count( + document, + WebFeature::kShapeOutsidePaddingBoxDifferentFromMarginBox); + } break; case CSSBoxType::kContent: + UseCounter::Count(document, WebFeature::kShapeOutsideContentBox); if (is_horizontal_writing_mode) new_reference_box_logical_size.Shrink( layout_box_.BorderAndPaddingWidth(), @@ -79,6 +101,12 @@ void ShapeOutsideInfo::SetReferenceBoxLogicalSize( new_reference_box_logical_size.Shrink( layout_box_.BorderAndPaddingHeight(), layout_box_.BorderAndPaddingWidth()); + + if (new_reference_box_logical_size != margin_box_for_use_counter) { + UseCounter::Count( + document, + WebFeature::kShapeOutsideContentBoxDifferentFromMarginBox); + } break; case CSSBoxType::kMissing: NOTREACHED(); @@ -93,6 +121,17 @@ void ShapeOutsideInfo::SetReferenceBoxLogicalSize( reference_box_logical_size_ = new_reference_box_logical_size; } +void ShapeOutsideInfo::SetPercentageResolutionInlineSize( + LayoutUnit percentage_resolution_inline_size) { + DCHECK(RuntimeEnabledFeatures::LayoutNGEnabled()); + + if (percentage_resolution_inline_size_ == percentage_resolution_inline_size) + return; + + MarkShapeAsDirty(); + percentage_resolution_inline_size_ = percentage_resolution_inline_size; +} + static bool CheckShapeImageOrigin(Document& document, const StyleImage& style_image) { if (style_image.IsGeneratedImage()) @@ -160,24 +199,25 @@ const Shape& ShapeOutsideInfo::ComputedShape() const { if (Shape* shape = shape_.get()) return *shape; - AutoReset<bool> is_in_computing_shape(&is_computing_shape_, true); + base::AutoReset<bool> is_in_computing_shape(&is_computing_shape_, true); const ComputedStyle& style = *layout_box_.Style(); DCHECK(layout_box_.ContainingBlock()); - const ComputedStyle& containing_block_style = - *layout_box_.ContainingBlock()->Style(); + const LayoutBlock& containing_block = *layout_box_.ContainingBlock(); + const ComputedStyle& containing_block_style = containing_block.StyleRef(); WritingMode writing_mode = containing_block_style.GetWritingMode(); // Make sure contentWidth is not negative. This can happen when containing // block has a vertical scrollbar and its content is smaller than the // scrollbar width. - LayoutUnit maximum_value = - layout_box_.ContainingBlock() - ? std::max(LayoutUnit(), - layout_box_.ContainingBlock()->ContentWidth()) - : LayoutUnit(); - float margin = FloatValueForLength(layout_box_.Style()->ShapeMargin(), - maximum_value.ToFloat()); + LayoutUnit percentage_resolution_inline_size = + containing_block.IsLayoutNGMixin() + ? percentage_resolution_inline_size_ + : std::max(LayoutUnit(), containing_block.ContentWidth()); + + float margin = + FloatValueForLength(layout_box_.Style()->ShapeMargin(), + percentage_resolution_inline_size.ToFloat()); float shape_image_threshold = style.ShapeImageThreshold(); DCHECK(style.ShapeOutside()); @@ -365,8 +405,8 @@ ShapeOutsideDeltas ShapeOutsideInfo::ComputeDeltasForContainingBlockLine( containing_block.Style()->IsLeftToRightDirection() ? containing_block.MarginStartForChild(layout_box_) : containing_block.MarginEndForChild(layout_box_); - LayoutUnit raw_left_margin_box_delta( - segment.logical_left + LogicalLeftOffset() + logical_left_margin); + LayoutUnit raw_left_margin_box_delta = + segment.logical_left + LogicalLeftOffset() + logical_left_margin; LayoutUnit left_margin_box_delta = clampTo<LayoutUnit>( raw_left_margin_box_delta, LayoutUnit(), float_margin_box_width); @@ -374,10 +414,10 @@ ShapeOutsideDeltas ShapeOutsideInfo::ComputeDeltasForContainingBlockLine( containing_block.Style()->IsLeftToRightDirection() ? containing_block.MarginEndForChild(layout_box_) : containing_block.MarginStartForChild(layout_box_); - LayoutUnit raw_right_margin_box_delta( + LayoutUnit raw_right_margin_box_delta = segment.logical_right + LogicalLeftOffset() - containing_block.LogicalWidthForChild(layout_box_) - - logical_right_margin); + logical_right_margin; LayoutUnit right_margin_box_delta = clampTo<LayoutUnit>( raw_right_margin_box_delta, -float_margin_box_width, LayoutUnit()); diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.h b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.h index c940bff0e94..b2ac9f0f360 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.h +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.h @@ -95,6 +95,7 @@ class ShapeOutsideInfo final { public: void SetReferenceBoxLogicalSize(LayoutSize); + void SetPercentageResolutionInlineSize(LayoutUnit); LayoutUnit ShapeLogicalTop() const { return ComputedShape().ShapeMarginLogicalBoundingBox().Y() + @@ -178,6 +179,7 @@ class ShapeOutsideInfo final { const LayoutBox& layout_box_; mutable std::unique_ptr<Shape> shape_; LayoutSize reference_box_logical_size_; + LayoutUnit percentage_resolution_inline_size_; ShapeOutsideDeltas shape_outside_deltas_; mutable bool is_computing_shape_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/subtree_layout_scope.h b/chromium/third_party/blink/renderer/core/layout/subtree_layout_scope.h index d8f32a82870..d5f506c7d16 100644 --- a/chromium/third_party/blink/renderer/core/layout/subtree_layout_scope.h +++ b/chromium/third_party/blink/renderer/core/layout/subtree_layout_scope.h @@ -31,7 +31,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SUBTREE_LAYOUT_SCOPE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SUBTREE_LAYOUT_SCOPE_H_ -#include "third_party/blink/renderer/core/inspector/InspectorTraceEvents.h" +#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc index ddbe809a927..df0f405f749 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc @@ -25,10 +25,11 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/style/shadow_list.h" #include "third_party/blink/renderer/core/svg/svg_element.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" namespace blink { @@ -47,6 +48,7 @@ void LayoutSVGBlock::AbsoluteRects(Vector<IntRect>&, const LayoutPoint&) const { void LayoutSVGBlock::WillBeDestroyed() { SVGResourcesCache::ClientDestroyed(*this); + SVGResources::ClearClipPathFilterMask(*GetElement(), Style()); LayoutBlockFlow::WillBeDestroyed(); } @@ -73,6 +75,7 @@ void LayoutSVGBlock::StyleDidChange(StyleDifference diff, } LayoutBlock::StyleDidChange(diff, old_style); + SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef()); SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef()); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_ellipse.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_ellipse.cc index f7a9a9b684e..ad066f7f40e 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_ellipse.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_ellipse.cc @@ -98,14 +98,13 @@ void LayoutSVGEllipse::CalculateRadiiAndCenter() { } bool LayoutSVGEllipse::ShapeDependentStrokeContains(const FloatPoint& point) { - // The optimized check below for circles does not support non-scaling or - // discontinuous strokes. - if (use_path_fallback_ || !HasContinuousStroke() || - radii_.Width() != radii_.Height()) { - if (!HasPath()) - CreatePath(); + if (radii_.Width() < 0 || radii_.Height() < 0) + return false; + + // The optimized check below for circles does not support non-circular and + // the cases that we set use_path_fallback_ in UpdateShapeFromElement(). + if (use_path_fallback_ || radii_.Width() != radii_.Height()) return LayoutSVGShape::ShapeDependentStrokeContains(point); - } const FloatPoint center = FloatPoint(center_.X() - point.X(), center_.Y() - point.Y()); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc index eb31ff5f2ae..6736f00c101 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc @@ -12,15 +12,6 @@ class LayoutSVGForeignObjectTest : public RenderingTest { public: LayoutSVGForeignObjectTest() : RenderingTest(SingleChildLocalFrameClient::Create()) {} - - const Node* HitTest(int x, int y) { - HitTestResult result( - HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive | - HitTestRequest::kAllowChildFrameContent), - IntPoint(x, y)); - GetLayoutView().HitTest(result); - return result.InnerNode(); - } }; TEST_F(LayoutSVGForeignObjectTest, DivInForeignObject) { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc index 8fd4c1384e6..5ab1d05e39f 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc @@ -25,6 +25,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h" #include "third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/svg/svg_a_element.h" @@ -119,6 +120,8 @@ void LayoutSVGInline::AbsoluteQuads(Vector<FloatQuad>& quads, void LayoutSVGInline::WillBeDestroyed() { SVGResourcesCache::ClientDestroyed(*this); + SVGResources::ClearClipPathFilterMask(ToSVGElement(*GetNode()), Style()); + SVGResources::ClearPaints(ToSVGElement(*GetNode()), Style()); LayoutInline::WillBeDestroyed(); } @@ -128,6 +131,9 @@ void LayoutSVGInline::StyleDidChange(StyleDifference diff, SetNeedsBoundariesUpdate(); LayoutInline::StyleDidChange(diff, old_style); + SVGResources::UpdateClipPathFilterMask(ToSVGElement(*GetNode()), old_style, + StyleRef()); + SVGResources::UpdatePaints(ToSVGElement(*GetNode()), old_style, StyleRef()); SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef()); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc index a62462cfe7f..c8708e90b56 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc @@ -34,6 +34,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_container.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/svg/svg_graphics_element.h" @@ -92,6 +93,7 @@ FloatRect LayoutSVGModelObject::LocalBoundingBoxRectForAccessibility() const { void LayoutSVGModelObject::WillBeDestroyed() { SVGResourcesCache::ClientDestroyed(*this); + SVGResources::ClearClipPathFilterMask(*GetElement(), Style()); LayoutObject::WillBeDestroyed(); } @@ -134,6 +136,7 @@ void LayoutSVGModelObject::StyleDidChange(StyleDifference diff, } LayoutObject::StyleDidChange(diff, old_style); + SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef()); SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef()); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc index 2e95d31b6f9..a3eee4a939e 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc @@ -38,6 +38,17 @@ LayoutSVGPath::LayoutSVGPath(SVGGeometryElement* node) : LayoutSVGShape(node) {} LayoutSVGPath::~LayoutSVGPath() = default; +void LayoutSVGPath::StyleDidChange(StyleDifference diff, + const ComputedStyle* old_style) { + LayoutSVGShape::StyleDidChange(diff, old_style); + SVGResources::UpdateMarkers(*GetElement(), old_style, StyleRef()); +} + +void LayoutSVGPath::WillBeDestroyed() { + SVGResources::ClearMarkers(*GetElement(), Style()); + LayoutSVGShape::WillBeDestroyed(); +} + void LayoutSVGPath::UpdateShapeFromElement() { LayoutSVGShape::UpdateShapeFromElement(); ProcessMarkerPositions(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h index 45949839cc9..b1f6c080e44 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h @@ -42,6 +42,9 @@ class LayoutSVGPath final : public LayoutSVGShape { const char* GetName() const override { return "LayoutSVGPath"; } private: + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; + void WillBeDestroyed() override; + void UpdateShapeFromElement() override; FloatRect HitTestStrokeBoundingBox() const override; FloatRect CalculateUpdatedStrokeBoundingBox() const; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_rect.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_rect.cc index 5d9ae41433c..81ab1129b3b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_rect.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_rect.cc @@ -85,14 +85,10 @@ void LayoutSVGRect::UpdateShapeFromElement() { } bool LayoutSVGRect::ShapeDependentStrokeContains(const FloatPoint& point) { - // The optimized code below does not support non-simple strokes so we need - // to fall back to LayoutSVGShape::shapeDependentStrokeContains in these - // cases. - if (use_path_fallback_ || !DefinitelyHasSimpleStroke()) { - if (!HasPath()) - CreatePath(); + // The optimized code below does not support the cases that we set + // use_path_fallback_ in UpdateShapeFromElement(). + if (use_path_fallback_) return LayoutSVGShape::ShapeDependentStrokeContains(point); - } const float half_stroke_width = StrokeWidth() / 2; const float half_width = fill_bounding_box_.Width() / 2; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc index 9e361f0ccac..5be8e9757a3 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc @@ -120,21 +120,21 @@ void LayoutSVGResourceClipper::RemoveAllClientsFromCache( : SVGResourceClient::kParentOnlyInvalidation); } -Optional<Path> LayoutSVGResourceClipper::AsPath() { +base::Optional<Path> LayoutSVGResourceClipper::AsPath() { if (clip_content_path_validity_ == kClipContentPathValid) - return Optional<Path>(clip_content_path_); + return base::Optional<Path>(clip_content_path_); if (clip_content_path_validity_ == kClipContentPathInvalid) - return WTF::nullopt; + return base::nullopt; DCHECK_EQ(clip_content_path_validity_, kClipContentPathUnknown); clip_content_path_validity_ = kClipContentPathInvalid; // If the current clip-path gets clipped itself, we have to fallback to // masking. if (StyleRef().ClipPath()) - return WTF::nullopt; + return base::nullopt; unsigned op_count = 0; - Optional<SkOpBuilder> clip_path_builder; + base::Optional<SkOpBuilder> clip_path_builder; SkPath resolved_path; for (const SVGElement& child_element : Traversal<SVGElement>::ChildrenOf(*GetElement())) { @@ -142,14 +142,14 @@ Optional<Path> LayoutSVGResourceClipper::AsPath() { if (strategy == ClipStrategy::kNone) continue; if (strategy == ClipStrategy::kMask) - return WTF::nullopt; + return base::nullopt; // Multiple shapes require PathOps. In some degenerate cases PathOps can // exhibit quadratic behavior, so we cap the number of ops to a reasonable // count. const unsigned kMaxOps = 42; if (++op_count > kMaxOps) - return WTF::nullopt; + return base::nullopt; if (clip_path_builder) { clip_path_builder->add(PathFromElement(child_element).GetSkPath(), kUnion_SkPathOp); @@ -167,7 +167,7 @@ Optional<Path> LayoutSVGResourceClipper::AsPath() { clip_path_builder->resolve(&resolved_path); clip_content_path_ = std::move(resolved_path); clip_content_path_validity_ = kClipContentPathValid; - return Optional<Path>(clip_content_path_); + return base::Optional<Path>(clip_content_path_); } sk_sp<const PaintRecord> LayoutSVGResourceClipper::CreatePaintRecord() { @@ -214,6 +214,18 @@ void LayoutSVGResourceClipper::CalculateLocalClipBounds() { } } +AffineTransform LayoutSVGResourceClipper::CalculateClipTransform( + const FloatRect& reference_box) const { + AffineTransform transform = + ToSVGClipPathElement(GetElement()) + ->CalculateTransform(SVGElement::kIncludeMotionTransform); + if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { + transform.Translate(reference_box.X(), reference_box.Y()); + transform.ScaleNonUniform(reference_box.Width(), reference_box.Height()); + } + return transform; +} + bool LayoutSVGResourceClipper::HitTestClipContent( const FloatRect& object_bounding_box, const FloatPoint& node_at_point) { @@ -221,21 +233,12 @@ bool LayoutSVGResourceClipper::HitTestClipContent( if (!SVGLayoutSupport::PointInClippingArea(*this, point)) return false; - if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { - AffineTransform transform; - transform.Translate(object_bounding_box.X(), object_bounding_box.Y()); - transform.ScaleNonUniform(object_bounding_box.Width(), - object_bounding_box.Height()); - point = transform.Inverse().MapPoint(point); - } - - AffineTransform animated_local_transform = - ToSVGClipPathElement(GetElement()) - ->CalculateTransform(SVGElement::kIncludeMotionTransform); - if (!animated_local_transform.IsInvertible()) + AffineTransform user_space_transform = + CalculateClipTransform(object_bounding_box); + if (!user_space_transform.IsInvertible()) return false; - point = animated_local_transform.Inverse().MapPoint(point); + point = user_space_transform.Inverse().MapPoint(point); for (const SVGElement& child_element : Traversal<SVGElement>::ChildrenOf(*GetElement())) { @@ -245,9 +248,8 @@ bool LayoutSVGResourceClipper::HitTestClipContent( HitTestResult result(HitTestRequest::kSVGClipContent, hit_point); LayoutObject* layout_object = child_element.GetLayoutObject(); - if (layout_object->IsBoxModelObject() && - ToLayoutBoxModelObject(layout_object)->HasSelfPaintingLayer()) - continue; + DCHECK(!layout_object->IsBoxModelObject() || + !ToLayoutBoxModelObject(layout_object)->HasSelfPaintingLayer()); if (layout_object->NodeAtFloatPoint(result, point, kHitTestForeground)) return true; @@ -264,14 +266,7 @@ FloatRect LayoutSVGResourceClipper::ResourceBoundingBox( if (local_clip_bounds_.IsEmpty()) CalculateLocalClipBounds(); - AffineTransform transform = - ToSVGClipPathElement(GetElement()) - ->CalculateTransform(SVGElement::kIncludeMotionTransform); - if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { - transform.Translate(reference_box.X(), reference_box.Y()); - transform.ScaleNonUniform(reference_box.Width(), reference_box.Height()); - } - return transform.MapRect(local_clip_bounds_); + return CalculateClipTransform(reference_box).MapRect(local_clip_bounds_); } void LayoutSVGResourceClipper::WillBeDestroyed() { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h index e979d7cc562..dd32bc669ff 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h @@ -48,8 +48,9 @@ class LayoutSVGResourceClipper final : public LayoutSVGResourceContainer { ->CurrentValue() ->EnumValue(); } + AffineTransform CalculateClipTransform(const FloatRect& reference_box) const; - Optional<Path> AsPath(); + base::Optional<Path> AsPath(); sk_sp<const PaintRecord> CreatePaintRecord(); bool HasCycle() { return in_clip_expansion_; } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc index 3fee1a35841..c1d14a5c912 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc @@ -19,11 +19,11 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" #include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" namespace blink { @@ -55,7 +55,7 @@ void LayoutSVGResourceContainer::UpdateLayout() { if (is_in_layout_) return; - AutoReset<bool> in_layout_change(&is_in_layout_, true); + base::AutoReset<bool> in_layout_change(&is_in_layout_, true); LayoutSVGHiddenContainer::UpdateLayout(); @@ -64,40 +64,23 @@ void LayoutSVGResourceContainer::UpdateLayout() { void LayoutSVGResourceContainer::WillBeDestroyed() { LayoutSVGHiddenContainer::WillBeDestroyed(); - // The resource is being torn down. If we have any clients, move those to be - // pending on the resource (if one exists.) + // The resource is being torn down. + // TODO(fs): Remove this when SVGResources is gone. if (LocalSVGResource* resource = ResourceForContainer(*this)) - MakeClientsPending(*resource); + resource->NotifyResourceDestroyed(*this); } void LayoutSVGResourceContainer::StyleDidChange( StyleDifference diff, const ComputedStyle* old_style) { LayoutSVGHiddenContainer::StyleDidChange(diff, old_style); - // The resource has (read: may have) been attached. Notify any pending - // clients that they can now try to add themselves as clients to the - // resource. - if (LocalSVGResource* resource = ResourceForContainer(*this)) { - if (resource->Target() == GetElement()) - resource->NotifyPendingClients(); - } -} - -void LayoutSVGResourceContainer::MakeClientsPending( - LocalSVGResource& resource) { - RemoveAllClientsFromCache(); - - for (auto* client : clients_) { - // Unlink the resource from the client's SVGResources. - SVGResources* resources = - SVGResourcesCache::CachedResourcesForLayoutObject(*client); - // Or else the client wouldn't be in the list in the first place. - DCHECK(resources); - resources->ResourceDestroyed(this); - - resource.AddWatch(ToSVGElement(*client->GetNode())); - } - clients_.clear(); + // The resource has been attached. Notify any pending clients that + // they can now try to add themselves as clients to the resource. + // TODO(fs): Remove this when SVGResources is gone. + if (old_style) + return; + if (LocalSVGResource* resource = ResourceForContainer(*this)) + resource->NotifyResourceAttached(*this); } void LayoutSVGResourceContainer::MarkAllClientsForInvalidation( @@ -105,7 +88,7 @@ void LayoutSVGResourceContainer::MarkAllClientsForInvalidation( if (is_invalidating_) return; LocalSVGResource* resource = ResourceForContainer(*this); - if (clients_.IsEmpty() && (!resource || !resource->HasClients())) + if (!resource || !resource->HasClients()) return; // Remove modes for which invalidations have already been // performed. If no modes remain we are done. @@ -115,25 +98,6 @@ void LayoutSVGResourceContainer::MarkAllClientsForInvalidation( completed_invalidations_mask_ |= invalidation_mask; is_invalidating_ = true; - bool needs_layout = - invalidation_mask & SVGResourceClient::kLayoutInvalidation; - bool mark_for_invalidation = - invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation; - - // Invalidate clients registered on the this object (via SVGResources). - for (auto* client : clients_) { - DCHECK(client->IsSVG()); - if (client->IsSVGResourceContainer()) { - ToLayoutSVGResourceContainer(client)->RemoveAllClientsFromCache( - mark_for_invalidation); - continue; - } - - if (mark_for_invalidation) - MarkClientForInvalidation(*client, invalidation_mask); - - MarkForLayoutAndParentResourceInvalidation(*client, needs_layout); - } // Invalidate clients registered via an SVGResource. if (resource) @@ -162,17 +126,6 @@ void LayoutSVGResourceContainer::MarkClientForInvalidation( client.SetNeedsBoundariesUpdate(); } -void LayoutSVGResourceContainer::AddClient(LayoutObject& client) { - clients_.insert(&client); - ClearInvalidationMask(); -} - -bool LayoutSVGResourceContainer::RemoveClient(LayoutObject& client) { - RemoveClientFromCache(client); - clients_.erase(&client); - return clients_.IsEmpty(); -} - void LayoutSVGResourceContainer::InvalidateCacheAndMarkForLayout( LayoutInvalidationReasonForTracing reason, SubtreeLayoutScope* layout_scope) { @@ -195,24 +148,25 @@ void LayoutSVGResourceContainer::InvalidateCacheAndMarkForLayout( static inline void RemoveFromCacheAndInvalidateDependencies( LayoutObject& object, bool needs_layout) { + if (!object.GetNode() || !object.GetNode()->IsSVGElement()) + return; + SVGElement& element = ToSVGElement(*object.GetNode()); + if (SVGResources* resources = SVGResourcesCache::CachedResourcesForLayoutObject(object)) { + SVGResourceClient* client = element.GetSVGResourceClient(); if (InvalidationModeMask invalidation_mask = - resources->RemoveClientFromCacheAffectingObjectBounds(object)) { + resources->RemoveClientFromCacheAffectingObjectBounds(*client)) { LayoutSVGResourceContainer::MarkClientForInvalidation(object, invalidation_mask); } } - if (!object.GetNode() || !object.GetNode()->IsSVGElement()) - return; - - ToSVGElement(object.GetNode()) - ->NotifyIncomingReferences([needs_layout](SVGElement& element) { - DCHECK(element.GetLayoutObject()); - LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( - *element.GetLayoutObject(), needs_layout); - }); + element.NotifyIncomingReferences([needs_layout](SVGElement& element) { + DCHECK(element.GetLayoutObject()); + LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( + *element.GetLayoutObject(), needs_layout); + }); } void LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h index 815e18917fa..4f9c61ee06e 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h @@ -25,8 +25,6 @@ namespace blink { -class LocalSVGResource; - enum LayoutSVGResourceType { kMaskerResourceType, kMarkerResourceType, @@ -45,10 +43,9 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { virtual void RemoveAllClientsFromCache(bool mark_for_invalidation = true) = 0; // Remove any cached data for the |client|, and return true if so. - virtual bool RemoveClientFromCache(LayoutObject& client) { return false; } + virtual bool RemoveClientFromCache(SVGResourceClient&) { return false; } void UpdateLayout() override; - void StyleDidChange(StyleDifference, const ComputedStyle* old_style) final; bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectSVGResourceContainer || LayoutSVGHiddenContainer::IsOfType(type); @@ -63,11 +60,6 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { resource_type == kRadialGradientResourceType; } - // Detach all clients from this resource, and add them as watches to the tree - // scope's resource entry (the argument.) - void MakeClientsPending(LocalSVGResource&); - bool HasClients() const { return !clients_.IsEmpty(); } - void InvalidateCacheAndMarkForLayout(LayoutInvalidationReasonForTracing, SubtreeLayoutScope* = nullptr); void InvalidateCacheAndMarkForLayout(SubtreeLayoutScope* = nullptr); @@ -83,23 +75,18 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { // Used from RemoveAllClientsFromCache methods. void MarkAllClientsForInvalidation(InvalidationModeMask); + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) final; void WillBeDestroyed() override; bool is_in_layout_; private: - friend class SVGResourcesCache; - void AddClient(LayoutObject&); - bool RemoveClient(LayoutObject&); - // Track global (markAllClientsForInvalidation) invalidations to avoid // redundant crawls. unsigned completed_invalidations_mask_ : 8; unsigned is_invalidating_ : 1; // 23 padding bits available - - HashSet<LayoutObject*> clients_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGResourceContainer, diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc index 31e1ab4c897..a9559a578de 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc @@ -75,7 +75,7 @@ void LayoutSVGResourceFilter::RemoveAllClientsFromCache( : SVGResourceClient::kParentOnlyInvalidation); } -bool LayoutSVGResourceFilter::RemoveClientFromCache(LayoutObject& client) { +bool LayoutSVGResourceFilter::RemoveClientFromCache(SVGResourceClient& client) { auto entry = filter_.find(&client); if (entry == filter_.end()) return false; @@ -127,14 +127,13 @@ void LayoutSVGResourceFilter::PrimitiveAttributeChanged( if (!primitive.SetFilterEffectAttribute(effect, attribute)) return; node_map->InvalidateDependentEffects(effect); - - // Issue paint invalidations for the image on the screen. - MarkClientForInvalidation(*filter.key, - SVGResourceClient::kPaintInvalidation); } if (LocalSVGResource* resource = - ToSVGFilterElement(GetElement())->AssociatedResource()) - resource->NotifyContentChanged(SVGResourceClient::kPaintInvalidation); + ToSVGFilterElement(GetElement())->AssociatedResource()) { + resource->NotifyContentChanged( + SVGResourceClient::kPaintInvalidation | + SVGResourceClient::kSkipAncestorInvalidation); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h index a9169846171..23372b07167 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h @@ -80,7 +80,7 @@ class LayoutSVGResourceFilter final : public LayoutSVGResourceContainer { } void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; - bool RemoveClientFromCache(LayoutObject&) override; + bool RemoveClientFromCache(SVGResourceClient&) override; FloatRect ResourceBoundingBox(const LayoutObject*); @@ -93,12 +93,12 @@ class LayoutSVGResourceFilter final : public LayoutSVGResourceContainer { static const LayoutSVGResourceType kResourceType = kFilterResourceType; LayoutSVGResourceType ResourceType() const override { return kResourceType; } - FilterData* GetFilterDataForLayoutObject(const LayoutObject* object) { - return filter_.at(const_cast<LayoutObject*>(object)); + FilterData* GetFilterDataForClient(const SVGResourceClient* client) { + return filter_.at(const_cast<SVGResourceClient*>(client)); } - void SetFilterDataForLayoutObject(LayoutObject* object, - FilterData* filter_data) { - filter_.Set(object, filter_data); + void SetFilterDataForClient(const SVGResourceClient* client, + FilterData* filter_data) { + filter_.Set(const_cast<SVGResourceClient*>(client), filter_data); } protected: @@ -107,7 +107,8 @@ class LayoutSVGResourceFilter final : public LayoutSVGResourceContainer { private: void DisposeFilterMap(); - using FilterMap = PersistentHeapHashMap<LayoutObject*, Member<FilterData>>; + using FilterMap = + PersistentHeapHashMap<Member<SVGResourceClient>, Member<FilterData>>; FilterMap filter_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc index ef2795a5292..db3bf84ce05 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc @@ -40,7 +40,8 @@ void LayoutSVGResourceGradient::RemoveAllClientsFromCache( : SVGResourceClient::kParentOnlyInvalidation); } -bool LayoutSVGResourceGradient::RemoveClientFromCache(LayoutObject& client) { +bool LayoutSVGResourceGradient::RemoveClientFromCache( + SVGResourceClient& client) { auto entry = gradient_map_.find(&client); if (entry == gradient_map_.end()) return false; @@ -49,7 +50,7 @@ bool LayoutSVGResourceGradient::RemoveClientFromCache(LayoutObject& client) { } SVGPaintServer LayoutSVGResourceGradient::PreparePaintServer( - const LayoutObject& object, + const SVGResourceClient& client, const FloatRect& object_bounding_box) { ClearInvalidationMask(); @@ -71,7 +72,7 @@ SVGPaintServer LayoutSVGResourceGradient::PreparePaintServer( return SVGPaintServer::Invalid(); std::unique_ptr<GradientData>& gradient_data = - gradient_map_.insert(&object, nullptr).stored_value->value; + gradient_map_.insert(&client, nullptr).stored_value->value; if (!gradient_data) gradient_data = std::make_unique<GradientData>(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h index 3dfbe11d4f5..896308a0796 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h @@ -44,9 +44,9 @@ class LayoutSVGResourceGradient : public LayoutSVGResourcePaintServer { explicit LayoutSVGResourceGradient(SVGGradientElement*); void RemoveAllClientsFromCache(bool mark_for_invalidation = true) final; - bool RemoveClientFromCache(LayoutObject&) final; + bool RemoveClientFromCache(SVGResourceClient&) final; - SVGPaintServer PreparePaintServer(const LayoutObject&, + SVGPaintServer PreparePaintServer(const SVGResourceClient&, const FloatRect& object_bounding_box) final; bool IsChildAllowed(LayoutObject* child, const ComputedStyle&) const final; @@ -62,7 +62,9 @@ class LayoutSVGResourceGradient : public LayoutSVGResourcePaintServer { private: bool should_collect_gradient_attributes_ : 1; - HashMap<const LayoutObject*, std::unique_ptr<GradientData>> gradient_map_; + using GradientMap = PersistentHeapHashMap<Member<const SVGResourceClient>, + std::unique_ptr<GradientData>>; + GradientMap gradient_map_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc index 09f27694cdd..9e256694a52 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc @@ -21,8 +21,8 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" namespace blink { @@ -36,7 +36,7 @@ void LayoutSVGResourceMarker::UpdateLayout() { if (is_in_layout_) return; - AutoReset<bool> in_layout_change(&is_in_layout_, true); + base::AutoReset<bool> in_layout_change(&is_in_layout_, true); // LayoutSVGHiddenContainer overwrites layout(). We need the // layouting of LayoutSVGContainer for calculating local diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc index 6c6adbd5e6e..7dead57bf3a 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc @@ -146,7 +146,8 @@ SVGPaintServer SVGPaintServer::RequestForLayoutObject( if (!paint_description.resource) return SVGPaintServer(paint_description.color); SVGPaintServer paint_server = paint_description.resource->PreparePaintServer( - layout_object, layout_object.ObjectBoundingBox()); + *SVGResources::GetClient(layout_object), + layout_object.ObjectBoundingBox()); if (paint_server.IsValid()) return paint_server; if (paint_description.has_fallback) diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h index 60010bc3a8e..9f096f3d2a4 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h @@ -103,7 +103,7 @@ class LayoutSVGResourcePaintServer : public LayoutSVGResourceContainer { ~LayoutSVGResourcePaintServer() override; virtual SVGPaintServer PreparePaintServer( - const LayoutObject&, + const SVGResourceClient&, const FloatRect& object_bounding_box) = 0; // Helper utilities used in to access the underlying resources for DRT. diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc index 98f6a544ddf..af180fc1017 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc @@ -60,7 +60,8 @@ void LayoutSVGResourcePattern::RemoveAllClientsFromCache( : SVGResourceClient::kParentOnlyInvalidation); } -bool LayoutSVGResourcePattern::RemoveClientFromCache(LayoutObject& client) { +bool LayoutSVGResourcePattern::RemoveClientFromCache( + SVGResourceClient& client) { auto entry = pattern_map_.find(&client); if (entry == pattern_map_.end()) return false; @@ -69,7 +70,7 @@ bool LayoutSVGResourcePattern::RemoveClientFromCache(LayoutObject& client) { } PatternData* LayoutSVGResourcePattern::PatternForClient( - const LayoutObject& object, + const SVGResourceClient& client, const FloatRect& object_bounding_box) { DCHECK(!should_collect_pattern_attributes_); @@ -77,10 +78,10 @@ PatternData* LayoutSVGResourcePattern::PatternForClient( // invalidation (painting animated images may trigger layout invals which // delete our map entry). Hopefully that will be addressed at some point, and // then we can optimize the lookup. - if (PatternData* current_data = pattern_map_.at(&object)) + if (PatternData* current_data = pattern_map_.at(&client)) return current_data; - return pattern_map_.Set(&object, BuildPatternData(object_bounding_box)) + return pattern_map_.Set(&client, BuildPatternData(object_bounding_box)) .stored_value->value.get(); } @@ -133,7 +134,7 @@ std::unique_ptr<PatternData> LayoutSVGResourcePattern::BuildPatternData( } SVGPaintServer LayoutSVGResourcePattern::PreparePaintServer( - const LayoutObject& object, + const SVGResourceClient& client, const FloatRect& object_bounding_box) { ClearInvalidationMask(); @@ -156,7 +157,7 @@ SVGPaintServer LayoutSVGResourcePattern::PreparePaintServer( object_bounding_box.IsEmpty()) return SVGPaintServer::Invalid(); - PatternData* pattern_data = PatternForClient(object, object_bounding_box); + PatternData* pattern_data = PatternForClient(client, object_bounding_box); if (!pattern_data || !pattern_data->pattern) return SVGPaintServer::Invalid(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h index b71cd1d29eb..322b3cef08b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h @@ -42,10 +42,10 @@ class LayoutSVGResourcePattern final : public LayoutSVGResourcePaintServer { const char* GetName() const override { return "LayoutSVGResourcePattern"; } void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; - bool RemoveClientFromCache(LayoutObject&) override; + bool RemoveClientFromCache(SVGResourceClient&) override; SVGPaintServer PreparePaintServer( - const LayoutObject&, + const SVGResourceClient&, const FloatRect& object_bounding_box) override; static const LayoutSVGResourceType kResourceType = kPatternResourceType; @@ -56,7 +56,7 @@ class LayoutSVGResourcePattern final : public LayoutSVGResourcePaintServer { const FloatRect& object_bounding_box); sk_sp<PaintRecord> AsPaintRecord(const FloatSize&, const AffineTransform&) const; - PatternData* PatternForClient(const LayoutObject&, + PatternData* PatternForClient(const SVGResourceClient&, const FloatRect& object_bounding_box); const LayoutSVGResourceContainer* ResolveContentElement() const; @@ -78,7 +78,9 @@ class LayoutSVGResourcePattern final : public LayoutSVGResourcePaintServer { // same => we should be able to cache a single display list per // LayoutSVGResourcePattern + one Pattern(shader) for each client -- this // would avoid re-recording when multiple clients share the same pattern. - HashMap<const LayoutObject*, std::unique_ptr<PatternData>> pattern_map_; + using PatternMap = PersistentHeapHashMap<Member<const SVGResourceClient>, + std::unique_ptr<PatternData>>; + PatternMap pattern_map_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc index f35b4a16e36..caa1a0d5809 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc @@ -65,7 +65,7 @@ LayoutSVGRoot::LayoutSVGRoot(SVGElement* node) LayoutSVGRoot::~LayoutSVGRoot() = default; -void LayoutSVGRoot::ComputeIntrinsicSizingInfo( +void LayoutSVGRoot::UnscaledIntrinsicSizingInfo( IntrinsicSizingInfo& intrinsic_sizing_info) const { // https://www.w3.org/TR/SVG/coords.html#IntrinsicSizing @@ -91,6 +91,13 @@ void LayoutSVGRoot::ComputeIntrinsicSizingInfo( intrinsic_sizing_info.Transpose(); } +void LayoutSVGRoot::ComputeIntrinsicSizingInfo( + IntrinsicSizingInfo& intrinsic_sizing_info) const { + UnscaledIntrinsicSizingInfo(intrinsic_sizing_info); + + intrinsic_sizing_info.size.Scale(StyleRef().EffectiveZoom()); +} + bool LayoutSVGRoot::IsEmbeddedThroughSVGImage() const { return SVGImage::IsInSVGImage(ToSVGSVGElement(GetNode())); } @@ -251,6 +258,7 @@ void LayoutSVGRoot::PaintReplaced(const PaintInfo& paint_info, void LayoutSVGRoot::WillBeDestroyed() { SVGResourcesCache::ClientDestroyed(*this); + SVGResources::ClearClipPathFilterMask(ToSVGSVGElement(*GetNode()), Style()); LayoutReplaced::WillBeDestroyed(); } @@ -300,6 +308,8 @@ void LayoutSVGRoot::StyleDidChange(StyleDifference diff, IntrinsicSizingInfoChanged(); LayoutReplaced::StyleDidChange(diff, old_style); + SVGResources::UpdateClipPathFilterMask(ToSVGSVGElement(*GetNode()), old_style, + StyleRef()); SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef()); } @@ -502,9 +512,11 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result, // don't clip to the viewport, the visual overflow rect. // FIXME: This should be an intersection when rect-based hit tests are // supported by nodeAtFloatPoint. - if (ContentBoxRect().Contains(point_in_border_box) || - (!ShouldApplyViewportClip() && - VisualOverflowRect().Contains(point_in_border_box))) { + bool skip_children = (result.GetHitTestRequest().GetStopNode() == this); + if (!skip_children && + (ContentBoxRect().Contains(point_in_border_box) || + (!ShouldApplyViewportClip() && + VisualOverflowRect().Contains(point_in_border_box)))) { const AffineTransform& local_to_parent_transform = LocalToSVGParentTransform(); if (local_to_parent_transform.IsInvertible()) { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h index 8b4c3a51221..70952638ac7 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h @@ -39,7 +39,7 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced { bool IsEmbeddedThroughFrameContainingSVGDocument() const; void IntrinsicSizingInfoChanged() const; - void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; + void UnscaledIntrinsicSizingInfo(IntrinsicSizingInfo&) const; // If you have a LayoutSVGRoot, use firstChild or lastChild instead. void SlowFirstChild() const = delete; @@ -110,6 +110,7 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced { LayoutReplaced::IsOfType(type); } + void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; LayoutUnit ComputeReplacedLogicalWidth( ShouldComputePreferred = kComputeActual) const override; LayoutUnit ComputeReplacedLogicalHeight( diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc index 0545143fb20..7d84e24d65c 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc @@ -62,12 +62,21 @@ LayoutSVGShape::LayoutSVGShape(SVGGeometryElement* node) LayoutSVGShape::~LayoutSVGShape() = default; +void LayoutSVGShape::StyleDidChange(StyleDifference diff, + const ComputedStyle* old_style) { + LayoutSVGModelObject::StyleDidChange(diff, old_style); + SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef()); +} + +void LayoutSVGShape::WillBeDestroyed() { + SVGResources::ClearPaints(*GetElement(), Style()); + LayoutSVGModelObject::WillBeDestroyed(); +} + void LayoutSVGShape::CreatePath() { if (!path_) path_ = std::make_unique<Path>(); *path_ = ToSVGGeometryElement(GetElement())->AsPath(); - if (rare_data_.get()) - rare_data_->cached_non_scaling_stroke_path_.Clear(); } float LayoutSVGShape::DashScaleFactor() const { @@ -78,8 +87,15 @@ float LayoutSVGShape::DashScaleFactor() const { void LayoutSVGShape::UpdateShapeFromElement() { CreatePath(); - fill_bounding_box_ = CalculateObjectBoundingBox(); + + if (HasNonScalingStroke()) { + // NonScalingStrokeTransform may depend on LocalTransform which in turn may + // depend on ObjectBoundingBox, thus we need to call them in this order. + UpdateLocalTransform(); + UpdateNonScalingStrokeData(); + } + stroke_bounding_box_ = CalculateStrokeBoundingBox(); } @@ -139,17 +155,21 @@ FloatRect LayoutSVGShape::HitTestStrokeBoundingBox() const { } bool LayoutSVGShape::ShapeDependentStrokeContains(const FloatPoint& point) { - DCHECK(path_); + // In case the subclass didn't create path during UpdateShapeFromElement() + // for optimization but still calls this method. + if (!HasPath()) + CreatePath(); + StrokeData stroke_data; SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(), *this, DashScaleFactor()); if (HasNonScalingStroke()) { - AffineTransform non_scaling_transform = NonScalingStrokeTransform(); - Path* use_path = NonScalingStrokePath(path_.get(), non_scaling_transform); - - return use_path->StrokeContains(non_scaling_transform.MapPoint(point), - stroke_data); + // The reason is similar to the above code about HasPath(). + if (!rare_data_) + UpdateNonScalingStrokeData(); + return NonScalingStrokePath().StrokeContains( + NonScalingStrokeTransform().MapPoint(point), stroke_data); } return path_->StrokeContains(point, stroke_data); @@ -243,7 +263,10 @@ void LayoutSVGShape::UpdateLayout() { // UpdateShapeFromElement() also updates the object & stroke bounds - which // feeds into the visual rect - so we need to call it for both the // shape-update and the bounds-update flag. - if (needs_shape_update_ || needs_boundaries_update_) { + // We also need to update stroke bounds if HasNonScalingStroke() because the + // shape may be affected by ancestor transforms. + if (needs_shape_update_ || needs_boundaries_update_ || + HasNonScalingStroke()) { FloatRect old_object_bounding_box = ObjectBoundingBox(); UpdateShapeFromElement(); if (old_object_bounding_box != ObjectBoundingBox()) { @@ -292,21 +315,9 @@ void LayoutSVGShape::UpdateLayout() { ClearNeedsLayout(); } -Path* LayoutSVGShape::NonScalingStrokePath( - const Path* path, - const AffineTransform& stroke_transform) const { - LayoutSVGShapeRareData& rare_data = EnsureRareData(); - if (!rare_data.cached_non_scaling_stroke_path_.IsEmpty() && - stroke_transform == rare_data.cached_non_scaling_stroke_transform_) - return &rare_data.cached_non_scaling_stroke_path_; - - rare_data.cached_non_scaling_stroke_path_ = *path; - rare_data.cached_non_scaling_stroke_path_.Transform(stroke_transform); - rare_data.cached_non_scaling_stroke_transform_ = stroke_transform; - return &rare_data.cached_non_scaling_stroke_path_; -} +void LayoutSVGShape::UpdateNonScalingStrokeData() { + DCHECK(HasNonScalingStroke()); -AffineTransform LayoutSVGShape::NonScalingStrokeTransform() const { // Compute the CTM to the SVG root. This should probably be the CTM all the // way to the "canvas" of the page ("host" coordinate system), but with our // current approach of applying/painting non-scaling-stroke, that can break in @@ -320,7 +331,15 @@ AffineTransform LayoutSVGShape::NonScalingStrokeTransform() const { // here. t.SetE(0); t.SetF(0); - return t; + + auto& rare_data = EnsureRareData(); + if (rare_data.non_scaling_stroke_transform_ != t) { + SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kStyle); + rare_data.non_scaling_stroke_transform_ = t; + } + + rare_data.non_scaling_stroke_path_ = *path_; + rare_data.non_scaling_stroke_path_.Transform(t); } void LayoutSVGShape::Paint(const PaintInfo& paint_info, @@ -400,12 +419,11 @@ FloatRect LayoutSVGShape::CalculateStrokeBoundingBox() const { SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(), *this, DashScaleFactor()); if (HasNonScalingStroke()) { - AffineTransform non_scaling_transform = NonScalingStrokeTransform(); + const auto& non_scaling_transform = NonScalingStrokeTransform(); if (non_scaling_transform.IsInvertible()) { - Path* use_path = - NonScalingStrokePath(path_.get(), non_scaling_transform); + const auto& non_scaling_stroke = NonScalingStrokePath(); FloatRect stroke_bounding_rect = - use_path->StrokeBoundingRect(stroke_data); + non_scaling_stroke.StrokeBoundingRect(stroke_data); stroke_bounding_rect = non_scaling_transform.Inverse().MapRect(stroke_bounding_rect); stroke_bounding_box.Unite(stroke_bounding_rect); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h index cd1d8279bb5..d2bdfa17f82 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h @@ -51,8 +51,8 @@ struct LayoutSVGShapeRareData { public: LayoutSVGShapeRareData() = default; - Path cached_non_scaling_stroke_path_; - AffineTransform cached_non_scaling_stroke_transform_; + Path non_scaling_stroke_path_; + AffineTransform non_scaling_stroke_transform_; DISALLOW_COPY_AND_ASSIGN(LayoutSVGShapeRareData); }; @@ -88,8 +88,17 @@ class LayoutSVGShape : public LayoutSVGModelObject { bool HasNonScalingStroke() const { return Style()->SvgStyle().VectorEffect() == VE_NON_SCALING_STROKE; } - Path* NonScalingStrokePath(const Path*, const AffineTransform&) const; - AffineTransform NonScalingStrokeTransform() const; + const Path& NonScalingStrokePath() const { + DCHECK(HasNonScalingStroke()); + DCHECK(rare_data_); + return rare_data_->non_scaling_stroke_path_; + } + const AffineTransform& NonScalingStrokeTransform() const { + DCHECK(HasNonScalingStroke()); + DCHECK(rare_data_); + return rare_data_->non_scaling_stroke_transform_; + } + AffineTransform LocalSVGTransform() const final { return local_transform_; } virtual const Vector<MarkerPosition>* MarkerPositions() const { @@ -107,6 +116,9 @@ class LayoutSVGShape : public LayoutSVGModelObject { const char* GetName() const override { return "LayoutSVGShape"; } protected: + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; + void WillBeDestroyed() override; + float VisualRectOutsetForRasterEffects() const override; void ClearPath() { path_.reset(); } @@ -155,6 +167,7 @@ class LayoutSVGShape : public LayoutSVGModelObject { FloatRect StrokeBoundingBox() const final { return stroke_bounding_box_; } FloatRect CalculateObjectBoundingBox() const; FloatRect CalculateStrokeBoundingBox() const; + void UpdateNonScalingStrokeData(); bool UpdateLocalTransform(); private: diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc index c1b5acc44e7..5a85c7afdf3 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc @@ -38,6 +38,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h" #include "third_party/blink/renderer/core/paint/svg_text_painter.h" @@ -71,9 +72,15 @@ LayoutSVGText::~LayoutSVGText() { DCHECK(descendant_text_nodes_.IsEmpty()); } +void LayoutSVGText::StyleDidChange(StyleDifference diff, + const ComputedStyle* old_style) { + LayoutSVGBlock::StyleDidChange(diff, old_style); + SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef()); +} + void LayoutSVGText::WillBeDestroyed() { descendant_text_nodes_.clear(); - + SVGResources::ClearPaints(*GetElement(), Style()); LayoutSVGBlock::WillBeDestroyed(); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h index e22ee07774a..1523d8f454c 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h @@ -85,6 +85,8 @@ class LayoutSVGText final : public LayoutSVGBlock { void AddChild(LayoutObject* child, LayoutObject* before_child = nullptr) override; void RemoveChild(LayoutObject*) override; + + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void WillBeDestroyed() override; RootInlineBox* CreateRootInlineBox() override; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc index 380077331fa..5319bbbf394 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc @@ -199,7 +199,8 @@ bool SVGRootInlineBox::NodeAtPoint(HitTestResult& result, const LayoutPoint& accumulated_offset, LayoutUnit line_top, LayoutUnit line_bottom) { - for (InlineBox* leaf = FirstLeafChild(); leaf; leaf = leaf->NextLeafChild()) { + // Iterate the text boxes in reverse so that the top-most node will be considered first. + for (InlineBox* leaf = LastLeafChild(); leaf; leaf = leaf->PrevLeafChild()) { if (!leaf->IsSVGInlineTextBox()) continue; if (leaf->NodeAtPoint(result, location_in_container, accumulated_offset, diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc index 10e7e46daad..45a7d90fa47 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc @@ -40,8 +40,8 @@ #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/svg/svg_element.h" -#include "third_party/blink/renderer/platform/geometry/transform_state.h" #include "third_party/blink/renderer/platform/graphics/stroke_data.h" +#include "third_party/blink/renderer/platform/transforms/transform_state.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc index 6f0461021d1..8dbf9a61289 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc @@ -86,13 +86,13 @@ class TextStreamSeparator { : separator_(s), need_to_separate_(false) {} private: - friend TextStream& operator<<(TextStream&, TextStreamSeparator&); + friend WTF::TextStream& operator<<(WTF::TextStream&, TextStreamSeparator&); String separator_; bool need_to_separate_; }; -TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep) { +WTF::TextStream& operator<<(WTF::TextStream& ts, TextStreamSeparator& sep) { if (sep.need_to_separate_) ts << sep.separator_; else @@ -101,28 +101,42 @@ TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep) { } template <typename ValueType> -static void WriteNameValuePair(TextStream& ts, +static void WriteNameValuePair(WTF::TextStream& ts, const char* name, ValueType value) { ts << " [" << name << "=" << value << "]"; } +static void WriteSVGResourceIfNotNull(WTF::TextStream& ts, + const char* name, + const StyleSVGResource* value, + TreeScope& tree_scope) { + if (!value) + return; + AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( + value->Url(), tree_scope); + WriteNameValuePair(ts, name, id); +} + template <typename ValueType> -static void WriteNameAndQuotedValue(TextStream& ts, +static void WriteNameAndQuotedValue(WTF::TextStream& ts, const char* name, ValueType value) { ts << " [" << name << "=\"" << value << "\"]"; } -static void WriteIfNotEmpty(TextStream& ts, - const char* name, - const String& value) { - if (!value.IsEmpty()) - WriteNameValuePair(ts, name, value); +static void WriteQuotedSVGResource(WTF::TextStream& ts, + const char* name, + const StyleSVGResource* value, + TreeScope& tree_scope) { + DCHECK(value); + AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( + value->Url(), tree_scope); + WriteNameAndQuotedValue(ts, name, id); } template <typename ValueType> -static void WriteIfNotDefault(TextStream& ts, +static void WriteIfNotDefault(WTF::TextStream& ts, const char* name, ValueType value, ValueType default_value) { @@ -130,7 +144,8 @@ static void WriteIfNotDefault(TextStream& ts, WriteNameValuePair(ts, name, value); } -TextStream& operator<<(TextStream& ts, const AffineTransform& transform) { +WTF::TextStream& operator<<(WTF::TextStream& ts, + const AffineTransform& transform) { if (transform.IsIdentity()) { ts << "identity"; } else { @@ -142,7 +157,7 @@ TextStream& operator<<(TextStream& ts, const AffineTransform& transform) { return ts; } -static TextStream& operator<<(TextStream& ts, const WindRule rule) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, const WindRule rule) { switch (rule) { case RULE_NONZERO: ts << "NON-ZERO"; @@ -174,39 +189,26 @@ String SVGEnumerationToString(Enum value) { } // namespace -static TextStream& operator<<(TextStream& ts, - const SVGUnitTypes::SVGUnitType& unit_type) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const SVGUnitTypes::SVGUnitType& unit_type) { ts << SVGEnumerationToString<SVGUnitTypes::SVGUnitType>(unit_type); return ts; } -static TextStream& operator<<(TextStream& ts, - const SVGMarkerUnitsType& marker_unit) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const SVGMarkerUnitsType& marker_unit) { ts << SVGEnumerationToString<SVGMarkerUnitsType>(marker_unit); return ts; } -static TextStream& operator<<(TextStream& ts, - const SVGMarkerOrientType& orient_type) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const SVGMarkerOrientType& orient_type) { ts << SVGEnumerationToString<SVGMarkerOrientType>(orient_type); return ts; } -// FIXME: Maybe this should be in DashArray.cpp -static TextStream& operator<<(TextStream& ts, const DashArray& a) { - ts << "{"; - DashArray::const_iterator end = a.end(); - for (DashArray::const_iterator it = a.begin(); it != end; ++it) { - if (it != a.begin()) - ts << ", "; - ts << *it; - } - ts << "}"; - return ts; -} - // FIXME: Maybe this should be in GraphicsTypes.cpp -static TextStream& operator<<(TextStream& ts, LineCap style) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, LineCap style) { switch (style) { case kButtCap: ts << "BUTT"; @@ -222,7 +224,7 @@ static TextStream& operator<<(TextStream& ts, LineCap style) { } // FIXME: Maybe this should be in GraphicsTypes.cpp -static TextStream& operator<<(TextStream& ts, LineJoin style) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, LineJoin style) { switch (style) { case kMiterJoin: ts << "MITER"; @@ -237,13 +239,14 @@ static TextStream& operator<<(TextStream& ts, LineJoin style) { return ts; } -static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const SVGSpreadMethodType& type) { ts << SVGEnumerationToString<SVGSpreadMethodType>(type).UpperASCII(); return ts; } static void WriteSVGPaintingResource( - TextStream& ts, + WTF::TextStream& ts, const SVGPaintDescription& paint_description) { DCHECK(paint_description.is_valid); if (!paint_description.resource) { @@ -268,7 +271,7 @@ static void WriteSVGPaintingResource( ts << " [id=\"" << element->GetIdAttribute() << "\"]"; } -static void WriteStyle(TextStream& ts, const LayoutObject& object) { +static void WriteStyle(WTF::TextStream& ts, const LayoutObject& object) { const ComputedStyle& style = object.StyleRef(); const SVGComputedStyle& svg_style = style.SvgStyle(); @@ -326,19 +329,24 @@ static void WriteStyle(TextStream& ts, const LayoutObject& object) { WriteIfNotDefault(ts, "clip rule", svg_style.ClipRule(), RULE_NONZERO); } - WriteIfNotEmpty(ts, "start marker", svg_style.MarkerStartResource()); - WriteIfNotEmpty(ts, "middle marker", svg_style.MarkerMidResource()); - WriteIfNotEmpty(ts, "end marker", svg_style.MarkerEndResource()); + TreeScope& tree_scope = object.GetDocument(); + WriteSVGResourceIfNotNull(ts, "start marker", svg_style.MarkerStartResource(), + tree_scope); + WriteSVGResourceIfNotNull(ts, "middle marker", svg_style.MarkerMidResource(), + tree_scope); + WriteSVGResourceIfNotNull(ts, "end marker", svg_style.MarkerEndResource(), + tree_scope); } -static TextStream& WritePositionAndStyle(TextStream& ts, - const LayoutObject& object) { +static WTF::TextStream& WritePositionAndStyle(WTF::TextStream& ts, + const LayoutObject& object) { ts << " " << object.ObjectBoundingBox(); WriteStyle(ts, object); return ts; } -static TextStream& operator<<(TextStream& ts, const LayoutSVGShape& shape) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const LayoutSVGShape& shape) { WritePositionAndStyle(ts, shape); SVGElement* svg_element = shape.GetElement(); @@ -410,13 +418,15 @@ static TextStream& operator<<(TextStream& ts, const LayoutSVGShape& shape) { return ts; } -static TextStream& operator<<(TextStream& ts, const LayoutSVGRoot& root) { +static WTF::TextStream& operator<<(WTF::TextStream& ts, + const LayoutSVGRoot& root) { ts << " " << root.FrameRect(); WriteStyle(ts, root); return ts; } -static void WriteLayoutSVGTextBox(TextStream& ts, const LayoutSVGText& text) { +static void WriteLayoutSVGTextBox(WTF::TextStream& ts, + const LayoutSVGText& text) { SVGRootInlineBox* box = ToSVGRootInlineBox(text.FirstRootBox()); if (!box) return; @@ -433,7 +443,7 @@ static void WriteLayoutSVGTextBox(TextStream& ts, const LayoutSVGText& text) { } } -static inline void WriteSVGInlineTextBox(TextStream& ts, +static inline void WriteSVGInlineTextBox(WTF::TextStream& ts, SVGInlineTextBox* text_box, int indent) { Vector<SVGTextFragment>& fragments = text_box->TextFragments(); @@ -498,7 +508,7 @@ static inline void WriteSVGInlineTextBox(TextStream& ts, } } -static inline void WriteSVGInlineTextBoxes(TextStream& ts, +static inline void WriteSVGInlineTextBoxes(WTF::TextStream& ts, const LayoutText& text, int indent) { for (InlineTextBox* box : text.TextBoxes()) { @@ -509,7 +519,7 @@ static inline void WriteSVGInlineTextBoxes(TextStream& ts, } } -static void WriteStandardPrefix(TextStream& ts, +static void WriteStandardPrefix(WTF::TextStream& ts, const LayoutObject& object, int indent) { WriteIndent(ts, indent); @@ -519,7 +529,7 @@ static void WriteStandardPrefix(TextStream& ts, ts << " {" << object.GetNode()->nodeName() << "}"; } -static void WriteChildren(TextStream& ts, +static void WriteChildren(WTF::TextStream& ts, const LayoutObject& object, int indent) { for (LayoutObject* child = object.SlowFirstChild(); child; @@ -528,7 +538,7 @@ static void WriteChildren(TextStream& ts, } static inline void WriteCommonGradientProperties( - TextStream& ts, + WTF::TextStream& ts, const GradientAttributes& attrs) { WriteNameValuePair(ts, "gradientUnits", attrs.GradientUnits()); @@ -546,7 +556,7 @@ static inline void WriteCommonGradientProperties( } } -void WriteSVGResourceContainer(TextStream& ts, +void WriteSVGResourceContainer(WTF::TextStream& ts, const LayoutObject& object, int indent) { WriteStandardPrefix(ts, object, indent); @@ -652,7 +662,7 @@ void WriteSVGResourceContainer(TextStream& ts, WriteChildren(ts, object, indent); } -void WriteSVGContainer(TextStream& ts, +void WriteSVGContainer(WTF::TextStream& ts, const LayoutObject& container, int indent) { // Currently LayoutSVGResourceFilterPrimitive has no meaningful output. @@ -665,13 +675,13 @@ void WriteSVGContainer(TextStream& ts, WriteChildren(ts, container, indent); } -void Write(TextStream& ts, const LayoutSVGRoot& root, int indent) { +void Write(WTF::TextStream& ts, const LayoutSVGRoot& root, int indent) { WriteStandardPrefix(ts, root, indent); ts << root << "\n"; WriteChildren(ts, root, indent); } -void WriteSVGText(TextStream& ts, const LayoutSVGText& text, int indent) { +void WriteSVGText(WTF::TextStream& ts, const LayoutSVGText& text, int indent) { WriteStandardPrefix(ts, text, indent); WritePositionAndStyle(ts, text); WriteLayoutSVGTextBox(ts, text); @@ -680,7 +690,9 @@ void WriteSVGText(TextStream& ts, const LayoutSVGText& text, int indent) { WriteChildren(ts, text, indent); } -void WriteSVGInline(TextStream& ts, const LayoutSVGInline& text, int indent) { +void WriteSVGInline(WTF::TextStream& ts, + const LayoutSVGInline& text, + int indent) { WriteStandardPrefix(ts, text, indent); WritePositionAndStyle(ts, text); ts << "\n"; @@ -688,7 +700,7 @@ void WriteSVGInline(TextStream& ts, const LayoutSVGInline& text, int indent) { WriteChildren(ts, text, indent); } -void WriteSVGInlineText(TextStream& ts, +void WriteSVGInlineText(WTF::TextStream& ts, const LayoutSVGInlineText& text, int indent) { WriteStandardPrefix(ts, text, indent); @@ -698,20 +710,24 @@ void WriteSVGInlineText(TextStream& ts, WriteSVGInlineTextBoxes(ts, text, indent); } -void WriteSVGImage(TextStream& ts, const LayoutSVGImage& image, int indent) { +void WriteSVGImage(WTF::TextStream& ts, + const LayoutSVGImage& image, + int indent) { WriteStandardPrefix(ts, image, indent); WritePositionAndStyle(ts, image); ts << "\n"; WriteResources(ts, image, indent); } -void Write(TextStream& ts, const LayoutSVGShape& shape, int indent) { +void Write(WTF::TextStream& ts, const LayoutSVGShape& shape, int indent) { WriteStandardPrefix(ts, shape, indent); ts << shape << "\n"; WriteResources(ts, shape, indent); } -void WriteResources(TextStream& ts, const LayoutObject& object, int indent) { +void WriteResources(WTF::TextStream& ts, + const LayoutObject& object, + int indent) { SVGResources* resources = SVGResourcesCache::CachedResourcesForLayoutObject(object); if (!resources) @@ -721,7 +737,8 @@ void WriteResources(TextStream& ts, const LayoutObject& object, int indent) { if (LayoutSVGResourceMasker* masker = resources->Masker()) { WriteIndent(ts, indent); ts << " "; - WriteNameAndQuotedValue(ts, "masker", style.SvgStyle().MaskerResource()); + WriteQuotedSVGResource(ts, "masker", style.SvgStyle().MaskerResource(), + tree_scope); ts << " "; WriteStandardPrefix(ts, *masker, 0); ts << " " << masker->ResourceBoundingBox(&object) << "\n"; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h index 25f79408e05..c322a891e29 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h @@ -26,7 +26,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_SVG_LAYOUT_TREE_AS_TEXT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_SVG_LAYOUT_TREE_AS_TEXT_H_ -#include "third_party/blink/renderer/platform/text/text_stream.h" +#include "third_party/blink/renderer/platform/wtf/text/text_stream.h" namespace blink { @@ -39,15 +39,19 @@ class LayoutSVGRoot; class LayoutSVGText; // functions used by the main LayoutTreeAsText code -void Write(TextStream&, const LayoutSVGShape&, int indent); -void Write(TextStream&, const LayoutSVGRoot&, int indent); -void WriteSVGResourceContainer(TextStream&, const LayoutObject&, int indent); -void WriteSVGContainer(TextStream&, const LayoutObject&, int indent); -void WriteSVGImage(TextStream&, const LayoutSVGImage&, int indent); -void WriteSVGInlineText(TextStream&, const LayoutSVGInlineText&, int indent); -void WriteSVGText(TextStream&, const LayoutSVGText&, int indent); -void WriteSVGInline(TextStream&, const LayoutSVGInline&, int indent); -void WriteResources(TextStream&, const LayoutObject&, int indent); +void Write(WTF::TextStream&, const LayoutSVGShape&, int indent); +void Write(WTF::TextStream&, const LayoutSVGRoot&, int indent); +void WriteSVGResourceContainer(WTF::TextStream&, + const LayoutObject&, + int indent); +void WriteSVGContainer(WTF::TextStream&, const LayoutObject&, int indent); +void WriteSVGImage(WTF::TextStream&, const LayoutSVGImage&, int indent); +void WriteSVGInlineText(WTF::TextStream&, + const LayoutSVGInlineText&, + int indent); +void WriteSVGText(WTF::TextStream&, const LayoutSVGText&, int indent); +void WriteSVGInline(WTF::TextStream&, const LayoutSVGInline&, int indent); +void WriteResources(WTF::TextStream&, const LayoutObject&, int indent); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc index 32789c4aa5c..b0c868405df 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc @@ -27,6 +27,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/svg/svg_pattern_element.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" @@ -44,6 +45,10 @@ using namespace SVGNames; SVGResources::SVGResources() : linked_resource_(nullptr) {} +SVGResourceClient* SVGResources::GetClient(const LayoutObject& object) { + return ToSVGElement(object.GetNode())->GetSVGResourceClient(); +} + static HashSet<AtomicString>& ClipperFilterMaskerTags() { DEFINE_STATIC_LOCAL( HashSet<AtomicString>, tag_list, @@ -113,28 +118,24 @@ bool IsResourceOfType<LayoutSVGResourcePaintServer>( return container->IsSVGPaintServer(); } -template <> -bool IsResourceOfType<LayoutSVGResourceContainer>( - LayoutSVGResourceContainer* container) { - return true; -} - template <typename ContainerType> -ContainerType* AttachToResource(SVGTreeScopeResources& tree_scope_resources, - const AtomicString& id, - SVGElement& element) { - LocalSVGResource* resource = tree_scope_resources.ResourceForId(id); +ContainerType* CastResource(SVGResource* resource) { if (!resource) return nullptr; if (LayoutSVGResourceContainer* container = resource->ResourceContainer()) { if (IsResourceOfType<ContainerType>(container)) return static_cast<ContainerType*>(container); } - resource->AddWatch(element); return nullptr; } + +template <typename ContainerType> +ContainerType* CastResource(StyleSVGResource& style_resource) { + return CastResource<ContainerType>(style_resource.Resource()); } +} // namespace + bool SVGResources::HasResourceData() const { return clipper_filter_masker_data_ || marker_data_ || fill_stroke_data_ || linked_resource_; @@ -160,10 +161,6 @@ std::unique_ptr<SVGResources> SVGResources::BuildResources( const AtomicString& tag_name = element.localName(); DCHECK(!tag_name.IsNull()); - TreeScope& tree_scope = element.TreeScopeForIdResolution(); - SVGTreeScopeResources& tree_scope_resources = - tree_scope.EnsureSVGTreeScopedResources(); - const SVGComputedStyle& style = computed_style.SvgStyle(); std::unique_ptr<SVGResources> resources; @@ -173,11 +170,9 @@ std::unique_ptr<SVGResources> SVGResources::BuildResources( if (clip_path_operation->GetType() == ClipPathOperation::REFERENCE) { const ReferenceClipPathOperation& clip_path_reference = ToReferenceClipPathOperation(*clip_path_operation); - AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( - clip_path_reference.Url(), tree_scope); EnsureResources(resources).SetClipper( - AttachToResource<LayoutSVGResourceClipper>(tree_scope_resources, id, - element)); + CastResource<LayoutSVGResourceClipper>( + clip_path_reference.Resource())); } } @@ -188,80 +183,60 @@ std::unique_ptr<SVGResources> SVGResources::BuildResources( if (filter_operation.GetType() == FilterOperation::REFERENCE) { const auto& reference_filter_operation = ToReferenceFilterOperation(filter_operation); - AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( - reference_filter_operation.Url(), tree_scope); EnsureResources(resources).SetFilter( - AttachToResource<LayoutSVGResourceFilter>(tree_scope_resources, - id, element)); + CastResource<LayoutSVGResourceFilter>( + reference_filter_operation.Resource())); } } } - if (style.HasMasker()) { + if (StyleSVGResource* masker_resource = style.MaskerResource()) { EnsureResources(resources).SetMasker( - AttachToResource<LayoutSVGResourceMasker>( - tree_scope_resources, style.MaskerResource(), element)); + CastResource<LayoutSVGResourceMasker>(*masker_resource)); } } if (style.HasMarkers() && SupportsMarkers(element)) { - EnsureResources(resources).SetMarkerStart( - AttachToResource<LayoutSVGResourceMarker>( - tree_scope_resources, style.MarkerStartResource(), element)); - EnsureResources(resources).SetMarkerMid( - AttachToResource<LayoutSVGResourceMarker>( - tree_scope_resources, style.MarkerMidResource(), element)); - EnsureResources(resources).SetMarkerEnd( - AttachToResource<LayoutSVGResourceMarker>( - tree_scope_resources, style.MarkerEndResource(), element)); + if (StyleSVGResource* marker_start_resource = style.MarkerStartResource()) { + EnsureResources(resources).SetMarkerStart( + CastResource<LayoutSVGResourceMarker>(*marker_start_resource)); + } + if (StyleSVGResource* marker_mid_resource = style.MarkerMidResource()) { + EnsureResources(resources).SetMarkerMid( + CastResource<LayoutSVGResourceMarker>(*marker_mid_resource)); + } + if (StyleSVGResource* marker_end_resource = style.MarkerEndResource()) { + EnsureResources(resources).SetMarkerEnd( + CastResource<LayoutSVGResourceMarker>(*marker_end_resource)); + } } if (FillAndStrokeTags().Contains(tag_name)) { - if (style.HasFill() && style.FillPaint().HasUrl()) { - AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( - style.FillPaint().GetUrl(), tree_scope); + if (StyleSVGResource* fill_resource = style.FillPaint().Resource()) { EnsureResources(resources).SetFill( - AttachToResource<LayoutSVGResourcePaintServer>(tree_scope_resources, - id, element)); + CastResource<LayoutSVGResourcePaintServer>(*fill_resource)); } - if (style.HasStroke() && style.StrokePaint().HasUrl()) { - AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( - style.StrokePaint().GetUrl(), tree_scope); + if (StyleSVGResource* stroke_resource = style.StrokePaint().Resource()) { EnsureResources(resources).SetStroke( - AttachToResource<LayoutSVGResourcePaintServer>(tree_scope_resources, - id, element)); + CastResource<LayoutSVGResourcePaintServer>(*stroke_resource)); } } if (auto* pattern = ToSVGPatternElementOrNull(element)) { - AtomicString id = SVGURIReference::FragmentIdentifierFromIRIString( - pattern->HrefString(), tree_scope); - EnsureResources(resources).SetLinkedResource( - AttachToResource<LayoutSVGResourceContainer>(tree_scope_resources, id, - element)); + const SVGPatternElement* directly_referenced_pattern = + pattern->ReferencedElement(); + if (directly_referenced_pattern) { + EnsureResources(resources).SetLinkedResource( + ToLayoutSVGResourceContainerOrNull( + directly_referenced_pattern->GetLayoutObject())); + } } return (!resources || !resources->HasResourceData()) ? nullptr : std::move(resources); } -void SVGResources::RemoveUnreferencedResources(const LayoutObject& object) { - SVGTreeScopeResources& tree_scope_resources = - ToSVGElement(*object.GetNode()) - .TreeScopeForIdResolution() - .EnsureSVGTreeScopedResources(); - tree_scope_resources.RemoveUnreferencedResources(); -} - -void SVGResources::RemoveWatchesForElement(Element& element) { - SECURITY_DCHECK(element.IsSVGElement()); - SVGElement& svg_element = ToSVGElement(element); - SVGTreeScopeResources& tree_scope_resources = - svg_element.TreeScopeForIdResolution().EnsureSVGTreeScopedResources(); - tree_scope_resources.RemoveWatchesForElement(svg_element); -} - void SVGResources::LayoutIfNeeded() { if (clipper_filter_masker_data_) { if (LayoutSVGResourceClipper* clipper = @@ -294,7 +269,7 @@ void SVGResources::LayoutIfNeeded() { } InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( - LayoutObject& client) const { + SVGResourceClient& client) const { if (!clipper_filter_masker_data_) return 0; InvalidationModeMask invalidation_flags = 0; @@ -310,7 +285,7 @@ InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( } InvalidationModeMask SVGResources::RemoveClientFromCache( - LayoutObject& client) const { + SVGResourceClient& client) const { if (!HasResourceData()) return 0; @@ -648,4 +623,145 @@ void SVGResources::Dump(const LayoutObject* object) { } #endif +void SVGResources::UpdateClipPathFilterMask(SVGElement& element, + const ComputedStyle* old_style, + const ComputedStyle& style) { + const bool had_client = element.GetSVGResourceClient(); + if (auto* reference_clip = + ToReferenceClipPathOperationOrNull(style.ClipPath())) + reference_clip->AddClient(element.EnsureSVGResourceClient()); + if (style.HasFilter()) + style.Filter().AddClient(element.EnsureSVGResourceClient()); + if (StyleSVGResource* masker_resource = style.SvgStyle().MaskerResource()) + masker_resource->AddClient(element.EnsureSVGResourceClient()); + if (had_client) + ClearClipPathFilterMask(element, old_style); +} + +void SVGResources::ClearClipPathFilterMask(SVGElement& element, + const ComputedStyle* style) { + if (!style) + return; + SVGResourceClient* client = element.GetSVGResourceClient(); + if (!client) + return; + if (auto* old_reference_clip = + ToReferenceClipPathOperationOrNull(style->ClipPath())) + old_reference_clip->RemoveClient(*client); + if (style->HasFilter()) + style->Filter().RemoveClient(*client); + if (StyleSVGResource* masker_resource = style->SvgStyle().MaskerResource()) + masker_resource->RemoveClient(*client); +} + +void SVGResources::UpdatePaints(SVGElement& element, + const ComputedStyle* old_style, + const ComputedStyle& style) { + const bool had_client = element.GetSVGResourceClient(); + const SVGComputedStyle& svg_style = style.SvgStyle(); + if (StyleSVGResource* paint_resource = svg_style.FillPaint().Resource()) + paint_resource->AddClient(element.EnsureSVGResourceClient()); + if (StyleSVGResource* paint_resource = svg_style.StrokePaint().Resource()) + paint_resource->AddClient(element.EnsureSVGResourceClient()); + if (had_client) + ClearPaints(element, old_style); +} + +void SVGResources::ClearPaints(SVGElement& element, + const ComputedStyle* style) { + if (!style) + return; + SVGResourceClient* client = element.GetSVGResourceClient(); + if (!client) + return; + const SVGComputedStyle& old_svg_style = style->SvgStyle(); + if (StyleSVGResource* paint_resource = old_svg_style.FillPaint().Resource()) + paint_resource->RemoveClient(*client); + if (StyleSVGResource* paint_resource = old_svg_style.StrokePaint().Resource()) + paint_resource->RemoveClient(*client); +} + +void SVGResources::UpdateMarkers(SVGElement& element, + const ComputedStyle* old_style, + const ComputedStyle& style) { + const bool had_client = element.GetSVGResourceClient(); + const SVGComputedStyle& svg_style = style.SvgStyle(); + if (StyleSVGResource* marker_resource = svg_style.MarkerStartResource()) + marker_resource->AddClient(element.EnsureSVGResourceClient()); + if (StyleSVGResource* marker_resource = svg_style.MarkerMidResource()) + marker_resource->AddClient(element.EnsureSVGResourceClient()); + if (StyleSVGResource* marker_resource = svg_style.MarkerEndResource()) + marker_resource->AddClient(element.EnsureSVGResourceClient()); + if (had_client) + ClearMarkers(element, old_style); +} + +void SVGResources::ClearMarkers(SVGElement& element, + const ComputedStyle* style) { + if (!style) + return; + SVGResourceClient* client = element.GetSVGResourceClient(); + if (!client) + return; + const SVGComputedStyle& old_svg_style = style->SvgStyle(); + if (StyleSVGResource* marker_resource = old_svg_style.MarkerStartResource()) + marker_resource->RemoveClient(*client); + if (StyleSVGResource* marker_resource = old_svg_style.MarkerMidResource()) + marker_resource->RemoveClient(*client); + if (StyleSVGResource* marker_resource = old_svg_style.MarkerEndResource()) + marker_resource->RemoveClient(*client); +} + +SVGElementResourceClient::SVGElementResourceClient(SVGElement* element) + : element_(element) {} + +void SVGElementResourceClient::ResourceContentChanged( + InvalidationModeMask invalidation_mask) { + LayoutObject* layout_object = element_->GetLayoutObject(); + if (!layout_object) + return; + bool mark_for_invalidation = + invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation; + if (layout_object->IsSVGResourceContainer()) { + ToLayoutSVGResourceContainer(layout_object) + ->RemoveAllClientsFromCache(mark_for_invalidation); + return; + } + + if (mark_for_invalidation) { + LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object, + invalidation_mask); + } + + // Special case for filter invalidation. + if (invalidation_mask & SVGResourceClient::kSkipAncestorInvalidation) + return; + + bool needs_layout = + invalidation_mask & SVGResourceClient::kLayoutInvalidation; + LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( + *layout_object, needs_layout); +} + +void SVGElementResourceClient::ResourceElementChanged() { + if (LayoutObject* layout_object = element_->GetLayoutObject()) + SVGResourcesCache::ResourceReferenceChanged(*layout_object); +} + +void SVGElementResourceClient::ResourceDestroyed( + LayoutSVGResourceContainer* resource) { + LayoutObject* layout_object = element_->GetLayoutObject(); + if (!layout_object) + return; + SVGResources* resources = + SVGResourcesCache::CachedResourcesForLayoutObject(*layout_object); + if (resources) + resources->ResourceDestroyed(resource); +} + +void SVGElementResourceClient::Trace(Visitor* visitor) { + visitor->Trace(element_); + SVGResourceClient::Trace(visitor); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h index 8d61dd166fb..a70b4f60af3 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h @@ -25,13 +25,13 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" +#include "third_party/blink/renderer/core/svg/svg_resource_client.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" namespace blink { class ComputedStyle; -class Element; class LayoutObject; class LayoutSVGResourceClipper; class LayoutSVGResourceFilter; @@ -47,11 +47,23 @@ class SVGResources { public: SVGResources(); + static SVGResourceClient* GetClient(const LayoutObject&); + static std::unique_ptr<SVGResources> BuildResources(const LayoutObject&, const ComputedStyle&); - static void RemoveWatchesForElement(Element&); - static void RemoveUnreferencedResources(const LayoutObject&); + static void UpdateClipPathFilterMask(SVGElement&, + const ComputedStyle* old_style, + const ComputedStyle&); + static void ClearClipPathFilterMask(SVGElement&, const ComputedStyle*); + static void UpdatePaints(SVGElement&, + const ComputedStyle* old_style, + const ComputedStyle&); + static void ClearPaints(SVGElement&, const ComputedStyle*); + static void UpdateMarkers(SVGElement&, + const ComputedStyle* old_style, + const ComputedStyle&); + static void ClearMarkers(SVGElement&, const ComputedStyle*); void LayoutIfNeeded(); @@ -98,9 +110,9 @@ class SVGResources { void BuildSetOfResources(HashSet<LayoutSVGResourceContainer*>&); // Methods operating on all cached resources - InvalidationModeMask RemoveClientFromCache(LayoutObject&) const; + InvalidationModeMask RemoveClientFromCache(SVGResourceClient&) const; InvalidationModeMask RemoveClientFromCacheAffectingObjectBounds( - LayoutObject&) const; + SVGResourceClient&) const; void ResourceDestroyed(LayoutSVGResourceContainer*); void ClearReferencesTo(LayoutSVGResourceContainer*); @@ -188,6 +200,24 @@ class SVGResources { DISALLOW_COPY_AND_ASSIGN(SVGResources); }; +class SVGElementResourceClient final + : public GarbageCollected<SVGElementResourceClient>, + public SVGResourceClient { + USING_GARBAGE_COLLECTED_MIXIN(SVGElementResourceClient); + + public: + explicit SVGElementResourceClient(SVGElement*); + + void ResourceContentChanged(InvalidationModeMask) override; + void ResourceElementChanged() override; + void ResourceDestroyed(LayoutSVGResourceContainer*) override; + + void Trace(Visitor*) override; + + private: + Member<SVGElement> element_; +}; + } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc index 517cc361a62..9cc13bbfbb5 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc @@ -50,24 +50,15 @@ void SVGResourcesCache::AddResourcesFromLayoutObject( SVGResources* resources = cache_.Set(&object, std::move(new_resources)).stored_value->value.get(); + // Run cycle-detection _afterwards_, so self-references can be caught as well. HashSet<LayoutSVGResourceContainer*> resource_set; resources->BuildSetOfResources(resource_set); - // Run cycle-detection _afterwards_, so self-references can be caught as well. - { - SVGResourcesCycleSolver solver(object); - for (auto* resource_container : resource_set) { - if (solver.FindCycle(resource_container)) - resources->ClearReferencesTo(resource_container); - } - resource_set.clear(); + SVGResourcesCycleSolver solver(object); + for (auto* resource_container : resource_set) { + if (solver.FindCycle(resource_container)) + resources->ClearReferencesTo(resource_container); } - - // Walk resources and register the layout object as a client of each resource. - resources->BuildSetOfResources(resource_set); - - for (auto* resource_container : resource_set) - resource_container->AddClient(object); } void SVGResourcesCache::RemoveResourcesFromLayoutObject(LayoutObject& object) { @@ -77,19 +68,6 @@ void SVGResourcesCache::RemoveResourcesFromLayoutObject(LayoutObject& object) { // Removal of the resource may cause removal of paint property nodes. object.SetNeedsPaintPropertyUpdate(); - - // Walk resources and unregister the layout object as a client of each - // resource. - HashSet<LayoutSVGResourceContainer*> resource_set; - resources->BuildSetOfResources(resource_set); - - bool did_empty_client_set = false; - for (auto* resource_container : resource_set) - did_empty_client_set |= resource_container->RemoveClient(object); - - // Remove any registrations that became empty after the above. - if (did_empty_client_set) - SVGResources::RemoveUnreferencedResources(object); } static inline SVGResourcesCache& ResourcesCache(Document& document) { @@ -110,8 +88,9 @@ void SVGResourcesCache::ClientLayoutChanged(LayoutObject& object) { // or we have filter resources, which could depend on the layout of children. if (!object.SelfNeedsLayout() && !resources->Filter()) return; + SVGResourceClient* client = SVGResources::GetClient(object); if (InvalidationModeMask invalidation_flags = - resources->RemoveClientFromCache(object)) { + resources->RemoveClientFromCache(*client)) { LayoutSVGResourceContainer::MarkClientForInvalidation(object, invalidation_flags); } @@ -227,15 +206,27 @@ SVGResourcesCache::TemporaryStyleScope::TemporaryStyleScope( const ComputedStyle& temporary_style) : layout_object_(layout_object), original_style_(style), + temporary_style_(temporary_style), styles_are_equal_(style == temporary_style) { + if (styles_are_equal_) + return; + DCHECK(LayoutObjectCanHaveResources(layout_object_)); + SVGElement& element = ToSVGElement(*layout_object_.GetNode()); + SVGResources::UpdatePaints(element, nullptr, temporary_style_); SwitchTo(temporary_style); } -void SVGResourcesCache::TemporaryStyleScope::SwitchTo( - const ComputedStyle& style) { - DCHECK(LayoutObjectCanHaveResources(layout_object_)); +SVGResourcesCache::TemporaryStyleScope::~TemporaryStyleScope() { if (styles_are_equal_) return; + SVGElement& element = ToSVGElement(*layout_object_.GetNode()); + SVGResources::ClearPaints(element, &temporary_style_); + SwitchTo(original_style_); +} + +void SVGResourcesCache::TemporaryStyleScope::SwitchTo( + const ComputedStyle& style) { + DCHECK(!styles_are_equal_); SVGResourcesCache& cache = ResourcesCache(layout_object_.GetDocument()); cache.RemoveResourcesFromLayoutObject(layout_object_); cache.AddResourcesFromLayoutObject(layout_object_, style); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h index ad9bc3a5f11..eec4f65b065 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h @@ -71,13 +71,14 @@ class SVGResourcesCache { TemporaryStyleScope(LayoutObject&, const ComputedStyle& original_style, const ComputedStyle& temporary_style); - ~TemporaryStyleScope() { SwitchTo(original_style_); } + ~TemporaryStyleScope(); private: void SwitchTo(const ComputedStyle&); LayoutObject& layout_object_; const ComputedStyle& original_style_; + const ComputedStyle& temporary_style_; const bool styles_are_equal_; DISALLOW_COPY_AND_ASSIGN(TemporaryStyleScope); }; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc index 690a9d5a35d..83ad1b140c5 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc @@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.h" +#include "base/auto_reset.h" #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h" #include "third_party/blink/renderer/core/layout/api/line_layout_svg_text_path.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h" @@ -30,7 +31,6 @@ #include "third_party/blink/renderer/core/svg/svg_element.h" #include "third_party/blink/renderer/core/svg/svg_length_context.h" #include "third_party/blink/renderer/core/svg/svg_text_content_element.h" -#include "third_party/blink/renderer/platform/wtf/auto_reset.h" namespace blink { @@ -258,8 +258,8 @@ static bool DefinesTextLengthWithSpacing(const InlineFlowBox* start) { void SVGTextLayoutEngine::LayoutCharactersInTextBoxes(InlineFlowBox* start) { bool text_length_spacing_in_effect = text_length_spacing_in_effect_ || DefinesTextLengthWithSpacing(start); - AutoReset<bool> text_length_spacing_scope(&text_length_spacing_in_effect_, - text_length_spacing_in_effect); + base::AutoReset<bool> text_length_spacing_scope( + &text_length_spacing_in_effect_, text_length_spacing_in_effect); for (InlineBox* child = start->FirstChild(); child; child = child->NextOnLine()) { diff --git a/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc b/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc index 68a8028c392..6bf81b4f8ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc @@ -10,7 +10,6 @@ #include "third_party/blink/renderer/core/loader/empty_clients.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" -#include "third_party/blink/renderer/platform/platform_frame_view.h" namespace blink { class TextAutosizerClient : public EmptyChromeClient { @@ -20,7 +19,7 @@ class TextAutosizerClient : public EmptyChromeClient { return value * device_scale_factor_; } IntRect ViewportToScreen(const IntRect& rect, - const PlatformFrameView* view) const override { + const LocalFrameView*) const override { IntRect scaled_rect(rect); scaled_rect.Scale(1 / device_scale_factor_); return scaled_rect; diff --git a/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_perftest.cc b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_perftest.cc new file mode 100644 index 00000000000..600bc28f23c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_perftest.cc @@ -0,0 +1,173 @@ +// 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 "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/layout/layout_embedded_content.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h" +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" +#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" +#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h" + +namespace blink { + +class VisualRectPerfTest : public RenderingTest { + public: + void RunPerfTest(unsigned iteration_count, + const LayoutBoxModelObject& target, + const LayoutBoxModelObject& ancestor, + const LayoutRect& rect); +}; + +void VisualRectPerfTest::RunPerfTest(unsigned iteration_count, + const LayoutBoxModelObject& object, + const LayoutBoxModelObject& ancestor, + const LayoutRect& rect) { + LayoutRect test_rect(rect); + base::TimeTicks start = base::TimeTicks::Now(); + for (unsigned count = 0; count < iteration_count; count++) { + object.MapToVisualRectInAncestorSpace(&ancestor, test_rect); + } + LOG(ERROR) << " Time to run MapToVisualRectInAncestorSpace: " + << (base::TimeTicks::Now() - start).InMilliseconds() << "ms"; + + start = base::TimeTicks::Now(); + size_t total_cache_bytes = 0; + for (unsigned count = 0; count < iteration_count; count++) { + object.MapToVisualRectInAncestorSpace(&ancestor, test_rect, + kUseGeometryMapper); + if (count == 0) { + total_cache_bytes = object.FirstFragment() + .LocalBorderBoxProperties() + .CacheMemoryUsageInBytes(); + } + GeometryMapper::ClearCache(); + } + + LOG(ERROR) + << " Time to run MapToVisualRectInAncestorSpace w/GeometryMapper: " + + << (base::TimeTicks::Now() - start).InMilliseconds() << "ms"; + LOG(ERROR) << " GeometryMapper cache storage size: " << total_cache_bytes + << " bytes"; +} + +TEST_F(VisualRectPerfTest, GeometryMapper) { + SetBodyInnerHTML(R"HTML( + <style> + body { + margin:0; + } + .paintLayer { + position: relative; + } + .transform { + transform: translateX(1px); + } + .target { + position: relative; + width: 100px; + height: 100px; + } + + </style> + <div id=singleDiv class=target></div> + <div> + <div> + <div> + <div> + <div> + <div> + <div> + <div> + <div> + <div> + <div id=nestedDiv class=target></div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer> + <div class=paintLayer + <div class=paintLayer> + <div class=paintLayer> + <div id=nestedPaintLayers class=target></div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class=transform> + <div class=transform> + <div class=transform> + <div class=transform> + <div class=transform> + <div class=transform> + <div class=transform> + <div class=transform + <div class=transform> + <div class=transform> + <div id=nestedTransform class=target></div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + )HTML"); + LayoutView* view = GetDocument().View()->GetLayoutView(); + LayoutRect rect(0, 0, 100, 100); + + unsigned kIterationCount = 1000000; + LOG(ERROR) << "Test with single div:"; + RunPerfTest(kIterationCount, + *ToLayoutBox( + GetDocument().getElementById("singleDiv")->GetLayoutObject()), + *view, rect); + + LOG(ERROR) << "Test with nested div:"; + RunPerfTest(kIterationCount, + *ToLayoutBox( + GetDocument().getElementById("nestedDiv")->GetLayoutObject()), + *view, rect); + + LOG(ERROR) << "Test with div nested under PaintLayers:"; + RunPerfTest( + kIterationCount, + *ToLayoutBox( + GetDocument().getElementById("nestedPaintLayers")->GetLayoutObject()), + *view, rect); + + LOG(ERROR) << "Test with div nested under transforms:"; + RunPerfTest( + kIterationCount, + *ToLayoutBox( + GetDocument().getElementById("nestedTransform")->GetLayoutObject()), + *view, rect); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc index eb8578dc98e..f890185dda6 100644 --- a/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc @@ -104,24 +104,26 @@ class VisualRectMappingTest : public PaintTestConfigurations, // Checks the result of MapToVisualRectInAncestorSpace with and without // geometry mapper. - void CheckMapToVisualRectInAncestorSpace( - LayoutRect rect, - LayoutRect expected, - const LayoutObject* object, - const LayoutBoxModelObject* ancestor) { + void CheckMapToVisualRectInAncestorSpace(LayoutRect rect, + LayoutRect expected, + const LayoutObject* object, + const LayoutBoxModelObject* ancestor, + VisualRectFlags flags, + bool expected_retval) { LayoutRect result(rect); - EXPECT_TRUE(object->MapToVisualRectInAncestorSpace(ancestor, result)); + EXPECT_EQ(expected_retval, + object->MapToVisualRectInAncestorSpace(ancestor, result, flags)); EXPECT_EQ(result, expected); result = rect; - EXPECT_TRUE(object->MapToVisualRectInAncestorSpace(ancestor, result, - kUseGeometryMapper)); + EXPECT_EQ(expected_retval, + object->MapToVisualRectInAncestorSpace( + ancestor, result, + static_cast<VisualRectFlags>(flags | kUseGeometryMapper))); EXPECT_EQ(result, expected); } }; -INSTANTIATE_TEST_CASE_P(All, - VisualRectMappingTest, - testing::ValuesIn(kAllSlimmingPaintTestConfigurations)); +INSTANTIATE_PAINT_TEST_CASE_P(VisualRectMappingTest); TEST_P(VisualRectMappingTest, LayoutText) { SetBodyInnerHTML(R"HTML( @@ -972,8 +974,9 @@ TEST_P(VisualRectMappingTest, FixedContentsInIframe) { while (root_view->GetFrame()->OwnerLayoutObject()) root_view = root_view->GetFrame()->OwnerLayoutObject()->View(); - CheckMapToVisualRectInAncestorSpace( - LayoutRect(0, 0, 400, 300), LayoutRect(0, 0, 400, 300), fixed, root_view); + CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 400, 300), + LayoutRect(0, 0, 400, 300), fixed, + root_view, kDefaultVisualRectFlags, true); ChildDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset( ScrollOffset(0, 50), kProgrammaticScroll); @@ -981,8 +984,9 @@ TEST_P(VisualRectMappingTest, FixedContentsInIframe) { // The fixed element should not scroll so the mapped visual rect should not // have changed. - CheckMapToVisualRectInAncestorSpace( - LayoutRect(0, 0, 400, 300), LayoutRect(0, 0, 400, 300), fixed, root_view); + CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 400, 300), + LayoutRect(0, 0, 400, 300), fixed, + root_view, kDefaultVisualRectFlags, true); } TEST_P(VisualRectMappingTest, FixedContentsWithScrollOffset) { @@ -1000,18 +1004,83 @@ TEST_P(VisualRectMappingTest, FixedContentsWithScrollOffset) { <div id='forcescroll' style='height:1000px;'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); auto* ancestor = ToLayoutBox(GetDocument().getElementById("ancestor")->GetLayoutObject()); auto* fixed = GetDocument().getElementById("fixed")->GetLayoutObject(); CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 400, 300), LayoutRect(0, -10, 400, 300), fixed, - ancestor); + ancestor, kDefaultVisualRectFlags, true); + + GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset( + ScrollOffset(0, 50), kProgrammaticScroll); + GetDocument().View()->UpdateAllLifecyclePhases(); + + // The fixed element does not scroll but the ancestor does which changes the + // visual rect. + CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 400, 300), + LayoutRect(0, 40, 400, 300), fixed, + ancestor, kDefaultVisualRectFlags, true); +} + +TEST_P(VisualRectMappingTest, FixedContentsUnderViewWithScrollOffset) { + GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( + true); + SetBodyInnerHTML(R"HTML( + <style>body { margin:0; } ::-webkit-scrollbar { display:none; }</style> + <div id='fixed' style=' + position:fixed; top:0; left:0; width:400px; height:300px;'> + </div> + <div id='forcescroll' style='height:1000px;'></div> + )HTML"); + + auto* fixed = GetDocument().getElementById("fixed")->GetLayoutObject(); + + CheckMapToVisualRectInAncestorSpace( + LayoutRect(0, 0, 400, 300), LayoutRect(0, 0, 400, 300), fixed, + fixed->View(), kDefaultVisualRectFlags, true); GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset( ScrollOffset(0, 50), kProgrammaticScroll); GetDocument().View()->UpdateAllLifecyclePhases(); + + // Results of mapping to ancestor are in absolute coordinates of the + // ancestor. Therefore a fixed-position element is (reverse) offset by scroll. + CheckMapToVisualRectInAncestorSpace( + LayoutRect(0, 0, 400, 300), LayoutRect(0, 50, 400, 300), fixed, + fixed->View(), kDefaultVisualRectFlags, true); +} + +TEST_P(VisualRectMappingTest, InclusiveIntersect) { + GetDocument().SetBaseURLOverride(KURL("http://test.com")); + SetBodyInnerHTML(R"HTML( + <style>body { margin:0; }</style> + <div id='ancestor' style='position: relative'> + <div style='width: 50px; height: 50px; overflow: hidden'> + <div id='child' style='width: 10px; height: 10px; position: relative; left: 50px'></div> + </div> + </div> + )HTML"); + + auto* ancestor = + ToLayoutBox(GetDocument().getElementById("ancestor")->GetLayoutObject()); + auto* child = + ToLayoutBox(GetDocument().getElementById("child")->GetLayoutObject()); + + CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 10, 10), + LayoutRect(50, 0, 0, 10), child, ancestor, + kEdgeInclusive, true); + + CheckMapToVisualRectInAncestorSpace(LayoutRect(1, 1, 10, 10), LayoutRect(), + child, ancestor, kEdgeInclusive, false); + + CheckMapToVisualRectInAncestorSpace(LayoutRect(1, 1, 10, 10), + LayoutRect(1, 1, 10, 10), child, child, + kEdgeInclusive, true); + + CheckMapToVisualRectInAncestorSpace(LayoutRect(0, 0, 10, 10), LayoutRect(), + child, ancestor, kDefaultVisualRectFlags, + false); } } // namespace blink |