blob: a16fba27a137033d50ce27545f57aca7ff8f613e [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// 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/layout_ruby_column.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/layout/layout_ruby.h"
#include "third_party/blink/renderer/core/layout/layout_ruby_as_block.h"
#include "third_party/blink/renderer/core/layout/layout_ruby_base.h"
#include "third_party/blink/renderer/core/layout/layout_ruby_text.h"
namespace blink {
namespace {
void UpdateRubyBaseStyle(const LayoutObject* child,
ComputedStyleBuilder& builder) {
DCHECK(child->IsRubyBase());
// FIXME: use WEBKIT_CENTER?
builder.SetTextAlign(ETextAlign::kCenter);
builder.SetHasLineIfEmpty(true);
}
} // namespace
LayoutRubyColumn::LayoutRubyColumn() : LayoutNGBlockFlow(nullptr) {
DCHECK(!RuntimeEnabledFeatures::RubyLineBreakableEnabled());
SetInline(true);
SetIsAtomicInlineLevel(true);
}
LayoutRubyColumn::~LayoutRubyColumn() = default;
void LayoutRubyColumn::RemoveLeftoverAnonymousBlock(LayoutBlock*) {
NOT_DESTROYED();
}
bool LayoutRubyColumn::HasRubyText() const {
NOT_DESTROYED();
// The only place where a ruby text can be is in the first position
// Note: As anonymous blocks, ruby columns do not have ':before' or ':after'
// content themselves.
return FirstChild() && FirstChild()->IsRubyText();
}
bool LayoutRubyColumn::HasRubyBase() const {
NOT_DESTROYED();
// The only place where a ruby base can be is in the last position
// Note: As anonymous blocks, ruby columns do not have ':before' or ':after'
// content themselves.
return LastChild() && LastChild()->IsRubyBase();
}
LayoutRubyText* LayoutRubyColumn::RubyText() const {
NOT_DESTROYED();
LayoutObject* child = FirstChild();
// If in future it becomes necessary to support floating or positioned ruby
// text, layout will have to be changed to handle them properly.
DCHECK(!child || !child->IsRubyText() ||
!child->IsFloatingOrOutOfFlowPositioned());
return DynamicTo<LayoutRubyText>(child);
}
LayoutRubyBase* LayoutRubyColumn::RubyBase() const {
NOT_DESTROYED();
return DynamicTo<LayoutRubyBase>(LastChild());
}
LayoutRubyBase& LayoutRubyColumn::EnsureRubyBase() {
NOT_DESTROYED();
if (auto* base = RubyBase()) {
return *base;
}
auto& new_base = CreateRubyBase(*this);
LayoutBlockFlow::AddChild(&new_base);
return new_base;
}
bool LayoutRubyColumn::IsChildAllowed(LayoutObject* child,
const ComputedStyle&) const {
NOT_DESTROYED();
return child->IsRubyText() || child->IsInline();
}
void LayoutRubyColumn::AddChild(LayoutObject* child,
LayoutObject* before_child) {
NOT_DESTROYED();
DCHECK(child);
if (child->IsRubyText()) {
if (!before_child) {
// LayoutRuby has already ascertained that we can add the child here.
DCHECK(!HasRubyText());
// prepend ruby texts as first child
LayoutBlockFlow::AddChild(child, FirstChild());
} else {
NOTREACHED() << before_child;
}
} else if (child->IsRubyBase()) {
DCHECK(!before_child);
DCHECK(!RubyBase());
LayoutBlockFlow::AddChild(child);
} else {
NOTREACHED() << child;
}
}
void LayoutRubyColumn::RemoveChild(LayoutObject* child) {
NOT_DESTROYED();
LayoutBlockFlow::RemoveChild(child);
if (!DocumentBeingDestroyed()) {
DCHECK(child->IsRubyBase() || child->IsRubyText());
if (auto* inline_ruby = DynamicTo<LayoutRuby>(Parent())) {
inline_ruby->DidRemoveChildFromColumn(*child);
} else {
To<LayoutRubyAsBlock>(Parent())->DidRemoveChildFromColumn(*child);
}
// Do nothing here! `this` might be destroyed by RubyContainer::Repair().
}
}
void LayoutRubyColumn::RemoveAllChildren() {
if (auto* text = RubyText()) {
LayoutBlockFlow::RemoveChild(text);
}
if (auto* base = RubyBase()) {
LayoutBlockFlow::RemoveChild(base);
if (base->IsPlaceholder()) {
// This RubyBase was created for a RubyText without a corresponding
// RubyBase. It should be destroyed here.
base->Destroy();
}
}
}
// static
LayoutRubyBase& LayoutRubyColumn::CreateRubyBase(
const LayoutObject& reference) {
auto* layout_object = MakeGarbageCollected<LayoutRubyBase>();
layout_object->SetDocumentForAnonymous(&reference.GetDocument());
ComputedStyleBuilder new_style_builder =
reference.GetDocument()
.GetStyleResolver()
.CreateAnonymousStyleBuilderWithDisplay(reference.StyleRef(),
EDisplay::kBlock);
UpdateRubyBaseStyle(layout_object, new_style_builder);
layout_object->SetStyle(new_style_builder.TakeStyle());
return *layout_object;
}
void LayoutRubyColumn::UpdateAnonymousChildStyle(
const LayoutObject* child,
ComputedStyleBuilder& builder) const {
NOT_DESTROYED();
if (child->IsRubyBase()) {
UpdateRubyBaseStyle(child, builder);
}
}
// static
LayoutRubyColumn& LayoutRubyColumn::Create(
const LayoutObject* parent_ruby,
const LayoutBlock& containing_block) {
DCHECK(parent_ruby);
DCHECK(parent_ruby->IsRuby());
LayoutRubyColumn* column = MakeGarbageCollected<LayoutRubyColumn>();
column->SetDocumentForAnonymous(&parent_ruby->GetDocument());
const ComputedStyle* new_style =
parent_ruby->GetDocument()
.GetStyleResolver()
.CreateAnonymousStyleWithDisplay(parent_ruby->StyleRef(),
EDisplay::kInlineBlock);
column->SetStyle(new_style);
return *column;
}
} // namespace blink